respreader

package
v0.15.3 Latest Latest
Warning

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

Go to latest
Published: Mar 20, 2024 License: Apache-2.0 Imports: 3 Imported by: 2

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

This section is empty.

Functions

This section is empty.

Types

type ReadCloser added in v0.0.3

type ReadCloser struct {
	// contains filtered or unexported fields
}

ReadCloser is a convenience type that implements io.ReadWriter. Write calls flush reader before writing the prompt.

func NewReadCloser added in v0.0.3

func NewReadCloser(iorw io.ReadCloser, timeout time.Duration, chunkTimeout time.Duration) *ReadCloser

NewReadCloser 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 (*ReadCloser) Close added in v0.0.3

func (rc *ReadCloser) Close() error

Close is a passthrough call.

func (*ReadCloser) Read added in v0.0.3

func (rc *ReadCloser) Read(buffer []byte) (int, error)

Read response using chunkTimeout and timeout

func (*ReadCloser) SetTimeout added in v0.0.3

func (rc *ReadCloser) SetTimeout(timeout, chunkTimeout time.Duration)

SetTimeout can be used to update the reader timeout

type ReadWriteCloser added in v0.0.3

type ReadWriteCloser struct {
	// contains filtered or unexported fields
}

ReadWriteCloser is a convenience type that implements io.ReadWriteCloser. Write calls flush reader before writing the prompt.

func NewReadWriteCloser added in v0.0.3

func NewReadWriteCloser(iorw io.ReadWriteCloser, timeout time.Duration, chunkTimeout time.Duration) *ReadWriteCloser

NewReadWriteCloser 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 (*ReadWriteCloser) Close added in v0.0.3

func (rwc *ReadWriteCloser) Close() error

Close is a passthrough call.

func (*ReadWriteCloser) Read added in v0.0.3

func (rwc *ReadWriteCloser) Read(buffer []byte) (int, error)

Read response using chunkTimeout and timeout

func (*ReadWriteCloser) SetTimeout added in v0.0.3

func (rwc *ReadWriteCloser) SetTimeout(timeout, chunkTimeout time.Duration)

SetTimeout can be used to update the reader timeout

func (*ReadWriteCloser) Write added in v0.0.3

func (rwc *ReadWriteCloser) Write(buffer []byte) (int, error)

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

type ReadWriter added in v0.0.3

type ReadWriter struct {
	// contains filtered or unexported fields
}

ReadWriter is a convenience type that implements io.ReadWriter. Write calls flush reader before writing the prompt.

func NewReadWriter added in v0.0.3

func NewReadWriter(iorw io.ReadWriter, timeout time.Duration, chunkTimeout time.Duration) *ReadWriter

NewReadWriter creates a new response reader

func (*ReadWriter) Read added in v0.0.3

func (rw *ReadWriter) Read(buffer []byte) (int, error)

Read response

func (*ReadWriter) SetTimeout added in v0.0.3

func (rw *ReadWriter) SetTimeout(timeout, chunkTimeout time.Duration)

SetTimeout can be used to update the reader timeout

func (*ReadWriter) Write added in v0.0.3

func (rw *ReadWriter) Write(buffer []byte) (int, error)

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

type Reader added in v0.0.3

type Reader struct {
	// contains filtered or unexported fields
}

Reader 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 NewReader added in v0.0.3

func NewReader(reader io.Reader, timeout time.Duration, chunkTimeout time.Duration) *Reader

NewReader 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 (*Reader) Flush added in v0.0.3

func (r *Reader) Flush() (int, error)

Flush is used to flush any input data

func (*Reader) Read added in v0.0.3

func (r *Reader) Read(buffer []byte) (int, error)

Read response

func (*Reader) SetTimeout added in v0.0.3

func (r *Reader) SetTimeout(timeout, chunkTimeout time.Duration)

SetTimeout can be used to update the reader timeout

Jump to

Keyboard shortcuts

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