Documentation ¶
Overview ¶
Package modbusone provides a Modbus library to implement both server and client using one set of APIs.
For sample code, see examples/memory, and handler2serial_test.go
Index ¶
- Constants
- Variables
- func BoolsToData(values []bool, fc FunctionCode) ([]byte, error)
- func BytesDelay(baudRate int64, n int) time.Duration
- func DataToBools(data []byte, count uint16, fc FunctionCode) ([]bool, error)
- func DataToRegisters(data []byte) ([]uint16, error)
- func DoTransactions(c RTUTransactionStarter, slaveID byte, reqs []PDU) (int, error)
- func GetPDUSizeFromHeader(header []byte, isClient bool) int
- func GetRTUSizeFromHeader(header []byte, isClient bool) int
- func MatchPDU(ask PDU, ans PDU) bool
- func MinDelay(baudRate int64) time.Duration
- func NewRTUPacketReader(r SerialContext, isClient bool) io.Reader
- func RegistersToData(values []uint16) ([]byte, error)
- func Uint64ToSlaveID(n uint64) (byte, error)
- type ExceptionCode
- type FunctionCode
- func (f FunctionCode) IsBool() bool
- func (f FunctionCode) IsReadToServer() bool
- func (f FunctionCode) IsSingle() bool
- func (f FunctionCode) IsUint16() bool
- func (f FunctionCode) IsWriteToServer() bool
- func (f FunctionCode) MakeRequestHeader(address, quantity uint16) (PDU, error)
- func (f FunctionCode) MaxPerPacket() uint16
- func (f FunctionCode) MaxPerPacketSized(size int) uint16
- func (f FunctionCode) MaxRange() uint16
- func (f FunctionCode) SeparateError() (bool, FunctionCode)
- func (f FunctionCode) Valid() bool
- func (f FunctionCode) WithError() FunctionCode
- type PDU
- func (p PDU) GetAddress() uint16
- func (p PDU) GetFunctionCode() FunctionCode
- func (p PDU) GetReplyValues() ([]byte, error)
- func (p PDU) GetRequestCount() uint16
- func (p PDU) GetRequestValues() ([]byte, error)
- func (p PDU) MakeReadReply(data []byte) PDU
- func (p PDU) MakeWriteReply() PDU
- func (p PDU) MakeWriteRequest(data []byte) PDU
- func (p PDU) ValidateRequest() error
- type ProtocalHandler
- type RTU
- type RTUClient
- func (c *RTUClient) DoTransaction(req PDU) error
- func (c *RTUClient) GetTransactionTimeOut(reqLen, ansLen int) time.Duration
- func (c *RTUClient) Serve(handler ProtocalHandler) error
- func (c *RTUClient) SetServerProcessingTime(t time.Duration)
- func (c *RTUClient) StartTransactionToServer(slaveID byte, req PDU, errChan chan error)
- type RTUServer
- type RTUTransactionStarter
- type SerialContext
- type Server
- type SimpleHandler
- type Stats
Constants ¶
const MaxPDUSize = 253
MaxPDUSize is the max possible size of a PDU packet
const MaxRTUSize = 256
MaxRTUSize is the max possible size of a RTU packet
Variables ¶
var DebugOut io.Writer
DebugOut sets where to print debug messages
var ErrFcNotSupported = errors.New("this FunctionCode is not supported")
ErrFcNotSupported is another version of EcIllegalFunction, encountering of this error shows the error is locally generated, not a remote ExceptionCode.
var ErrServerTimeOut = errors.New("server timed out")
ErrServerTimeOut is the time out error for StartTransaction
var ErrorCrc = fmt.Errorf("RTU data crc not valid")
var OverSizeMaxRTU = MaxRTUSize
OverSizeMaxRTU overides MaxRTUSize when OverSizeSupport is true
var OverSizeSupport = false
OverSizeSupport ignores max packet size and encoded number of bytes to support over sided implementations encountered in the wild. This setting only applies to the server end, since client is always reserved in what it requestes. Also change OverSizeMaxRTU properly
Functions ¶
func BoolsToData ¶
func BoolsToData(values []bool, fc FunctionCode) ([]byte, error)
BoolsToData translates []bool to the data part of PDU dependent on FunctionCode.
func BytesDelay ¶
BytesDelay returns the time it takes to send n bytes in baudRate
func DataToBools ¶
func DataToBools(data []byte, count uint16, fc FunctionCode) ([]bool, error)
DataToBools translates the data part of PDU to []bool dependent on FunctionCode.
func DataToRegisters ¶
DataToRegisters translates the data part of PDU to []uint16.
func DoTransactions ¶
func DoTransactions(c RTUTransactionStarter, slaveID byte, reqs []PDU) (int, error)
DoTransactions runs the reqs transactions in order. If any error is encountered, it returns early and reports the index number and error message
func GetPDUSizeFromHeader ¶
GetPDUSizeFromHeader returns the expected sized of a pdu packet with the given PDU header, if not enough info is in the header, then it returns the shortest possible. isClient is true if a client/master is reading the packet.
func GetRTUSizeFromHeader ¶
GetRTUSizeFromHeader returns the expected sized of a rtu packet with the given RTU header, if not enough info is in the header, then it returns the shortest possible. isClient is true if a client/master is reading the packet.
func MatchPDU ¶
MatchPDU returns true if ans is a valid reply to ask, including normal and error code replies.
func NewRTUPacketReader ¶
func NewRTUPacketReader(r SerialContext, isClient bool) io.Reader
NewRTUPacketReader create a Reader that attempt to read full packets.
func RegistersToData ¶
RegistersToData translates []uint16 to the data part of PDU.
func Uint64ToSlaveID ¶
Uint64ToSlaveID is a helper function for reading configuration data to SlaveID. See also flag.Uint64 and strcov.ParseUint
Types ¶
type ExceptionCode ¶
type ExceptionCode byte
ExceptionCode Modbus exception codes
const ( //EcOK is invented for no error EcOK ExceptionCode = 0 //EcInternal is invented for error reading ExceptionCode EcInternal ExceptionCode = 255 EcIllegalFunction ExceptionCode = 1 EcIllegalDataAddress ExceptionCode = 2 EcIllegalDataValue ExceptionCode = 3 EcServerDeviceFailure ExceptionCode = 4 EcAcknowledge ExceptionCode = 5 EcServerDeviceBusy ExceptionCode = 6 EcMemoryParityError ExceptionCode = 8 EcGatewayTargetDeviceFailedToRespond ExceptionCode = 11 )
Defined exception codes, 5 to 11 are not used
func ToExceptionCode ¶
func ToExceptionCode(err error) ExceptionCode
ToExceptionCode turns an error into an ExceptionCode (to send in PDU), best effort with EcServerDeviceFailure as fail back.
func (ExceptionCode) Error ¶
func (e ExceptionCode) Error() string
Error implements error for ExceptionCode
type FunctionCode ¶
type FunctionCode byte
FunctionCode Modebus function codes
const ( FcReadCoils FunctionCode = 1 FcReadDiscreteInputs FunctionCode = 2 FcReadHoldingRegisters FunctionCode = 3 FcReadInputRegisters FunctionCode = 4 FcWriteSingleCoil FunctionCode = 5 FcWriteSingleRegister FunctionCode = 6 FcWriteMultipleCoils FunctionCode = 15 FcWriteMultipleRegisters FunctionCode = 16 )
Implemented FunctionCodes
func (FunctionCode) IsBool ¶
func (f FunctionCode) IsBool() bool
IsBool returns true if the FunctionCode concerns boolean values
func (FunctionCode) IsReadToServer ¶
func (f FunctionCode) IsReadToServer() bool
IsReadToServer returns true if the FunctionCode is a write. FunctionCode 23 is both a read and write.
func (FunctionCode) IsSingle ¶
func (f FunctionCode) IsSingle() bool
IsSingle returns true if the FunctionCode can transmit only one value
func (FunctionCode) IsUint16 ¶
func (f FunctionCode) IsUint16() bool
IsUint16 returns true if the FunctionCode concerns 16bit values
func (FunctionCode) IsWriteToServer ¶
func (f FunctionCode) IsWriteToServer() bool
IsWriteToServer returns true if the FunctionCode is a write. FunctionCode 23 is both a read and write.
func (FunctionCode) MakeRequestHeader ¶
func (f FunctionCode) MakeRequestHeader(address, quantity uint16) (PDU, error)
MakeRequestHeader makes a particular pdu without any data, to be used for client side StartTransaction. The inverse functions are PDU.GetFunctionCode() .GetAddress() and .GetRequestCount()
func (FunctionCode) MaxPerPacket ¶
func (f FunctionCode) MaxPerPacket() uint16
MaxPerPacket returns the max number of values a FunctionCode can carry.
func (FunctionCode) MaxPerPacketSized ¶
func (f FunctionCode) MaxPerPacketSized(size int) uint16
MaxPerPacketSized returns the max number of values a FunctionCode can carry, if we are to further limit PDU packet size from MaxRTUSize. At least 1 (8 for bools) is returned if size is too small.
func (FunctionCode) MaxRange ¶
func (f FunctionCode) MaxRange() uint16
MaxRange is the largest address in the Modbus protocol.
func (FunctionCode) SeparateError ¶
func (f FunctionCode) SeparateError() (bool, FunctionCode)
SeparateError test if FunctionCode is an error response, and also return the version without error flag set
func (FunctionCode) Valid ¶
func (f FunctionCode) Valid() bool
Valid test if FunctionCode is a supported function, and not an error response
func (FunctionCode) WithError ¶
func (f FunctionCode) WithError() FunctionCode
WithError return a copy of FunctionCode with the error flag set.
type PDU ¶
type PDU []byte
PDU is the Modbus Protocol Data Unit
func ExceptionReplyPacket ¶
func ExceptionReplyPacket(req PDU, e ExceptionCode) PDU
ExceptionReplyPacket make a PDU packet to reply to request req with ExceptionCode e
func MakePDURequestHeaders ¶
func MakePDURequestHeaders(fc FunctionCode, address, quantity uint16, appendTO []PDU) ([]PDU, error)
MakePDURequestHeaders generates the list of PDU request headers by spliting quantity into allowed sizes. Returns an error if quantity is out of range.
func MakePDURequestHeadersSized ¶
func MakePDURequestHeadersSized(fc FunctionCode, address, quantity uint16, maxPerPacket uint16, appendTO []PDU) ([]PDU, error)
MakePDURequestHeadersSized generates the list of PDU request headers by spliting quantity into sizes of maxPerPacket or less. Returns an error if quantity is out of range.
You can use FunctionCode.MaxPerPacketSized to calculate one with the wanted byte length.
func (PDU) GetAddress ¶
GetAddress returns the stating address, If PDU is invalid, behavior is undefined (can panic).
func (PDU) GetFunctionCode ¶
func (p PDU) GetFunctionCode() FunctionCode
GetFunctionCode returns the function code
func (PDU) GetReplyValues ¶
GetReplyValues returns the values in a read reply
func (PDU) GetRequestCount ¶
GetRequestCount returns the number of values requested, If PDU is invalid (too short), behavior is undefined (can panic).
func (PDU) GetRequestValues ¶
GetRequestValues returns the values in a write request
func (PDU) MakeReadReply ¶
MakeReadReply produces the reply PDU based on the request PDU and read data
func (PDU) MakeWriteReply ¶
MakeWriteReply assumes the request is a successful write, and make the associated response
func (PDU) MakeWriteRequest ¶
MakeWriteRequest produces the request PDU based on the request PDU header and (locally) read data
func (PDU) ValidateRequest ¶
ValidateRequest tests for errors in a received Request PDU packet. Use ToExceptionCode to get the ExceptionCode for error. Checks for errors 2 and 3 are done in GetRequestValues.
type ProtocalHandler ¶
type ProtocalHandler interface { //OnInput is called on the server for a write request, //or on the client for read reply. //For write to server on server side, data is part of req. //For read from server on client side, req is the req from client, and //data is part of reply. OnWrite(req PDU, data []byte) error //OnOutput is called on the server for a read request, //or on the client before write requst. //For read from server on the server side, req is from client and data is //part of reply. //For write to server on the client side, req is from local action //(such as RTUClient.StartTransaction), and data will be added to req to send //to server. OnRead(req PDU) (data []byte, err error) //OnError is called on the client when it receive a well formed //error from server OnError(req PDU, errRep PDU) }
ProtocalHandler handles PDUs based on if it is a write or read from the local perspective.
type RTU ¶
type RTU []byte
RTU is the Modbus RTU Application Data Unit
func (RTU) IsMulticast ¶
IsMulticast returns true if slaveID is the multicast address 0
type RTUClient ¶
type RTUClient struct { SlaveID byte // contains filtered or unexported fields }
RTUClient implements Client/Master side logic for RTU over a SerialContext to be used by a ProtocalHandler
func NewRTUCLient ¶
func NewRTUCLient(com SerialContext, slaveID byte) *RTUClient
NewRTUCLient create a new client communicating over SerialContext with the give slaveID as default.
func (*RTUClient) DoTransaction ¶
DoTransaction starts a transaction, and returns a channel that returns an error or nil, with the default slaveID.
DoTransaction is blocking.
For read from server, the PDU is sent as is (after been warped up in RTU) For write to server, the data part given will be ignored, and filled in by data from handler.
func (*RTUClient) GetTransactionTimeOut ¶
GetTransactionTimeOut returns the total time to wait for a transaction (server response) to time out, given the expected length of RTU packets. This function is also used internally to calculate timeout.
func (*RTUClient) Serve ¶
func (c *RTUClient) Serve(handler ProtocalHandler) error
Serve serves RTUClient side handlers, must close SerialContext after error is returned, to clean up.
func (*RTUClient) SetServerProcessingTime ¶
SetServerProcessingTime sets the time to wait for a server response, the total wait time also includes the time needed for data transmission
func (*RTUClient) StartTransactionToServer ¶
StartTransactionToServer starts a transaction, with a custom slaveID. errChan is required and usable, an error is set is the transaction failed, or nil for success.
StartTransactionToServer is not blocking.
For read from server, the PDU is sent as is (after been warped up in RTU) For write to server, the data part given will be ignored, and filled in by data from handler.
type RTUServer ¶
type RTUServer struct { SlaveID byte // contains filtered or unexported fields }
RTUServer implements Server/Slave side logic for RTU over a SerialContext to be used by a ProtocalHandler
func NewRTUServer ¶
func NewRTUServer(com SerialContext, slaveID byte) *RTUServer
NewRTUServer creates a RTU server on SerialContext listening on slaveID
func (*RTUServer) Serve ¶
func (s *RTUServer) Serve(handler ProtocalHandler) error
Serve runs the server and only returns after unrecoverable error, such as SerialContext is closed. Read is assumed to only read full packets, as per RTU delay based spec.
type RTUTransactionStarter ¶
type RTUTransactionStarter interface {
StartTransactionToServer(slaveID byte, req PDU, errChan chan error)
}
RTUTransactionStarter is an interface implemented by RTUClient.
type SerialContext ¶
type SerialContext interface { io.ReadWriteCloser //RTUMinDelay returns the minimum required delay between packets for framing MinDelay() time.Duration //RTUBytesDelay returns the duration is takes to send n bytes BytesDelay(n int) time.Duration //Stats reporting Stats() *Stats }
SerialContext is an interface implemented by SerialPort, can also be mocked for testing.
func NewSerialContext ¶
func NewSerialContext(conn io.ReadWriteCloser, baudRate int64) SerialContext
NewSerialContext creates a SerialContext from any io.ReadWriteCloser
type Server ¶
type Server interface {
Serve(handler ProtocalHandler) error
}
Server is the common interface for all Clients and Servers that use ProtocalHandlers
type SimpleHandler ¶
type SimpleHandler struct { //ReadDiscreteInputs handles server side FC=2 ReadDiscreteInputs func(address, quantity uint16) ([]bool, error) //ReadDiscreteInputs handles client side FC=2 WriteDiscreteInputs func(address uint16, values []bool) error //ReadCoils handles client side FC=5&15, server side FC=1 ReadCoils func(address, quantity uint16) ([]bool, error) //WriteCoils handles client side FC=1, server side FC=5&15 WriteCoils func(address uint16, values []bool) error //ReadInputRegisters handles server side FC=4 ReadInputRegisters func(address, quantity uint16) ([]uint16, error) //ReadDiscreteInputs handles client side FC=4 WriteInputRegisters func(address uint16, values []uint16) error //ReadHoldingRegisters handles client side FC=6&16, server side FC=3 ReadHoldingRegisters func(address, quantity uint16) ([]uint16, error) //WriteHoldingRegisters handles client side FC=3, server side FC=6&16 WriteHoldingRegisters func(address uint16, values []uint16) error //OnErrorImp handles OnError OnErrorImp func(req PDU, errRep PDU) }
SimpleHandler implements ProtocalHandler, any nil function returns ErrFcNotSupported
func (*SimpleHandler) OnError ¶
func (h *SimpleHandler) OnError(req PDU, errRep PDU)
OnError is called by a Server, set OnErrorImp to catch the calls