diff --git a/api/modem/gps/status.go b/api/modem/gps/status.go index 368f2b5..4f9277a 100644 --- a/api/modem/gps/status.go +++ b/api/modem/gps/status.go @@ -42,14 +42,14 @@ checkLoop: } st.GotResponses = true - g.logger.Println("NMEA check:", s) + // g.logger.Println("NMEA check:", s) values := strings.Split(s, ",") if len(values[0]) != 6 { return StatusNil, fmt.Errorf("nmea invalid sentence: %s", s) } switch values[0][3:] { // Switch by content case "GSV": // Any satelites - g.logger.Println("check GSV") + // g.logger.Println("check GSV") // Check len if len(values) < 17 { g.logger.Println("GSV too small values") @@ -83,7 +83,7 @@ checkLoop: } st.FoundSatelitesCount = satCount case "GSA": // Active satelites - g.logger.Println("check GSA") + // g.logger.Println("check GSA") // Check len if len(values) < 17 { @@ -107,7 +107,7 @@ checkLoop: } st.ActiveSatelitesCount = max(st.ActiveSatelitesCount, count) case "RMC": // Minimum GPS data - g.logger.Println("check RMC") + // g.logger.Println("check RMC") // Check len if len(values) < 12 { g.logger.Println("RMC too small values") @@ -125,7 +125,7 @@ checkLoop: st.IsValidData = true } case "GST": - g.logger.Println("check GST") + // g.logger.Println("check GST") // Check len if len(values) < 8 { g.logger.Println("GST too small values") diff --git a/api/modem/internet/ic.go b/api/modem/internet/ic.go index 32d4b85..e8ab955 100644 --- a/api/modem/internet/ic.go +++ b/api/modem/internet/ic.go @@ -8,125 +8,147 @@ import ( "strings" "gitea.unprism.ru/KRBL/sim-modem/api/modem/at" - "gitea.unprism.ru/KRBL/sim-modem/api/modem/utils" ) -var apns = map[string]string{ - "Tinkoff": "m.tinkoff", -} - -const pppConfigName = "annalistnet" -const pppConfig = ` -# Annalist project custom internet connection - -# APN: -connect "/usr/sbin/chat -v -f /etc/chatscripts/gprs -T %s" - -# Port: -%s - -# Baudrate: -%d - -noipdefault -usepeerdns -defaultroute -persist -noauth -nocrtscts -local -` - type conn struct { logger *log.Logger port at.Port + + pppPort string + + isConnectExecuted bool + isInited bool } type Conn interface { - Init() error + Init(pppPort string) error + Connect() error Disconnect() error - Ping() bool // Is connected + + IsConnected() bool // Check interface existance + Ping() error + io.Closer } func New(logger *log.Logger, port at.Port) Conn { return &conn{ - logger: logger, - port: port, + logger: logger, + port: port, + isConnectExecuted: false, + isInited: false, } } -func (c *conn) Connect() error { - if ok, err := utils.CheckService(c.port, c.logger); err != nil || !ok { - if err != nil { - return fmt.Errorf("check service: %w", err) - } - return fmt.Errorf("no service") +func (c *conn) Init(pppPort string) error { + c.pppPort = pppPort + // Setup only setup + if err := c.setup(); err != nil { + return fmt.Errorf("setup: %w", err) } + c.isInited = true + return nil +} + +func (c *conn) Connect() error { + if !c.isInited { + return fmt.Errorf("internet submodule is not inited") + } + // Check is already connected + if c.isConnectExecuted { + return fmt.Errorf("already connected") + } + // Check signal + // if ok, err := utils.CheckService(c.port, c.logger); err != nil || !ok { + // if err != nil { + // return fmt.Errorf("check service: %w", err) + // } + // return fmt.Errorf("no service") + // } resp, err := exec.Command("pon", pppConfigName).Output() if err != nil { - return fmt.Errorf("execute connect cmd: %w", err) + return fmt.Errorf("execute pon cmd: %w", err) } - c.logger.Println("DEBUG pon response:", string(resp)) + if len(resp) > 0 { + c.logger.Println("pon response:", string(resp)) + } + c.isConnectExecuted = true + + // Set default route + + c.logger.Println("\x1b[38;2;0;255;0mInternet connected.\x1b[38;2;255;255;255m") return nil } func (c *conn) Disconnect() error { + // return nil // Temporary do not turn off inet + if !c.isConnectExecuted { + return nil + // return fmt.Errorf("internet is not connected") + } + c.isConnectExecuted = false resp, err := exec.Command("poff", pppConfigName).Output() if err != nil { - return fmt.Errorf("execute disconnect cmd: %w", err) + return fmt.Errorf("execute poff cmd: %w", err) } - c.logger.Println("DEBUG poff response:", string(resp)) + if len(resp) > 0 { + c.logger.Println("poff response:", string(resp)) + } + c.isConnectExecuted = false return nil } -func (c *conn) Init() error { - // Setup - if err := c.setup(); err != nil { - return fmt.Errorf("setup: %w", err) - } - // // Connect - // c.logger.Println("Connect...") - // if err := c.Connect(); err != nil { - // return fmt.Errorf("connect: %w", err) - // } - - // //DEBUG - // resp, err := exec.Command("ifconfig").Output() - // if err != nil { - // return fmt.Errorf("execute ifconfig cmd: %w", err) - // } - // c.logger.Println("DEBUG ifconfig resp:", string(resp)) - - // // Test connectin using Ping - // c.logger.Println("Test...") - // if !c.Ping() { - // return fmt.Errorf("ping failed") - // } - return nil -} - -func (c *conn) Ping() bool { +func (c *conn) Ping() error { // Test - try to connect to Google DNS // ping -I ppp0 8.8.8.8 resp, err := exec.Command("ping", "8.8.8.8").Output() if err != nil { - c.logger.Println("Ping 1 cmd error:", err) + c.logger.Println("Ping default interface cmd error:", err) } - c.logger.Println("Ping 1 resp:", string(resp)) + c.logger.Println("Ping default interface resp:", string(resp)) resp, err = exec.Command("ping", "-I", "ppp0", "8.8.8.8").Output() if err != nil { - c.logger.Println("Ping 2 cmd error:", err) + c.logger.Println("Ping ppp0 interface cmd error:", err) } - c.logger.Println("Ping 2 resp:", string(resp)) + c.logger.Println("Ping ppp0 interface resp:", string(resp)) - return !(strings.Contains(string(resp), "Destination Host Unreachable") || strings.Contains(string(resp), "Destination Net Unreachable")) // tmp solution + if strings.Contains(string(resp), "Destination Host Unreachable") || strings.Contains(string(resp), "Destination Net Unreachable") { + return fmt.Errorf("ping response: %s", string(resp)) + } + return nil +} + +func (c *conn) IsConnected() bool { + if !c.isConnectExecuted { + return false + } + // Make "ifconfig" request + resp, err := exec.Command("ifconfig").Output() + if err != nil { + c.logger.Println("ifconfig cmd error:", err.Error()) + return false + } + lines := strings.Split(string(resp), "\n") + for _, l := range lines { + if len(l) == 0 { + continue + } + if l[0] == ' ' { + continue + } + interfaceName := strings.Split(l, ":")[0] + if interfaceName == "ppp0" { + return true + } + } + return false // Did not found } func (c *conn) Close() error { + c.isInited = false if err := c.Disconnect(); err != nil { return fmt.Errorf("diconnect: %w", err) } diff --git a/api/modem/internet/setup.go b/api/modem/internet/setup.go index 09ccdf5..498ca4b 100644 --- a/api/modem/internet/setup.go +++ b/api/modem/internet/setup.go @@ -9,27 +9,40 @@ import ( "gitea.unprism.ru/KRBL/sim-modem/api/modem/utils" ) +var apns = map[string]string{ + "Tinkoff": "m.tinkoff", + "Megafon": "internet", + "BeeLine": "internet.beeline.ru", +} + +const pppConfigName = "annalistnet" +const pppConfig = ` +# Annalist project custom internet connection + +# APN: +connect "/usr/sbin/chat -v -f /etc/chatscripts/gprs -T %s" + +# Port: +%s + +# Baudrate: +%d + +noipdefault +usepeerdns +defaultroute +persist +noauth +nocrtscts +local +` + func (c *conn) setup() error { - c.logger.Println("Check requirenments...") if err := c.checkReqs(); err != nil { return fmt.Errorf("check requirenments: %w", err) } - // DEBUG show networks and signal - resp, err := c.port.Send("AT+COPS?") - if err != nil { - return fmt.Errorf("AT+COPS? request: %w", err) - } - c.logger.Println("DEBUG networks:", resp) - - resp, err = c.port.Send("AT+CSQ") - if err != nil { - return fmt.Errorf("AT+CSQ request: %w", err) - } - c.logger.Println("DEBUG signal quality:", resp) - // Configure ppp // what is better ASK If /etc/ppp/peers/annalistnet not exists - c.logger.Println("Configure ppp...") if err := c.configurePPP(); err != nil { return fmt.Errorf("configure ppp: %w", err) } @@ -75,9 +88,8 @@ func (c *conn) ensurePackage(pname string) error { func (c *conn) checkPackageExist(pname string) bool { resp, err := exec.Command("apt-mark", "showmanual", pname).Output() - c.logger.Println("CHECK:", resp) if err != nil { - c.logger.Println("CHECK PACKAGE ERROR: ", err.Error()) + c.logger.Println("check package error: ", err.Error()) return false } return string(resp[:len(pname)]) == pname @@ -103,8 +115,8 @@ func (c *conn) configurePPP() error { } // Make config - c.logger.Printf("Config values: %s, %s, %d", apn, c.port.GetName(), c.port.GetBaudrate()) - config := fmt.Sprintf(pppConfig, apn, c.port.GetName(), c.port.GetBaudrate()) + c.logger.Printf("Config ppp values: %s, %s, %d", apn, c.pppPort, c.port.GetBaudrate()) + config := fmt.Sprintf(pppConfig, apn, c.pppPort, c.port.GetBaudrate()) // Write to file f, err := os.OpenFile("/etc/ppp/peers/"+pppConfigName, os.O_CREATE|os.O_WRONLY, 0666) diff --git a/api/modem/modem.go b/api/modem/modem.go index ec969e7..aea9af9 100644 --- a/api/modem/modem.go +++ b/api/modem/modem.go @@ -6,6 +6,7 @@ import ( "log" "os" "os/exec" + "strconv" "strings" "sync" "time" @@ -17,6 +18,9 @@ import ( "gitea.unprism.ru/KRBL/sim-modem/api/modem/sms" ) +// yy/MM/dd,hh:mm:ss+zzzz +const timeLayout = "06/01/02,15:04:05-0700" + type ModemData struct { Port string `json:"Port"` gps.Data @@ -53,14 +57,16 @@ type Modem interface { IsConnected() bool Update() error GetData() ModemData + GetTime() (time.Time, error) PowerOn() error PowerOff() error // Access to SMS, GPS, AT interfaces mostly for debug - At() at.Port // Send - Gps() gps.Gps // Update, GetData, GetStatus - Sms() sms.Sms // Send, ReadNew + At() at.Port // Send + Gps() gps.Gps // Update, GetData, GetStatus + Sms() sms.Sms // Send, ReadNew + Ic() internet.Conn // Connect, Disconnect io.Closer } @@ -80,29 +86,41 @@ func (m *modem) Init() error { defer m.mutex.Unlock() // Turn module on - m.logger.Println("=============================== Turn on module") + m.logger.Println("=============================== Init gpio") if err := m.onOffPin.Init(); err != nil { return fmt.Errorf("gpio pin init: %w", err) } // Search m.logger.Println("=============================== Search") - // Soft search - if err := m.searchPort(true); err != nil { + ports, err := m.searchPort(true) + if 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 len(ports) == 0 { + // Wide search + ports, err := m.searchPort(true) + if err != nil { + return fmt.Errorf("wide port search: %w", err) + } + if len(ports) == 0 { + return fmt.Errorf("no AT ports found") } } - if m.port == nil { - return fmt.Errorf("no port is detected") + if len(ports) == 1 { + // return fmt.Errorf("only one AT port found") + m.logger.Println("!!!!! only one AT port found") } + // !!!! + // Internet connection and serial connection on one port is impossible, so: + // port[0] is for serial + // port[1] is for internet(ppp) + m.logger.Println(ports) + // Connect m.logger.Println("=============================== Connect") + m.port = at.New(m.logger, ports[0], m.baudrate) if err := m.connect(); err != nil { return fmt.Errorf("connect: %w", err) } @@ -113,17 +131,11 @@ func (m *modem) Init() error { m.logger.Println("=============================== Init submodules") // m.ic = internet.New(log.New(submodulesLogger, "modem-internet : ", log.LstdFlags), m.port) - // if err := m.ic.Init(); err != nil { + // if err := m.ic.Init(ports[1]); err != nil { // m.logger.Printf("\x1b[38;2;255;0;0mInternet: %s\x1b[38;2;255;255;255m\n", err.Error()) // } else { // m.logger.Println("\x1b[38;2;0;255;0mInternet OK\x1b[38;2;255;255;255m") // } - // - // if err := m.ic.Connect(); err != nil { - // m.logger.Println("connect to internet: %w", err) - // } else { - // m.logger.Println("internet connected") - // } // m.sms = sms.New(log.New(submodulesLogger, "modem-sms : ", log.LstdFlags), m.port) // if err := m.sms.Init(); err != nil { @@ -176,6 +188,35 @@ func (m *modem) GetData() ModemData { } } +func (m *modem) GetTime() (time.Time, error) { + // Make request + resp, err := m.port.Send("AT+CCLK?") + if err != nil { + return time.Time{}, err + } + if !resp.Check() || !resp.CheckFront("+CCLK: ") { + return time.Time{}, fmt.Errorf("CCLK? error response: %s", resp.String()) + } + // Extract time string + values := strings.Split(strings.Split(resp.String(), "\n")[0], "\"") + if len(values) < 2 { + return time.Time{}, fmt.Errorf("invalid values (len): [%s]", values) + } + timeStr := values[1] + if len(timeStr) != len("yy/MM/dd,hh:mm:ss+zz") { + return time.Time{}, fmt.Errorf("invalid time string: %s", timeStr) + } + // Convert time zone + timeZoneStr := timeStr[len(timeStr)-2:] + timeZone, err := strconv.Atoi(timeZoneStr) + if err != nil { + return time.Time{}, fmt.Errorf("parse time zone: %w", err) + } + timeStr = fmt.Sprintf("%s%02d%02d", timeStr[:len(timeStr)-2], timeZone/4, timeZone%4) + // Parse to golang time + return time.Parse(timeLayout, timeStr) +} + func (m *modem) PowerOn() error { m.onOffPin.PowerOn() // DEBUG do not want to wait 30 seconds return nil @@ -205,24 +246,38 @@ func (m *modem) At() at.Port { return m.port } +func (m *modem) Ic() internet.Conn { + return m.ic +} + func (m *modem) Close() error { // I can't return error so I log it m.mutex.Lock() defer m.mutex.Unlock() - if err := m.sms.Close(); err != nil { - m.logger.Printf("\x1b[38;2;255;0;0mclose sms error: %s\x1b[38;2;255;255;255m\n", err.Error()) + // Close submodules + if m.sms != nil { + if err := m.sms.Close(); err != nil { + m.logger.Printf("\x1b[38;2;255;0;0mclose sms error: %s\x1b[38;2;255;255;255m\n", err.Error()) + } + } + if m.ic != nil { + if err := m.ic.Close(); err != nil { + m.logger.Printf("\x1b[38;2;255;0;0mclose internet error: %s\x1b[38;2;255;255;255m\n", err.Error()) + } + } + if m.gps != nil { + if err := m.gps.Close(); err != nil { + m.logger.Printf("\x1b[38;2;255;0;0mclose gps error: %s\x1b[38;2;255;255;255m\n", err.Error()) + } } - if err := m.port.Close(); err != nil { - m.logger.Printf("\x1b[38;2;255;0;0mclose serial port error: %s\x1b[38;2;255;255;255m\n", err.Error()) - } + // Close gpio and serial if err := m.onOffPin.Close(); err != nil { m.logger.Printf("\x1b[38;2;255;0;0mclose gpio pin error: %s\x1b[38;2;255;255;255m\n", err.Error()) } - if err := m.ic.Close(); err != nil { - m.logger.Printf("\x1b[38;2;255;0;0mclose internet connection error: %s\x1b[38;2;255;255;255m\n", err.Error()) + if err := m.port.Close(); err != nil { + m.logger.Printf("\x1b[38;2;255;0;0mclose serial port error: %s\x1b[38;2;255;255;255m\n", err.Error()) } - return nil } @@ -381,34 +436,6 @@ func (m *modem) checkPort(portName string) (outErr error) { 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 { @@ -420,7 +447,36 @@ func (m *modem) ping() error { return nil } -func getTtyDevices() ([]string, error) { +func (m *modem) searchPort(isSoft bool) ([]string, error) { + // Get ports + ports, err := getTtyPorts(isSoft) + if err != nil { + fmt.Errorf("get devices: %w", err) + } + // Check ports + return m.getAtPorts(ports) +} + +func (m *modem) getAtPorts(ports []string) ([]string, error) { + outPorts := make([]string, 0) + 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 + } + + m.logger.Print("Found AT port: ", p) + outPorts = append(outPorts, "/dev/"+p) + } + return outPorts, nil +} + +func getTtyPorts(isSoft bool) ([]string, error) { + if isSoft { + return []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"}, nil + } // Get ports /**/ out, err := exec.Command("ls", "--", "/dev/tty[!0-9]*").Output() diff --git a/api/modem/sms/sms.go b/api/modem/sms/sms.go index 2926d89..3443d9e 100644 --- a/api/modem/sms/sms.go +++ b/api/modem/sms/sms.go @@ -41,14 +41,24 @@ func (d *dialer) Init() error { return fmt.Errorf("check PIN: %w", err) } // Setup prefered message storage - if err := d.setupMsgSt(); err != nil { - d.logger.Printf("ERROR setup msg storage: %s\n", err.Error()) + // if err := d.setupMsgSt(); err != nil { // Does not use store now + // d.logger.Printf("ERROR setup msg storage: %s\n", err.Error()) + // } + + // Set notifications into console + if resp, err := d.port.Send("AT+CNMI=2,2"); err != nil || !resp.Check() { + if err != nil { + return err + } + return fmt.Errorf("CNMI= error response: %s", resp.String()) } + // Check number if resp, err := d.port.Send("AT+CNUM"); err != nil || !resp.Check() { if err != nil { - return fmt.Errorf("AT+CNUM request ") + return err } + return fmt.Errorf("CNUM error response: %s", resp.String()) } return nil } diff --git a/main.go b/main.go index a14cde5..2593008 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,6 @@ import ( "time" "gitea.unprism.ru/KRBL/sim-modem/api/modem" - "gitea.unprism.ru/KRBL/sim-modem/api/modem/at" ) func main() { @@ -17,6 +16,11 @@ func main() { ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer cancel() + // Just for logs + go func(ctx context.Context) { + <-ctx.Done() + log.Println("GOT INTERUPT SIGNAL") + }(ctx) if err := mainE(ctx); err != nil { log.Println("MAIN finished with error:", err.Error()) @@ -29,25 +33,37 @@ func mainE(ctx context.Context) error { m := modem.New(log.New(logger.Writer(), "modem : ", log.LstdFlags)) logger.Println("||||||||||||||||| INIT |||||||||||||||") - if err := m.Init(); err != nil { - logger.Println("Init ended with error:", err.Error()) - logger.Println("Try to turn on") - if err := m.PowerOn(); err != nil { - return err - } - logger.Println("Reinit") - if err := m.Init(); err != nil { - return err +initLoop: + for { + select { + case <-ctx.Done(): + logger.Println("Break init loop") + return nil + default: + if err := m.Init(); err != nil { + logger.Println("Init ended with error:", err.Error()) + // logger.Println("Turn on...") + // if err := m.PowerOn(); err != nil { + // logger.Println("Turn on error:", err.Error()) + // } + continue initLoop + } + break initLoop } } if !m.IsConnected() { - logger.Println("AAAAAAAAAAAAAAA Modem is not connected") + logger.Println("Modem is not connected") return nil } + defer func() { + logger.Println("||||||||||||||||| CLOSE |||||||||||||||") + m.Close() + }() - // logger.Println("||||||||||||||||| GET INFO |||||||||||||||||") - // logger.Println(m.Update()) - // logger.Printf("DATA: %+v\n", m.GetData()) + // Connect to internet + // if err := m.Ic().Connect(); err != nil { + // return fmt.Errorf("connect to internet: %w", err) + // } logger.Println("||||||||||||||||| SMS |||||||||||||||||") Cmd := func(cmd string) { @@ -56,13 +72,6 @@ func mainE(ctx context.Context) error { } _ = Cmd - var resp at.Resp - var err error - buf := make([]byte, 256) - _ = resp - _ = err - _ = buf - // Select ME PMS // logger.Println("SEND SMS") // logger.Println(m.Sms().Send("+79218937173", "CGSG forever!!!")) @@ -78,17 +87,30 @@ func mainE(ctx context.Context) error { // Cmd("AT+CPSI?") // resp, err = m.At().Send("AT+CNMI=2,2") for { - + select { + case <-ctx.Done(): + logger.Println("Break main loop") + return nil + default: + // Cmd("AT+CPSI?") + // Cmd("AT+CSQ") + // Cmd("AT+CCLK?") + // logger.Println(m.Gps().GetStatus()) + // m.Update() + // st, _ := m.Gps().GetStatus() + // logger.Printf("GPS STATUS: %+v", st) + // logger.Printf("GPS DATA: %+v", m.GetData()) + logger.Println(m.GetTime()) + time.Sleep(2 * time.Second) + } // Cmd("AT+CSQ") // Cmd("AT+COPS?") - Cmd("AT+CPSI?") // if err := m.CheckSignal(); err != nil { // logger.Println(err) // } else { // logger.Println("AAAAAAAAAAA THERE IS SIGNAL") // } - time.Sleep(500 * time.Millisecond) // readLen, err := m.At().SerialPort().Read(buf) // if err != nil { // return err