go-serial icon indicating copy to clipboard operation
go-serial copied to clipboard

SetDeadline implementation

Open maitredede opened this issue 2 years ago • 5 comments

Hello,

Thanks for PR #109 (timeouts).

I am using your lib (go 1.16.7 on linux/amd64) to drive Ingenico payement hardware that has timeouts defined in its communication protocol. But the timeouts are not the same between protocol states. For example : when I write a ENQ byte, there is a response timeout of 2s; when I ended sending something, I have to listen 2s before I can start sending a new item; when there is a communication conflict, the timeout is 5s before retry...

My first driving implementation was based on your lib, and did not fully respected all the timeouts. My second implementation use directly the /dev/ttyACM0 file (using *os.File), and respects the timeouts, using the *os.File.SetDeadline func.

So I would like if it is possible to add a SetDeadline implementation that can be called when needed, and that may have the same behavior that the *os.File. :slightly_smiling_face:

maitredede avatar Aug 24 '21 20:08 maitredede

A quick and dirty implementation may be :

func (port *unixPort) SetDeadline(t time.Time) error {
	return port.SetReadTimeout(time.Until(t))
}

but it would be also interesting to have timeouts/deadlines on .Write() :slightly_smiling_face:

maitredede avatar Aug 24 '21 21:08 maitredede

Hello, More precisions about my use case, I need to communicate with devices that respects some timouts, when both reading and writing.

Either the native timeout or the context approach can be used in my case, but I need both read and write timeouts :smiley:

maitredede avatar Jan 20 '23 02:01 maitredede

I need this as well. I would like to use io.ReadFull on the go-serial reader/Port interface. But since go-serial's SetReadTimeout() only causes go-serial's Read() implementation to do return 0, nil, the ReadFull gets stuck. My workaround might end up using SetReadTimeout(), and the doing my own Read-loop, reading 1 byte at a time into my buffer and interpreting n, nil as the timeout. related #148

quite avatar Jan 23 '23 15:01 quite

How about the same interface as net.conn?

SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error

which could be set using for example port.SetReadDeadline(time.Now().Add(time.Second)).

If implementing the same interface as net.Conn one could keep much code between networked and serial connections without much change. As long as one of the existing interfaces from either from this or os.File is used then I'm happy.

kmpm avatar May 17 '23 18:05 kmpm

How about the same interface as net.conn?

SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error

which could be set using for example port.SetReadDeadline(time.Now().Add(time.Second)).

If implementing the same interface as net.Conn one could keep much code between networked and serial connections without much change. As long as one of the existing interfaces from either from this or os.File is used then I'm happy.

I second this! I had to implement serial port handling in software that was already using net.Conn, so having the deadline functions would have made Port basically a drop in replacement. It would be nice to have it in the long run, but for the time being this is what I've come up with:

type portWithDeadline struct {
	serial.Port

	writeDeadline *time.Time
}

func (p portWithDeadline) SetDeadline(t time.Time) error {
	p.SetWriteDeadline(t)
	return p.SetReadDeadline(t)
}

func (p portWithDeadline) SetReadDeadline(t time.Time) error {
	return p.SetReadTimeout(time.Until(t))
}

func (p portWithDeadline) SetWriteDeadline(t time.Time) error {
	p.writeDeadline = &t
	return nil
}

func (p portWithDeadline) Write(b []byte) (n int, err error) {
	if p.writeDeadline == nil || p.writeDeadline.IsZero() {
		return p.Port.Write(b)
	}

	finishedCh := make(chan struct{})
	timeoutCh := time.After(time.Until(*p.writeDeadline))
	go func() {
		defer close(finishedCh)
		n, err = p.Port.Write(b)
	}()

	select {
	case <-timeoutCh:
		return 0, errors.New("write operation timed out")
	case <-finishedCh:
		return n, err
	}
}

dschmidt avatar Sep 20 '23 07:09 dschmidt