Add: status page
This commit is contained in:
@ -3,6 +3,7 @@ package pages
|
||||
import "io"
|
||||
|
||||
type Page interface {
|
||||
// Warning: now threre is no protection from double activation/diactivation
|
||||
Activate()
|
||||
Diactivate()
|
||||
|
||||
|
29
api/pages/status.go
Normal file
29
api/pages/status.go
Normal file
@ -0,0 +1,29 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"gitea.unprism.ru/KRBL/mpu/mpu"
|
||||
"gitea.unprism.ru/KRBL/sim-modem/api/modem/gps"
|
||||
)
|
||||
|
||||
// Some supplement types and constants
|
||||
|
||||
// This struct contains status of the system that will be on the display
|
||||
type systemStatus struct {
|
||||
mutex sync.Mutex
|
||||
|
||||
// Status data
|
||||
gpsData gps.Data
|
||||
mpuData mpu.Data
|
||||
|
||||
rssi int // Received signal strength indicator (check gitea.unprism.ru/KRBL/sim-modem/api/modem/utils/signal.go)
|
||||
service string // Internet service name, could be: "NO SERVICE", "GSM", "WCDMA", "LTE", "TDS"
|
||||
}
|
||||
|
||||
type SystemStatusSetter interface {
|
||||
SetGps(newData gps.Data)
|
||||
SetMpu(newData mpu.Data)
|
||||
SetRssi(newData int)
|
||||
SetService(newData string)
|
||||
}
|
195
api/pages/statuspage.go
Normal file
195
api/pages/statuspage.go
Normal file
@ -0,0 +1,195 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user