├── doc ├── Protocol_V1.3.docx └── README.md ├── go ├── Godeps │ ├── Readme │ └── Godeps.json ├── vendor │ └── github.com │ │ ├── jacobsa │ │ └── go-serial │ │ │ ├── serial │ │ │ ├── Makefile │ │ │ ├── open_freebsd.go │ │ │ ├── open_linux.go │ │ │ ├── serial.go │ │ │ ├── open_darwin.go │ │ │ └── open_windows.go │ │ │ └── LICENSE │ │ └── golang │ │ └── glog │ │ ├── README │ │ ├── glog_file.go │ │ ├── LICENSE │ │ └── glog.go ├── cmd │ ├── sds011cmd │ │ └── main.go │ └── sds011 │ │ └── main.go └── sds011 │ └── sds011.go ├── .gitignore ├── README.md └── LICENSE /doc/Protocol_V1.3.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryszard/sds011/HEAD/doc/Protocol_V1.3.docx -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | Note: [Protocol_V1.3.docx](./Protocol_V1.3.docx) is owned by Nova Fitness Co., Ltd, I am including it for reference only. -------------------------------------------------------------------------------- /go/Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /go/vendor/github.com/jacobsa/go-serial/serial/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG=github.com/jacobsa/go-serial/serial 4 | GOFILES=\ 5 | serial.go\ 6 | open_$(GOOS).go\ 7 | 8 | include $(GOROOT)/src/Make.pkg 9 | -------------------------------------------------------------------------------- /go/Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/ryszard/sds011/go", 3 | "GoVersion": "go1.8", 4 | "GodepVersion": "v79", 5 | "Packages": [ 6 | "./..." 7 | ], 8 | "Deps": [ 9 | { 10 | "ImportPath": "github.com/golang/glog", 11 | "Rev": "fca8c8854093a154ff1eb580aae10276ad6b1b5f" 12 | }, 13 | { 14 | "ImportPath": "github.com/jacobsa/go-serial/serial", 15 | "Rev": "6f298b3ae27eacbbad51a827dcce21d792fb45e1" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /go/vendor/github.com/jacobsa/go-serial/serial/open_freebsd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package serial 16 | 17 | import "io" 18 | 19 | func openInternal(options OpenOptions) (io.ReadWriteCloser, error) { 20 | return nil, "Not implemented on this OS." 21 | } 22 | -------------------------------------------------------------------------------- /go/cmd/sds011cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | 8 | log "github.com/golang/glog" 9 | "github.com/ryszard/sds011/go/sds011" 10 | ) 11 | 12 | var portPath = flag.String("port_path", "/dev/ttyUSB0", "serial port path") 13 | 14 | func main() { 15 | flag.Parse() 16 | 17 | sensor, err := sds011.New(*portPath) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | defer sensor.Close() 22 | 23 | switch cmd := flag.Arg(0); cmd { 24 | case "cycle": 25 | dutyCycle, err := sensor.Cycle() 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | fmt.Println(dutyCycle) 30 | case "set_cycle": 31 | v, err := strconv.Atoi(flag.Arg(1)) 32 | if err != nil { 33 | log.Fatalf("bad number: %v", flag.Arg(1)) 34 | } 35 | if err := sensor.SetCycle(uint8(v)); err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | default: 40 | log.Errorf("flag.Args: %v", flag.Args()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 3 | *.o 4 | *.a 5 | *.so 6 | 7 | # Folders 8 | _obj 9 | _test 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.test 25 | *.prof 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Icon must end with two \r 32 | Icon 33 | 34 | 35 | # Thumbnails 36 | ._* 37 | 38 | # Files that might appear in the root of a volume 39 | .DocumentRevisions-V100 40 | .fseventsd 41 | .Spotlight-V100 42 | .TemporaryItems 43 | .Trashes 44 | .VolumeIcon.icns 45 | 46 | # Directories potentially created on remote AFP share 47 | .AppleDB 48 | .AppleDesktop 49 | Network Trash Folder 50 | Temporary Items 51 | .apdisk 52 | 53 | *~ 54 | \#*\# 55 | /.emacs.desktop 56 | /.emacs.desktop.lock 57 | *.elc 58 | auto-save-list 59 | tramp 60 | .\#* 61 | 62 | # Org-mode 63 | .org-id-locations 64 | *_archive 65 | 66 | # flymake-mode 67 | *_flymake.* 68 | 69 | # eshell files 70 | /eshell/history 71 | /eshell/lastdir 72 | 73 | # elpa packages 74 | /elpa/ 75 | 76 | # reftex files 77 | *.rel 78 | 79 | # AUCTeX auto folder 80 | /auto/ 81 | 82 | # cask packages 83 | .cask/ 84 | -------------------------------------------------------------------------------- /go/vendor/github.com/golang/glog/README: -------------------------------------------------------------------------------- 1 | glog 2 | ==== 3 | 4 | Leveled execution logs for Go. 5 | 6 | This is an efficient pure Go implementation of leveled logs in the 7 | manner of the open source C++ package 8 | http://code.google.com/p/google-glog 9 | 10 | By binding methods to booleans it is possible to use the log package 11 | without paying the expense of evaluating the arguments to the log. 12 | Through the -vmodule flag, the package also provides fine-grained 13 | control over logging at the file level. 14 | 15 | The comment from glog.go introduces the ideas: 16 | 17 | Package glog implements logging analogous to the Google-internal 18 | C++ INFO/ERROR/V setup. It provides functions Info, Warning, 19 | Error, Fatal, plus formatting variants such as Infof. It 20 | also provides V-style logging controlled by the -v and 21 | -vmodule=file=2 flags. 22 | 23 | Basic examples: 24 | 25 | glog.Info("Prepare to repel boarders") 26 | 27 | glog.Fatalf("Initialization failed: %s", err) 28 | 29 | See the documentation for the V function for an explanation 30 | of these examples: 31 | 32 | if glog.V(2) { 33 | glog.Info("Starting transaction...") 34 | } 35 | 36 | glog.V(2).Infoln("Processed", nItems, "elements") 37 | 38 | 39 | The repository contains an open source version of the log package 40 | used inside Google. The master copy of the source lives inside 41 | Google, not here. The code in this repo is for export only and is not itself 42 | under development. Feature requests will be ignored. 43 | 44 | Send bug reports to golang-nuts@googlegroups.com. 45 | -------------------------------------------------------------------------------- /go/cmd/sds011/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Ryszard Szopa 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // sds011 is a simple reader for the SDS011 Air Quality Sensor. It 16 | // outputs data in TSV to standard output (timestamp formatted 17 | // according to RFC3339, PM2.5 levels, PM10 levels). 18 | package main 19 | 20 | import ( 21 | "flag" 22 | "fmt" 23 | "log" 24 | "os" 25 | "time" 26 | 27 | "github.com/ryszard/sds011/go/sds011" 28 | ) 29 | 30 | var ( 31 | portPath = flag.String("port_path", "/dev/ttyUSB0", "serial port path") 32 | ) 33 | 34 | func init() { 35 | flag.Usage = func() { 36 | fmt.Fprint(os.Stderr, 37 | `sds011 reads data from the SDS011 sensor and sends them to stdout as CSV. 38 | 39 | The columns are: an RFC3339 timestamp, the PM2.5 level, the PM10 level.`) 40 | fmt.Fprintf(os.Stderr, "\n\nUsage of %s:\n", os.Args[0]) 41 | flag.PrintDefaults() 42 | } 43 | } 44 | 45 | func main() { 46 | flag.Parse() 47 | 48 | sensor, err := sds011.New(*portPath) 49 | if err != nil { 50 | log.Fatal(err) 51 | } 52 | defer sensor.Close() 53 | 54 | for { 55 | point, err := sensor.Get() 56 | if err != nil { 57 | log.Printf("ERROR: sensor.Get: %v", err) 58 | continue 59 | } 60 | fmt.Fprintf(os.Stdout, "%v,%v,%v\n", point.Timestamp.Format(time.RFC3339), point.PM25, point.PM10) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nova SDS011 PM sensor serial reader written in Go. 2 | 3 | # Quickstart 4 | 5 | I am assuming that your SDS011 is connected to a Raspberry Pi, like 6 | mine. In my experience it's easier to cross compile the reader on your 7 | laptop and then scp it to the Pi. The fact that Go compiles everything 8 | statically means you don't have to worry about dependencies, which is 9 | nice. 10 | 11 | ``` 12 | $ GOOS=linux GOARCH=arm go build ./go/cmd/sds011 && rsync --progress -v -e ssh sds011 pi@pi: 13 | ``` 14 | 15 | Depending on the version of your Pi, you may need to add `GOARM=6`. 16 | 17 | The output will look something like this: 18 | 19 | ``` 20 | pi@raspberrypi ~ $ ./sds011 21 | 2017-02-24T11:38:44Z,3.2,3.5 22 | 2017-02-24T11:38:46Z,3.2,3.5 23 | 2017-02-24T11:38:49Z,3.1,3.4 24 | 2017-02-24T11:38:50Z,3.1,3.4 25 | 2017-02-24T11:38:53Z,3.2,3.5 26 | 2017-02-24T11:38:54Z,3.2,3.6 27 | 2017-02-24T11:38:56Z,3.2,3.6 28 | ``` 29 | 30 | This is CSV containing first the timestamp (in RFC3339 format), then 31 | the PM2.5 levels, then the PM10 levels. 32 | 33 | # Usage 34 | 35 | As the output of `sds011` is CSV, it should be easy to process. There 36 | are a few things you can do. First, you can save to a file and either 37 | process it right there, or export it for processing. Second, you can 38 | pipe it into a script that will export it. This is what I am doing, as 39 | disk space is scarce on my Pi (one USB port is used by the meter, the 40 | other by the WiFi dongle, so I there's no other storage connected). 41 | 42 | 43 | If you are using the SDS011 for air quality measurements at home, you 44 | probably don't need the from every second. So, in order to increase 45 | its lifespan, you can set it to work in a cycle: sleep for some number 46 | of minutes, wake up for 30s, go back to sleep. To do that, you can use 47 | `sds011cmd`: 48 | 49 | ``` 50 | $ GOOS=linux GOARCH=arm go build ./go/cmd/sds011cmd && rsync --progress -v -e ssh sds011 pi@pi: 51 | $ ssh pi@pi './sds011 set_cycle 30' # set it to 30 minutes, the maximum 52 | $ ssh pi@pi './sds011cmd cycle' 53 | 30 54 | ``` 55 | 56 | The you can read it with `sds011`. Note that you probably should not 57 | be trying to read and sends commands to the sensor at the same time. 58 | 59 | # Advanced 60 | 61 | If you need something more complex, you should be able to write a Go 62 | program. Take a look at 63 | https://godoc.org/github.com/ryszard/sds011/go/sds011 for an idea of what you can do. 64 | 65 | # License 66 | 67 | [Apache 2.0](https://www.tldrlegal.com/l/apache2), please see the file 68 | LICENSE. -------------------------------------------------------------------------------- /go/vendor/github.com/golang/glog/glog_file.go: -------------------------------------------------------------------------------- 1 | // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ 2 | // 3 | // Copyright 2013 Google Inc. All Rights Reserved. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | // File I/O for logs. 18 | 19 | package glog 20 | 21 | import ( 22 | "errors" 23 | "flag" 24 | "fmt" 25 | "os" 26 | "os/user" 27 | "path/filepath" 28 | "strings" 29 | "sync" 30 | "time" 31 | ) 32 | 33 | // MaxSize is the maximum size of a log file in bytes. 34 | var MaxSize uint64 = 1024 * 1024 * 1800 35 | 36 | // logDirs lists the candidate directories for new log files. 37 | var logDirs []string 38 | 39 | // If non-empty, overrides the choice of directory in which to write logs. 40 | // See createLogDirs for the full list of possible destinations. 41 | var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") 42 | 43 | func createLogDirs() { 44 | if *logDir != "" { 45 | logDirs = append(logDirs, *logDir) 46 | } 47 | logDirs = append(logDirs, os.TempDir()) 48 | } 49 | 50 | var ( 51 | pid = os.Getpid() 52 | program = filepath.Base(os.Args[0]) 53 | host = "unknownhost" 54 | userName = "unknownuser" 55 | ) 56 | 57 | func init() { 58 | h, err := os.Hostname() 59 | if err == nil { 60 | host = shortHostname(h) 61 | } 62 | 63 | current, err := user.Current() 64 | if err == nil { 65 | userName = current.Username 66 | } 67 | 68 | // Sanitize userName since it may contain filepath separators on Windows. 69 | userName = strings.Replace(userName, `\`, "_", -1) 70 | } 71 | 72 | // shortHostname returns its argument, truncating at the first period. 73 | // For instance, given "www.google.com" it returns "www". 74 | func shortHostname(hostname string) string { 75 | if i := strings.Index(hostname, "."); i >= 0 { 76 | return hostname[:i] 77 | } 78 | return hostname 79 | } 80 | 81 | // logName returns a new log file name containing tag, with start time t, and 82 | // the name for the symlink for tag. 83 | func logName(tag string, t time.Time) (name, link string) { 84 | name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", 85 | program, 86 | host, 87 | userName, 88 | tag, 89 | t.Year(), 90 | t.Month(), 91 | t.Day(), 92 | t.Hour(), 93 | t.Minute(), 94 | t.Second(), 95 | pid) 96 | return name, program + "." + tag 97 | } 98 | 99 | var onceLogDirs sync.Once 100 | 101 | // create creates a new log file and returns the file and its filename, which 102 | // contains tag ("INFO", "FATAL", etc.) and t. If the file is created 103 | // successfully, create also attempts to update the symlink for that tag, ignoring 104 | // errors. 105 | func create(tag string, t time.Time) (f *os.File, filename string, err error) { 106 | onceLogDirs.Do(createLogDirs) 107 | if len(logDirs) == 0 { 108 | return nil, "", errors.New("log: no log dirs") 109 | } 110 | name, link := logName(tag, t) 111 | var lastErr error 112 | for _, dir := range logDirs { 113 | fname := filepath.Join(dir, name) 114 | f, err := os.Create(fname) 115 | if err == nil { 116 | symlink := filepath.Join(dir, link) 117 | os.Remove(symlink) // ignore err 118 | os.Symlink(name, symlink) // ignore err 119 | return f, fname, nil 120 | } 121 | lastErr = err 122 | } 123 | return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) 124 | } 125 | -------------------------------------------------------------------------------- /go/vendor/github.com/jacobsa/go-serial/serial/open_linux.go: -------------------------------------------------------------------------------- 1 | package serial 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "os" 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | // 12 | // Grab the constants with the following little program, to avoid using cgo: 13 | // 14 | // #include 15 | // #include 16 | // #include 17 | // 18 | // int main(int argc, const char **argv) { 19 | // printf("TCSETS2 = 0x%08X\n", TCSETS2); 20 | // printf("BOTHER = 0x%08X\n", BOTHER); 21 | // printf("NCCS = %d\n", NCCS); 22 | // return 0; 23 | // } 24 | // 25 | const ( 26 | kTCSETS2 = 0x402C542B 27 | kBOTHER = 0x1000 28 | kNCCS = 19 29 | ) 30 | 31 | // 32 | // Types from asm-generic/termbits.h 33 | // 34 | 35 | type cc_t byte 36 | type speed_t uint32 37 | type tcflag_t uint32 38 | type termios2 struct { 39 | c_iflag tcflag_t // input mode flags 40 | c_oflag tcflag_t // output mode flags 41 | c_cflag tcflag_t // control mode flags 42 | c_lflag tcflag_t // local mode flags 43 | c_line cc_t // line discipline 44 | c_cc [kNCCS]cc_t // control characters 45 | c_ispeed speed_t // input speed 46 | c_ospeed speed_t // output speed 47 | } 48 | 49 | // Constants for RS485 operation 50 | 51 | const ( 52 | sER_RS485_ENABLED = (1 << 0) 53 | sER_RS485_RTS_ON_SEND = (1 << 1) 54 | sER_RS485_RTS_AFTER_SEND = (1 << 2) 55 | sER_RS485_RX_DURING_TX = (1 << 4) 56 | tIOCSRS485 = 0x542F 57 | ) 58 | 59 | type serial_rs485 struct { 60 | flags uint32 61 | delay_rts_before_send uint32 62 | delay_rts_after_send uint32 63 | padding [5]uint32 64 | } 65 | 66 | // 67 | // Returns a pointer to an instantiates termios2 struct, based on the given 68 | // OpenOptions. Termios2 is a Linux extension which allows arbitrary baud rates 69 | // to be specified. 70 | // 71 | func makeTermios2(options OpenOptions) (*termios2, error) { 72 | 73 | // Sanity check inter-character timeout and minimum read size options. 74 | 75 | vtime := uint(round(float64(options.InterCharacterTimeout)/100.0) * 100) 76 | vmin := options.MinimumReadSize 77 | 78 | if vmin == 0 && vtime < 100 { 79 | return nil, errors.New("invalid values for InterCharacterTimeout and MinimumReadSize") 80 | } 81 | 82 | if vtime > 25500 { 83 | return nil, errors.New("invalid value for InterCharacterTimeout") 84 | } 85 | 86 | ccOpts := [kNCCS]cc_t{} 87 | ccOpts[syscall.VTIME] = cc_t(vtime / 100) 88 | ccOpts[syscall.VMIN] = cc_t(vmin) 89 | 90 | t2 := &termios2{ 91 | c_cflag: syscall.CLOCAL | syscall.CREAD | kBOTHER, 92 | c_ispeed: speed_t(options.BaudRate), 93 | c_ospeed: speed_t(options.BaudRate), 94 | c_cc: ccOpts, 95 | } 96 | 97 | switch options.StopBits { 98 | case 1: 99 | case 2: 100 | t2.c_cflag |= syscall.CSTOPB 101 | 102 | default: 103 | return nil, errors.New("invalid setting for StopBits") 104 | } 105 | 106 | switch options.ParityMode { 107 | case PARITY_NONE: 108 | case PARITY_ODD: 109 | t2.c_cflag |= syscall.PARENB 110 | t2.c_cflag |= syscall.PARODD 111 | 112 | case PARITY_EVEN: 113 | t2.c_cflag |= syscall.PARENB 114 | 115 | default: 116 | return nil, errors.New("invalid setting for ParityMode") 117 | } 118 | 119 | switch options.DataBits { 120 | case 5: 121 | t2.c_cflag |= syscall.CS5 122 | case 6: 123 | t2.c_cflag |= syscall.CS6 124 | case 7: 125 | t2.c_cflag |= syscall.CS7 126 | case 8: 127 | t2.c_cflag |= syscall.CS8 128 | default: 129 | return nil, errors.New("invalid setting for DataBits") 130 | } 131 | 132 | return t2, nil 133 | } 134 | 135 | func openInternal(options OpenOptions) (io.ReadWriteCloser, error) { 136 | 137 | file, openErr := 138 | os.OpenFile( 139 | options.PortName, 140 | syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 141 | 0600) 142 | if openErr != nil { 143 | return nil, openErr 144 | } 145 | 146 | // Clear the non-blocking flag set above. 147 | nonblockErr := syscall.SetNonblock(int(file.Fd()), false) 148 | if nonblockErr != nil { 149 | return nil, nonblockErr 150 | } 151 | 152 | t2, optErr := makeTermios2(options) 153 | if optErr != nil { 154 | return nil, optErr 155 | } 156 | 157 | r, _, errno := syscall.Syscall( 158 | syscall.SYS_IOCTL, 159 | uintptr(file.Fd()), 160 | uintptr(kTCSETS2), 161 | uintptr(unsafe.Pointer(t2))) 162 | 163 | if errno != 0 { 164 | return nil, os.NewSyscallError("SYS_IOCTL", errno) 165 | } 166 | 167 | if r != 0 { 168 | return nil, errors.New("unknown error from SYS_IOCTL") 169 | } 170 | 171 | if options.Rs485Enable { 172 | rs485 := serial_rs485{ 173 | sER_RS485_ENABLED, 174 | uint32(options.Rs485DelayRtsBeforeSend), 175 | uint32(options.Rs485DelayRtsAfterSend), 176 | [5]uint32{0, 0, 0, 0, 0}, 177 | } 178 | 179 | if options.Rs485RtsHighDuringSend { 180 | rs485.flags |= sER_RS485_RTS_ON_SEND 181 | } 182 | 183 | if options.Rs485RtsHighAfterSend { 184 | rs485.flags |= sER_RS485_RTS_AFTER_SEND 185 | } 186 | 187 | r, _, errno := syscall.Syscall( 188 | syscall.SYS_IOCTL, 189 | uintptr(file.Fd()), 190 | uintptr(tIOCSRS485), 191 | uintptr(unsafe.Pointer(&rs485))) 192 | 193 | if errno != 0 { 194 | return nil, os.NewSyscallError("SYS_IOCTL (RS485)", errno) 195 | } 196 | 197 | if r != 0 { 198 | return nil, errors.New("Unknown error from SYS_IOCTL (RS485)") 199 | } 200 | } 201 | 202 | return file, nil 203 | } 204 | -------------------------------------------------------------------------------- /go/vendor/github.com/jacobsa/go-serial/serial/serial.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package serial provides routines for interacting with serial ports. 16 | // Currently it supports only OS X; see the readme file for details. 17 | 18 | package serial 19 | 20 | import ( 21 | "io" 22 | "math" 23 | ) 24 | 25 | // Valid parity values. 26 | type ParityMode int 27 | 28 | const ( 29 | PARITY_NONE ParityMode = 0 30 | PARITY_ODD ParityMode = 1 31 | PARITY_EVEN ParityMode = 2 32 | ) 33 | 34 | // OpenOptions is the struct containing all of the options necessary for 35 | // opening a serial port. 36 | type OpenOptions struct { 37 | // The name of the port, e.g. "/dev/tty.usbserial-A8008HlV". 38 | PortName string 39 | 40 | // The baud rate for the port. 41 | BaudRate uint 42 | 43 | // The number of data bits per frame. Legal values are 5, 6, 7, and 8. 44 | DataBits uint 45 | 46 | // The number of stop bits per frame. Legal values are 1 and 2. 47 | StopBits uint 48 | 49 | // The type of parity bits to use for the connection. Currently parity errors 50 | // are simply ignored; that is, bytes are delivered to the user no matter 51 | // whether they were received with a parity error or not. 52 | ParityMode ParityMode 53 | 54 | // An inter-character timeout value, in milliseconds, and a minimum number of 55 | // bytes to block for on each read. A call to Read() that otherwise may block 56 | // waiting for more data will return immediately if the specified amount of 57 | // time elapses between successive bytes received from the device or if the 58 | // minimum number of bytes has been exceeded. 59 | // 60 | // Note that the inter-character timeout value may be rounded to the nearest 61 | // 100 ms on some systems, and that behavior is undefined if calls to Read 62 | // supply a buffer whose length is less than the minimum read size. 63 | // 64 | // Behaviors for various settings for these values are described below. For 65 | // more information, see the discussion of VMIN and VTIME here: 66 | // 67 | // http://www.unixwiz.net/techtips/termios-vmin-vtime.html 68 | // 69 | // InterCharacterTimeout = 0 and MinimumReadSize = 0 (the default): 70 | // This arrangement is not legal; you must explicitly set at least one of 71 | // these fields to a positive number. (If MinimumReadSize is zero then 72 | // InterCharacterTimeout must be at least 100.) 73 | // 74 | // InterCharacterTimeout > 0 and MinimumReadSize = 0 75 | // If data is already available on the read queue, it is transferred to 76 | // the caller's buffer and the Read() call returns immediately. 77 | // Otherwise, the call blocks until some data arrives or the 78 | // InterCharacterTimeout milliseconds elapse from the start of the call. 79 | // Note that in this configuration, InterCharacterTimeout must be at 80 | // least 100 ms. 81 | // 82 | // InterCharacterTimeout > 0 and MinimumReadSize > 0 83 | // Calls to Read() return when at least MinimumReadSize bytes are 84 | // available or when InterCharacterTimeout milliseconds elapse between 85 | // received bytes. The inter-character timer is not started until the 86 | // first byte arrives. 87 | // 88 | // InterCharacterTimeout = 0 and MinimumReadSize > 0 89 | // Calls to Read() return only when at least MinimumReadSize bytes are 90 | // available. The inter-character timer is not used. 91 | // 92 | // For windows usage, these options (termios) do not conform well to the 93 | // windows serial port / comms abstractions. Please see the code in 94 | // open_windows setCommTimeouts function for full documentation. 95 | // Summary: 96 | // Setting MinimumReadSize > 0 will cause the serialPort to block until 97 | // until data is available on the port. 98 | // Setting IntercharacterTimeout > 0 and MinimumReadSize == 0 will cause 99 | // the port to either wait until IntercharacterTimeout wait time is 100 | // exceeded OR there is character data to return from the port. 101 | // 102 | 103 | InterCharacterTimeout uint 104 | MinimumReadSize uint 105 | 106 | // Use to enable RS485 mode -- probably only valid on some Linux platforms 107 | Rs485Enable bool 108 | 109 | // Set to true for logic level high during send 110 | Rs485RtsHighDuringSend bool 111 | 112 | // Set to true for logic level high after send 113 | Rs485RtsHighAfterSend bool 114 | 115 | // set to receive data during sending 116 | Rs485RxDuringTx bool 117 | 118 | // RTS delay before send 119 | Rs485DelayRtsBeforeSend int 120 | 121 | // RTS delay after send 122 | Rs485DelayRtsAfterSend int 123 | } 124 | 125 | // Open creates an io.ReadWriteCloser based on the supplied options struct. 126 | func Open(options OpenOptions) (io.ReadWriteCloser, error) { 127 | // Redirect to the OS-specific function. 128 | return openInternal(options) 129 | } 130 | 131 | // Rounds a float to the nearest integer. 132 | func round(f float64) float64 { 133 | return math.Floor(f + 0.5) 134 | } 135 | -------------------------------------------------------------------------------- /go/vendor/github.com/jacobsa/go-serial/serial/open_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // This file contains OS-specific constants and types that work on OS X (tested 16 | // on version 10.6.8). 17 | // 18 | // Helpful documentation for some of these options: 19 | // 20 | // http://www.unixwiz.net/techtips/termios-vmin-vtime.html 21 | // http://www.taltech.com/support/entry/serial_intro 22 | // http://www.cs.utah.edu/dept/old/texinfo/glibc-manual-0.02/library_16.html 23 | // http://permalink.gmane.org/gmane.linux.kernel/103713 24 | // 25 | 26 | package serial 27 | 28 | import ( 29 | "errors" 30 | "io" 31 | ) 32 | import "os" 33 | import "syscall" 34 | import "unsafe" 35 | 36 | // termios types 37 | type cc_t byte 38 | type speed_t uint64 39 | type tcflag_t uint64 40 | 41 | // sys/termios.h 42 | const ( 43 | kCS5 = 0x00000000 44 | kCS6 = 0x00000100 45 | kCS7 = 0x00000200 46 | kCS8 = 0x00000300 47 | kCLOCAL = 0x00008000 48 | kCREAD = 0x00000800 49 | kCSTOPB = 0x00000400 50 | kIGNPAR = 0x00000004 51 | kPARENB = 0x00001000 52 | kPARODD = 0x00002000 53 | 54 | kNCCS = 20 55 | 56 | kVMIN = tcflag_t(16) 57 | kVTIME = tcflag_t(17) 58 | ) 59 | 60 | const ( 61 | 62 | // sys/ttycom.h 63 | kTIOCGETA = 1078490131 64 | kTIOCSETA = 2152231956 65 | 66 | // IOKit: serial/ioss.h 67 | kIOSSIOSPEED = 0x80045402 68 | ) 69 | 70 | // sys/termios.h 71 | type termios struct { 72 | c_iflag tcflag_t 73 | c_oflag tcflag_t 74 | c_cflag tcflag_t 75 | c_lflag tcflag_t 76 | c_cc [kNCCS]cc_t 77 | c_ispeed speed_t 78 | c_ospeed speed_t 79 | } 80 | 81 | // setTermios updates the termios struct associated with a serial port file 82 | // descriptor. This sets appropriate options for how the OS interacts with the 83 | // port. 84 | func setTermios(fd uintptr, src *termios) error { 85 | // Make the ioctl syscall that sets the termios struct. 86 | r1, _, errno := 87 | syscall.Syscall( 88 | syscall.SYS_IOCTL, 89 | fd, 90 | uintptr(kTIOCSETA), 91 | uintptr(unsafe.Pointer(src))) 92 | 93 | // Did the syscall return an error? 94 | if errno != 0 { 95 | return os.NewSyscallError("SYS_IOCTL", errno) 96 | } 97 | 98 | // Just in case, check the return value as well. 99 | if r1 != 0 { 100 | return errors.New("Unknown error from SYS_IOCTL.") 101 | } 102 | 103 | return nil 104 | } 105 | 106 | func convertOptions(options OpenOptions) (*termios, error) { 107 | var result termios 108 | 109 | // Ignore modem status lines. We don't want to receive SIGHUP when the serial 110 | // port is disconnected, for example. 111 | result.c_cflag |= kCLOCAL 112 | 113 | // Enable receiving data. 114 | // 115 | // NOTE(jacobsa): I don't know exactly what this flag is for. The man page 116 | // seems to imply that it shouldn't really exist. 117 | result.c_cflag |= kCREAD 118 | 119 | // Sanity check inter-character timeout and minimum read size options. 120 | vtime := uint(round(float64(options.InterCharacterTimeout)/100.0) * 100) 121 | vmin := options.MinimumReadSize 122 | 123 | if vmin == 0 && vtime < 100 { 124 | return nil, errors.New("Invalid values for InterCharacterTimeout and MinimumReadSize.") 125 | } 126 | 127 | if vtime > 25500 { 128 | return nil, errors.New("Invalid value for InterCharacterTimeout.") 129 | } 130 | 131 | // Set VMIN and VTIME. Make sure to convert to tenths of seconds for VTIME. 132 | result.c_cc[kVTIME] = cc_t(vtime / 100) 133 | result.c_cc[kVMIN] = cc_t(vmin) 134 | 135 | // Set an arbitrary baudrate. We'll set the real one later. 136 | result.c_ispeed = 14400 137 | result.c_ospeed = 14400 138 | 139 | // Data bits 140 | switch options.DataBits { 141 | case 5: 142 | result.c_cflag |= kCS5 143 | case 6: 144 | result.c_cflag |= kCS6 145 | case 7: 146 | result.c_cflag |= kCS7 147 | case 8: 148 | result.c_cflag |= kCS8 149 | default: 150 | return nil, errors.New("Invalid setting for DataBits.") 151 | } 152 | 153 | // Stop bits 154 | switch options.StopBits { 155 | case 1: 156 | // Nothing to do; CSTOPB is already cleared. 157 | case 2: 158 | result.c_cflag |= kCSTOPB 159 | default: 160 | return nil, errors.New("Invalid setting for StopBits.") 161 | } 162 | 163 | // Parity mode 164 | switch options.ParityMode { 165 | case PARITY_NONE: 166 | // Nothing to do; PARENB is already not set. 167 | case PARITY_ODD: 168 | // Enable parity generation and receiving at the hardware level using 169 | // PARENB, but continue to deliver all bytes to the user no matter what (by 170 | // not setting INPCK). Also turn on odd parity mode. 171 | result.c_cflag |= kPARENB 172 | result.c_cflag |= kPARODD 173 | case PARITY_EVEN: 174 | // Enable parity generation and receiving at the hardware level using 175 | // PARENB, but continue to deliver all bytes to the user no matter what (by 176 | // not setting INPCK). Leave out PARODD to use even mode. 177 | result.c_cflag |= kPARENB 178 | default: 179 | return nil, errors.New("Invalid setting for ParityMode.") 180 | } 181 | 182 | return &result, nil 183 | } 184 | 185 | func openInternal(options OpenOptions) (io.ReadWriteCloser, error) { 186 | // Open the serial port in non-blocking mode, since otherwise the OS will 187 | // wait for the CARRIER line to be asserted. 188 | file, err := 189 | os.OpenFile( 190 | options.PortName, 191 | syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 192 | 0600) 193 | 194 | if err != nil { 195 | return nil, err 196 | } 197 | 198 | // We want to do blocking I/O, so clear the non-blocking flag set above. 199 | r1, _, errno := 200 | syscall.Syscall( 201 | syscall.SYS_FCNTL, 202 | uintptr(file.Fd()), 203 | uintptr(syscall.F_SETFL), 204 | uintptr(0)) 205 | 206 | if errno != 0 { 207 | return nil, os.NewSyscallError("SYS_FCNTL", errno) 208 | } 209 | 210 | if r1 != 0 { 211 | return nil, errors.New("Unknown error from SYS_FCNTL.") 212 | } 213 | 214 | // Set standard termios options. 215 | terminalOptions, err := convertOptions(options) 216 | if err != nil { 217 | return nil, err 218 | } 219 | 220 | err = setTermios(file.Fd(), terminalOptions) 221 | if err != nil { 222 | return nil, err 223 | } 224 | 225 | // Set baud rate with the IOSSIOSPEED ioctl, to support non-standard speeds. 226 | r2, _, errno2 := syscall.Syscall( 227 | syscall.SYS_IOCTL, 228 | uintptr(file.Fd()), 229 | uintptr(kIOSSIOSPEED), 230 | uintptr(unsafe.Pointer(&options.BaudRate))); 231 | 232 | if errno2 != 0 { 233 | return nil, os.NewSyscallError("SYS_IOCTL", errno2) 234 | } 235 | 236 | if r2 != 0 { 237 | return nil, errors.New("Unknown error from SYS_IOCTL.") 238 | } 239 | 240 | // We're done. 241 | return file, nil 242 | } 243 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 22 | 23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 24 | 25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 26 | 27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 28 | 29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 30 | 31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 32 | 33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 34 | 35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | You must cause any modified files to carry prominent notices stating that You changed the files; and 39 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 40 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 41 | 42 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 43 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 44 | 45 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 46 | 47 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 48 | 49 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 50 | 51 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 52 | 53 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /go/vendor/github.com/jacobsa/go-serial/serial/open_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package serial 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "os" 21 | "sync" 22 | "syscall" 23 | "unsafe" 24 | ) 25 | 26 | type serialPort struct { 27 | f *os.File 28 | fd syscall.Handle 29 | rl sync.Mutex 30 | wl sync.Mutex 31 | ro *syscall.Overlapped 32 | wo *syscall.Overlapped 33 | } 34 | 35 | type structDCB struct { 36 | DCBlength, BaudRate uint32 37 | flags [4]byte 38 | wReserved, XonLim, XoffLim uint16 39 | ByteSize, Parity, StopBits byte 40 | XonChar, XoffChar, ErrorChar, EofChar, EvtChar byte 41 | wReserved1 uint16 42 | } 43 | 44 | type structTimeouts struct { 45 | ReadIntervalTimeout uint32 46 | ReadTotalTimeoutMultiplier uint32 47 | ReadTotalTimeoutConstant uint32 48 | WriteTotalTimeoutMultiplier uint32 49 | WriteTotalTimeoutConstant uint32 50 | } 51 | 52 | func openInternal(options OpenOptions) (io.ReadWriteCloser, error) { 53 | if len(options.PortName) > 0 && options.PortName[0] != '\\' { 54 | options.PortName = "\\\\.\\" + options.PortName 55 | } 56 | 57 | h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(options.PortName), 58 | syscall.GENERIC_READ|syscall.GENERIC_WRITE, 59 | 0, 60 | nil, 61 | syscall.OPEN_EXISTING, 62 | syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED, 63 | 0) 64 | if err != nil { 65 | return nil, err 66 | } 67 | f := os.NewFile(uintptr(h), options.PortName) 68 | defer func() { 69 | if err != nil { 70 | f.Close() 71 | } 72 | }() 73 | 74 | if err = setCommState(h, options); err != nil { 75 | return nil, err 76 | } 77 | if err = setupComm(h, 64, 64); err != nil { 78 | return nil, err 79 | } 80 | if err = setCommTimeouts(h, options); err != nil { 81 | return nil, err 82 | } 83 | if err = setCommMask(h); err != nil { 84 | return nil, err 85 | } 86 | 87 | ro, err := newOverlapped() 88 | if err != nil { 89 | return nil, err 90 | } 91 | wo, err := newOverlapped() 92 | if err != nil { 93 | return nil, err 94 | } 95 | port := new(serialPort) 96 | port.f = f 97 | port.fd = h 98 | port.ro = ro 99 | port.wo = wo 100 | 101 | return port, nil 102 | } 103 | 104 | func (p *serialPort) Close() error { 105 | return p.f.Close() 106 | } 107 | 108 | func (p *serialPort) Write(buf []byte) (int, error) { 109 | p.wl.Lock() 110 | defer p.wl.Unlock() 111 | 112 | if err := resetEvent(p.wo.HEvent); err != nil { 113 | return 0, err 114 | } 115 | var n uint32 116 | err := syscall.WriteFile(p.fd, buf, &n, p.wo) 117 | if err != nil && err != syscall.ERROR_IO_PENDING { 118 | return int(n), err 119 | } 120 | return getOverlappedResult(p.fd, p.wo) 121 | } 122 | 123 | func (p *serialPort) Read(buf []byte) (int, error) { 124 | if p == nil || p.f == nil { 125 | return 0, fmt.Errorf("Invalid port on read %v %v", p, p.f) 126 | } 127 | 128 | p.rl.Lock() 129 | defer p.rl.Unlock() 130 | 131 | if err := resetEvent(p.ro.HEvent); err != nil { 132 | return 0, err 133 | } 134 | var done uint32 135 | err := syscall.ReadFile(p.fd, buf, &done, p.ro) 136 | if err != nil && err != syscall.ERROR_IO_PENDING { 137 | return int(done), err 138 | } 139 | return getOverlappedResult(p.fd, p.ro) 140 | } 141 | 142 | var ( 143 | nSetCommState, 144 | nSetCommTimeouts, 145 | nSetCommMask, 146 | nSetupComm, 147 | nGetOverlappedResult, 148 | nCreateEvent, 149 | nResetEvent uintptr 150 | ) 151 | 152 | func init() { 153 | k32, err := syscall.LoadLibrary("kernel32.dll") 154 | if err != nil { 155 | panic("LoadLibrary " + err.Error()) 156 | } 157 | defer syscall.FreeLibrary(k32) 158 | 159 | nSetCommState = getProcAddr(k32, "SetCommState") 160 | nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts") 161 | nSetCommMask = getProcAddr(k32, "SetCommMask") 162 | nSetupComm = getProcAddr(k32, "SetupComm") 163 | nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult") 164 | nCreateEvent = getProcAddr(k32, "CreateEventW") 165 | nResetEvent = getProcAddr(k32, "ResetEvent") 166 | } 167 | 168 | func getProcAddr(lib syscall.Handle, name string) uintptr { 169 | addr, err := syscall.GetProcAddress(lib, name) 170 | if err != nil { 171 | panic(name + " " + err.Error()) 172 | } 173 | return addr 174 | } 175 | 176 | func setCommState(h syscall.Handle, options OpenOptions) error { 177 | var params structDCB 178 | params.DCBlength = uint32(unsafe.Sizeof(params)) 179 | 180 | params.flags[0] = 0x01 // fBinary 181 | params.flags[0] |= 0x10 // Assert DSR 182 | 183 | if options.ParityMode != PARITY_NONE { 184 | params.flags[0] |= 0x03 // fParity 185 | params.Parity = byte(options.ParityMode) 186 | } 187 | 188 | if options.StopBits == 1 { 189 | params.StopBits = 0 190 | } else if options.StopBits == 2 { 191 | params.StopBits = 2 192 | } 193 | 194 | params.BaudRate = uint32(options.BaudRate) 195 | params.ByteSize = byte(options.DataBits) 196 | 197 | r, _, err := syscall.Syscall(nSetCommState, 2, uintptr(h), uintptr(unsafe.Pointer(¶ms)), 0) 198 | if r == 0 { 199 | return err 200 | } 201 | return nil 202 | } 203 | 204 | func setCommTimeouts(h syscall.Handle, options OpenOptions) error { 205 | var timeouts structTimeouts 206 | const MAXDWORD = 1<<32 - 1 207 | timeoutConstant := uint32(round(float64(options.InterCharacterTimeout) / 100.0)) 208 | readIntervalTimeout := uint32(options.MinimumReadSize) 209 | 210 | if timeoutConstant > 0 && readIntervalTimeout == 0 { 211 | //Assume we're setting for non blocking IO. 212 | timeouts.ReadIntervalTimeout = MAXDWORD 213 | timeouts.ReadTotalTimeoutMultiplier = MAXDWORD 214 | timeouts.ReadTotalTimeoutConstant = timeoutConstant 215 | } else if readIntervalTimeout > 0 { 216 | // Assume we want to block and wait for input. 217 | timeouts.ReadIntervalTimeout = readIntervalTimeout 218 | timeouts.ReadTotalTimeoutMultiplier = 1 219 | timeouts.ReadTotalTimeoutConstant = 1 220 | } else { 221 | // No idea what we intended, use defaults 222 | // default config does what it did before. 223 | timeouts.ReadIntervalTimeout = MAXDWORD 224 | timeouts.ReadTotalTimeoutMultiplier = MAXDWORD 225 | timeouts.ReadTotalTimeoutConstant = MAXDWORD - 1 226 | } 227 | 228 | /* 229 | Empirical testing has shown that to have non-blocking IO we need to set: 230 | ReadTotalTimeoutConstant > 0 and 231 | ReadTotalTimeoutMultiplier = MAXDWORD and 232 | ReadIntervalTimeout = MAXDWORD 233 | 234 | The documentation states that ReadIntervalTimeout is set in MS but 235 | empirical investigation determines that it seems to interpret in units 236 | of 100ms. 237 | 238 | If InterCharacterTimeout is set at all it seems that the port will block 239 | indefinitly until a character is received. Not all circumstances have been 240 | tested. The input of an expert would be appreciated. 241 | 242 | From http://msdn.microsoft.com/en-us/library/aa363190(v=VS.85).aspx 243 | 244 | For blocking I/O see below: 245 | 246 | Remarks: 247 | 248 | If an application sets ReadIntervalTimeout and 249 | ReadTotalTimeoutMultiplier to MAXDWORD and sets 250 | ReadTotalTimeoutConstant to a value greater than zero and 251 | less than MAXDWORD, one of the following occurs when the 252 | ReadFile function is called: 253 | 254 | If there are any bytes in the input buffer, ReadFile returns 255 | immediately with the bytes in the buffer. 256 | 257 | If there are no bytes in the input buffer, ReadFile waits 258 | until a byte arrives and then returns immediately. 259 | 260 | If no bytes arrive within the time specified by 261 | ReadTotalTimeoutConstant, ReadFile times out. 262 | */ 263 | 264 | r, _, err := syscall.Syscall(nSetCommTimeouts, 2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)), 0) 265 | if r == 0 { 266 | return err 267 | } 268 | return nil 269 | } 270 | 271 | func setupComm(h syscall.Handle, in, out int) error { 272 | r, _, err := syscall.Syscall(nSetupComm, 3, uintptr(h), uintptr(in), uintptr(out)) 273 | if r == 0 { 274 | return err 275 | } 276 | return nil 277 | } 278 | 279 | func setCommMask(h syscall.Handle) error { 280 | const EV_RXCHAR = 0x0001 281 | r, _, err := syscall.Syscall(nSetCommMask, 2, uintptr(h), EV_RXCHAR, 0) 282 | if r == 0 { 283 | return err 284 | } 285 | return nil 286 | } 287 | 288 | func resetEvent(h syscall.Handle) error { 289 | r, _, err := syscall.Syscall(nResetEvent, 1, uintptr(h), 0, 0) 290 | if r == 0 { 291 | return err 292 | } 293 | return nil 294 | } 295 | 296 | func newOverlapped() (*syscall.Overlapped, error) { 297 | var overlapped syscall.Overlapped 298 | r, _, err := syscall.Syscall6(nCreateEvent, 4, 0, 1, 0, 0, 0, 0) 299 | if r == 0 { 300 | return nil, err 301 | } 302 | overlapped.HEvent = syscall.Handle(r) 303 | return &overlapped, nil 304 | } 305 | 306 | func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, error) { 307 | var n int 308 | r, _, err := syscall.Syscall6(nGetOverlappedResult, 4, 309 | uintptr(h), 310 | uintptr(unsafe.Pointer(overlapped)), 311 | uintptr(unsafe.Pointer(&n)), 1, 0, 0) 312 | if r == 0 { 313 | return n, err 314 | } 315 | 316 | return n, nil 317 | } 318 | -------------------------------------------------------------------------------- /go/vendor/github.com/golang/glog/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /go/vendor/github.com/jacobsa/go-serial/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /go/sds011/sds011.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Ryszard Szopa 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package sds011 implements a library to read the protocol of SDS011, 16 | // an air quality sensor than can work with Raspberry Pi. 17 | package sds011 18 | 19 | import ( 20 | "bytes" 21 | "encoding/binary" 22 | "errors" 23 | "fmt" 24 | "io" 25 | "time" 26 | 27 | log "github.com/golang/glog" 28 | "github.com/jacobsa/go-serial/serial" 29 | ) 30 | 31 | type command byte 32 | type mode byte 33 | 34 | const ( 35 | commandReportMode command = 2 36 | commandQuery command = 4 37 | commandDeviceID command = 5 38 | commandWorkState command = 6 39 | commandFirmware command = 7 40 | commandCycle command = 8 41 | 42 | modeGet mode = 0 43 | modeSet mode = 1 44 | 45 | reportModeActive byte = 0 46 | reportModeQuery byte = 1 47 | 48 | workStateSleeping byte = 0 49 | workStateMeasuring byte = 1 50 | ) 51 | 52 | // response is what we get on the wire from the sensor. Its meaning 53 | // depends on what it is a reply to. 54 | type response struct { 55 | Header byte // always 0xAA 56 | Command byte // 0xC0 if in active mode, if reply 0xC5 57 | Data [6]byte 58 | CheckSum byte 59 | Tail byte // always 0xAB 60 | } 61 | 62 | // IsReply returns true if this response is a reply to a command (as 63 | // opposed to measurements). 64 | func (resp *response) IsReply() bool { 65 | return resp.Command == 0xC5 66 | } 67 | 68 | // PM25 returns the sensor's PM2.5 reading. It will panic if this 69 | // isn't a reply containing the readings. 70 | func (resp *response) PM25() float64 { 71 | if resp.IsReply() { 72 | panic(fmt.Sprintf("access to field that doesn't work with this type of response %#v", resp)) 73 | } 74 | return float64(binary.LittleEndian.Uint16(resp.Data[0:2])) / 10.0 75 | } 76 | 77 | // PM10 returns the sensor's PM10 reading. It will panic if this isn't 78 | // a reply containing the readings. 79 | func (resp *response) PM10() float64 { 80 | if resp.IsReply() { 81 | panic(fmt.Sprintf("access to field that doesn't work with this type of response %#v", resp)) 82 | } 83 | 84 | return float64(binary.LittleEndian.Uint16(resp.Data[2:4])) / 10.0 85 | } 86 | 87 | func (resp *response) checkMatches(cmd command) { 88 | if resp.Data[0] != byte(cmd) { 89 | panic(fmt.Sprintf("access to field that doesn't work with this type of response %#v", resp)) 90 | } 91 | } 92 | 93 | // ID returns the response's device ID. 94 | func (resp *response) ID() uint16 { 95 | if resp.IsReply() { 96 | panic(fmt.Sprintf("access to field that doesn't work with this type of response %#v", resp)) 97 | } 98 | 99 | return binary.LittleEndian.Uint16(resp.Data[4:6]) 100 | } 101 | 102 | // Firmware returns the version of firmware, as a date (yy-mm-dd). It 103 | // will panic if this is the wrong kind of response. 104 | func (resp *response) Firmware() string { 105 | resp.checkMatches(commandFirmware) 106 | return fmt.Sprintf("%02d-%02d-%02d", resp.Data[1], resp.Data[2], resp.Data[3]) 107 | } 108 | 109 | // DeviceID returns the device id. It will panic if this is the 110 | func (resp *response) DeviceID() string { 111 | resp.checkMatches(commandDeviceID) 112 | return fmt.Sprintf("%02d%02d", resp.Data[1], resp.Data[2]) 113 | } 114 | 115 | func (resp *response) ReportMode() byte { 116 | resp.checkMatches(commandReportMode) 117 | return resp.Data[2] 118 | } 119 | 120 | func (resp *response) Cycle() uint8 { 121 | resp.checkMatches(commandCycle) 122 | return resp.Data[2] 123 | } 124 | 125 | func (resp *response) WorkState() byte { 126 | resp.checkMatches(commandWorkState) 127 | return resp.Data[2] 128 | } 129 | 130 | type request struct { 131 | Header byte // 1 always 0xAA 132 | SendMarker byte // 2 always 0xB4 133 | Command byte // 3 command 134 | Mode byte // 4 getting 0, setting 1 135 | Data [11]byte // 5-15 136 | DeviceID [2]byte // 16-17 0xFFFF for all device IDs. 137 | CheckSum byte // 18 See makeRequest 138 | Tail byte // 19 always 0xAB 139 | } 140 | 141 | func makeRequest(cmd command, mod mode, value byte) *request { 142 | data := [11]byte{} 143 | data[0] = value 144 | 145 | req := &request{ 146 | Header: 0xAA, 147 | SendMarker: 0xB4, 148 | Command: byte(cmd), 149 | Mode: byte(mod), 150 | Data: data, 151 | DeviceID: [2]byte{0xFF, 0xFF}, 152 | Tail: 0xAB, 153 | } 154 | checksum := int(req.Command) + int(req.Mode) 155 | for _, v := range data { 156 | checksum += int(v) 157 | } 158 | for _, v := range req.DeviceID { 159 | checksum += int(v) 160 | } 161 | req.CheckSum = byte(checksum % 256) 162 | return req 163 | } 164 | 165 | // IsCorrect returns nil if the responses checksum matches, an error 166 | // otherwise. 167 | func (resp *response) IsCorrect() error { 168 | 169 | var checkSum byte 170 | for i := 0; i < 6; i++ { 171 | checkSum += resp.Data[i] 172 | } 173 | 174 | if checkSum != resp.CheckSum { 175 | return fmt.Errorf("bad checksum: %#v", resp) 176 | } 177 | return nil 178 | } 179 | 180 | // A Point represents a single reading from the sensor. 181 | type Point struct { 182 | PM25 float64 183 | PM10 float64 184 | Timestamp time.Time 185 | } 186 | 187 | func (point *Point) String() string { 188 | return fmt.Sprintf("PM2.5: %v μg/m³ PM10: %v μg/m³", point.PM25, point.PM10) 189 | } 190 | 191 | // Sensor represents an SDS011 sensor. 192 | type Sensor struct { 193 | rwc io.ReadWriteCloser 194 | } 195 | 196 | func (sensor *Sensor) send(cmd command, mod mode, data byte) error { 197 | b := new(bytes.Buffer) 198 | if err := binary.Write(b, binary.LittleEndian, makeRequest(cmd, mod, data)); err != nil { 199 | return err 200 | } 201 | log.V(6).Infof("sending bytes: %#v", b.Bytes()) 202 | _, err := sensor.rwc.Write(b.Bytes()) 203 | return err 204 | } 205 | 206 | // receive reads one response from the wire. 207 | func (sensor *Sensor) receive() (*response, error) { 208 | data := new(response) 209 | if err := binary.Read(sensor.rwc, binary.LittleEndian, data); err != nil { 210 | return nil, err 211 | } 212 | if err := data.IsCorrect(); err != nil { 213 | return nil, err 214 | } 215 | return data, nil 216 | } 217 | 218 | func (sensor *Sensor) receiveReply() (*response, error) { 219 | // FIXME(ryszard): This should support timeouts. 220 | for i := 0; i < 10; i++ { 221 | resp, err := sensor.receive() 222 | if err != nil { 223 | return nil, err 224 | } 225 | if resp.IsReply() { 226 | return resp, nil 227 | } 228 | log.V(6).Infof("received data, but not a reply: %#v", resp) 229 | } 230 | return nil, errors.New("no reply") 231 | 232 | } 233 | 234 | // ReportMode returns true if the device is in active mode, false if 235 | // in query mode. 236 | func (sensor *Sensor) ReportMode() (bool, error) { 237 | if err := sensor.send(commandReportMode, modeGet, 0); err != nil { 238 | return false, err 239 | } 240 | data, err := sensor.receiveReply() 241 | if err != nil { 242 | return false, err 243 | } 244 | log.V(6).Infof("ReportMode response: %#v", data) 245 | return data.ReportMode() == reportModeActive, nil 246 | } 247 | 248 | // MakeActive makes the sensor actively report its measurements. 249 | func (sensor *Sensor) MakeActive() error { 250 | if err := sensor.send(commandReportMode, modeSet, reportModeActive); err != nil { 251 | return err 252 | } 253 | data, err := sensor.receiveReply() 254 | if err != nil { 255 | return err 256 | } 257 | log.V(6).Infof("MakeActive: %#v", data) 258 | return nil 259 | } 260 | 261 | // MakePassive stop the sensor from actively reporting its 262 | // measurements. You will need to send a Query command. 263 | func (sensor *Sensor) MakePassive() error { 264 | log.V(6).Infof("make passive") 265 | if err := sensor.send(commandReportMode, modeSet, reportModeQuery); err != nil { 266 | return err 267 | } 268 | data, err := sensor.receiveReply() 269 | if err != nil { 270 | return err 271 | } 272 | log.V(6).Infof("MakePassive response: %#v", data) 273 | return nil 274 | } 275 | 276 | // DeviceID returns the sensor's device ID. 277 | func (sensor *Sensor) DeviceID() (string, error) { 278 | if err := sensor.send(commandDeviceID, modeGet, 0); err != nil { 279 | return "", err 280 | } 281 | data, err := sensor.receiveReply() 282 | if err != nil { 283 | return "", err 284 | } 285 | log.V(6).Infof("DeviceID: %#v", data) 286 | return data.DeviceID(), nil 287 | 288 | } 289 | 290 | // Firmware returns the firmware version (a yy-mm-dd date). 291 | func (sensor *Sensor) Firmware() (string, error) { 292 | if err := sensor.send(commandFirmware, modeGet, 0); err != nil { 293 | return "", err 294 | } 295 | data, err := sensor.receiveReply() 296 | if err != nil { 297 | return "", err 298 | } 299 | log.V(6).Infof("Firmare: %#v", data) 300 | return data.Firmware(), nil 301 | 302 | } 303 | 304 | // Cycle returns the current cycle length in minutes. If it's 0 it 305 | // means that cycle is not set, and the sensor is streaming data 306 | // continuously. 307 | func (sensor *Sensor) Cycle() (uint8, error) { 308 | if err := sensor.send(commandCycle, modeGet, 0); err != nil { 309 | return 0, err 310 | } 311 | data, err := sensor.receiveReply() 312 | if err != nil { 313 | return 0, err 314 | } 315 | log.V(6).Infof("Cycle: %#v", data) 316 | return data.Cycle(), nil 317 | } 318 | 319 | // SetCycle sets the cycle length. The value is the cycle's length in 320 | // minutes, accepting values from 1 to 30. If you pass it 0 it will 321 | // disable cycle work, and the sensor will just stream data. 322 | func (sensor *Sensor) SetCycle(value uint8) error { 323 | if value < 0 || value > 30 { 324 | return fmt.Errorf("duty cycle: bad value %v. Should be between 0 and 30.", value) 325 | } 326 | if err := sensor.send(commandCycle, modeSet, value); err != nil { 327 | return err 328 | } 329 | data, err := sensor.receiveReply() 330 | if err != nil { 331 | return err 332 | } 333 | log.V(6).Infof("SetCycle: %#v", data) 334 | return nil 335 | } 336 | 337 | // Query returns one reading. 338 | func (sensor *Sensor) Query() (*Point, error) { 339 | if err := sensor.send(commandQuery, modeGet, 0); err != nil { 340 | return nil, err 341 | } 342 | return sensor.Get() 343 | } 344 | 345 | // IsAwake returns true if the sensor is awake. 346 | func (sensor *Sensor) IsAwake() (bool, error) { 347 | if err := sensor.send(commandWorkState, modeGet, 0); err != nil { 348 | return false, err 349 | } 350 | data, err := sensor.receiveReply() 351 | if err != nil { 352 | return false, err 353 | } 354 | log.V(6).Infof("IsAwake WorkState: %#v", data) 355 | return data.WorkState() == workStateMeasuring, nil 356 | } 357 | 358 | // Awake awakes the sensor if it is in sleep mode. 359 | func (sensor *Sensor) Awake() error { 360 | if err := sensor.send(commandWorkState, modeSet, workStateMeasuring); err != nil { 361 | return err 362 | } 363 | data, err := sensor.receiveReply() 364 | if err != nil { 365 | return err 366 | } 367 | log.V(6).Infof("Awake WorkState: %#v", data) 368 | return nil 369 | } 370 | 371 | // Sleep puts the sensor to sleep. 372 | func (sensor *Sensor) Sleep() error { 373 | if err := sensor.send(commandWorkState, modeSet, workStateSleeping); err != nil { 374 | return err 375 | } 376 | data, err := sensor.receiveReply() 377 | if err != nil { 378 | return err 379 | } 380 | log.V(6).Infof("WorkState: %#v", data) 381 | return nil 382 | } 383 | 384 | // Close closes the underlying serial port. 385 | func (sensor *Sensor) Close() { 386 | sensor.rwc.Close() 387 | } 388 | 389 | // New returns a sensor that will read data from serial port for which 390 | // the path was provided. It is the responsibility of the caller to 391 | // close the sensor. 392 | func New(portPath string) (*Sensor, error) { 393 | options := serial.OpenOptions{ 394 | PortName: portPath, 395 | BaudRate: 9600, 396 | DataBits: 8, 397 | StopBits: 1, 398 | MinimumReadSize: 4, 399 | } 400 | 401 | port, err := serial.Open(options) 402 | if err != nil { 403 | return nil, err 404 | } 405 | return NewSensor(port), nil 406 | } 407 | 408 | // NewSensor returns a sensor that will read its data from the provided 409 | // read-write-closer. 410 | func NewSensor(rwc io.ReadWriteCloser) *Sensor { 411 | return &Sensor{rwc: rwc} 412 | } 413 | 414 | // Get will read one measurement. It will block until data is 415 | // available. It only makes sense to call read if the sensor is in 416 | // active mode. 417 | func (sensor *Sensor) Get() (point *Point, err error) { 418 | data, err := sensor.receive() 419 | if err != nil { 420 | return nil, err 421 | } 422 | log.V(6).Infof("Query data: %#v", data) 423 | return &Point{PM25: data.PM25(), PM10: data.PM10(), Timestamp: time.Now()}, nil 424 | } 425 | -------------------------------------------------------------------------------- /go/vendor/github.com/golang/glog/glog.go: -------------------------------------------------------------------------------- 1 | // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ 2 | // 3 | // Copyright 2013 Google Inc. All Rights Reserved. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | // Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. 18 | // It provides functions Info, Warning, Error, Fatal, plus formatting variants such as 19 | // Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags. 20 | // 21 | // Basic examples: 22 | // 23 | // glog.Info("Prepare to repel boarders") 24 | // 25 | // glog.Fatalf("Initialization failed: %s", err) 26 | // 27 | // See the documentation for the V function for an explanation of these examples: 28 | // 29 | // if glog.V(2) { 30 | // glog.Info("Starting transaction...") 31 | // } 32 | // 33 | // glog.V(2).Infoln("Processed", nItems, "elements") 34 | // 35 | // Log output is buffered and written periodically using Flush. Programs 36 | // should call Flush before exiting to guarantee all log output is written. 37 | // 38 | // By default, all log statements write to files in a temporary directory. 39 | // This package provides several flags that modify this behavior. 40 | // As a result, flag.Parse must be called before any logging is done. 41 | // 42 | // -logtostderr=false 43 | // Logs are written to standard error instead of to files. 44 | // -alsologtostderr=false 45 | // Logs are written to standard error as well as to files. 46 | // -stderrthreshold=ERROR 47 | // Log events at or above this severity are logged to standard 48 | // error as well as to files. 49 | // -log_dir="" 50 | // Log files will be written to this directory instead of the 51 | // default temporary directory. 52 | // 53 | // Other flags provide aids to debugging. 54 | // 55 | // -log_backtrace_at="" 56 | // When set to a file and line number holding a logging statement, 57 | // such as 58 | // -log_backtrace_at=gopherflakes.go:234 59 | // a stack trace will be written to the Info log whenever execution 60 | // hits that statement. (Unlike with -vmodule, the ".go" must be 61 | // present.) 62 | // -v=0 63 | // Enable V-leveled logging at the specified level. 64 | // -vmodule="" 65 | // The syntax of the argument is a comma-separated list of pattern=N, 66 | // where pattern is a literal file name (minus the ".go" suffix) or 67 | // "glob" pattern and N is a V level. For instance, 68 | // -vmodule=gopher*=3 69 | // sets the V level to 3 in all Go files whose names begin "gopher". 70 | // 71 | package glog 72 | 73 | import ( 74 | "bufio" 75 | "bytes" 76 | "errors" 77 | "flag" 78 | "fmt" 79 | "io" 80 | stdLog "log" 81 | "os" 82 | "path/filepath" 83 | "runtime" 84 | "strconv" 85 | "strings" 86 | "sync" 87 | "sync/atomic" 88 | "time" 89 | ) 90 | 91 | // severity identifies the sort of log: info, warning etc. It also implements 92 | // the flag.Value interface. The -stderrthreshold flag is of type severity and 93 | // should be modified only through the flag.Value interface. The values match 94 | // the corresponding constants in C++. 95 | type severity int32 // sync/atomic int32 96 | 97 | // These constants identify the log levels in order of increasing severity. 98 | // A message written to a high-severity log file is also written to each 99 | // lower-severity log file. 100 | const ( 101 | infoLog severity = iota 102 | warningLog 103 | errorLog 104 | fatalLog 105 | numSeverity = 4 106 | ) 107 | 108 | const severityChar = "IWEF" 109 | 110 | var severityName = []string{ 111 | infoLog: "INFO", 112 | warningLog: "WARNING", 113 | errorLog: "ERROR", 114 | fatalLog: "FATAL", 115 | } 116 | 117 | // get returns the value of the severity. 118 | func (s *severity) get() severity { 119 | return severity(atomic.LoadInt32((*int32)(s))) 120 | } 121 | 122 | // set sets the value of the severity. 123 | func (s *severity) set(val severity) { 124 | atomic.StoreInt32((*int32)(s), int32(val)) 125 | } 126 | 127 | // String is part of the flag.Value interface. 128 | func (s *severity) String() string { 129 | return strconv.FormatInt(int64(*s), 10) 130 | } 131 | 132 | // Get is part of the flag.Value interface. 133 | func (s *severity) Get() interface{} { 134 | return *s 135 | } 136 | 137 | // Set is part of the flag.Value interface. 138 | func (s *severity) Set(value string) error { 139 | var threshold severity 140 | // Is it a known name? 141 | if v, ok := severityByName(value); ok { 142 | threshold = v 143 | } else { 144 | v, err := strconv.Atoi(value) 145 | if err != nil { 146 | return err 147 | } 148 | threshold = severity(v) 149 | } 150 | logging.stderrThreshold.set(threshold) 151 | return nil 152 | } 153 | 154 | func severityByName(s string) (severity, bool) { 155 | s = strings.ToUpper(s) 156 | for i, name := range severityName { 157 | if name == s { 158 | return severity(i), true 159 | } 160 | } 161 | return 0, false 162 | } 163 | 164 | // OutputStats tracks the number of output lines and bytes written. 165 | type OutputStats struct { 166 | lines int64 167 | bytes int64 168 | } 169 | 170 | // Lines returns the number of lines written. 171 | func (s *OutputStats) Lines() int64 { 172 | return atomic.LoadInt64(&s.lines) 173 | } 174 | 175 | // Bytes returns the number of bytes written. 176 | func (s *OutputStats) Bytes() int64 { 177 | return atomic.LoadInt64(&s.bytes) 178 | } 179 | 180 | // Stats tracks the number of lines of output and number of bytes 181 | // per severity level. Values must be read with atomic.LoadInt64. 182 | var Stats struct { 183 | Info, Warning, Error OutputStats 184 | } 185 | 186 | var severityStats = [numSeverity]*OutputStats{ 187 | infoLog: &Stats.Info, 188 | warningLog: &Stats.Warning, 189 | errorLog: &Stats.Error, 190 | } 191 | 192 | // Level is exported because it appears in the arguments to V and is 193 | // the type of the v flag, which can be set programmatically. 194 | // It's a distinct type because we want to discriminate it from logType. 195 | // Variables of type level are only changed under logging.mu. 196 | // The -v flag is read only with atomic ops, so the state of the logging 197 | // module is consistent. 198 | 199 | // Level is treated as a sync/atomic int32. 200 | 201 | // Level specifies a level of verbosity for V logs. *Level implements 202 | // flag.Value; the -v flag is of type Level and should be modified 203 | // only through the flag.Value interface. 204 | type Level int32 205 | 206 | // get returns the value of the Level. 207 | func (l *Level) get() Level { 208 | return Level(atomic.LoadInt32((*int32)(l))) 209 | } 210 | 211 | // set sets the value of the Level. 212 | func (l *Level) set(val Level) { 213 | atomic.StoreInt32((*int32)(l), int32(val)) 214 | } 215 | 216 | // String is part of the flag.Value interface. 217 | func (l *Level) String() string { 218 | return strconv.FormatInt(int64(*l), 10) 219 | } 220 | 221 | // Get is part of the flag.Value interface. 222 | func (l *Level) Get() interface{} { 223 | return *l 224 | } 225 | 226 | // Set is part of the flag.Value interface. 227 | func (l *Level) Set(value string) error { 228 | v, err := strconv.Atoi(value) 229 | if err != nil { 230 | return err 231 | } 232 | logging.mu.Lock() 233 | defer logging.mu.Unlock() 234 | logging.setVState(Level(v), logging.vmodule.filter, false) 235 | return nil 236 | } 237 | 238 | // moduleSpec represents the setting of the -vmodule flag. 239 | type moduleSpec struct { 240 | filter []modulePat 241 | } 242 | 243 | // modulePat contains a filter for the -vmodule flag. 244 | // It holds a verbosity level and a file pattern to match. 245 | type modulePat struct { 246 | pattern string 247 | literal bool // The pattern is a literal string 248 | level Level 249 | } 250 | 251 | // match reports whether the file matches the pattern. It uses a string 252 | // comparison if the pattern contains no metacharacters. 253 | func (m *modulePat) match(file string) bool { 254 | if m.literal { 255 | return file == m.pattern 256 | } 257 | match, _ := filepath.Match(m.pattern, file) 258 | return match 259 | } 260 | 261 | func (m *moduleSpec) String() string { 262 | // Lock because the type is not atomic. TODO: clean this up. 263 | logging.mu.Lock() 264 | defer logging.mu.Unlock() 265 | var b bytes.Buffer 266 | for i, f := range m.filter { 267 | if i > 0 { 268 | b.WriteRune(',') 269 | } 270 | fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) 271 | } 272 | return b.String() 273 | } 274 | 275 | // Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the 276 | // struct is not exported. 277 | func (m *moduleSpec) Get() interface{} { 278 | return nil 279 | } 280 | 281 | var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") 282 | 283 | // Syntax: -vmodule=recordio=2,file=1,gfs*=3 284 | func (m *moduleSpec) Set(value string) error { 285 | var filter []modulePat 286 | for _, pat := range strings.Split(value, ",") { 287 | if len(pat) == 0 { 288 | // Empty strings such as from a trailing comma can be ignored. 289 | continue 290 | } 291 | patLev := strings.Split(pat, "=") 292 | if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { 293 | return errVmoduleSyntax 294 | } 295 | pattern := patLev[0] 296 | v, err := strconv.Atoi(patLev[1]) 297 | if err != nil { 298 | return errors.New("syntax error: expect comma-separated list of filename=N") 299 | } 300 | if v < 0 { 301 | return errors.New("negative value for vmodule level") 302 | } 303 | if v == 0 { 304 | continue // Ignore. It's harmless but no point in paying the overhead. 305 | } 306 | // TODO: check syntax of filter? 307 | filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) 308 | } 309 | logging.mu.Lock() 310 | defer logging.mu.Unlock() 311 | logging.setVState(logging.verbosity, filter, true) 312 | return nil 313 | } 314 | 315 | // isLiteral reports whether the pattern is a literal string, that is, has no metacharacters 316 | // that require filepath.Match to be called to match the pattern. 317 | func isLiteral(pattern string) bool { 318 | return !strings.ContainsAny(pattern, `\*?[]`) 319 | } 320 | 321 | // traceLocation represents the setting of the -log_backtrace_at flag. 322 | type traceLocation struct { 323 | file string 324 | line int 325 | } 326 | 327 | // isSet reports whether the trace location has been specified. 328 | // logging.mu is held. 329 | func (t *traceLocation) isSet() bool { 330 | return t.line > 0 331 | } 332 | 333 | // match reports whether the specified file and line matches the trace location. 334 | // The argument file name is the full path, not the basename specified in the flag. 335 | // logging.mu is held. 336 | func (t *traceLocation) match(file string, line int) bool { 337 | if t.line != line { 338 | return false 339 | } 340 | if i := strings.LastIndex(file, "/"); i >= 0 { 341 | file = file[i+1:] 342 | } 343 | return t.file == file 344 | } 345 | 346 | func (t *traceLocation) String() string { 347 | // Lock because the type is not atomic. TODO: clean this up. 348 | logging.mu.Lock() 349 | defer logging.mu.Unlock() 350 | return fmt.Sprintf("%s:%d", t.file, t.line) 351 | } 352 | 353 | // Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the 354 | // struct is not exported 355 | func (t *traceLocation) Get() interface{} { 356 | return nil 357 | } 358 | 359 | var errTraceSyntax = errors.New("syntax error: expect file.go:234") 360 | 361 | // Syntax: -log_backtrace_at=gopherflakes.go:234 362 | // Note that unlike vmodule the file extension is included here. 363 | func (t *traceLocation) Set(value string) error { 364 | if value == "" { 365 | // Unset. 366 | t.line = 0 367 | t.file = "" 368 | } 369 | fields := strings.Split(value, ":") 370 | if len(fields) != 2 { 371 | return errTraceSyntax 372 | } 373 | file, line := fields[0], fields[1] 374 | if !strings.Contains(file, ".") { 375 | return errTraceSyntax 376 | } 377 | v, err := strconv.Atoi(line) 378 | if err != nil { 379 | return errTraceSyntax 380 | } 381 | if v <= 0 { 382 | return errors.New("negative or zero value for level") 383 | } 384 | logging.mu.Lock() 385 | defer logging.mu.Unlock() 386 | t.line = v 387 | t.file = file 388 | return nil 389 | } 390 | 391 | // flushSyncWriter is the interface satisfied by logging destinations. 392 | type flushSyncWriter interface { 393 | Flush() error 394 | Sync() error 395 | io.Writer 396 | } 397 | 398 | func init() { 399 | flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files") 400 | flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") 401 | flag.Var(&logging.verbosity, "v", "log level for V logs") 402 | flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") 403 | flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") 404 | flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") 405 | 406 | // Default stderrThreshold is ERROR. 407 | logging.stderrThreshold = errorLog 408 | 409 | logging.setVState(0, nil, false) 410 | go logging.flushDaemon() 411 | } 412 | 413 | // Flush flushes all pending log I/O. 414 | func Flush() { 415 | logging.lockAndFlushAll() 416 | } 417 | 418 | // loggingT collects all the global state of the logging setup. 419 | type loggingT struct { 420 | // Boolean flags. Not handled atomically because the flag.Value interface 421 | // does not let us avoid the =true, and that shorthand is necessary for 422 | // compatibility. TODO: does this matter enough to fix? Seems unlikely. 423 | toStderr bool // The -logtostderr flag. 424 | alsoToStderr bool // The -alsologtostderr flag. 425 | 426 | // Level flag. Handled atomically. 427 | stderrThreshold severity // The -stderrthreshold flag. 428 | 429 | // freeList is a list of byte buffers, maintained under freeListMu. 430 | freeList *buffer 431 | // freeListMu maintains the free list. It is separate from the main mutex 432 | // so buffers can be grabbed and printed to without holding the main lock, 433 | // for better parallelization. 434 | freeListMu sync.Mutex 435 | 436 | // mu protects the remaining elements of this structure and is 437 | // used to synchronize logging. 438 | mu sync.Mutex 439 | // file holds writer for each of the log types. 440 | file [numSeverity]flushSyncWriter 441 | // pcs is used in V to avoid an allocation when computing the caller's PC. 442 | pcs [1]uintptr 443 | // vmap is a cache of the V Level for each V() call site, identified by PC. 444 | // It is wiped whenever the vmodule flag changes state. 445 | vmap map[uintptr]Level 446 | // filterLength stores the length of the vmodule filter chain. If greater 447 | // than zero, it means vmodule is enabled. It may be read safely 448 | // using sync.LoadInt32, but is only modified under mu. 449 | filterLength int32 450 | // traceLocation is the state of the -log_backtrace_at flag. 451 | traceLocation traceLocation 452 | // These flags are modified only under lock, although verbosity may be fetched 453 | // safely using atomic.LoadInt32. 454 | vmodule moduleSpec // The state of the -vmodule flag. 455 | verbosity Level // V logging level, the value of the -v flag/ 456 | } 457 | 458 | // buffer holds a byte Buffer for reuse. The zero value is ready for use. 459 | type buffer struct { 460 | bytes.Buffer 461 | tmp [64]byte // temporary byte array for creating headers. 462 | next *buffer 463 | } 464 | 465 | var logging loggingT 466 | 467 | // setVState sets a consistent state for V logging. 468 | // l.mu is held. 469 | func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { 470 | // Turn verbosity off so V will not fire while we are in transition. 471 | logging.verbosity.set(0) 472 | // Ditto for filter length. 473 | atomic.StoreInt32(&logging.filterLength, 0) 474 | 475 | // Set the new filters and wipe the pc->Level map if the filter has changed. 476 | if setFilter { 477 | logging.vmodule.filter = filter 478 | logging.vmap = make(map[uintptr]Level) 479 | } 480 | 481 | // Things are consistent now, so enable filtering and verbosity. 482 | // They are enabled in order opposite to that in V. 483 | atomic.StoreInt32(&logging.filterLength, int32(len(filter))) 484 | logging.verbosity.set(verbosity) 485 | } 486 | 487 | // getBuffer returns a new, ready-to-use buffer. 488 | func (l *loggingT) getBuffer() *buffer { 489 | l.freeListMu.Lock() 490 | b := l.freeList 491 | if b != nil { 492 | l.freeList = b.next 493 | } 494 | l.freeListMu.Unlock() 495 | if b == nil { 496 | b = new(buffer) 497 | } else { 498 | b.next = nil 499 | b.Reset() 500 | } 501 | return b 502 | } 503 | 504 | // putBuffer returns a buffer to the free list. 505 | func (l *loggingT) putBuffer(b *buffer) { 506 | if b.Len() >= 256 { 507 | // Let big buffers die a natural death. 508 | return 509 | } 510 | l.freeListMu.Lock() 511 | b.next = l.freeList 512 | l.freeList = b 513 | l.freeListMu.Unlock() 514 | } 515 | 516 | var timeNow = time.Now // Stubbed out for testing. 517 | 518 | /* 519 | header formats a log header as defined by the C++ implementation. 520 | It returns a buffer containing the formatted header and the user's file and line number. 521 | The depth specifies how many stack frames above lives the source line to be identified in the log message. 522 | 523 | Log lines have this form: 524 | Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... 525 | where the fields are defined as follows: 526 | L A single character, representing the log level (eg 'I' for INFO) 527 | mm The month (zero padded; ie May is '05') 528 | dd The day (zero padded) 529 | hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds 530 | threadid The space-padded thread ID as returned by GetTID() 531 | file The file name 532 | line The line number 533 | msg The user-supplied message 534 | */ 535 | func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { 536 | _, file, line, ok := runtime.Caller(3 + depth) 537 | if !ok { 538 | file = "???" 539 | line = 1 540 | } else { 541 | slash := strings.LastIndex(file, "/") 542 | if slash >= 0 { 543 | file = file[slash+1:] 544 | } 545 | } 546 | return l.formatHeader(s, file, line), file, line 547 | } 548 | 549 | // formatHeader formats a log header using the provided file name and line number. 550 | func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { 551 | now := timeNow() 552 | if line < 0 { 553 | line = 0 // not a real line number, but acceptable to someDigits 554 | } 555 | if s > fatalLog { 556 | s = infoLog // for safety. 557 | } 558 | buf := l.getBuffer() 559 | 560 | // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. 561 | // It's worth about 3X. Fprintf is hard. 562 | _, month, day := now.Date() 563 | hour, minute, second := now.Clock() 564 | // Lmmdd hh:mm:ss.uuuuuu threadid file:line] 565 | buf.tmp[0] = severityChar[s] 566 | buf.twoDigits(1, int(month)) 567 | buf.twoDigits(3, day) 568 | buf.tmp[5] = ' ' 569 | buf.twoDigits(6, hour) 570 | buf.tmp[8] = ':' 571 | buf.twoDigits(9, minute) 572 | buf.tmp[11] = ':' 573 | buf.twoDigits(12, second) 574 | buf.tmp[14] = '.' 575 | buf.nDigits(6, 15, now.Nanosecond()/1000, '0') 576 | buf.tmp[21] = ' ' 577 | buf.nDigits(7, 22, pid, ' ') // TODO: should be TID 578 | buf.tmp[29] = ' ' 579 | buf.Write(buf.tmp[:30]) 580 | buf.WriteString(file) 581 | buf.tmp[0] = ':' 582 | n := buf.someDigits(1, line) 583 | buf.tmp[n+1] = ']' 584 | buf.tmp[n+2] = ' ' 585 | buf.Write(buf.tmp[:n+3]) 586 | return buf 587 | } 588 | 589 | // Some custom tiny helper functions to print the log header efficiently. 590 | 591 | const digits = "0123456789" 592 | 593 | // twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i]. 594 | func (buf *buffer) twoDigits(i, d int) { 595 | buf.tmp[i+1] = digits[d%10] 596 | d /= 10 597 | buf.tmp[i] = digits[d%10] 598 | } 599 | 600 | // nDigits formats an n-digit integer at buf.tmp[i], 601 | // padding with pad on the left. 602 | // It assumes d >= 0. 603 | func (buf *buffer) nDigits(n, i, d int, pad byte) { 604 | j := n - 1 605 | for ; j >= 0 && d > 0; j-- { 606 | buf.tmp[i+j] = digits[d%10] 607 | d /= 10 608 | } 609 | for ; j >= 0; j-- { 610 | buf.tmp[i+j] = pad 611 | } 612 | } 613 | 614 | // someDigits formats a zero-prefixed variable-width integer at buf.tmp[i]. 615 | func (buf *buffer) someDigits(i, d int) int { 616 | // Print into the top, then copy down. We know there's space for at least 617 | // a 10-digit number. 618 | j := len(buf.tmp) 619 | for { 620 | j-- 621 | buf.tmp[j] = digits[d%10] 622 | d /= 10 623 | if d == 0 { 624 | break 625 | } 626 | } 627 | return copy(buf.tmp[i:], buf.tmp[j:]) 628 | } 629 | 630 | func (l *loggingT) println(s severity, args ...interface{}) { 631 | buf, file, line := l.header(s, 0) 632 | fmt.Fprintln(buf, args...) 633 | l.output(s, buf, file, line, false) 634 | } 635 | 636 | func (l *loggingT) print(s severity, args ...interface{}) { 637 | l.printDepth(s, 1, args...) 638 | } 639 | 640 | func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) { 641 | buf, file, line := l.header(s, depth) 642 | fmt.Fprint(buf, args...) 643 | if buf.Bytes()[buf.Len()-1] != '\n' { 644 | buf.WriteByte('\n') 645 | } 646 | l.output(s, buf, file, line, false) 647 | } 648 | 649 | func (l *loggingT) printf(s severity, format string, args ...interface{}) { 650 | buf, file, line := l.header(s, 0) 651 | fmt.Fprintf(buf, format, args...) 652 | if buf.Bytes()[buf.Len()-1] != '\n' { 653 | buf.WriteByte('\n') 654 | } 655 | l.output(s, buf, file, line, false) 656 | } 657 | 658 | // printWithFileLine behaves like print but uses the provided file and line number. If 659 | // alsoLogToStderr is true, the log message always appears on standard error; it 660 | // will also appear in the log file unless --logtostderr is set. 661 | func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) { 662 | buf := l.formatHeader(s, file, line) 663 | fmt.Fprint(buf, args...) 664 | if buf.Bytes()[buf.Len()-1] != '\n' { 665 | buf.WriteByte('\n') 666 | } 667 | l.output(s, buf, file, line, alsoToStderr) 668 | } 669 | 670 | // output writes the data to the log files and releases the buffer. 671 | func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) { 672 | l.mu.Lock() 673 | if l.traceLocation.isSet() { 674 | if l.traceLocation.match(file, line) { 675 | buf.Write(stacks(false)) 676 | } 677 | } 678 | data := buf.Bytes() 679 | if !flag.Parsed() { 680 | os.Stderr.Write([]byte("ERROR: logging before flag.Parse: ")) 681 | os.Stderr.Write(data) 682 | } else if l.toStderr { 683 | os.Stderr.Write(data) 684 | } else { 685 | if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() { 686 | os.Stderr.Write(data) 687 | } 688 | if l.file[s] == nil { 689 | if err := l.createFiles(s); err != nil { 690 | os.Stderr.Write(data) // Make sure the message appears somewhere. 691 | l.exit(err) 692 | } 693 | } 694 | switch s { 695 | case fatalLog: 696 | l.file[fatalLog].Write(data) 697 | fallthrough 698 | case errorLog: 699 | l.file[errorLog].Write(data) 700 | fallthrough 701 | case warningLog: 702 | l.file[warningLog].Write(data) 703 | fallthrough 704 | case infoLog: 705 | l.file[infoLog].Write(data) 706 | } 707 | } 708 | if s == fatalLog { 709 | // If we got here via Exit rather than Fatal, print no stacks. 710 | if atomic.LoadUint32(&fatalNoStacks) > 0 { 711 | l.mu.Unlock() 712 | timeoutFlush(10 * time.Second) 713 | os.Exit(1) 714 | } 715 | // Dump all goroutine stacks before exiting. 716 | // First, make sure we see the trace for the current goroutine on standard error. 717 | // If -logtostderr has been specified, the loop below will do that anyway 718 | // as the first stack in the full dump. 719 | if !l.toStderr { 720 | os.Stderr.Write(stacks(false)) 721 | } 722 | // Write the stack trace for all goroutines to the files. 723 | trace := stacks(true) 724 | logExitFunc = func(error) {} // If we get a write error, we'll still exit below. 725 | for log := fatalLog; log >= infoLog; log-- { 726 | if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. 727 | f.Write(trace) 728 | } 729 | } 730 | l.mu.Unlock() 731 | timeoutFlush(10 * time.Second) 732 | os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. 733 | } 734 | l.putBuffer(buf) 735 | l.mu.Unlock() 736 | if stats := severityStats[s]; stats != nil { 737 | atomic.AddInt64(&stats.lines, 1) 738 | atomic.AddInt64(&stats.bytes, int64(len(data))) 739 | } 740 | } 741 | 742 | // timeoutFlush calls Flush and returns when it completes or after timeout 743 | // elapses, whichever happens first. This is needed because the hooks invoked 744 | // by Flush may deadlock when glog.Fatal is called from a hook that holds 745 | // a lock. 746 | func timeoutFlush(timeout time.Duration) { 747 | done := make(chan bool, 1) 748 | go func() { 749 | Flush() // calls logging.lockAndFlushAll() 750 | done <- true 751 | }() 752 | select { 753 | case <-done: 754 | case <-time.After(timeout): 755 | fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout) 756 | } 757 | } 758 | 759 | // stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. 760 | func stacks(all bool) []byte { 761 | // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. 762 | n := 10000 763 | if all { 764 | n = 100000 765 | } 766 | var trace []byte 767 | for i := 0; i < 5; i++ { 768 | trace = make([]byte, n) 769 | nbytes := runtime.Stack(trace, all) 770 | if nbytes < len(trace) { 771 | return trace[:nbytes] 772 | } 773 | n *= 2 774 | } 775 | return trace 776 | } 777 | 778 | // logExitFunc provides a simple mechanism to override the default behavior 779 | // of exiting on error. Used in testing and to guarantee we reach a required exit 780 | // for fatal logs. Instead, exit could be a function rather than a method but that 781 | // would make its use clumsier. 782 | var logExitFunc func(error) 783 | 784 | // exit is called if there is trouble creating or writing log files. 785 | // It flushes the logs and exits the program; there's no point in hanging around. 786 | // l.mu is held. 787 | func (l *loggingT) exit(err error) { 788 | fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err) 789 | // If logExitFunc is set, we do that instead of exiting. 790 | if logExitFunc != nil { 791 | logExitFunc(err) 792 | return 793 | } 794 | l.flushAll() 795 | os.Exit(2) 796 | } 797 | 798 | // syncBuffer joins a bufio.Writer to its underlying file, providing access to the 799 | // file's Sync method and providing a wrapper for the Write method that provides log 800 | // file rotation. There are conflicting methods, so the file cannot be embedded. 801 | // l.mu is held for all its methods. 802 | type syncBuffer struct { 803 | logger *loggingT 804 | *bufio.Writer 805 | file *os.File 806 | sev severity 807 | nbytes uint64 // The number of bytes written to this file 808 | } 809 | 810 | func (sb *syncBuffer) Sync() error { 811 | return sb.file.Sync() 812 | } 813 | 814 | func (sb *syncBuffer) Write(p []byte) (n int, err error) { 815 | if sb.nbytes+uint64(len(p)) >= MaxSize { 816 | if err := sb.rotateFile(time.Now()); err != nil { 817 | sb.logger.exit(err) 818 | } 819 | } 820 | n, err = sb.Writer.Write(p) 821 | sb.nbytes += uint64(n) 822 | if err != nil { 823 | sb.logger.exit(err) 824 | } 825 | return 826 | } 827 | 828 | // rotateFile closes the syncBuffer's file and starts a new one. 829 | func (sb *syncBuffer) rotateFile(now time.Time) error { 830 | if sb.file != nil { 831 | sb.Flush() 832 | sb.file.Close() 833 | } 834 | var err error 835 | sb.file, _, err = create(severityName[sb.sev], now) 836 | sb.nbytes = 0 837 | if err != nil { 838 | return err 839 | } 840 | 841 | sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) 842 | 843 | // Write header. 844 | var buf bytes.Buffer 845 | fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) 846 | fmt.Fprintf(&buf, "Running on machine: %s\n", host) 847 | fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) 848 | fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") 849 | n, err := sb.file.Write(buf.Bytes()) 850 | sb.nbytes += uint64(n) 851 | return err 852 | } 853 | 854 | // bufferSize sizes the buffer associated with each log file. It's large 855 | // so that log records can accumulate without the logging thread blocking 856 | // on disk I/O. The flushDaemon will block instead. 857 | const bufferSize = 256 * 1024 858 | 859 | // createFiles creates all the log files for severity from sev down to infoLog. 860 | // l.mu is held. 861 | func (l *loggingT) createFiles(sev severity) error { 862 | now := time.Now() 863 | // Files are created in decreasing severity order, so as soon as we find one 864 | // has already been created, we can stop. 865 | for s := sev; s >= infoLog && l.file[s] == nil; s-- { 866 | sb := &syncBuffer{ 867 | logger: l, 868 | sev: s, 869 | } 870 | if err := sb.rotateFile(now); err != nil { 871 | return err 872 | } 873 | l.file[s] = sb 874 | } 875 | return nil 876 | } 877 | 878 | const flushInterval = 30 * time.Second 879 | 880 | // flushDaemon periodically flushes the log file buffers. 881 | func (l *loggingT) flushDaemon() { 882 | for _ = range time.NewTicker(flushInterval).C { 883 | l.lockAndFlushAll() 884 | } 885 | } 886 | 887 | // lockAndFlushAll is like flushAll but locks l.mu first. 888 | func (l *loggingT) lockAndFlushAll() { 889 | l.mu.Lock() 890 | l.flushAll() 891 | l.mu.Unlock() 892 | } 893 | 894 | // flushAll flushes all the logs and attempts to "sync" their data to disk. 895 | // l.mu is held. 896 | func (l *loggingT) flushAll() { 897 | // Flush from fatal down, in case there's trouble flushing. 898 | for s := fatalLog; s >= infoLog; s-- { 899 | file := l.file[s] 900 | if file != nil { 901 | file.Flush() // ignore error 902 | file.Sync() // ignore error 903 | } 904 | } 905 | } 906 | 907 | // CopyStandardLogTo arranges for messages written to the Go "log" package's 908 | // default logs to also appear in the Google logs for the named and lower 909 | // severities. Subsequent changes to the standard log's default output location 910 | // or format may break this behavior. 911 | // 912 | // Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not 913 | // recognized, CopyStandardLogTo panics. 914 | func CopyStandardLogTo(name string) { 915 | sev, ok := severityByName(name) 916 | if !ok { 917 | panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) 918 | } 919 | // Set a log format that captures the user's file and line: 920 | // d.go:23: message 921 | stdLog.SetFlags(stdLog.Lshortfile) 922 | stdLog.SetOutput(logBridge(sev)) 923 | } 924 | 925 | // logBridge provides the Write method that enables CopyStandardLogTo to connect 926 | // Go's standard logs to the logs provided by this package. 927 | type logBridge severity 928 | 929 | // Write parses the standard logging line and passes its components to the 930 | // logger for severity(lb). 931 | func (lb logBridge) Write(b []byte) (n int, err error) { 932 | var ( 933 | file = "???" 934 | line = 1 935 | text string 936 | ) 937 | // Split "d.go:23: message" into "d.go", "23", and "message". 938 | if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { 939 | text = fmt.Sprintf("bad log format: %s", b) 940 | } else { 941 | file = string(parts[0]) 942 | text = string(parts[2][1:]) // skip leading space 943 | line, err = strconv.Atoi(string(parts[1])) 944 | if err != nil { 945 | text = fmt.Sprintf("bad line number: %s", b) 946 | line = 1 947 | } 948 | } 949 | // printWithFileLine with alsoToStderr=true, so standard log messages 950 | // always appear on standard error. 951 | logging.printWithFileLine(severity(lb), file, line, true, text) 952 | return len(b), nil 953 | } 954 | 955 | // setV computes and remembers the V level for a given PC 956 | // when vmodule is enabled. 957 | // File pattern matching takes the basename of the file, stripped 958 | // of its .go suffix, and uses filepath.Match, which is a little more 959 | // general than the *? matching used in C++. 960 | // l.mu is held. 961 | func (l *loggingT) setV(pc uintptr) Level { 962 | fn := runtime.FuncForPC(pc) 963 | file, _ := fn.FileLine(pc) 964 | // The file is something like /a/b/c/d.go. We want just the d. 965 | if strings.HasSuffix(file, ".go") { 966 | file = file[:len(file)-3] 967 | } 968 | if slash := strings.LastIndex(file, "/"); slash >= 0 { 969 | file = file[slash+1:] 970 | } 971 | for _, filter := range l.vmodule.filter { 972 | if filter.match(file) { 973 | l.vmap[pc] = filter.level 974 | return filter.level 975 | } 976 | } 977 | l.vmap[pc] = 0 978 | return 0 979 | } 980 | 981 | // Verbose is a boolean type that implements Infof (like Printf) etc. 982 | // See the documentation of V for more information. 983 | type Verbose bool 984 | 985 | // V reports whether verbosity at the call site is at least the requested level. 986 | // The returned value is a boolean of type Verbose, which implements Info, Infoln 987 | // and Infof. These methods will write to the Info log if called. 988 | // Thus, one may write either 989 | // if glog.V(2) { glog.Info("log this") } 990 | // or 991 | // glog.V(2).Info("log this") 992 | // The second form is shorter but the first is cheaper if logging is off because it does 993 | // not evaluate its arguments. 994 | // 995 | // Whether an individual call to V generates a log record depends on the setting of 996 | // the -v and --vmodule flags; both are off by default. If the level in the call to 997 | // V is at least the value of -v, or of -vmodule for the source file containing the 998 | // call, the V call will log. 999 | func V(level Level) Verbose { 1000 | // This function tries hard to be cheap unless there's work to do. 1001 | // The fast path is two atomic loads and compares. 1002 | 1003 | // Here is a cheap but safe test to see if V logging is enabled globally. 1004 | if logging.verbosity.get() >= level { 1005 | return Verbose(true) 1006 | } 1007 | 1008 | // It's off globally but it vmodule may still be set. 1009 | // Here is another cheap but safe test to see if vmodule is enabled. 1010 | if atomic.LoadInt32(&logging.filterLength) > 0 { 1011 | // Now we need a proper lock to use the logging structure. The pcs field 1012 | // is shared so we must lock before accessing it. This is fairly expensive, 1013 | // but if V logging is enabled we're slow anyway. 1014 | logging.mu.Lock() 1015 | defer logging.mu.Unlock() 1016 | if runtime.Callers(2, logging.pcs[:]) == 0 { 1017 | return Verbose(false) 1018 | } 1019 | v, ok := logging.vmap[logging.pcs[0]] 1020 | if !ok { 1021 | v = logging.setV(logging.pcs[0]) 1022 | } 1023 | return Verbose(v >= level) 1024 | } 1025 | return Verbose(false) 1026 | } 1027 | 1028 | // Info is equivalent to the global Info function, guarded by the value of v. 1029 | // See the documentation of V for usage. 1030 | func (v Verbose) Info(args ...interface{}) { 1031 | if v { 1032 | logging.print(infoLog, args...) 1033 | } 1034 | } 1035 | 1036 | // Infoln is equivalent to the global Infoln function, guarded by the value of v. 1037 | // See the documentation of V for usage. 1038 | func (v Verbose) Infoln(args ...interface{}) { 1039 | if v { 1040 | logging.println(infoLog, args...) 1041 | } 1042 | } 1043 | 1044 | // Infof is equivalent to the global Infof function, guarded by the value of v. 1045 | // See the documentation of V for usage. 1046 | func (v Verbose) Infof(format string, args ...interface{}) { 1047 | if v { 1048 | logging.printf(infoLog, format, args...) 1049 | } 1050 | } 1051 | 1052 | // Info logs to the INFO log. 1053 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 1054 | func Info(args ...interface{}) { 1055 | logging.print(infoLog, args...) 1056 | } 1057 | 1058 | // InfoDepth acts as Info but uses depth to determine which call frame to log. 1059 | // InfoDepth(0, "msg") is the same as Info("msg"). 1060 | func InfoDepth(depth int, args ...interface{}) { 1061 | logging.printDepth(infoLog, depth, args...) 1062 | } 1063 | 1064 | // Infoln logs to the INFO log. 1065 | // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. 1066 | func Infoln(args ...interface{}) { 1067 | logging.println(infoLog, args...) 1068 | } 1069 | 1070 | // Infof logs to the INFO log. 1071 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 1072 | func Infof(format string, args ...interface{}) { 1073 | logging.printf(infoLog, format, args...) 1074 | } 1075 | 1076 | // Warning logs to the WARNING and INFO logs. 1077 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 1078 | func Warning(args ...interface{}) { 1079 | logging.print(warningLog, args...) 1080 | } 1081 | 1082 | // WarningDepth acts as Warning but uses depth to determine which call frame to log. 1083 | // WarningDepth(0, "msg") is the same as Warning("msg"). 1084 | func WarningDepth(depth int, args ...interface{}) { 1085 | logging.printDepth(warningLog, depth, args...) 1086 | } 1087 | 1088 | // Warningln logs to the WARNING and INFO logs. 1089 | // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. 1090 | func Warningln(args ...interface{}) { 1091 | logging.println(warningLog, args...) 1092 | } 1093 | 1094 | // Warningf logs to the WARNING and INFO logs. 1095 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 1096 | func Warningf(format string, args ...interface{}) { 1097 | logging.printf(warningLog, format, args...) 1098 | } 1099 | 1100 | // Error logs to the ERROR, WARNING, and INFO logs. 1101 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 1102 | func Error(args ...interface{}) { 1103 | logging.print(errorLog, args...) 1104 | } 1105 | 1106 | // ErrorDepth acts as Error but uses depth to determine which call frame to log. 1107 | // ErrorDepth(0, "msg") is the same as Error("msg"). 1108 | func ErrorDepth(depth int, args ...interface{}) { 1109 | logging.printDepth(errorLog, depth, args...) 1110 | } 1111 | 1112 | // Errorln logs to the ERROR, WARNING, and INFO logs. 1113 | // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. 1114 | func Errorln(args ...interface{}) { 1115 | logging.println(errorLog, args...) 1116 | } 1117 | 1118 | // Errorf logs to the ERROR, WARNING, and INFO logs. 1119 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 1120 | func Errorf(format string, args ...interface{}) { 1121 | logging.printf(errorLog, format, args...) 1122 | } 1123 | 1124 | // Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, 1125 | // including a stack trace of all running goroutines, then calls os.Exit(255). 1126 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 1127 | func Fatal(args ...interface{}) { 1128 | logging.print(fatalLog, args...) 1129 | } 1130 | 1131 | // FatalDepth acts as Fatal but uses depth to determine which call frame to log. 1132 | // FatalDepth(0, "msg") is the same as Fatal("msg"). 1133 | func FatalDepth(depth int, args ...interface{}) { 1134 | logging.printDepth(fatalLog, depth, args...) 1135 | } 1136 | 1137 | // Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, 1138 | // including a stack trace of all running goroutines, then calls os.Exit(255). 1139 | // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. 1140 | func Fatalln(args ...interface{}) { 1141 | logging.println(fatalLog, args...) 1142 | } 1143 | 1144 | // Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, 1145 | // including a stack trace of all running goroutines, then calls os.Exit(255). 1146 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 1147 | func Fatalf(format string, args ...interface{}) { 1148 | logging.printf(fatalLog, format, args...) 1149 | } 1150 | 1151 | // fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks. 1152 | // It allows Exit and relatives to use the Fatal logs. 1153 | var fatalNoStacks uint32 1154 | 1155 | // Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). 1156 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 1157 | func Exit(args ...interface{}) { 1158 | atomic.StoreUint32(&fatalNoStacks, 1) 1159 | logging.print(fatalLog, args...) 1160 | } 1161 | 1162 | // ExitDepth acts as Exit but uses depth to determine which call frame to log. 1163 | // ExitDepth(0, "msg") is the same as Exit("msg"). 1164 | func ExitDepth(depth int, args ...interface{}) { 1165 | atomic.StoreUint32(&fatalNoStacks, 1) 1166 | logging.printDepth(fatalLog, depth, args...) 1167 | } 1168 | 1169 | // Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). 1170 | func Exitln(args ...interface{}) { 1171 | atomic.StoreUint32(&fatalNoStacks, 1) 1172 | logging.println(fatalLog, args...) 1173 | } 1174 | 1175 | // Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). 1176 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 1177 | func Exitf(format string, args ...interface{}) { 1178 | atomic.StoreUint32(&fatalNoStacks, 1) 1179 | logging.printf(fatalLog, format, args...) 1180 | } 1181 | --------------------------------------------------------------------------------