sim-modem/api/modem/at/at.go
2024-08-10 15:21:27 +03:00

172 lines
3.5 KiB
Go

package at
import (
"fmt"
"io"
"log"
"sync"
"time"
"go.bug.st/serial"
)
// Some constants
const (
ReadTimeout = time.Second
InputBufSize = 512
)
type atPort struct {
logger *log.Logger
mutex sync.Mutex // Mutex for all operation with serial port
baudrate int
portName string
port serial.Port
inputBuf []byte
}
type Port interface {
GetName() string
GetBaudrate() int
SerialPort() serial.Port // For extra need
Mutex() sync.Locker // retruns pointer to mutex for advanced use like readign NMEA reports
Connect() error
Disconnect() error
IsConnected() bool
RawSend(msg string, timeout time.Duration) (string, error)
Send(cmd string) (Resp, error)
SendWithTimeout(cmd string, timeout time.Duration) (Resp, error)
io.Closer
}
func New(logger *log.Logger, portName string, baudrate int) Port {
return &atPort{
logger: logger,
portName: portName,
baudrate: baudrate,
inputBuf: make([]byte, InputBufSize),
}
}
func (p *atPort) GetName() string {
return p.portName
}
func (p *atPort) GetBaudrate() int {
return p.baudrate
}
func (p *atPort) SerialPort() serial.Port {
return p.port
}
func (p *atPort) Mutex() sync.Locker {
return &p.mutex
}
func (p *atPort) Connect() error {
p.mutex.Lock()
defer p.mutex.Unlock()
p.logger.Println("Connecting to", p.portName, "...")
s, err := serial.Open(p.portName, &serial.Mode{BaudRate: p.baudrate})
if err != nil {
return fmt.Errorf("open port: %w", err)
}
// s.Close() There is no open f
// s.Open()
p.port = s
p.port.SetReadTimeout(ReadTimeout)
p.port.ResetInputBuffer()
p.port.ResetOutputBuffer()
return nil
}
func (p *atPort) Disconnect() error {
p.mutex.Lock()
defer func() {
p.port = nil
p.mutex.Unlock()
}()
if err := p.port.Close(); err != nil {
return fmt.Errorf("close port: %w", err)
}
return nil
}
func (p *atPort) IsConnected() bool {
p.mutex.Lock()
defer p.mutex.Unlock()
return p.port != nil
}
// Low level write/read function
func (p *atPort) RawSend(msg string, timeout time.Duration) (string, error) {
p.mutex.Lock()
defer p.mutex.Unlock()
// Write
if _, err := p.port.Write([]byte(msg)); err != nil {
return "", fmt.Errorf("serial port write: %w", err)
}
time.Sleep(timeout)
// Read
outBuf := make([]byte, 0)
readLoop:
for {
readLen, err := p.port.Read(p.inputBuf)
if err != nil {
return "", fmt.Errorf("port read: %w", err)
}
if readLen == 0 {
break readLoop
}
outBuf = append(outBuf, p.inputBuf[:readLen]...)
if readLen < len(p.inputBuf) {
break readLoop
}
}
// p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(outBuf), "\x1b[38;2;255;255;255m")
return string(outBuf), nil
}
func (p *atPort) Send(cmd string) (Resp, error) {
rawResp, err := p.RawSend(cmd+"\r\n", time.Microsecond)
if err != nil {
return RespNil, fmt.Errorf("%s request: %w", cmd, err)
}
if len(rawResp) <= 4 {
return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp))
}
resp := rawResp[2 : len(rawResp)-2] // Cut \r\n
return Resp(resp), nil
}
func (p *atPort) SendWithTimeout(cmd string, timeout time.Duration) (Resp, error) {
rawResp, err := p.RawSend(cmd+"\r\n", timeout)
if err != nil {
return RespNil, fmt.Errorf("%s request: %w", cmd, err)
}
if len(rawResp) <= 4 {
return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp))
}
resp := rawResp[2 : len(rawResp)-2] // Cut \r\n
return Resp(resp), nil
}
func (p *atPort) Close() error {
p.mutex.Lock()
defer p.mutex.Unlock()
return p.port.Close()
}