Add: status page
This commit is contained in:
parent
a4fda8303b
commit
0976b5c8d0
1
Makefile
1
Makefile
@ -17,5 +17,4 @@ get:
|
|||||||
go get .
|
go get .
|
||||||
|
|
||||||
update:
|
update:
|
||||||
go env
|
|
||||||
go get -u
|
go get -u
|
23
api/api.go
23
api/api.go
@ -20,10 +20,8 @@ type displayApi struct {
|
|||||||
|
|
||||||
curPage pages.Page
|
curPage pages.Page
|
||||||
|
|
||||||
initPage pages.InitPage
|
initPage pages.InitPage
|
||||||
// statusPage pages.StatusPage
|
statusPage pages.StatusPage
|
||||||
|
|
||||||
st status
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Work process:
|
// Work process:
|
||||||
@ -33,7 +31,9 @@ type displayApi struct {
|
|||||||
// Status page - gps, time, rssi, service, ...
|
// Status page - gps, time, rssi, service, ...
|
||||||
|
|
||||||
type Display interface {
|
type Display interface {
|
||||||
|
// Put them to different functions because of different returns
|
||||||
SwitchToInitPage() pages.InitPageContent
|
SwitchToInitPage() pages.InitPageContent
|
||||||
|
SwitchToStatusPage() pages.StatusPageContent
|
||||||
io.Closer
|
io.Closer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,10 +53,16 @@ func New() (Display, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("new init page: %w", err)
|
return nil, fmt.Errorf("new init page: %w", err)
|
||||||
}
|
}
|
||||||
|
// Status page
|
||||||
|
statusPage, err := pages.NewStatusPage(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("new init page: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return &displayApi{
|
return &displayApi{
|
||||||
d: d,
|
d: d,
|
||||||
initPage: initPage,
|
initPage: initPage,
|
||||||
|
statusPage: statusPage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +81,11 @@ func (d *displayApi) SwitchToInitPage() pages.InitPageContent {
|
|||||||
return d.initPage
|
return d.initPage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *displayApi) SwitchToStatusPage() pages.StatusPageContent {
|
||||||
|
d.switchToPage(d.statusPage)
|
||||||
|
return d.statusPage
|
||||||
|
}
|
||||||
|
|
||||||
func (d *displayApi) Close() error {
|
func (d *displayApi) Close() error {
|
||||||
// !!! TMP DEBUG BAD CODE
|
// !!! TMP DEBUG BAD CODE
|
||||||
d.d.Clear()
|
d.d.Clear()
|
||||||
|
@ -3,6 +3,7 @@ package pages
|
|||||||
import "io"
|
import "io"
|
||||||
|
|
||||||
type Page interface {
|
type Page interface {
|
||||||
|
// Warning: now threre is no protection from double activation/diactivation
|
||||||
Activate()
|
Activate()
|
||||||
Diactivate()
|
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
|
||||||
|
}
|
@ -1,67 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"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 status struct {
|
|
||||||
mutex sync.Mutex
|
|
||||||
|
|
||||||
// Status data
|
|
||||||
time time.Time
|
|
||||||
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 StatusUpdater interface {
|
|
||||||
UpdateTime(newTime time.Time)
|
|
||||||
UpdateGps(newData gps.Data)
|
|
||||||
UpdateMpu(newData mpu.Data)
|
|
||||||
UpdateRssi(newData int)
|
|
||||||
UpdateService(newData string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *status) UpdateTime(newTime time.Time) {
|
|
||||||
st.mutex.Lock()
|
|
||||||
defer st.mutex.Unlock()
|
|
||||||
|
|
||||||
st.time = newTime
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *status) UpdateGps(newData gps.Data) {
|
|
||||||
st.mutex.Lock()
|
|
||||||
defer st.mutex.Unlock()
|
|
||||||
|
|
||||||
st.gpsData = newData
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *status) UpdateMpu(newData mpu.Data) {
|
|
||||||
st.mutex.Lock()
|
|
||||||
defer st.mutex.Unlock()
|
|
||||||
|
|
||||||
st.mpuData = newData
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *status) UpdateRssi(newData int) {
|
|
||||||
st.mutex.Lock()
|
|
||||||
defer st.mutex.Unlock()
|
|
||||||
|
|
||||||
st.rssi = newData
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *status) UpdateService(newData string) {
|
|
||||||
st.mutex.Lock()
|
|
||||||
defer st.mutex.Unlock()
|
|
||||||
|
|
||||||
st.service = newData
|
|
||||||
}
|
|
@ -74,7 +74,7 @@ var stdFont = []byte{
|
|||||||
0x61, 0x11, 0x9, 0x7, 0x0, // 7
|
0x61, 0x11, 0x9, 0x7, 0x0, // 7
|
||||||
0x36, 0x49, 0x49, 0x36, 0x0, // 8
|
0x36, 0x49, 0x49, 0x36, 0x0, // 8
|
||||||
0x6, 0x49, 0x49, 0x3e, 0x0, // 9
|
0x6, 0x49, 0x49, 0x3e, 0x0, // 9
|
||||||
0x50, 0x0, 0x0, 0x0, 0x0, // :
|
0x00, 0x0, 0x22, 0x0, 0x0, // :
|
||||||
0x80, 0x50, 0x0, 0x0, 0x0, // ;
|
0x80, 0x50, 0x0, 0x0, 0x0, // ;
|
||||||
0x10, 0x28, 0x44, 0x0, 0x0, // <
|
0x10, 0x28, 0x44, 0x0, 0x0, // <
|
||||||
0x14, 0x14, 0x14, 0x0, 0x0, // =
|
0x14, 0x14, 0x14, 0x0, 0x0, // =
|
||||||
@ -192,7 +192,7 @@ var stdFont = []byte{
|
|||||||
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
||||||
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
||||||
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
||||||
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
0x03, 0x03, 0x00, 0x00, 0x00, // °
|
||||||
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
||||||
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
||||||
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
0x7f, 0x41, 0x41, 0x41, 0x7f, // Empty
|
||||||
|
2
go.mod
2
go.mod
@ -4,7 +4,7 @@ go 1.22.5
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.unprism.ru/KRBL/mpu v0.1.0
|
gitea.unprism.ru/KRBL/mpu v0.1.0
|
||||||
gitea.unprism.ru/KRBL/sim-modem v0.1.7
|
gitea.unprism.ru/KRBL/sim-modem v0.1.8
|
||||||
github.com/stianeikeland/go-rpio/v4 v4.6.0
|
github.com/stianeikeland/go-rpio/v4 v4.6.0
|
||||||
golang.org/x/text v0.17.0
|
golang.org/x/text v0.17.0
|
||||||
periph.io/x/conn/v3 v3.7.1
|
periph.io/x/conn/v3 v3.7.1
|
||||||
|
29
main.go
29
main.go
@ -17,14 +17,13 @@ import (
|
|||||||
"gitea.unprism.ru/yotia/display-test/drawer"
|
"gitea.unprism.ru/yotia/display-test/drawer"
|
||||||
|
|
||||||
"gitea.unprism.ru/KRBL/sim-modem/api/modem"
|
"gitea.unprism.ru/KRBL/sim-modem/api/modem"
|
||||||
|
"gitea.unprism.ru/KRBL/sim-modem/api/modem/gps"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
displayUpdateTimeout = 10 * time.Millisecond
|
displayUpdateTimeout = 10 * time.Millisecond
|
||||||
)
|
)
|
||||||
|
|
||||||
type _ = api.StatusUpdater
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Println("CGSG forever!!!")
|
log.Println("CGSG forever!!!")
|
||||||
|
|
||||||
@ -184,13 +183,15 @@ func mainE(ctx context.Context) error {
|
|||||||
defer a.Close()
|
defer a.Close()
|
||||||
logger.Println("Inited")
|
logger.Println("Inited")
|
||||||
|
|
||||||
|
// !!! Init example
|
||||||
|
|
||||||
|
// Setup init page
|
||||||
ip := a.SwitchToInitPage()
|
ip := a.SwitchToInitPage()
|
||||||
ip.SetTitle("KRBL")
|
ip.SetTitle("KRBL")
|
||||||
ip.GetProgressBar().SetProgress(0)
|
pb := progress.NewHandler(ip.GetProgressBar(), 10)
|
||||||
time.Sleep(300 * time.Millisecond)
|
|
||||||
pb := progress.NewHandler(ip.GetProgressBar(), 100)
|
|
||||||
|
|
||||||
for range 100 {
|
// Simulate init process
|
||||||
|
for range 10 {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil
|
return nil
|
||||||
@ -198,7 +199,21 @@ func mainE(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
pb.Checkpoint()
|
pb.Checkpoint()
|
||||||
}
|
}
|
||||||
time.Sleep(400 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
// !!! Status page simulating
|
||||||
|
|
||||||
|
// Switch
|
||||||
|
sp := a.SwitchToStatusPage()
|
||||||
|
sp.SetGps(gps.Data{
|
||||||
|
Latitude: 3113.330650,
|
||||||
|
LatitudeIndicator: "N",
|
||||||
|
Longitude: 12121.262554,
|
||||||
|
LongitudeIndicator: "E",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Just wait now
|
||||||
|
<-ctx.Done()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user