sim-modem/api/modem/modem.go

385 lines
8.5 KiB
Go
Raw Normal View History

2024-07-18 16:34:26 +00:00
package modem
import (
"fmt"
2024-07-25 13:58:09 +00:00
"io"
2024-07-18 16:34:26 +00:00
"log"
"os"
"os/exec"
"strings"
"time"
2024-07-22 15:53:34 +00:00
"github.com/CGSG-2021-AE4/modem-test/api/modem/at"
2024-07-26 19:58:34 +00:00
"github.com/CGSG-2021-AE4/modem-test/api/modem/gpio"
"github.com/CGSG-2021-AE4/modem-test/api/modem/internet"
2024-07-29 13:51:54 +00:00
"github.com/CGSG-2021-AE4/modem-test/api/modem/sms"
2024-07-18 16:34:26 +00:00
)
2024-07-25 13:58:09 +00:00
type ModemData struct {
Port string `json:"Port"`
GpsData
}
2024-07-18 16:34:26 +00:00
type modem struct {
2024-07-23 14:59:26 +00:00
// Internal values
logger *log.Logger
// Serial values
2024-07-21 13:05:09 +00:00
baudrate int
2024-07-23 16:02:28 +00:00
deviceName string
port at.Port
2024-07-18 16:34:26 +00:00
2024-07-23 14:59:26 +00:00
// Gpio values
2024-07-26 19:58:34 +00:00
onOffPin gpio.Pin
2024-07-18 16:34:26 +00:00
// Other values
lastUpdateTime time.Time
// GPS
gpsInfo GpsData
// Internet connection
ic internet.Conn
2024-07-29 13:51:54 +00:00
// Sms and calls
sms sms.Dialer
2024-07-18 16:34:26 +00:00
}
type Modem interface {
Init() error
2024-07-25 13:58:09 +00:00
Validate() bool
2024-07-18 16:34:26 +00:00
Update() error
2024-07-25 13:58:09 +00:00
GetInfo() ModemData
2024-07-29 13:51:54 +00:00
2024-07-29 17:03:22 +00:00
// Temp access to SMS and AT interface
2024-07-29 13:51:54 +00:00
Sms() sms.Dialer
2024-07-29 17:03:22 +00:00
At() at.Port
2024-07-29 13:51:54 +00:00
2024-07-25 13:58:09 +00:00
io.Closer
2024-07-18 16:34:26 +00:00
}
2024-07-23 16:02:28 +00:00
func New(logger *log.Logger) Modem {
2024-07-18 16:34:26 +00:00
return &modem{
2024-07-23 16:02:28 +00:00
logger: logger,
2024-07-18 16:34:26 +00:00
baudrate: 115200,
onOffPin: gpio.New(log.New(logger.Writer(), "gpio", log.LstdFlags), 6),
2024-07-18 16:34:26 +00:00
lastUpdateTime: time.Now(),
}
}
func (m *modem) Init() error {
2024-07-21 13:05:09 +00:00
// Turn module on
m.logger.Println("=============================== Turn on module")
2024-07-23 19:04:15 +00:00
if err := m.onOffPin.Init(); err != nil {
return fmt.Errorf("gpio pin init: %w", err)
}
// m.onOffPin.PowerOn()
2024-07-21 13:05:09 +00:00
2024-07-23 14:59:26 +00:00
// Search
m.logger.Println("=============================== Search")
2024-07-21 13:05:09 +00:00
// Soft search
2024-07-23 16:02:28 +00:00
if err := m.searchPort(true); err != nil {
2024-07-18 16:34:26 +00:00
return fmt.Errorf("soft port search: %w", err)
}
2024-07-23 14:59:26 +00:00
// Wide search
2024-07-21 13:05:09 +00:00
if m.port == nil {
2024-07-23 16:02:28 +00:00
if err := m.searchPort(false); err != nil {
2024-07-18 16:34:26 +00:00
return fmt.Errorf("not soft port search: %w", err)
}
}
2024-07-21 13:05:09 +00:00
if m.port == nil {
2024-07-23 16:02:28 +00:00
return fmt.Errorf("no port is detected")
2024-07-18 16:34:26 +00:00
}
2024-07-21 13:05:09 +00:00
// Connect
2024-07-23 14:59:26 +00:00
m.logger.Println("=============================== Connect")
2024-07-25 13:58:09 +00:00
if err := m.connect(); err != nil {
2024-07-18 16:34:26 +00:00
return fmt.Errorf("connect: %w", err)
}
2024-07-23 14:26:24 +00:00
2024-07-21 13:05:09 +00:00
// Tests
2024-07-23 14:59:26 +00:00
m.logger.Println("=============================== Test")
2024-07-25 13:58:09 +00:00
if err := m.testGPS(); err != nil {
2024-07-18 16:34:26 +00:00
return fmt.Errorf("testGPS: %w", err)
}
2024-07-29 13:51:54 +00:00
// // Establish internet connection
// m.logger.Println("=============================== Internet")
// m.ic = internet.New(log.New(m.logger.Writer(), "internet", log.LstdFlags), m.port)
// if err := m.ic.Init(); err != nil {
// return fmt.Errorf("internet connection init: %w", err)
// }
// Init sms dialer
m.sms = sms.New(log.New(m.logger.Writer(), "sms", log.LstdFlags), m.port)
if err := m.sms.Init(); err != nil {
return fmt.Errorf("sms dialer init %w", err)
}
2024-07-18 16:34:26 +00:00
return nil
}
2024-07-25 13:58:09 +00:00
func (m *modem) Validate() bool {
return m.isConnected()
2024-07-23 16:02:28 +00:00
}
func (m *modem) Update() error {
2024-07-25 13:58:09 +00:00
if !m.isConnected() {
2024-07-23 16:02:28 +00:00
m.logger.Println("No connection to module")
return nil
}
m.logger.Println("Update")
// ans, err := m.port.Request(at.CmdQuestion, "CGPSINFO")
// if err != nil {
// return fmt.Errorf("check GPS info mode: %w", err)
// }
// ok := ans == "1"
// if !ok {
// _, err := m.port.Request(at.CmdCheck, "CGPSINFO")
// if err != nil {
// return fmt.Errorf("switch to GPS info mode: %w", err)
// }
// m.logger.Println("switched to GPS mode")
// } else {
// m.logger.Println("mode in right GPS mode")
// }
// Update
m.logger.Println("Receiving GPS data...")
resp, err := m.port.Send("AT+CGPSINFO")
if err != nil {
return fmt.Errorf("receive GPS data: %w", err)
}
if !resp.Check() {
return fmt.Errorf("error response")
}
m.logger.Println("Decoding data...")
if err := m.gpsInfo.decode(strings.Split(strings.Replace(resp.RmFront("+CGPSINFO:").String(), "\r", "", -1), "\n")[0]); err != nil {
m.logger.Println("Gps info decode error:", err.Error())
return nil
}
m.logger.Println("Decoded successfully")
return nil
}
2024-07-25 13:58:09 +00:00
func (m *modem) GetInfo() ModemData {
return ModemData{
Port: m.port.GetName(),
GpsData: m.gpsInfo,
2024-07-23 16:02:28 +00:00
}
2024-07-25 13:58:09 +00:00
}
2024-07-23 16:02:28 +00:00
2024-07-29 13:51:54 +00:00
func (m *modem) Sms() sms.Dialer {
return m.sms
}
2024-07-29 17:03:22 +00:00
func (m *modem) At() at.Port {
return m.port
}
2024-07-25 13:58:09 +00:00
func (m *modem) Close() error {
2024-07-29 13:51:54 +00:00
if err := m.sms.Close(); err != nil {
return fmt.Errorf("sms: %w", err)
}
2024-07-25 13:58:09 +00:00
// Not right way I think
if err := m.port.Close(); err != nil {
return fmt.Errorf("serial port: %w", err)
2024-07-25 13:58:09 +00:00
}
if err := m.onOffPin.Close(); err != nil {
return fmt.Errorf("gpio pin: %w", err)
2024-07-23 16:02:28 +00:00
}
if err := m.ic.Close(); err != nil {
return fmt.Errorf("internet connection: %w", err)
}
2024-07-23 16:02:28 +00:00
return nil
}
2024-07-25 13:58:09 +00:00
func (m *modem) connect() error {
if m.port == nil {
2024-07-25 13:58:09 +00:00
return fmt.Errorf("port is not defined")
}
return m.port.Connect()
2024-07-23 16:02:28 +00:00
}
2024-07-25 13:58:09 +00:00
func (m *modem) disconnect() error {
if m.port == nil {
2024-07-25 13:58:09 +00:00
return fmt.Errorf("port is not defined")
}
return m.port.Disconnect()
2024-07-23 16:02:28 +00:00
}
2024-07-25 13:58:09 +00:00
func (m *modem) isConnected() bool {
if m.port != nil {
return m.port.IsConnected()
2024-07-23 16:02:28 +00:00
}
2024-07-25 13:58:09 +00:00
return false
}
func (m *modem) testGPS() error {
m.logger.Println("Testing GPS")
if err := m.Update(); err != nil {
return fmt.Errorf("update: %w", err)
2024-07-23 16:02:28 +00:00
}
2024-07-25 13:58:09 +00:00
m.logger.Println("Current coords:", m.getShortInfo())
return nil
2024-07-23 16:02:28 +00:00
}
// Difference: I do not set \n at the end of string
2024-07-25 13:58:09 +00:00
func (m *modem) getShortInfo() string {
2024-07-23 16:02:28 +00:00
return fmt.Sprintf("%f,%s,%f,%s", m.gpsInfo.Latitude, m.gpsInfo.LatitudeIndicator, m.gpsInfo.Longitude, m.gpsInfo.LongitudeIndicator)
}
2024-07-25 13:58:09 +00:00
func (m *modem) saveGPS(path string) error {
2024-07-23 16:02:28 +00:00
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return fmt.Errorf("open file: %w", err)
}
defer f.Close()
2024-07-25 13:58:09 +00:00
if _, err = f.WriteString(m.getShortInfo()); err != nil {
2024-07-23 16:02:28 +00:00
return fmt.Errorf("write file: %W", err)
}
return nil
}
2024-07-29 17:03:22 +00:00
2024-07-23 19:04:15 +00:00
func (m *modem) testConsole() {
for {
var inStr string
fmt.Scanln(&inStr)
if inStr == "exit" {
return
}
resp, err := m.port.Send(inStr)
if err != nil {
m.logger.Println("ERROR:", err.Error())
}
m.logger.Println(resp)
m.logger.Println("------------------")
2024-07-23 19:04:15 +00:00
}
}
2024-07-23 16:02:28 +00:00
func (m *modem) checkPort(portName string) (outErr error) {
defer func() {
if outErr != nil { // Clear port if there is error
m.port = nil
}
}()
2024-07-21 13:05:09 +00:00
// Check device for existance
if _, err := os.Stat(portName); err != nil {
return fmt.Errorf("device does not exist")
}
// Check serial connection
// Connect
2024-07-23 16:02:28 +00:00
m.port = at.New(m.logger, portName, m.baudrate)
2024-07-22 17:37:02 +00:00
if err := m.port.Connect(); err != nil {
2024-07-21 13:05:09 +00:00
return fmt.Errorf("connect: %w", err)
}
2024-07-22 17:37:02 +00:00
defer m.port.Disconnect() // Do not bother about errors...
2024-07-21 13:05:09 +00:00
2024-07-23 19:04:15 +00:00
// Reset input
if err := m.port.GetSerialPort().ResetInputBuffer(); err != nil {
return fmt.Errorf("reset input buffer: %w", err)
}
// Reset output
if err := m.port.GetSerialPort().ResetOutputBuffer(); err != nil {
return fmt.Errorf("reset output buffer: %w", err)
}
m.port.Send("ATE0") // This shit sometimes enables echo mode... why... when... but it can
2024-07-29 15:53:55 +00:00
// m.port.Send("\r\n")
2024-07-23 19:04:15 +00:00
2024-07-21 13:05:09 +00:00
// Ping
2024-07-23 14:59:26 +00:00
m.logger.Println("Ping...")
2024-07-23 16:02:28 +00:00
if err := m.ping(); err != nil {
2024-07-21 13:05:09 +00:00
return fmt.Errorf("ping error: %w", err)
}
2024-07-23 19:04:15 +00:00
m.logger.Println("\x1b[38;2;0;255;0mOK\x1b[38;2;255;255;255m")
2024-07-21 13:05:09 +00:00
// Check model
2024-07-23 14:59:26 +00:00
m.logger.Println("Check model...")
2024-07-23 09:22:53 +00:00
resp, err := m.port.Send("AT+CGMM")
if err != nil {
return fmt.Errorf("get model: %w", err)
}
if !resp.Check() {
return fmt.Errorf("error response: %s", resp)
}
2024-07-23 14:26:24 +00:00
model := strings.Split(resp.String(), "\n")[0]
2024-07-21 13:05:09 +00:00
if err != nil {
return fmt.Errorf("get model: %w", err)
}
2024-07-23 14:26:24 +00:00
rightModel := "SIMCOM_SIM7600E-H"
2024-07-23 14:59:26 +00:00
// m.logger.Printf("[% x]\n [% x]", []byte("SIMCOM_SIM7600E-H"), []byte(model))
2024-07-29 15:53:55 +00:00
if len(model) >= len(rightModel) && model[:len(rightModel)] != rightModel {
2024-07-21 13:05:09 +00:00
return fmt.Errorf("invalid modem model: %s", model)
}
2024-07-23 19:04:15 +00:00
m.logger.Println("\x1b[38;2;0;255;0mOK\x1b[38;2;255;255;255m")
2024-07-21 13:05:09 +00:00
return nil
}
2024-07-18 16:34:26 +00:00
2024-07-23 16:02:28 +00:00
func (m *modem) searchPort(isSoft bool) error {
2024-07-18 16:34:26 +00:00
// Get ports
2024-07-21 13:05:09 +00:00
ports := []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"}
if !isSoft {
2024-07-25 13:58:09 +00:00
ps, err := getTtyDevices()
2024-07-18 16:34:26 +00:00
if err != nil {
2024-07-21 13:05:09 +00:00
fmt.Errorf("get serial devices: %w", err)
2024-07-18 16:34:26 +00:00
}
2024-07-21 13:05:09 +00:00
ports = ps
2024-07-18 16:34:26 +00:00
}
2024-07-21 13:05:09 +00:00
2024-07-18 16:34:26 +00:00
// Check ports
2024-07-21 13:05:09 +00:00
SearchLoop:
2024-07-18 16:34:26 +00:00
for _, p := range ports {
2024-07-23 14:59:26 +00:00
m.logger.Printf("Checking port %s ...\n", p)
2024-07-18 16:34:26 +00:00
2024-07-21 13:05:09 +00:00
if err := m.checkPort("/dev/" + p); err != nil {
2024-07-23 19:04:15 +00:00
m.logger.Printf("\x1b[38;2;255;0;0mCheck failed: %s\x1b[38;2;255;255;255m\n", err.Error())
2024-07-21 13:05:09 +00:00
continue SearchLoop
2024-07-18 16:34:26 +00:00
}
2024-07-23 14:59:26 +00:00
m.logger.Print("Found modem on port: ", p)
2024-07-23 16:02:28 +00:00
m.port = at.New(m.logger, "/dev/"+p, m.baudrate)
2024-07-18 16:34:26 +00:00
return nil
}
return nil
}
2024-07-23 16:02:28 +00:00
func (m *modem) ping() error {
2024-07-23 09:22:53 +00:00
resp, err := m.port.Send("AT")
if err != nil {
return err
}
2024-07-23 14:26:24 +00:00
if !resp.Check() {
2024-07-23 09:22:53 +00:00
return fmt.Errorf("connection lost")
}
return nil
2024-07-18 16:34:26 +00:00
}
2024-07-25 13:58:09 +00:00
func getTtyDevices() ([]string, error) {
2024-07-21 13:05:09 +00:00
// Get ports
/**/
2024-07-25 13:58:09 +00:00
out, err := exec.Command("ls", "--", "/dev/tty[!0-9]*").Output()
2024-07-21 13:05:09 +00:00
if err != nil {
return nil, fmt.Errorf("execute ls command: %w", err)
}
2024-07-25 13:58:09 +00:00
ports := strings.Split(string(out), "\n")
return ports, nil
2024-07-21 13:05:09 +00:00
}
2024-07-18 16:34:26 +00:00
/*
TODOs:
maybe to store read/write buf in obj
QUESTIONS:
2024-07-21 13:05:09 +00:00
JSON why you clamp
2024-07-18 16:34:26 +00:00
*/
/*
*/