respreader

package
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 9, 2020 License: Apache-2.0 Imports: 3 Imported by: 7

README

Response Reader

Package respreader provides a convenient way to read data from devices that use prompt/response protocols such as Modbus (and other RS485 protocols) and modem AT commands. The fundamental assumption is a device takes some variable amount of time to respond to a request, formats up a packet, and then streams it out the serial port. Once the response data starts streaming, and significant gap with no data indicates the response is complete.

see https://godoc.org/github.com/simpleiot/simpleiot/respreader for documentation

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

View Source
var ErrorTimeout = errors.New("timeout")

ErrorTimeout indicates the reader timed out

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, ErrorTimeout 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.

func (*ResponseReadCloser) Read

func (rrwc *ResponseReadCloser) Read(buffer []byte) (int, error)

Read response using chunkTimeout and timeout

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, ErrorTimeout 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.

func (*ResponseReadWriteCloser) Read

func (rrwc *ResponseReadWriteCloser) Read(buffer []byte) (int, error)

Read response using chunkTimeout and timeout

func (*ResponseReadWriteCloser) Write

func (rrwc *ResponseReadWriteCloser) Write(buffer []byte) (int, error)

Write flushes all data from reader, and then passes through write 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

func (*ResponseReadWriter) Read

func (rrw *ResponseReadWriter) Read(buffer []byte) (int, error)

Read response

func (*ResponseReadWriter) Write

func (rrw *ResponseReadWriter) Write(buffer []byte) (int, error)

Write flushes all data from reader, and then passes through write call.

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, ErrorTimeout 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

func (*ResponseReader) Read

func (rr *ResponseReader) Read(buffer []byte) (int, error)

Read response

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL