Documentation
¶
Overview ¶
Package respreader provides a convenient way to frame response data from devices that use prompt/response protocols such as Modbus, other RS485 protocols, and modem AT commands. The fundamental assumption is a device takes some variable amount of time to respond to a prompt, formats up a response, and then streams it out the serial port. Once the response data starts streaming, any significant gap in the response with no data indicates the response is complete. A Read() blocks until it detects this "gap" or the overall timeout is reached, and then returns with accumulated data.
This method of framing a response has the following advantages:
1) minimizes the wasted time waiting for a response to the chunkTimeout defined below. More simplistic implementations often take the worst case response time for all packets and simply wait that amount of time for the response to arrive. This works, but the bus is tied up during this worst case wait that could be used for sending the next packet.
2) It is simple in that you don't have to parse the response on the fly to determine when it is complete, yet it can detect the end of a response fairly quickly.
The obvious disadvantage of this method of framing is that the device may insert a significant delay in sending the response that will cause the reader to think the response is complete. As long as this delay is still significantly shorter than the overall response time, it can still work fairly well. Some experimentation may be required to optimize the chunkTimeout setting.
Example using a serial port:
import (
"io"
"github.com/jacobsa/go-serial/serial"
"github.com/simpleiot/simpleiot/respreader"
)
options := serial.OpenOptions{
PortName: "/dev/ttyUSB0",
BaudRate: 9600,
DataBits: 8,
StopBits: 1,
MinimumReadSize: 0,
// with serial ports, you just set
// InterCharacterTimeout to 100 or larger.
// Otherwise, the goroutine reading the serial
// port will never exit when you close the read
// and will still data the next time you open
// the port. Be aware it may take 100ms for this
// to close. The linux kernel only accepts timeouts
// in increments of 0.1s. When using serial ports it
// makes sense to set the chunkTimeout to 100ms as well.
// With Go files, a read is supposed to return when
// the File is closed, but this does not seem to be
// working with Linux serial devices.
InterCharacterTimeout: 100,
RTSCTSFlowControl: true,
}
port, err := serial.Open(options)
port = respreader.NewResponseReadWriteCloser(port, time.Second,
time.Millisecond * 50)
// send out prompt
port.Write("ATAI")
// read response
data := make([]byte, 128)
count, err := port.Read(data)
data = data[0:count]
// now process response ...
// to close the reader process, you must call Close on the reader.
// This sets a flag that causes the reader goroutine to exit.
port.Close()
Three types are provided for convenience that wrap io.Reader, io.ReadWriter, and io.ReadWriteCloser.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ResponseReadCloser ¶
type ResponseReadCloser struct {
// contains filtered or unexported fields
}
ResponseReadCloser is a convenience type that implements io.ReadWriter. Write calls flush reader before writing the prompt.
func NewResponseReadCloser ¶
func NewResponseReadCloser(iorw io.ReadCloser, timeout time.Duration, chunkTimeout time.Duration) *ResponseReadCloser
NewResponseReadCloser creates a new response reader
timeout is used to specify an overall timeout. If this timeout is encountered, io.EOF is returned.
chunkTimeout is used to specify the max timeout between chunks of data once the response is started. If a delay of chunkTimeout is encountered, the response is considered finished and the Read returns.
func (*ResponseReadCloser) Close ¶
func (rrwc *ResponseReadCloser) Close() error
Close is a passthrough call.
type ResponseReadWriteCloser ¶
type ResponseReadWriteCloser struct {
// contains filtered or unexported fields
}
ResponseReadWriteCloser is a convenience type that implements io.ReadWriteCloser. Write calls flush reader before writing the prompt.
func NewResponseReadWriteCloser ¶
func NewResponseReadWriteCloser(iorw io.ReadWriteCloser, timeout time.Duration, chunkTimeout time.Duration) *ResponseReadWriteCloser
NewResponseReadWriteCloser creates a new response reader
timeout is used to specify an overall timeout. If this timeout is encountered, io.EOF is returned.
chunkTimeout is used to specify the max timeout between chunks of data once the response is started. If a delay of chunkTimeout is encountered, the response is considered finished and the Read returns.
func (*ResponseReadWriteCloser) Close ¶
func (rrwc *ResponseReadWriteCloser) Close() error
Close is a passthrough call.
type ResponseReadWriter ¶
type ResponseReadWriter struct {
// contains filtered or unexported fields
}
ResponseReadWriter is a convenience type that implements io.ReadWriter. Write calls flush reader before writing the prompt.
func NewResponseReadWriter ¶
func NewResponseReadWriter(iorw io.ReadWriter, timeout time.Duration, chunkTimeout time.Duration) *ResponseReadWriter
NewResponseReadWriter creates a new response reader
type ResponseReader ¶
type ResponseReader struct {
// contains filtered or unexported fields
}
ResponseReader is used for prompt/response communication protocols where a prompt is sent, and some time later a response is received. Typically, the target takes some amount to formulate the response, and then streams it out. There are two delays: an overall timeout, and then an inter character timeout that is activated once the first byte is received. The thought is that once you received the 1st byte, all the data should stream out continuously and a short timeout can be used to determine the end of the packet.
func NewResponseReader ¶
func NewResponseReader(reader io.Reader, timeout time.Duration, chunkTimeout time.Duration) *ResponseReader
NewResponseReader creates a new response reader.
timeout is used to specify an overall timeout. If this timeout is encountered, io.EOF is returned.
chunkTimeout is used to specify the max timeout between chunks of data once the response is started. If a delay of chunkTimeout is encountered, the response is considered finished and the Read returns.
func (*ResponseReader) Flush ¶
func (rr *ResponseReader) Flush() (int, error)
Flush is used to flush any input data