delete some unused files
This commit is contained in:
parent
dc10d3b6de
commit
206c82b9e4
22 changed files with 0 additions and 1570 deletions
1
go.mod
1
go.mod
|
@ -3,6 +3,5 @@ module github.com/DrJosh9000/sungrow
|
|||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/goburrow/serial v0.1.0
|
||||
github.com/prometheus/client_golang v1.9.0
|
||||
)
|
||||
|
|
|
@ -1,227 +0,0 @@
|
|||
// Copyright 2014 Quoc-Viet Nguyen. All rights reserved.
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the BSD license. See the LICENSE file for details.
|
||||
|
||||
package modbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
asciiStart = ":"
|
||||
asciiEnd = "\r\n"
|
||||
asciiMinSize = 3
|
||||
asciiMaxSize = 513
|
||||
|
||||
hexTable = "0123456789ABCDEF"
|
||||
)
|
||||
|
||||
// ASCIIClientHandler implements Packager and Transporter interface.
|
||||
type ASCIIClientHandler struct {
|
||||
asciiPackager
|
||||
asciiSerialTransporter
|
||||
}
|
||||
|
||||
// NewASCIIClientHandler allocates and initializes a ASCIIClientHandler.
|
||||
func NewASCIIClientHandler(address string) *ASCIIClientHandler {
|
||||
handler := &ASCIIClientHandler{}
|
||||
handler.Address = address
|
||||
handler.Timeout = serialTimeout
|
||||
handler.IdleTimeout = serialIdleTimeout
|
||||
return handler
|
||||
}
|
||||
|
||||
// ASCIIClient creates ASCII client with default handler and given connect string.
|
||||
func ASCIIClient(address string) Client {
|
||||
handler := NewASCIIClientHandler(address)
|
||||
return NewClient(handler)
|
||||
}
|
||||
|
||||
// asciiPackager implements Packager interface.
|
||||
type asciiPackager struct {
|
||||
SlaveId byte
|
||||
}
|
||||
|
||||
// Encode encodes PDU in a ASCII frame:
|
||||
// Start : 1 char
|
||||
// Address : 2 chars
|
||||
// Function : 2 chars
|
||||
// Data : 0 up to 2x252 chars
|
||||
// LRC : 2 chars
|
||||
// End : 2 chars
|
||||
func (mb *asciiPackager) Encode(pdu *ProtocolDataUnit) (adu []byte, err error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
if _, err = buf.WriteString(asciiStart); err != nil {
|
||||
return
|
||||
}
|
||||
if err = writeHex(&buf, []byte{mb.SlaveId, pdu.FunctionCode}); err != nil {
|
||||
return
|
||||
}
|
||||
if err = writeHex(&buf, pdu.Data); err != nil {
|
||||
return
|
||||
}
|
||||
// Exclude the beginning colon and terminating CRLF pair characters
|
||||
var lrc lrc
|
||||
lrc.reset()
|
||||
lrc.pushByte(mb.SlaveId).pushByte(pdu.FunctionCode).pushBytes(pdu.Data)
|
||||
if err = writeHex(&buf, []byte{lrc.value()}); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = buf.WriteString(asciiEnd); err != nil {
|
||||
return
|
||||
}
|
||||
adu = buf.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
// Verify verifies response length, frame boundary and slave id.
|
||||
func (mb *asciiPackager) Verify(aduRequest []byte, aduResponse []byte) (err error) {
|
||||
length := len(aduResponse)
|
||||
// Minimum size (including address, function and LRC)
|
||||
if length < asciiMinSize+6 {
|
||||
err = fmt.Errorf("modbus: response length '%v' does not meet minimum '%v'", length, 9)
|
||||
return
|
||||
}
|
||||
// Length excluding colon must be an even number
|
||||
if length%2 != 1 {
|
||||
err = fmt.Errorf("modbus: response length '%v' is not an even number", length-1)
|
||||
return
|
||||
}
|
||||
// First char must be a colon
|
||||
str := string(aduResponse[0:len(asciiStart)])
|
||||
if str != asciiStart {
|
||||
err = fmt.Errorf("modbus: response frame '%v'... is not started with '%v'", str, asciiStart)
|
||||
return
|
||||
}
|
||||
// 2 last chars must be \r\n
|
||||
str = string(aduResponse[len(aduResponse)-len(asciiEnd):])
|
||||
if str != asciiEnd {
|
||||
err = fmt.Errorf("modbus: response frame ...'%v' is not ended with '%v'", str, asciiEnd)
|
||||
return
|
||||
}
|
||||
// Slave id
|
||||
responseVal, err := readHex(aduResponse[1:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
requestVal, err := readHex(aduRequest[1:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if responseVal != requestVal {
|
||||
err = fmt.Errorf("modbus: response slave id '%v' does not match request '%v'", responseVal, requestVal)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Decode extracts PDU from ASCII frame and verify LRC.
|
||||
func (mb *asciiPackager) Decode(adu []byte) (pdu *ProtocolDataUnit, err error) {
|
||||
pdu = &ProtocolDataUnit{}
|
||||
// Slave address
|
||||
address, err := readHex(adu[1:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Function code
|
||||
if pdu.FunctionCode, err = readHex(adu[3:]); err != nil {
|
||||
return
|
||||
}
|
||||
// Data
|
||||
dataEnd := len(adu) - 4
|
||||
data := adu[5:dataEnd]
|
||||
pdu.Data = make([]byte, hex.DecodedLen(len(data)))
|
||||
if _, err = hex.Decode(pdu.Data, data); err != nil {
|
||||
return
|
||||
}
|
||||
// LRC
|
||||
lrcVal, err := readHex(adu[dataEnd:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Calculate checksum
|
||||
var lrc lrc
|
||||
lrc.reset()
|
||||
lrc.pushByte(address).pushByte(pdu.FunctionCode).pushBytes(pdu.Data)
|
||||
if lrcVal != lrc.value() {
|
||||
err = fmt.Errorf("modbus: response lrc '%v' does not match expected '%v'", lrcVal, lrc.value())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// asciiSerialTransporter implements Transporter interface.
|
||||
type asciiSerialTransporter struct {
|
||||
serialPort
|
||||
}
|
||||
|
||||
func (mb *asciiSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) {
|
||||
mb.serialPort.mu.Lock()
|
||||
defer mb.serialPort.mu.Unlock()
|
||||
|
||||
// Make sure port is connected
|
||||
if err = mb.serialPort.connect(); err != nil {
|
||||
return
|
||||
}
|
||||
// Start the timer to close when idle
|
||||
mb.serialPort.lastActivity = time.Now()
|
||||
mb.serialPort.startCloseTimer()
|
||||
|
||||
// Send the request
|
||||
mb.serialPort.logf("modbus: sending %q\n", aduRequest)
|
||||
if _, err = mb.port.Write(aduRequest); err != nil {
|
||||
return
|
||||
}
|
||||
// Get the response
|
||||
var n int
|
||||
var data [asciiMaxSize]byte
|
||||
length := 0
|
||||
for {
|
||||
if n, err = mb.port.Read(data[length:]); err != nil {
|
||||
return
|
||||
}
|
||||
length += n
|
||||
if length >= asciiMaxSize || n == 0 {
|
||||
break
|
||||
}
|
||||
// Expect end of frame in the data received
|
||||
if length > asciiMinSize {
|
||||
if string(data[length-len(asciiEnd):length]) == asciiEnd {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
aduResponse = data[:length]
|
||||
mb.serialPort.logf("modbus: received %q\n", aduResponse)
|
||||
return
|
||||
}
|
||||
|
||||
// writeHex encodes byte to string in hexadecimal, e.g. 0xA5 => "A5"
|
||||
// (encoding/hex only supports lowercase string).
|
||||
func writeHex(buf *bytes.Buffer, value []byte) (err error) {
|
||||
var str [2]byte
|
||||
for _, v := range value {
|
||||
str[0] = hexTable[v>>4]
|
||||
str[1] = hexTable[v&0x0F]
|
||||
|
||||
if _, err = buf.Write(str[:]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readHex decodes hexa string to byte, e.g. "8C" => 0x8C.
|
||||
func readHex(data []byte) (value byte, err error) {
|
||||
var dst [1]byte
|
||||
if _, err = hex.Decode(dst[:], data[0:2]); err != nil {
|
||||
return
|
||||
}
|
||||
value = dst[0]
|
||||
return
|
||||
}
|
|
@ -1,210 +0,0 @@
|
|||
// Copyright 2014 Quoc-Viet Nguyen. All rights reserved.
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the BSD license. See the LICENSE file for details.
|
||||
|
||||
package modbus
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
rtuMinSize = 4
|
||||
rtuMaxSize = 256
|
||||
|
||||
rtuExceptionSize = 5
|
||||
)
|
||||
|
||||
// RTUClientHandler implements Packager and Transporter interface.
|
||||
type RTUClientHandler struct {
|
||||
rtuPackager
|
||||
rtuSerialTransporter
|
||||
}
|
||||
|
||||
// NewRTUClientHandler allocates and initializes a RTUClientHandler.
|
||||
func NewRTUClientHandler(address string) *RTUClientHandler {
|
||||
handler := &RTUClientHandler{}
|
||||
handler.Address = address
|
||||
handler.Timeout = serialTimeout
|
||||
handler.IdleTimeout = serialIdleTimeout
|
||||
return handler
|
||||
}
|
||||
|
||||
// RTUClient creates RTU client with default handler and given connect string.
|
||||
func RTUClient(address string) Client {
|
||||
handler := NewRTUClientHandler(address)
|
||||
return NewClient(handler)
|
||||
}
|
||||
|
||||
// rtuPackager implements Packager interface.
|
||||
type rtuPackager struct {
|
||||
SlaveId byte
|
||||
}
|
||||
|
||||
// Encode encodes PDU in a RTU frame:
|
||||
// Slave Address : 1 byte
|
||||
// Function : 1 byte
|
||||
// Data : 0 up to 252 bytes
|
||||
// CRC : 2 byte
|
||||
func (mb *rtuPackager) Encode(pdu *ProtocolDataUnit) (adu []byte, err error) {
|
||||
length := len(pdu.Data) + 4
|
||||
if length > rtuMaxSize {
|
||||
err = fmt.Errorf("modbus: length of data '%v' must not be bigger than '%v'", length, rtuMaxSize)
|
||||
return
|
||||
}
|
||||
adu = make([]byte, length)
|
||||
|
||||
adu[0] = mb.SlaveId
|
||||
adu[1] = pdu.FunctionCode
|
||||
copy(adu[2:], pdu.Data)
|
||||
|
||||
// Append crc
|
||||
var crc crc
|
||||
crc.reset().pushBytes(adu[0 : length-2])
|
||||
checksum := crc.value()
|
||||
|
||||
adu[length-1] = byte(checksum >> 8)
|
||||
adu[length-2] = byte(checksum)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify verifies response length and slave id.
|
||||
func (mb *rtuPackager) Verify(aduRequest []byte, aduResponse []byte) (err error) {
|
||||
length := len(aduResponse)
|
||||
// Minimum size (including address, function and CRC)
|
||||
if length < rtuMinSize {
|
||||
err = fmt.Errorf("modbus: response length '%v' does not meet minimum '%v'", length, rtuMinSize)
|
||||
return
|
||||
}
|
||||
// Slave address must match
|
||||
if aduResponse[0] != aduRequest[0] {
|
||||
err = fmt.Errorf("modbus: response slave id '%v' does not match request '%v'", aduResponse[0], aduRequest[0])
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Decode extracts PDU from RTU frame and verify CRC.
|
||||
func (mb *rtuPackager) Decode(adu []byte) (pdu *ProtocolDataUnit, err error) {
|
||||
length := len(adu)
|
||||
// Calculate checksum
|
||||
var crc crc
|
||||
crc.reset().pushBytes(adu[0 : length-2])
|
||||
checksum := uint16(adu[length-1])<<8 | uint16(adu[length-2])
|
||||
if checksum != crc.value() {
|
||||
err = fmt.Errorf("modbus: response crc '%v' does not match expected '%v'", checksum, crc.value())
|
||||
return
|
||||
}
|
||||
// Function code & data
|
||||
pdu = &ProtocolDataUnit{}
|
||||
pdu.FunctionCode = adu[1]
|
||||
pdu.Data = adu[2 : length-2]
|
||||
return
|
||||
}
|
||||
|
||||
// rtuSerialTransporter implements Transporter interface.
|
||||
type rtuSerialTransporter struct {
|
||||
serialPort
|
||||
}
|
||||
|
||||
func (mb *rtuSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) {
|
||||
// Make sure port is connected
|
||||
if err = mb.serialPort.connect(); err != nil {
|
||||
return
|
||||
}
|
||||
// Start the timer to close when idle
|
||||
mb.serialPort.lastActivity = time.Now()
|
||||
mb.serialPort.startCloseTimer()
|
||||
|
||||
// Send the request
|
||||
mb.serialPort.logf("modbus: sending % x\n", aduRequest)
|
||||
if _, err = mb.port.Write(aduRequest); err != nil {
|
||||
return
|
||||
}
|
||||
function := aduRequest[1]
|
||||
functionFail := aduRequest[1] & 0x80
|
||||
bytesToRead := calculateResponseLength(aduRequest)
|
||||
time.Sleep(mb.calculateDelay(len(aduRequest) + bytesToRead))
|
||||
|
||||
var n int
|
||||
var n1 int
|
||||
var data [rtuMaxSize]byte
|
||||
//We first read the minimum length and then read either the full package
|
||||
//or the error package, depending on the error status (byte 2 of the response)
|
||||
n, err = io.ReadAtLeast(mb.port, data[:], rtuMinSize)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//if the function is correct
|
||||
if data[1] == function {
|
||||
//we read the rest of the bytes
|
||||
if n < bytesToRead {
|
||||
if bytesToRead > rtuMinSize && bytesToRead <= rtuMaxSize {
|
||||
if bytesToRead > n {
|
||||
n1, err = io.ReadFull(mb.port, data[n:bytesToRead])
|
||||
n += n1
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if data[1] == functionFail {
|
||||
//for error we need to read 5 bytes
|
||||
if n < rtuExceptionSize {
|
||||
n1, err = io.ReadFull(mb.port, data[n:rtuExceptionSize])
|
||||
}
|
||||
n += n1
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
aduResponse = data[:n]
|
||||
mb.serialPort.logf("modbus: received % x\n", aduResponse)
|
||||
return
|
||||
}
|
||||
|
||||
// calculateDelay roughly calculates time needed for the next frame.
|
||||
// See MODBUS over Serial Line - Specification and Implementation Guide (page 13).
|
||||
func (mb *rtuSerialTransporter) calculateDelay(chars int) time.Duration {
|
||||
var characterDelay, frameDelay int // us
|
||||
|
||||
if mb.BaudRate <= 0 || mb.BaudRate > 19200 {
|
||||
characterDelay = 750
|
||||
frameDelay = 1750
|
||||
} else {
|
||||
characterDelay = 15000000 / mb.BaudRate
|
||||
frameDelay = 35000000 / mb.BaudRate
|
||||
}
|
||||
return time.Duration(characterDelay*chars+frameDelay) * time.Microsecond
|
||||
}
|
||||
|
||||
func calculateResponseLength(adu []byte) int {
|
||||
length := rtuMinSize
|
||||
switch adu[1] {
|
||||
case FuncCodeReadDiscreteInputs,
|
||||
FuncCodeReadCoils:
|
||||
count := int(binary.BigEndian.Uint16(adu[4:]))
|
||||
length += 1 + count/8
|
||||
if count%8 != 0 {
|
||||
length++
|
||||
}
|
||||
case FuncCodeReadInputRegisters,
|
||||
FuncCodeReadHoldingRegisters,
|
||||
FuncCodeReadWriteMultipleRegisters:
|
||||
count := int(binary.BigEndian.Uint16(adu[4:]))
|
||||
length += 1 + count*2
|
||||
case FuncCodeWriteSingleCoil,
|
||||
FuncCodeWriteMultipleCoils,
|
||||
FuncCodeWriteSingleRegister,
|
||||
FuncCodeWriteMultipleRegisters:
|
||||
length += 4
|
||||
case FuncCodeMaskWriteRegister:
|
||||
length += 6
|
||||
case FuncCodeReadFIFOQueue:
|
||||
// undetermined
|
||||
default:
|
||||
}
|
||||
return length
|
||||
}
|
102
modbus/serial.go
102
modbus/serial.go
|
@ -1,102 +0,0 @@
|
|||
// Copyright 2014 Quoc-Viet Nguyen. All rights reserved.
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the BSD license. See the LICENSE file for details.
|
||||
|
||||
package modbus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/goburrow/serial"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default timeout
|
||||
serialTimeout = 5 * time.Second
|
||||
serialIdleTimeout = 60 * time.Second
|
||||
)
|
||||
|
||||
// serialPort has configuration and I/O controller.
|
||||
type serialPort struct {
|
||||
// Serial port configuration.
|
||||
serial.Config
|
||||
|
||||
Logger *log.Logger
|
||||
IdleTimeout time.Duration
|
||||
|
||||
mu sync.Mutex
|
||||
// port is platform-dependent data structure for serial port.
|
||||
port io.ReadWriteCloser
|
||||
lastActivity time.Time
|
||||
closeTimer *time.Timer
|
||||
}
|
||||
|
||||
func (mb *serialPort) Connect() (err error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
|
||||
return mb.connect()
|
||||
}
|
||||
|
||||
// connect connects to the serial port if it is not connected. Caller must hold the mutex.
|
||||
func (mb *serialPort) connect() error {
|
||||
if mb.port == nil {
|
||||
port, err := serial.Open(&mb.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mb.port = port
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mb *serialPort) Close() (err error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
|
||||
return mb.close()
|
||||
}
|
||||
|
||||
// close closes the serial port if it is connected. Caller must hold the mutex.
|
||||
func (mb *serialPort) close() (err error) {
|
||||
if mb.port != nil {
|
||||
err = mb.port.Close()
|
||||
mb.port = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (mb *serialPort) logf(format string, v ...interface{}) {
|
||||
if mb.Logger != nil {
|
||||
mb.Logger.Printf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func (mb *serialPort) startCloseTimer() {
|
||||
if mb.IdleTimeout <= 0 {
|
||||
return
|
||||
}
|
||||
if mb.closeTimer == nil {
|
||||
mb.closeTimer = time.AfterFunc(mb.IdleTimeout, mb.closeIdle)
|
||||
} else {
|
||||
mb.closeTimer.Reset(mb.IdleTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// closeIdle closes the connection if last activity is passed behind IdleTimeout.
|
||||
func (mb *serialPort) closeIdle() {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
|
||||
if mb.IdleTimeout <= 0 {
|
||||
return
|
||||
}
|
||||
idle := time.Now().Sub(mb.lastActivity)
|
||||
if idle >= mb.IdleTimeout {
|
||||
mb.logf("modbus: closing connection due to idle timeout: %v", idle)
|
||||
mb.close()
|
||||
}
|
||||
}
|
3
vendor/github.com/goburrow/serial/.gitattributes
generated
vendored
3
vendor/github.com/goburrow/serial/.gitattributes
generated
vendored
|
@ -1,3 +0,0 @@
|
|||
* text=auto
|
||||
*.go text
|
||||
*.bat text eol=crlf
|
13
vendor/github.com/goburrow/serial/.travis.yml
generated
vendored
13
vendor/github.com/goburrow/serial/.travis.yml
generated
vendored
|
@ -1,13 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.7
|
||||
- 1.8
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
- GOOS=linux go build
|
||||
- GOOS=darwin go build
|
||||
- GOOS=freebsd go build
|
||||
- GOOS=windows go build
|
19
vendor/github.com/goburrow/serial/LICENSE
generated
vendored
19
vendor/github.com/goburrow/serial/LICENSE
generated
vendored
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2015 Quoc-Viet Nguyen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
34
vendor/github.com/goburrow/serial/README.md
generated
vendored
34
vendor/github.com/goburrow/serial/README.md
generated
vendored
|
@ -1,34 +0,0 @@
|
|||
# serial [![Build Status](https://travis-ci.org/goburrow/serial.svg?branch=master)](https://travis-ci.org/goburrow/serial) [![GoDoc](https://godoc.org/github.com/goburrow/serial?status.svg)](https://godoc.org/github.com/goburrow/serial)
|
||||
## Example
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/goburrow/serial"
|
||||
)
|
||||
|
||||
func main() {
|
||||
port, err := serial.Open(&serial.Config{Address: "/dev/ttyUSB0"})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer port.Close()
|
||||
|
||||
_, err = port.Write([]byte("serial"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
## Testing
|
||||
|
||||
### Linux and Mac OS
|
||||
- `socat -d -d pty,raw,echo=0 pty,raw,echo=0`
|
||||
- on Mac OS, the socat command can be installed using homebrew:
|
||||
````brew install socat````
|
||||
|
||||
### Windows
|
||||
- [Null-modem emulator](http://com0com.sourceforge.net/)
|
||||
- [Terminal](https://sites.google.com/site/terminalbpp/)
|
2
vendor/github.com/goburrow/serial/generate.bat
generated
vendored
2
vendor/github.com/goburrow/serial/generate.bat
generated
vendored
|
@ -1,2 +0,0 @@
|
|||
go tool cgo -godefs types_windows.go | gofmt > ztypes_windows.go
|
||||
go generate syscall_windows.go
|
64
vendor/github.com/goburrow/serial/serial.go
generated
vendored
64
vendor/github.com/goburrow/serial/serial.go
generated
vendored
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
Package serial provides a cross-platform serial reader and writer.
|
||||
*/
|
||||
package serial
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrTimeout is occurred when timing out.
|
||||
ErrTimeout = errors.New("serial: timeout")
|
||||
)
|
||||
|
||||
// Config is common configuration for serial port.
|
||||
type Config struct {
|
||||
// Device path (/dev/ttyS0)
|
||||
Address string
|
||||
// Baud rate (default 19200)
|
||||
BaudRate int
|
||||
// Data bits: 5, 6, 7 or 8 (default 8)
|
||||
DataBits int
|
||||
// Stop bits: 1 or 2 (default 1)
|
||||
StopBits int
|
||||
// Parity: N - None, E - Even, O - Odd (default E)
|
||||
// (The use of no parity requires 2 stop bits.)
|
||||
Parity string
|
||||
// Read (Write) timeout.
|
||||
Timeout time.Duration
|
||||
// Configuration related to RS485
|
||||
RS485 RS485Config
|
||||
}
|
||||
|
||||
// platform independent RS485 config. Thie structure is ignored unless Enable is true.
|
||||
type RS485Config struct {
|
||||
// Enable RS485 support
|
||||
Enabled bool
|
||||
// Delay RTS prior to send
|
||||
DelayRtsBeforeSend time.Duration
|
||||
// Delay RTS after send
|
||||
DelayRtsAfterSend time.Duration
|
||||
// Set RTS high during send
|
||||
RtsHighDuringSend bool
|
||||
// Set RTS high after send
|
||||
RtsHighAfterSend bool
|
||||
// Rx during Tx
|
||||
RxDuringTx bool
|
||||
}
|
||||
|
||||
// Port is the interface for controlling serial port.
|
||||
type Port interface {
|
||||
io.ReadWriteCloser
|
||||
// Connect connects to the serial port.
|
||||
Open(*Config) error
|
||||
}
|
||||
|
||||
// Open opens a serial port.
|
||||
func Open(c *Config) (p Port, err error) {
|
||||
p = New()
|
||||
err = p.Open(c)
|
||||
return
|
||||
}
|
93
vendor/github.com/goburrow/serial/serial_bsd.go
generated
vendored
93
vendor/github.com/goburrow/serial/serial_bsd.go
generated
vendored
|
@ -1,93 +0,0 @@
|
|||
// +build freebsd openbsd netbsd
|
||||
|
||||
package serial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var baudRates = map[int]uint32{
|
||||
50: syscall.B50,
|
||||
75: syscall.B75,
|
||||
110: syscall.B110,
|
||||
134: syscall.B134,
|
||||
150: syscall.B150,
|
||||
200: syscall.B200,
|
||||
300: syscall.B300,
|
||||
600: syscall.B600,
|
||||
1200: syscall.B1200,
|
||||
1800: syscall.B1800,
|
||||
2400: syscall.B2400,
|
||||
4800: syscall.B4800,
|
||||
9600: syscall.B9600,
|
||||
19200: syscall.B19200,
|
||||
38400: syscall.B38400,
|
||||
57600: syscall.B57600,
|
||||
115200: syscall.B115200,
|
||||
230400: syscall.B230400,
|
||||
460800: syscall.B460800,
|
||||
}
|
||||
|
||||
var charSizes = map[int]uint32{
|
||||
5: syscall.CS5,
|
||||
6: syscall.CS6,
|
||||
7: syscall.CS7,
|
||||
8: syscall.CS8,
|
||||
}
|
||||
|
||||
// syscallSelect is a wapper for syscall.Select that only returns error.
|
||||
func syscallSelect(n int, r *syscall.FdSet, w *syscall.FdSet, e *syscall.FdSet, tv *syscall.Timeval) error {
|
||||
return syscall.Select(n, r, w, e, tv)
|
||||
}
|
||||
|
||||
// tcsetattr sets terminal file descriptor parameters.
|
||||
// See man tcsetattr(3).
|
||||
func tcsetattr(fd int, termios *syscall.Termios) (err error) {
|
||||
r, _, errno := syscall.Syscall(uintptr(syscall.SYS_IOCTL),
|
||||
uintptr(fd), uintptr(syscall.TIOCSETA), uintptr(unsafe.Pointer(termios)))
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
return
|
||||
}
|
||||
if r != 0 {
|
||||
err = fmt.Errorf("tcsetattr failed %v", r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// tcgetattr gets terminal file descriptor parameters.
|
||||
// See man tcgetattr(3).
|
||||
func tcgetattr(fd int, termios *syscall.Termios) (err error) {
|
||||
r, _, errno := syscall.Syscall(uintptr(syscall.SYS_IOCTL),
|
||||
uintptr(fd), uintptr(syscall.TIOCGETA), uintptr(unsafe.Pointer(termios)))
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
return
|
||||
}
|
||||
if r != 0 {
|
||||
err = fmt.Errorf("tcgetattr failed %v", r)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// fdget returns index and offset of fd in fds.
|
||||
func fdget(fd int, fds *syscall.FdSet) (index, offset int) {
|
||||
index = fd / (syscall.FD_SETSIZE / len(fds.X__fds_bits)) % len(fds.X__fds_bits)
|
||||
offset = fd % (syscall.FD_SETSIZE / len(fds.X__fds_bits))
|
||||
return
|
||||
}
|
||||
|
||||
// fdset implements FD_SET macro.
|
||||
func fdset(fd int, fds *syscall.FdSet) {
|
||||
idx, pos := fdget(fd, fds)
|
||||
fds.X__fds_bits[idx] = 1 << uint(pos)
|
||||
}
|
||||
|
||||
// fdisset implements FD_ISSET macro.
|
||||
func fdisset(fd int, fds *syscall.FdSet) bool {
|
||||
idx, pos := fdget(fd, fds)
|
||||
return fds.X__fds_bits[idx]&(1<<uint(pos)) != 0
|
||||
}
|
90
vendor/github.com/goburrow/serial/serial_darwin.go
generated
vendored
90
vendor/github.com/goburrow/serial/serial_darwin.go
generated
vendored
|
@ -1,90 +0,0 @@
|
|||
package serial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var baudRates = map[int]uint64{
|
||||
50: syscall.B50,
|
||||
75: syscall.B75,
|
||||
110: syscall.B110,
|
||||
134: syscall.B134,
|
||||
150: syscall.B150,
|
||||
200: syscall.B200,
|
||||
300: syscall.B300,
|
||||
600: syscall.B600,
|
||||
1200: syscall.B1200,
|
||||
1800: syscall.B1800,
|
||||
2400: syscall.B2400,
|
||||
4800: syscall.B4800,
|
||||
9600: syscall.B9600,
|
||||
19200: syscall.B19200,
|
||||
38400: syscall.B38400,
|
||||
57600: syscall.B57600,
|
||||
115200: syscall.B115200,
|
||||
230400: syscall.B230400,
|
||||
}
|
||||
|
||||
var charSizes = map[int]uint64{
|
||||
5: syscall.CS5,
|
||||
6: syscall.CS6,
|
||||
7: syscall.CS7,
|
||||
8: syscall.CS8,
|
||||
}
|
||||
|
||||
// syscallSelect is a wapper for syscall.Select that only returns error.
|
||||
func syscallSelect(n int, r *syscall.FdSet, w *syscall.FdSet, e *syscall.FdSet, tv *syscall.Timeval) error {
|
||||
return syscall.Select(n, r, w, e, tv)
|
||||
}
|
||||
|
||||
// tcsetattr sets terminal file descriptor parameters.
|
||||
// See man tcsetattr(3).
|
||||
func tcsetattr(fd int, termios *syscall.Termios) (err error) {
|
||||
r, _, errno := syscall.Syscall(uintptr(syscall.SYS_IOCTL),
|
||||
uintptr(fd), uintptr(syscall.TIOCSETA), uintptr(unsafe.Pointer(termios)))
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
return
|
||||
}
|
||||
if r != 0 {
|
||||
err = fmt.Errorf("tcsetattr failed %v", r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// tcgetattr gets terminal file descriptor parameters.
|
||||
// See man tcgetattr(3).
|
||||
func tcgetattr(fd int, termios *syscall.Termios) (err error) {
|
||||
r, _, errno := syscall.Syscall(uintptr(syscall.SYS_IOCTL),
|
||||
uintptr(fd), uintptr(syscall.TIOCGETA), uintptr(unsafe.Pointer(termios)))
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
return
|
||||
}
|
||||
if r != 0 {
|
||||
err = fmt.Errorf("tcgetattr failed %v", r)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// fdget returns index and offset of fd in fds.
|
||||
func fdget(fd int, fds *syscall.FdSet) (index, offset int) {
|
||||
index = fd / (syscall.FD_SETSIZE / len(fds.Bits)) % len(fds.Bits)
|
||||
offset = fd % (syscall.FD_SETSIZE / len(fds.Bits))
|
||||
return
|
||||
}
|
||||
|
||||
// fdset implements FD_SET macro.
|
||||
func fdset(fd int, fds *syscall.FdSet) {
|
||||
idx, pos := fdget(fd, fds)
|
||||
fds.Bits[idx] = 1 << uint(pos)
|
||||
}
|
||||
|
||||
// fdisset implements FD_ISSET macro.
|
||||
func fdisset(fd int, fds *syscall.FdSet) bool {
|
||||
idx, pos := fdget(fd, fds)
|
||||
return fds.Bits[idx]&(1<<uint(pos)) != 0
|
||||
}
|
103
vendor/github.com/goburrow/serial/serial_linux.go
generated
vendored
103
vendor/github.com/goburrow/serial/serial_linux.go
generated
vendored
|
@ -1,103 +0,0 @@
|
|||
package serial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var baudRates = map[int]uint32{
|
||||
50: syscall.B50,
|
||||
75: syscall.B75,
|
||||
110: syscall.B110,
|
||||
134: syscall.B134,
|
||||
150: syscall.B150,
|
||||
200: syscall.B200,
|
||||
300: syscall.B300,
|
||||
600: syscall.B600,
|
||||
1200: syscall.B1200,
|
||||
1800: syscall.B1800,
|
||||
2400: syscall.B2400,
|
||||
4800: syscall.B4800,
|
||||
9600: syscall.B9600,
|
||||
19200: syscall.B19200,
|
||||
38400: syscall.B38400,
|
||||
57600: syscall.B57600,
|
||||
115200: syscall.B115200,
|
||||
230400: syscall.B230400,
|
||||
460800: syscall.B460800,
|
||||
500000: syscall.B500000,
|
||||
576000: syscall.B576000,
|
||||
921600: syscall.B921600,
|
||||
1000000: syscall.B1000000,
|
||||
1152000: syscall.B1152000,
|
||||
1500000: syscall.B1500000,
|
||||
2000000: syscall.B2000000,
|
||||
2500000: syscall.B2500000,
|
||||
3000000: syscall.B3000000,
|
||||
3500000: syscall.B3500000,
|
||||
4000000: syscall.B4000000,
|
||||
}
|
||||
|
||||
var charSizes = map[int]uint32{
|
||||
5: syscall.CS5,
|
||||
6: syscall.CS6,
|
||||
7: syscall.CS7,
|
||||
8: syscall.CS8,
|
||||
}
|
||||
|
||||
// syscallSelect is a wapper for syscall.Select that only returns error.
|
||||
func syscallSelect(n int, r *syscall.FdSet, w *syscall.FdSet, e *syscall.FdSet, tv *syscall.Timeval) error {
|
||||
_, err := syscall.Select(n, r, w, e, tv)
|
||||
return err
|
||||
}
|
||||
|
||||
// tcsetattr sets terminal file descriptor parameters.
|
||||
// See man tcsetattr(3).
|
||||
func tcsetattr(fd int, termios *syscall.Termios) (err error) {
|
||||
r, _, errno := syscall.Syscall(uintptr(syscall.SYS_IOCTL),
|
||||
uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(termios)))
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
return
|
||||
}
|
||||
if r != 0 {
|
||||
err = fmt.Errorf("tcsetattr failed %v", r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// tcgetattr gets terminal file descriptor parameters.
|
||||
// See man tcgetattr(3).
|
||||
func tcgetattr(fd int, termios *syscall.Termios) (err error) {
|
||||
r, _, errno := syscall.Syscall(uintptr(syscall.SYS_IOCTL),
|
||||
uintptr(fd), uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(termios)))
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
return
|
||||
}
|
||||
if r != 0 {
|
||||
err = fmt.Errorf("tcgetattr failed %v", r)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// fdget returns index and offset of fd in fds.
|
||||
func fdget(fd int, fds *syscall.FdSet) (index, offset int) {
|
||||
index = fd / (syscall.FD_SETSIZE / len(fds.Bits)) % len(fds.Bits)
|
||||
offset = fd % (syscall.FD_SETSIZE / len(fds.Bits))
|
||||
return
|
||||
}
|
||||
|
||||
// fdset implements FD_SET macro.
|
||||
func fdset(fd int, fds *syscall.FdSet) {
|
||||
idx, pos := fdget(fd, fds)
|
||||
fds.Bits[idx] = 1 << uint(pos)
|
||||
}
|
||||
|
||||
// fdisset implements FD_ISSET macro.
|
||||
func fdisset(fd int, fds *syscall.FdSet) bool {
|
||||
idx, pos := fdget(fd, fds)
|
||||
return fds.Bits[idx]&(1<<uint(pos)) != 0
|
||||
}
|
265
vendor/github.com/goburrow/serial/serial_posix.go
generated
vendored
265
vendor/github.com/goburrow/serial/serial_posix.go
generated
vendored
|
@ -1,265 +0,0 @@
|
|||
// +build darwin linux freebsd openbsd netbsd
|
||||
|
||||
package serial
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// port implements Port interface.
|
||||
type port struct {
|
||||
fd int
|
||||
oldTermios *syscall.Termios
|
||||
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
const (
|
||||
rs485Enabled = 1 << 0
|
||||
rs485RTSOnSend = 1 << 1
|
||||
rs485RTSAfterSend = 1 << 2
|
||||
rs485RXDuringTX = 1 << 4
|
||||
rs485Tiocs = 0x542f
|
||||
)
|
||||
|
||||
// rs485_ioctl_opts is used to configure RS485 options in the driver
|
||||
type rs485_ioctl_opts struct {
|
||||
flags uint32
|
||||
delay_rts_before_send uint32
|
||||
delay_rts_after_send uint32
|
||||
padding [5]uint32
|
||||
}
|
||||
|
||||
// New allocates and returns a new serial port controller.
|
||||
func New() Port {
|
||||
return &port{fd: -1}
|
||||
}
|
||||
|
||||
// Open connects to the given serial port.
|
||||
func (p *port) Open(c *Config) (err error) {
|
||||
termios, err := newTermios(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// See man termios(3).
|
||||
// O_NOCTTY: no controlling terminal.
|
||||
// O_NDELAY: no data carrier detect.
|
||||
p.fd, err = syscall.Open(c.Address, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY|syscall.O_CLOEXEC, 0666)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Backup current termios to restore on closing.
|
||||
p.backupTermios()
|
||||
if err = p.setTermios(termios); err != nil {
|
||||
// No need to restore termios
|
||||
syscall.Close(p.fd)
|
||||
p.fd = -1
|
||||
p.oldTermios = nil
|
||||
return err
|
||||
}
|
||||
if err = enableRS485(p.fd, &c.RS485); err != nil {
|
||||
p.Close()
|
||||
return err
|
||||
}
|
||||
p.timeout = c.Timeout
|
||||
return
|
||||
}
|
||||
|
||||
func (p *port) Close() (err error) {
|
||||
if p.fd == -1 {
|
||||
return
|
||||
}
|
||||
p.restoreTermios()
|
||||
err = syscall.Close(p.fd)
|
||||
p.fd = -1
|
||||
p.oldTermios = nil
|
||||
return
|
||||
}
|
||||
|
||||
// Read reads from serial port. Port must be opened before calling this method.
|
||||
// It is blocked until all data received or timeout after p.timeout.
|
||||
func (p *port) Read(b []byte) (n int, err error) {
|
||||
var rfds syscall.FdSet
|
||||
|
||||
fd := p.fd
|
||||
fdset(fd, &rfds)
|
||||
|
||||
var tv *syscall.Timeval
|
||||
if p.timeout > 0 {
|
||||
timeout := syscall.NsecToTimeval(p.timeout.Nanoseconds())
|
||||
tv = &timeout
|
||||
}
|
||||
for {
|
||||
// If syscall.Select() returns EINTR (Interrupted system call), retry it
|
||||
if err = syscallSelect(fd+1, &rfds, nil, nil, tv); err == nil {
|
||||
break
|
||||
}
|
||||
if err != syscall.EINTR {
|
||||
err = fmt.Errorf("serial: could not select: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if !fdisset(fd, &rfds) {
|
||||
// Timeout
|
||||
err = ErrTimeout
|
||||
return
|
||||
}
|
||||
n, err = syscall.Read(fd, b)
|
||||
return
|
||||
}
|
||||
|
||||
// Write writes data to the serial port.
|
||||
func (p *port) Write(b []byte) (n int, err error) {
|
||||
n, err = syscall.Write(p.fd, b)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *port) setTermios(termios *syscall.Termios) (err error) {
|
||||
if err = tcsetattr(p.fd, termios); err != nil {
|
||||
err = fmt.Errorf("serial: could not set setting: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// backupTermios saves current termios setting.
|
||||
// Make sure that device file has been opened before calling this function.
|
||||
func (p *port) backupTermios() {
|
||||
oldTermios := &syscall.Termios{}
|
||||
if err := tcgetattr(p.fd, oldTermios); err != nil {
|
||||
// Warning only.
|
||||
log.Printf("serial: could not get setting: %v\n", err)
|
||||
return
|
||||
}
|
||||
// Will be reloaded when closing.
|
||||
p.oldTermios = oldTermios
|
||||
}
|
||||
|
||||
// restoreTermios restores backed up termios setting.
|
||||
// Make sure that device file has been opened before calling this function.
|
||||
func (p *port) restoreTermios() {
|
||||
if p.oldTermios == nil {
|
||||
return
|
||||
}
|
||||
if err := tcsetattr(p.fd, p.oldTermios); err != nil {
|
||||
// Warning only.
|
||||
log.Printf("serial: could not restore setting: %v\n", err)
|
||||
return
|
||||
}
|
||||
p.oldTermios = nil
|
||||
}
|
||||
|
||||
// Helpers for termios
|
||||
|
||||
func newTermios(c *Config) (termios *syscall.Termios, err error) {
|
||||
termios = &syscall.Termios{}
|
||||
flag := termios.Cflag
|
||||
// Baud rate
|
||||
if c.BaudRate == 0 {
|
||||
// 19200 is the required default.
|
||||
flag = syscall.B19200
|
||||
} else {
|
||||
var ok bool
|
||||
flag, ok = baudRates[c.BaudRate]
|
||||
if !ok {
|
||||
err = fmt.Errorf("serial: unsupported baud rate %v", c.BaudRate)
|
||||
return
|
||||
}
|
||||
}
|
||||
termios.Cflag |= flag
|
||||
// Input baud.
|
||||
cfSetIspeed(termios, flag)
|
||||
// Output baud.
|
||||
cfSetOspeed(termios, flag)
|
||||
// Character size.
|
||||
if c.DataBits == 0 {
|
||||
flag = syscall.CS8
|
||||
} else {
|
||||
var ok bool
|
||||
flag, ok = charSizes[c.DataBits]
|
||||
if !ok {
|
||||
err = fmt.Errorf("serial: unsupported character size %v", c.DataBits)
|
||||
return
|
||||
}
|
||||
}
|
||||
termios.Cflag |= flag
|
||||
// Stop bits
|
||||
switch c.StopBits {
|
||||
case 0, 1:
|
||||
// Default is one stop bit.
|
||||
// noop
|
||||
case 2:
|
||||
// CSTOPB: Set two stop bits.
|
||||
termios.Cflag |= syscall.CSTOPB
|
||||
default:
|
||||
err = fmt.Errorf("serial: unsupported stop bits %v", c.StopBits)
|
||||
return
|
||||
}
|
||||
switch c.Parity {
|
||||
case "N":
|
||||
// noop
|
||||
case "O":
|
||||
// PARODD: Parity is odd.
|
||||
termios.Cflag |= syscall.PARODD
|
||||
fallthrough
|
||||
case "", "E":
|
||||
// As mentioned in the modbus spec, the default parity mode must be Even parity
|
||||
// PARENB: Enable parity generation on output.
|
||||
termios.Cflag |= syscall.PARENB
|
||||
// INPCK: Enable input parity checking.
|
||||
termios.Iflag |= syscall.INPCK
|
||||
default:
|
||||
err = fmt.Errorf("serial: unsupported parity %v", c.Parity)
|
||||
return
|
||||
}
|
||||
// Control modes.
|
||||
// CREAD: Enable receiver.
|
||||
// CLOCAL: Ignore control lines.
|
||||
termios.Cflag |= syscall.CREAD | syscall.CLOCAL
|
||||
// Special characters.
|
||||
// VMIN: Minimum number of characters for noncanonical read.
|
||||
// VTIME: Time in deciseconds for noncanonical read.
|
||||
// Both are unused as NDELAY is we utilized when opening device.
|
||||
return
|
||||
}
|
||||
|
||||
// enableRS485 enables RS485 functionality of driver via an ioctl if the config says so
|
||||
func enableRS485(fd int, config *RS485Config) error {
|
||||
if !config.Enabled {
|
||||
return nil
|
||||
}
|
||||
rs485 := rs485_ioctl_opts{
|
||||
rs485Enabled,
|
||||
uint32(config.DelayRtsBeforeSend / time.Millisecond),
|
||||
uint32(config.DelayRtsAfterSend / time.Millisecond),
|
||||
[5]uint32{0, 0, 0, 0, 0},
|
||||
}
|
||||
|
||||
if config.RtsHighDuringSend {
|
||||
rs485.flags |= rs485RTSOnSend
|
||||
}
|
||||
if config.RtsHighAfterSend {
|
||||
rs485.flags |= rs485RTSAfterSend
|
||||
}
|
||||
if config.RxDuringTx {
|
||||
rs485.flags |= rs485RXDuringTX
|
||||
}
|
||||
|
||||
r, _, errno := syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(rs485Tiocs),
|
||||
uintptr(unsafe.Pointer(&rs485)))
|
||||
if errno != 0 {
|
||||
return os.NewSyscallError("SYS_IOCTL (RS485)", errno)
|
||||
}
|
||||
if r != 0 {
|
||||
return errors.New("serial: unknown error from SYS_IOCTL (RS485)")
|
||||
}
|
||||
return nil
|
||||
}
|
168
vendor/github.com/goburrow/serial/serial_windows.go
generated
vendored
168
vendor/github.com/goburrow/serial/serial_windows.go
generated
vendored
|
@ -1,168 +0,0 @@
|
|||
package serial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type port struct {
|
||||
handle syscall.Handle
|
||||
|
||||
oldDCB c_DCB
|
||||
oldTimeouts c_COMMTIMEOUTS
|
||||
}
|
||||
|
||||
// New allocates and returns a new serial port controller.
|
||||
func New() Port {
|
||||
return &port{
|
||||
handle: syscall.InvalidHandle,
|
||||
}
|
||||
}
|
||||
|
||||
// Open connects to the given serial port.
|
||||
func (p *port) Open(c *Config) (err error) {
|
||||
p.handle, err = newHandle(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
syscall.CloseHandle(p.handle)
|
||||
p.handle = syscall.InvalidHandle
|
||||
}
|
||||
}()
|
||||
err = p.setSerialConfig(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = p.setTimeouts(c)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *port) Close() (err error) {
|
||||
if p.handle == syscall.InvalidHandle {
|
||||
return
|
||||
}
|
||||
err1 := SetCommTimeouts(p.handle, &p.oldTimeouts)
|
||||
err2 := SetCommState(p.handle, &p.oldDCB)
|
||||
err = syscall.CloseHandle(p.handle)
|
||||
if err == nil {
|
||||
if err1 == nil {
|
||||
err = err2
|
||||
} else {
|
||||
err = err1
|
||||
}
|
||||
}
|
||||
p.handle = syscall.InvalidHandle
|
||||
return
|
||||
}
|
||||
|
||||
// Read reads from serial port.
|
||||
// It is blocked until data received or timeout after p.timeout.
|
||||
func (p *port) Read(b []byte) (n int, err error) {
|
||||
var done uint32
|
||||
if err = syscall.ReadFile(p.handle, b, &done, nil); err != nil {
|
||||
return
|
||||
}
|
||||
if done == 0 {
|
||||
err = ErrTimeout
|
||||
return
|
||||
}
|
||||
n = int(done)
|
||||
return
|
||||
}
|
||||
|
||||
// Write writes data to the serial port.
|
||||
func (p *port) Write(b []byte) (n int, err error) {
|
||||
var done uint32
|
||||
if err = syscall.WriteFile(p.handle, b, &done, nil); err != nil {
|
||||
return
|
||||
}
|
||||
n = int(done)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *port) setTimeouts(c *Config) error {
|
||||
var timeouts c_COMMTIMEOUTS
|
||||
// Read and write timeout
|
||||
if c.Timeout > 0 {
|
||||
timeout := toDWORD(int(c.Timeout.Nanoseconds() / 1E6))
|
||||
// wait until a byte arrived or time out
|
||||
timeouts.ReadIntervalTimeout = c_MAXDWORD
|
||||
timeouts.ReadTotalTimeoutMultiplier = c_MAXDWORD
|
||||
timeouts.ReadTotalTimeoutConstant = timeout
|
||||
timeouts.WriteTotalTimeoutConstant = timeout
|
||||
}
|
||||
err := GetCommTimeouts(p.handle, &p.oldTimeouts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetCommTimeouts(p.handle, &timeouts)
|
||||
if err != nil {
|
||||
// reset
|
||||
SetCommTimeouts(p.handle, &p.oldTimeouts)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *port) setSerialConfig(c *Config) error {
|
||||
var dcb c_DCB
|
||||
if c.BaudRate == 0 {
|
||||
dcb.BaudRate = 19200
|
||||
} else {
|
||||
dcb.BaudRate = toDWORD(c.BaudRate)
|
||||
}
|
||||
// Data bits
|
||||
if c.DataBits == 0 {
|
||||
dcb.ByteSize = 8
|
||||
} else {
|
||||
dcb.ByteSize = toBYTE(c.DataBits)
|
||||
}
|
||||
// Stop bits
|
||||
switch c.StopBits {
|
||||
case 0, 1:
|
||||
// Default is one stop bit.
|
||||
dcb.StopBits = c_ONESTOPBIT
|
||||
case 2:
|
||||
dcb.StopBits = c_TWOSTOPBITS
|
||||
default:
|
||||
return fmt.Errorf("serial: unsupported stop bits %v", c.StopBits)
|
||||
}
|
||||
// Parity
|
||||
switch c.Parity {
|
||||
case "", "E":
|
||||
// Default parity mode is Even.
|
||||
dcb.Parity = c_EVENPARITY
|
||||
dcb.Pad_cgo_0[0] |= 0x02 // fParity
|
||||
case "O":
|
||||
dcb.Parity = c_ODDPARITY
|
||||
dcb.Pad_cgo_0[0] |= 0x02 // fParity
|
||||
case "N":
|
||||
dcb.Parity = c_NOPARITY
|
||||
default:
|
||||
return fmt.Errorf("serial: unsupported parity %v", c.Parity)
|
||||
}
|
||||
dcb.Pad_cgo_0[0] |= 0x01 // fBinary
|
||||
|
||||
err := GetCommState(p.handle, &p.oldDCB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetCommState(p.handle, &dcb)
|
||||
if err != nil {
|
||||
SetCommState(p.handle, &p.oldDCB)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func newHandle(c *Config) (handle syscall.Handle, err error) {
|
||||
handle, err = syscall.CreateFile(
|
||||
syscall.StringToUTF16Ptr(c.Address),
|
||||
syscall.GENERIC_READ|syscall.GENERIC_WRITE,
|
||||
0, // mode
|
||||
nil, // security
|
||||
syscall.OPEN_EXISTING, // create mode
|
||||
0, // attributes
|
||||
0) // templates
|
||||
return
|
||||
}
|
15
vendor/github.com/goburrow/serial/termios_bsd.go
generated
vendored
15
vendor/github.com/goburrow/serial/termios_bsd.go
generated
vendored
|
@ -1,15 +0,0 @@
|
|||
// +build freebsd openbsd netbsd
|
||||
|
||||
package serial
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func cfSetIspeed(termios *syscall.Termios, speed uint32) {
|
||||
termios.Ispeed = speed
|
||||
}
|
||||
|
||||
func cfSetOspeed(termios *syscall.Termios, speed uint32) {
|
||||
termios.Ospeed = speed
|
||||
}
|
13
vendor/github.com/goburrow/serial/termios_darwin.go
generated
vendored
13
vendor/github.com/goburrow/serial/termios_darwin.go
generated
vendored
|
@ -1,13 +0,0 @@
|
|||
package serial
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func cfSetIspeed(termios *syscall.Termios, speed uint64) {
|
||||
termios.Ispeed = speed
|
||||
}
|
||||
|
||||
func cfSetOspeed(termios *syscall.Termios, speed uint64) {
|
||||
termios.Ospeed = speed
|
||||
}
|
15
vendor/github.com/goburrow/serial/termios_linux.go
generated
vendored
15
vendor/github.com/goburrow/serial/termios_linux.go
generated
vendored
|
@ -1,15 +0,0 @@
|
|||
// +build !mips,!mipsle,!mips64,!mips64le
|
||||
|
||||
package serial
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func cfSetIspeed(termios *syscall.Termios, speed uint32) {
|
||||
termios.Ispeed = speed
|
||||
}
|
||||
|
||||
func cfSetOspeed(termios *syscall.Termios, speed uint32) {
|
||||
termios.Ospeed = speed
|
||||
}
|
16
vendor/github.com/goburrow/serial/termios_mipsx.go
generated
vendored
16
vendor/github.com/goburrow/serial/termios_mipsx.go
generated
vendored
|
@ -1,16 +0,0 @@
|
|||
// +build linux
|
||||
// +build mips mipsle mips64 mips64le
|
||||
|
||||
package serial
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func cfSetIspeed(termios *syscall.Termios, speed uint32) {
|
||||
// MIPS has no Ispeed field in termios.
|
||||
}
|
||||
|
||||
func cfSetOspeed(termios *syscall.Termios, speed uint32) {
|
||||
// MIPS has no Ospeed field in termios.
|
||||
}
|
67
vendor/github.com/goburrow/serial/zsyscall_windows.go
generated
vendored
67
vendor/github.com/goburrow/serial/zsyscall_windows.go
generated
vendored
|
@ -1,67 +0,0 @@
|
|||
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||
|
||||
package serial
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
procGetCommState = modkernel32.NewProc("GetCommState")
|
||||
procSetCommState = modkernel32.NewProc("SetCommState")
|
||||
procGetCommTimeouts = modkernel32.NewProc("GetCommTimeouts")
|
||||
procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts")
|
||||
)
|
||||
|
||||
func GetCommState(handle syscall.Handle, dcb *c_DCB) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommState(handle syscall.Handle, dcb *c_DCB) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetCommTimeouts(handle syscall.Handle, timeouts *c_COMMTIMEOUTS) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetCommTimeouts(handle syscall.Handle, timeouts *c_COMMTIMEOUTS) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
47
vendor/github.com/goburrow/serial/ztypes_windows.go
generated
vendored
47
vendor/github.com/goburrow/serial/ztypes_windows.go
generated
vendored
|
@ -1,47 +0,0 @@
|
|||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_windows.go
|
||||
|
||||
package serial
|
||||
|
||||
const (
|
||||
c_MAXDWORD = 0xffffffff
|
||||
c_ONESTOPBIT = 0x0
|
||||
c_TWOSTOPBITS = 0x2
|
||||
c_EVENPARITY = 0x2
|
||||
c_ODDPARITY = 0x1
|
||||
c_NOPARITY = 0x0
|
||||
)
|
||||
|
||||
type c_COMMTIMEOUTS struct {
|
||||
ReadIntervalTimeout uint32
|
||||
ReadTotalTimeoutMultiplier uint32
|
||||
ReadTotalTimeoutConstant uint32
|
||||
WriteTotalTimeoutMultiplier uint32
|
||||
WriteTotalTimeoutConstant uint32
|
||||
}
|
||||
|
||||
type c_DCB struct {
|
||||
DCBlength uint32
|
||||
BaudRate uint32
|
||||
Pad_cgo_0 [4]byte
|
||||
WReserved uint16
|
||||
XonLim uint16
|
||||
XoffLim uint16
|
||||
ByteSize uint8
|
||||
Parity uint8
|
||||
StopBits uint8
|
||||
XonChar int8
|
||||
XoffChar int8
|
||||
ErrorChar int8
|
||||
EofChar int8
|
||||
EvtChar int8
|
||||
WReserved1 uint16
|
||||
}
|
||||
|
||||
func toDWORD(val int) uint32 {
|
||||
return uint32(val)
|
||||
}
|
||||
|
||||
func toBYTE(val int) uint8 {
|
||||
return uint8(val)
|
||||
}
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
|
@ -2,9 +2,6 @@
|
|||
github.com/beorn7/perks/quantile
|
||||
# github.com/cespare/xxhash/v2 v2.1.1
|
||||
github.com/cespare/xxhash/v2
|
||||
# github.com/goburrow/serial v0.1.0
|
||||
## explicit
|
||||
github.com/goburrow/serial
|
||||
# github.com/golang/protobuf v1.4.3
|
||||
github.com/golang/protobuf/proto
|
||||
github.com/golang/protobuf/ptypes
|
||||
|
|
Loading…
Reference in a new issue