package modem import ( "encoding/json" "fmt" "log" "os" "os/exec" "slices" "strings" "time" "github.com/CGSG-2021-AE4/modem-test/api/modem/at" ) type modem struct { // Internal values logger *log.Logger // Serial values baudrate int deviceName string port at.Port // Gpio values onOffPin gpioPin // Other values gpsInfo GpsInfo lastUpdateTime time.Time } type Modem interface { Init() error // SearchPort(isSoft bool) error Connect() error Disconnect() error IsAvailable() bool IsConnected() bool // Ping() error // SwitchToGpsMode() error // CalculateSpeed(newLatitude, newlongitude float64) Update() error GetInfo() (string, error) GetShortInfo() string SaveGPS(path string) error TestGPS() error } func New(logger *log.Logger) Modem { return &modem{ logger: logger, baudrate: 115200, onOffPin: gpioPin{Logger: logger, Pin: 6}, lastUpdateTime: time.Now(), } } func (m *modem) Init() error { // Turn module on log.Println("=============================== Turn on module") if err := m.onOffPin.Init(); err != nil { return fmt.Errorf("gpio pin init: %w", err) } // m.onOffPin.PowerOn() // Search m.logger.Println("=============================== Search") // Soft search if err := m.searchPort(true); err != nil { return fmt.Errorf("soft port search: %w", err) } // Wide search if m.port == nil { if err := m.searchPort(false); err != nil { return fmt.Errorf("not soft port search: %w", err) } } if m.port == nil { return fmt.Errorf("no port is detected") } // Connect m.logger.Println("=============================== Connect") if err := m.Connect(); err != nil { return fmt.Errorf("connect: %w", err) } // Tests m.logger.Println("=============================== Test") if err := m.TestGPS(); err != nil { return fmt.Errorf("testGPS: %w", err) } return nil } func (m *modem) Connect() error { if !m.IsAvailable() { return fmt.Errorf("port is not defined") } return m.port.Connect() } func (m *modem) Disconnect() error { if !m.IsAvailable() { return fmt.Errorf("port is not defined") } return m.port.Disconnect() } func (m *modem) IsAvailable() bool { return m.port != nil } func (m *modem) IsConnected() bool { if m.IsAvailable() { return m.port.IsConnected() } return false } func (m *modem) Update() error { if !m.IsConnected() { 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 } func (m *modem) TestGPS() error { m.logger.Println("Testing GPS") if err := m.switchToGpsMode(); err != nil { return fmt.Errorf("switch to GPS: %w", err) } if err := m.Update(); err != nil { return fmt.Errorf("update: %w", err) } m.logger.Println("Current coords:", m.GetShortInfo()) return nil } type ModemInfo struct { Port string `json:"Port"` GpsInfo } type Info struct { Modem ModemInfo `json:"Modem"` } func (m *modem) GetInfo() (string, error) { info := Info{ Modem: ModemInfo{ Port: m.port.GetName(), GpsInfo: m.gpsInfo, }, } buf, err := json.Marshal(info) if err != nil { fmt.Errorf("marshal info: %w", err) } return string(buf), nil // why you clamp } // Difference: I do not set \n at the end of string func (m *modem) GetShortInfo() string { return fmt.Sprintf("%f,%s,%f,%s", m.gpsInfo.Latitude, m.gpsInfo.LatitudeIndicator, m.gpsInfo.Longitude, m.gpsInfo.LongitudeIndicator) } func (m *modem) SaveGPS(path string) error { 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() if _, err = f.WriteString(m.GetShortInfo()); err != nil { return fmt.Errorf("write file: %W", err) } return nil } 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()) } log.Println(resp) log.Println("------------------") } } func (m *modem) checkPort(portName string) (outErr error) { defer func() { if outErr != nil { // Clear port if there is error m.port = nil } }() // Check device for existance if _, err := os.Stat(portName); err != nil { return fmt.Errorf("device does not exist") } // Check serial connection // Connect m.port = at.New(m.logger, portName, m.baudrate) if err := m.port.Connect(); err != nil { return fmt.Errorf("connect: %w", err) } defer m.port.Disconnect() // Do not bother about errors... // 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 // Ping m.logger.Println("Ping...") if err := m.ping(); err != nil { return fmt.Errorf("ping error: %w", err) } m.logger.Println("\x1b[38;2;0;255;0mOK\x1b[38;2;255;255;255m") // Check model m.logger.Println("Check model...") 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) } model := strings.Split(resp.String(), "\n")[0] if err != nil { return fmt.Errorf("get model: %w", err) } rightModel := "SIMCOM_SIM7600E-H" // m.logger.Printf("[% x]\n [% x]", []byte("SIMCOM_SIM7600E-H"), []byte(model)) if model[:len(rightModel)] != rightModel { return fmt.Errorf("invalid modem model: %s", model) } m.logger.Println("\x1b[38;2;0;255;0mOK\x1b[38;2;255;255;255m") return nil } func (m *modem) searchPort(isSoft bool) error { // Get ports ports := []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"} if !isSoft { ps, err := GetTtyDevices() if err != nil { fmt.Errorf("get serial devices: %w", err) } ports = ps } // Check ports SearchLoop: for _, p := range ports { m.logger.Printf("Checking port %s ...\n", p) if err := m.checkPort("/dev/" + p); err != nil { m.logger.Printf("\x1b[38;2;255;0;0mCheck failed: %s\x1b[38;2;255;255;255m\n", err.Error()) continue SearchLoop } m.logger.Print("Found modem on port: ", p) m.port = at.New(m.logger, "/dev/"+p, m.baudrate) return nil } return nil } func (m *modem) ping() error { resp, err := m.port.Send("AT") if err != nil { return err } if !resp.Check() { return fmt.Errorf("connection lost") } return nil } func (m *modem) switchToGpsMode() error { m.logger.Println("Enabling GPS mode...") // 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) } // Check gps mode status resp, err := m.port.Send("AT+CGPS?") if err != nil { return fmt.Errorf("make at ask: %w", err) } if !resp.Check() { return fmt.Errorf("error response") } ans := strings.Replace(strings.Split(strings.Split(resp.RmFront("+CGPS:").String(), "\n")[0], ",")[0], " ", "", -1) if ans == "1" { m.logger.Println("GPS already enabled") return nil } m.logger.Println(ans) // Modem is not in GPS mode resp, err = m.port.Send("AT+CGPS=1") if err != nil { return fmt.Errorf("try to switch to gps: %w", err) } if !resp.Check() { return fmt.Errorf("switch tp GPS failed") } m.logger.Println("GPS mode enabled") return nil } func GetTtyDevices() ([]string, error) { devices := []string{} // Get ports /**/ out, err := exec.Command("/bin/ls", "/dev").Output() if err != nil { return nil, fmt.Errorf("execute ls command: %w", err) } allPorts := strings.Split(string(out), "\n") for _, p := range allPorts { if len(p) > 3 && p[:3] == "tty" { devices = append(devices, p) } } slices.Reverse(devices) // ASK why return devices, nil } /* TODOs: maybe to store read/write buf in obj QUESTIONS: JSON why you clamp */ /* */