diff --git a/client_test.go b/client_test.go index 88c057f..de8e715 100644 --- a/client_test.go +++ b/client_test.go @@ -268,13 +268,21 @@ func TestUDPEmulator(t *testing.T) { timeout := 100 * time.Millisecond - connEmulator, err := xsensemulator.NewUDPSerialPort(addrEmulator, addrClient, timeout) + connEmulator, err := xsensemulator.NewUDPSerialPort( + addrEmulator, + addrClient, + xsensemulator.WithTimeout(timeout), + ) assert.NilError(t, err) defer func() { assert.NilError(t, connEmulator.Close()) }() - connClient, err := xsensemulator.NewUDPSerialPort(addrClient, addrEmulator, timeout) + connClient, err := xsensemulator.NewUDPSerialPort( + addrClient, + addrEmulator, + xsensemulator.WithTimeout(timeout), + ) assert.NilError(t, err) emu := xsensemulator.NewEmulator(connEmulator) diff --git a/xsensemulator/emulator.go b/xsensemulator/emulator.go index 632e779..6181b73 100644 --- a/xsensemulator/emulator.go +++ b/xsensemulator/emulator.go @@ -6,9 +6,7 @@ import ( "errors" "fmt" "io" - "net" "sync" - "time" "go.einride.tech/xsens" ) @@ -18,55 +16,6 @@ var ( ErrNotInOutputConfiguration = errors.New("not in output configuration") ) -type UDPSerialPort struct { - io.ReadWriteCloser - // Timeout for setting read/write deadlines - timeout time.Duration - OriginConn *net.UDPConn - DestinationAddr *net.UDPAddr -} - -func NewUDPSerialPort(origin, destination string, timeout time.Duration) (*UDPSerialPort, error) { - udpOriginAddr, err := net.ResolveUDPAddr("udp", origin) - if err != nil { - return nil, fmt.Errorf("new udp serial port: %w", err) - } - udpDestinationAddr, err := net.ResolveUDPAddr("udp", destination) - if err != nil { - return nil, fmt.Errorf("new udp serial port: %w", err) - } - originConn, err := net.ListenUDP("udp", udpOriginAddr) - if err != nil { - return nil, fmt.Errorf("new udp serial port: %w", err) - } - return &UDPSerialPort{ - OriginConn: originConn, - DestinationAddr: udpDestinationAddr, - timeout: timeout, - }, nil -} - -func (t UDPSerialPort) Read(p []byte) (n int, err error) { - err = t.OriginConn.SetReadDeadline(time.Now().Add(t.timeout)) - if err != nil { - return 0, fmt.Errorf("udp serial port read: %w", err) - } - n, _, err = t.OriginConn.ReadFromUDP(p) - return n, err -} - -func (t UDPSerialPort) Write(p []byte) (n int, err error) { - err = t.OriginConn.SetWriteDeadline(time.Now().Add(t.timeout)) - if err != nil { - return 0, fmt.Errorf("udp serial port write: %w", err) - } - return t.OriginConn.WriteToUDP(p, t.DestinationAddr) -} - -func (t UDPSerialPort) Close() error { - return t.OriginConn.Close() -} - type Emulator struct { port io.ReadWriteCloser w *bufio.Writer @@ -139,7 +88,9 @@ func (e *Emulator) Receive(ctx context.Context) error { e.mutex.Lock() e.lastMessageIdentifier = xsens.MessageIdentifierSetOutputConfiguration e.mutex.Unlock() - _, err := e.port.Write(xsens.NewMessage(xsens.MessageIdentifierSetOutputConfigurationAck, nil)) + _, err := e.port.Write( + xsens.NewMessage(xsens.MessageIdentifierSetOutputConfigurationAck, nil), + ) if err != nil { return fmt.Errorf("receive: %w", err) } @@ -168,7 +119,10 @@ func (e *Emulator) Transmit(m xsens.Message) error { return nil } -func (e *Emulator) MarshalMessage(measurement xsens.MeasurementData, dataType xsens.DataType) ([]byte, error) { +func (e *Emulator) MarshalMessage( + measurement xsens.MeasurementData, + dataType xsens.DataType, +) ([]byte, error) { var id xsens.DataIdentifier var isSet bool for _, d := range e.outputConf { diff --git a/xsensemulator/emulator_test.go b/xsensemulator/emulator_test.go index ad19594..549c632 100644 --- a/xsensemulator/emulator_test.go +++ b/xsensemulator/emulator_test.go @@ -153,7 +153,10 @@ func TestEmulator_Output(t *testing.T) { } packets := []byte{} for client.ScanMeasurementData() { - m, err := emulator.MarshalMessage(client.MeasurementData(), client.DataType()) + m, err := emulator.MarshalMessage( + client.MeasurementData(), + client.DataType(), + ) assert.NilError(t, err) packets = append(packets, m...) } diff --git a/xsensemulator/udpserialport.go b/xsensemulator/udpserialport.go new file mode 100644 index 0000000..bd768b1 --- /dev/null +++ b/xsensemulator/udpserialport.go @@ -0,0 +1,91 @@ +package xsensemulator + +import ( + "fmt" + "io" + "net" + "time" +) + +type UDPSerialPort struct { + io.ReadWriteCloser + opts *udpSerialPortOptions + OriginConn *net.UDPConn + DestinationAddr *net.UDPAddr +} + +func NewUDPSerialPort( + origin string, + destination string, + udpSerialOpts ...UDPSerialPortOption, +) (*UDPSerialPort, error) { + opts := defaultOptions() + for _, udpSerialOpt := range udpSerialOpts { + udpSerialOpt(opts) + } + + udpOriginAddr, err := net.ResolveUDPAddr("udp", origin) + if err != nil { + return nil, fmt.Errorf("new udp serial port: %w", err) + } + udpDestinationAddr, err := net.ResolveUDPAddr("udp", destination) + if err != nil { + return nil, fmt.Errorf("new udp serial port: %w", err) + } + originConn, err := net.ListenUDP("udp", udpOriginAddr) + if err != nil { + return nil, fmt.Errorf("new udp serial port: %w", err) + } + return &UDPSerialPort{ + opts: opts, + OriginConn: originConn, + DestinationAddr: udpDestinationAddr, + }, nil +} + +func (t UDPSerialPort) Read(p []byte) (n int, err error) { + // Check if a timeout opts have been added + if t.opts.timeout != time.Duration(0) { + err = t.OriginConn.SetReadDeadline(time.Now().Add(t.opts.timeout)) + if err != nil { + return 0, fmt.Errorf("udp serial port read: %w", err) + } + } + n, _, err = t.OriginConn.ReadFromUDP(p) + return n, err +} + +func (t UDPSerialPort) Write(p []byte) (n int, err error) { + // Check if a timeout opts have been added + if t.opts.timeout != time.Duration(0) { + err = t.OriginConn.SetWriteDeadline(time.Now().Add(t.opts.timeout)) + if err != nil { + return 0, fmt.Errorf("udp serial port write: %w", err) + } + } + return t.OriginConn.WriteToUDP(p, t.DestinationAddr) +} + +func (t UDPSerialPort) Close() error { + return t.OriginConn.Close() +} + +type udpSerialPortOptions struct { + // Timeout for setting read/write deadlines + timeout time.Duration +} + +// defaultOptions returns udpSerialPortOptions with sensible default values. +func defaultOptions() *udpSerialPortOptions { + return &udpSerialPortOptions{} +} + +// UDPSerialPortOption configures an UDPSerialPort. +type UDPSerialPortOption func(*udpSerialPortOptions) + +// WithTransmitInterface configures the interface to transmit on. +func WithTimeout(timeout time.Duration) UDPSerialPortOption { + return func(opt *udpSerialPortOptions) { + opt.timeout = timeout + } +}