sim-modem/api/modem/at/at.go

155 lines
3.1 KiB
Go
Raw Normal View History

2024-07-21 13:05:09 +00:00
package at
import (
"fmt"
"log"
"strings"
"time"
"go.bug.st/serial"
)
// Some constants
const (
ReadTimeout = time.Second
InputBufSize = 128
)
// Command types
type CmdType byte
// Command types base on request/answer semantic:
const (
CmdTest CmdType = iota
// AT\r\n
// OK
CmdCheck
// AT+<CMD>\r\n
// OK
CmdGet
// AT+<CMD>\r\n
// +<CMD>: <ANS>
CmdQuestion
// AT+<CMD>?\r\n
// +<CMD>: <ANS>
)
type atPort struct {
baudrate int
portName string
port serial.Port
inputBuf []byte
}
type Port interface {
Connect() error
Disconnect() error
Request(cmdType CmdType, cmd string) (string, error)
GetName() string
IsConnected() bool
GetSerialPort() serial.Port // For extra need
}
func New(portName string, baudrate int) Port {
return &atPort{
portName: portName,
baudrate: baudrate,
inputBuf: make([]byte, InputBufSize),
}
}
func (p *atPort) Connect() error {
log.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 {
defer func() {
p.port = nil
}()
if err := p.port.Close(); err != nil {
return fmt.Errorf("close port: %w", err)
}
return nil
}
// Low level write/read function
func (p *atPort) makeReq(msg string) (string, error) {
// Write
p.port.ResetInputBuffer()
log.Println("Write...") // DEBUG
if written, err := p.port.Write([]byte(msg)); err != nil {
return "", fmt.Errorf("serial port write: %w", err)
} else {
log.Println("Written:", written) // DEBUG
}
// Read
log.Println("Read...") // DEBUG
readLen, err := p.port.Read(p.inputBuf)
if err != nil {
return "", fmt.Errorf("port read: %w", err)
}
log.Println("Read: ", readLen, string(p.inputBuf[:readLen])) // DEBUG
return string(p.inputBuf[:readLen]), nil
}
func (p *atPort) Request(cmdType CmdType, cmd string) (string, error) {
msg := "AT"
// Make command
// By default it just will be AT check cmd
switch cmdType {
case CmdGet, CmdCheck:
msg += cmd
case CmdQuestion:
msg += cmd + "?"
}
msg += "\r\n"
rawResp, err := p.makeReq(msg)
log.Println("Got")
if err != nil {
return "", fmt.Errorf("make at request: %w", err)
}
if len(rawResp) == 0 {
return "", fmt.Errorf("read nothing")
}
resp := strings.Split(rawResp, "\n")
switch cmdType {
case CmdTest, CmdCheck:
// Check and test cmds do not suppose anything but OK
if len(resp[1]) >= 2 && resp[1][:2] == "OK" {
return "", nil
}
return "", fmt.Errorf("connection lost")
case CmdGet, CmdQuestion:
checkL := len(cmd) + 1
if len(resp[1]) >= checkL && resp[1][:checkL] != "+"+cmd {
return "", fmt.Errorf("connetion lost")
}
return strings.Split(resp[1], ":")[1], nil
}
return "", fmt.Errorf("undefined command type")
}
func (p *atPort) GetName() string {
return p.portName
}
func (p *atPort) IsConnected() bool {
return p.port != nil
}
func (p *atPort) GetSerialPort() serial.Port {
return p.port
}