delete some unused files

This commit is contained in:
Josh Deprez 2021-01-11 14:14:45 +11:00
parent dc10d3b6de
commit 206c82b9e4
22 changed files with 0 additions and 1570 deletions

1
go.mod
View File

@ -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
)

View File

@ -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
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -1,3 +0,0 @@
* text=auto
*.go text
*.bat text eol=crlf

View File

@ -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

View File

@ -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.

View File

@ -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/)

View File

@ -1,2 +0,0 @@
go tool cgo -godefs types_windows.go | gofmt > ztypes_windows.go
go generate syscall_windows.go

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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.
}

View File

@ -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
}

View File

@ -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
View File

@ -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