package pages import ( "context" "fmt" "math" "sync" "time" "gitea.unprism.ru/KRBL/mpu/mpu" "gitea.unprism.ru/KRBL/sim-modem/api/modem/gps" "gitea.unprism.ru/yotia/display-test/components" "gitea.unprism.ru/yotia/display-test/drawer" ) // Debug variant: // line0 - time // time1 - tempreture, speed // time2 - latitude // time3 - longitude const ( timeLayout = "01.09.06 15:04:05" ) type statusPage struct { drawer drawer.Drawer // Drawer with dysplay // Status data st systemStatus timeShift time.Duration // Visual components line0 components.Text line1 components.Text line2 components.Text line3 components.Text // Threads sync ctx context.Context cancel context.CancelFunc wg sync.WaitGroup } // Only functions that control content of page type StatusPageContent interface { SystemStatusSetter SetTimeShift(shift time.Duration) } type StatusPage interface { Page StatusPageContent } func NewStatusPage(d drawer.Drawer) (StatusPage, error) { // Check display if err := d.GetDisplay().IsReady(); err != nil { return nil, fmt.Errorf("display is ready: %w", err) } // Create visual components line0, err := components.NewText(d, 0, drawer.LineH*0) if err != nil { return nil, fmt.Errorf("create line0: %w", err) } line1, err := components.NewText(d, 0, drawer.LineH*1) if err != nil { return nil, fmt.Errorf("create line1: %w", err) } line2, err := components.NewText(d, 0, drawer.LineH*2) if err != nil { return nil, fmt.Errorf("create line2: %w", err) } line3, err := components.NewText(d, 0, drawer.LineH*3) if err != nil { return nil, fmt.Errorf("create line3: %w", err) } return &statusPage{ drawer: d, line0: line0, line1: line1, line2: line2, line3: line3, }, nil } func (p *statusPage) Activate() { // Draw p.drawer.Clear() //p.line0.SetStr("DEBUG line0") //p.line1.SetStr("DEBUG line1") //p.line2.SetStr("DEBUG line2") //p.line3.SetStr("DEBUG line3") p.line0.Draw() p.line1.Draw() p.line2.Draw() p.line3.Draw() // Setup threads p.ctx, p.cancel = context.WithCancel(context.Background()) go p.timeUpdateLoop() } func (p *statusPage) Diactivate() { // Stop all support threads p.cancel() p.wg.Wait() } func (p *statusPage) SetTimeShift(shift time.Duration) { p.st.mutex.Lock() defer p.st.mutex.Unlock() p.timeShift = shift } func (p *statusPage) SetGps(newData gps.Data) { p.st.mutex.Lock() defer p.st.mutex.Unlock() p.st.gpsData = newData // Format and update coords // Langitude, longitude store format: ddmm.mmmmmm, dddmm.mmmmmm // Latitude and longitude output format: // DD° MM.MMM' N // DDD° MM.MMM' W latStr := fmt.Sprintf(" %02d°%02.3f' %s", int(p.st.gpsData.Latitude)/100, math.Mod(p.st.gpsData.Latitude, 100), p.st.gpsData.LatitudeIndicator) p.line2.SetStr(latStr) logStr := fmt.Sprintf(" %03d°%02.3f' %s", int(p.st.gpsData.Longitude)/100, math.Mod(p.st.gpsData.Longitude, 100), p.st.gpsData.LongitudeIndicator) p.line3.SetStr(logStr) } func (p *statusPage) SetMpu(newData mpu.Data) { p.st.mutex.Lock() defer p.st.mutex.Unlock() p.st.mpuData = newData } func (p *statusPage) SetRssi(newData int) { p.st.mutex.Lock() defer p.st.mutex.Unlock() p.st.rssi = newData } func (p *statusPage) SetService(newData string) { p.st.mutex.Lock() defer p.st.mutex.Unlock() p.st.service = newData } func (p *statusPage) timeUpdateLoop() { p.wg.Add(1) // Because ticker do not send signal immediately p.line0.SetStr(time.Now().Add(p.timeShift).Format(timeLayout)) ticker := time.NewTicker(time.Second) for { select { case <-p.ctx.Done(): ticker.Stop() p.wg.Done() return case now := <-ticker.C: p.line0.SetStr(now.Add(p.timeShift).Format(timeLayout)) } } } func (p *statusPage) Close() (outErr error) { // TODO Not the best way... if err := p.line0.Close(); err != nil { outErr = fmt.Errorf("line 0 close: %w:", err) } if err := p.line1.Close(); err != nil { outErr = fmt.Errorf("line 1 close: %w:", err) } if err := p.line2.Close(); err != nil { outErr = fmt.Errorf("line 2 close: %w:", err) } if err := p.line3.Close(); err != nil { outErr = fmt.Errorf("line 3 close: %w:", err) } return }