Add: status page screen change support
This commit is contained in:
parent
0495860996
commit
58a0d17e47
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"gitea.unprism.ru/KRBL/mpu/mpu"
|
||||
@ -13,17 +14,27 @@ import (
|
||||
"gitea.unprism.ru/yotia/display-test/drawer"
|
||||
)
|
||||
|
||||
// Debug variant:
|
||||
// line0 - time
|
||||
// time1 - tempreture, speed
|
||||
// time2 - latitude
|
||||
// time3 - longitude
|
||||
/*
|
||||
Status page has status line at the top and three lines with primary data below.
|
||||
Status line shows time and signal and service icon at the right corner.
|
||||
|
||||
Main space can show several screens:
|
||||
1) GPS
|
||||
GPS coordinates
|
||||
2) System state
|
||||
temepature
|
||||
speed
|
||||
may be amount of cameras
|
||||
|
||||
Other pages can show for ex important logs.
|
||||
*/
|
||||
|
||||
const (
|
||||
timeLayout = "01.09.06 15:04:05"
|
||||
)
|
||||
|
||||
// Some layout constants
|
||||
screenN = 2
|
||||
screenChangeTimeout = 6 * time.Second
|
||||
)
|
||||
|
||||
type statusPage struct {
|
||||
drawer drawer.Drawer // Drawer with dysplay
|
||||
@ -41,6 +52,8 @@ type statusPage struct {
|
||||
// Layout values
|
||||
signalX int
|
||||
serviceX int
|
||||
// Current screen(index) (default - 0)
|
||||
curScreen atomic.Int32
|
||||
|
||||
// Threads sync
|
||||
ctx context.Context
|
||||
@ -103,23 +116,14 @@ func NewStatusPage(d drawer.Drawer) (StatusPage, error) {
|
||||
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()
|
||||
|
||||
// At the right-top corner there are signal then service status glyphs
|
||||
// Layout:
|
||||
// gap | signal | gap | service
|
||||
p.SetRssi(0)
|
||||
p.SetService("NO SERVICE")
|
||||
|
||||
// Setup threads
|
||||
p.ctx, p.cancel = context.WithCancel(context.Background())
|
||||
go p.timeUpdateLoop()
|
||||
go p.screenChangeLoop()
|
||||
}
|
||||
|
||||
func (p *statusPage) Diactivate() {
|
||||
@ -137,42 +141,60 @@ func (p *statusPage) SetTimeShift(shift time.Duration) {
|
||||
|
||||
func (p *statusPage) SetGps(newData gps.Data) {
|
||||
p.st.mutex.Lock()
|
||||
defer p.st.mutex.Unlock()
|
||||
|
||||
p.st.gpsData = newData
|
||||
p.st.mutex.Unlock()
|
||||
|
||||
// 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°%06.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°%06.3f' %s", int(p.st.gpsData.Longitude)/100, math.Mod(p.st.gpsData.Longitude, 100), p.st.gpsData.LongitudeIndicator)
|
||||
p.line3.SetStr(logStr)
|
||||
if p.curScreen.Load() == 0 { // Draw only on first screen
|
||||
p.drawCoords(&newData)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *statusPage) SetMpu(newData mpu.Data) {
|
||||
p.st.mutex.Lock()
|
||||
defer p.st.mutex.Unlock()
|
||||
|
||||
p.st.mpuData = newData
|
||||
p.st.mutex.Unlock()
|
||||
|
||||
if p.curScreen.Load() == 1 { // Draw only on second screen
|
||||
p.drawMpu(&newData)
|
||||
}
|
||||
}
|
||||
|
||||
// Only safely updates local values
|
||||
func (p *statusPage) setRssi(newData int) {
|
||||
func (p *statusPage) SetRssi(rssi int) {
|
||||
// Save data
|
||||
p.st.mutex.Lock()
|
||||
defer p.st.mutex.Unlock()
|
||||
p.st.rssi = rssi
|
||||
p.st.mutex.Unlock()
|
||||
|
||||
p.st.rssi = newData
|
||||
// Draw
|
||||
p.drawRssi(rssi)
|
||||
}
|
||||
|
||||
func (p *statusPage) setService(svc string) {
|
||||
func (p *statusPage) SetService(svc string) {
|
||||
// Save data
|
||||
p.st.mutex.Lock()
|
||||
defer p.st.mutex.Unlock()
|
||||
|
||||
p.st.service = svc
|
||||
p.st.mutex.Unlock()
|
||||
|
||||
// Draw
|
||||
p.drawService(svc)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func getSignalStrength(rssi int) int {
|
||||
@ -243,24 +265,7 @@ func getServiceStrength(service string) int {
|
||||
return 0 // Invalid value
|
||||
}
|
||||
|
||||
func (p *statusPage) SetRssi(rssi int) {
|
||||
p.setRssi(rssi) // Save data (with short lock)
|
||||
|
||||
// Update screen image
|
||||
ss := getSignalStrength(rssi) // Signal strength in [0; 4] range
|
||||
p.drawer.CopyImg(p.signalX, 0, drawer.CommonGlyphs[drawer.SignalStatusGlyphI+ss])
|
||||
p.drawer.GetDisplay().FlushByMask(p.drawer.GetDisplay().GetFlushMaskBit(1, 0))
|
||||
}
|
||||
|
||||
func (p *statusPage) SetService(svc string) {
|
||||
p.setService(svc) // Save data (with short lock)
|
||||
|
||||
// Update screen image
|
||||
ss := getServiceStrength(svc) // Service strength in [0; 4] range
|
||||
p.drawer.CopyImg(p.serviceX, 0, drawer.CommonGlyphs[drawer.ServiceStatusGlyphI+ss])
|
||||
p.drawer.GetDisplay().FlushByMask(p.drawer.GetDisplay().GetFlushMaskBit(1, 0))
|
||||
}
|
||||
|
||||
// Update time in top line
|
||||
func (p *statusPage) timeUpdateLoop() {
|
||||
p.wg.Add(1)
|
||||
|
||||
@ -280,20 +285,86 @@ func (p *statusPage) timeUpdateLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *statusPage) Close() (outErr error) {
|
||||
func (p *statusPage) screenChangeLoop() {
|
||||
p.wg.Add(1)
|
||||
|
||||
// TODO Not the best way...
|
||||
if err := p.line0.Close(); err != nil {
|
||||
outErr = fmt.Errorf("line 0 close: %w:", err)
|
||||
// Because ticker do not send signal immediately
|
||||
ticker := time.NewTicker(screenChangeTimeout)
|
||||
for {
|
||||
select {
|
||||
case <-p.ctx.Done():
|
||||
ticker.Stop()
|
||||
p.wg.Done()
|
||||
return
|
||||
case <-ticker.C:
|
||||
p.changeScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *statusPage) changeScreen() {
|
||||
scr := p.curScreen.Load()
|
||||
scr = (scr + 1) % screenN
|
||||
p.curScreen.Store(scr)
|
||||
p.drawScreen(int(scr))
|
||||
}
|
||||
|
||||
///////////////////// Draw functions /////////////////////
|
||||
|
||||
// Draw functions get draw data as arguments because otherwise they have to get it using mutex
|
||||
// Moreover they will lock it second time
|
||||
|
||||
func (p *statusPage) drawRssi(rssi int) {
|
||||
ss := getSignalStrength(rssi) // Signal strength in [0; 4] range
|
||||
p.drawer.CopyImg(p.signalX, 0, drawer.CommonGlyphs[drawer.SignalStatusGlyphI+ss])
|
||||
p.drawer.GetDisplay().FlushByMask(p.drawer.GetDisplay().GetFlushMaskBit(1, 0))
|
||||
}
|
||||
|
||||
func (p *statusPage) drawService(svc string) {
|
||||
ss := getServiceStrength(svc) // Service strength in [0; 4] range
|
||||
p.drawer.CopyImg(p.serviceX, 0, drawer.CommonGlyphs[drawer.ServiceStatusGlyphI+ss])
|
||||
p.drawer.GetDisplay().FlushByMask(p.drawer.GetDisplay().GetFlushMaskBit(1, 0))
|
||||
}
|
||||
|
||||
func (p *statusPage) drawCoords(data *gps.Data) {
|
||||
// Langitude, longitude store format: ddmm.mmmmmm, dddmm.mmmmmm
|
||||
// Latitude and longitude layout:
|
||||
// DD° MM.MMM' N
|
||||
// DDD° MM.MMM' W
|
||||
latStr := fmt.Sprintf(" %02d°%06.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°%06.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) drawMpu(data *mpu.Data) {
|
||||
// Layout:
|
||||
// temp ttt.ttC
|
||||
|
||||
tempStr := fmt.Sprintf("temperature %06.3fC", data.TempData)
|
||||
p.line2.SetStr(tempStr)
|
||||
}
|
||||
|
||||
// Draw main part depends on what current screen is active
|
||||
func (p *statusPage) drawScreen(screen int) {
|
||||
// Clear
|
||||
//p.drawer.FillBar(0, drawer.LineH, p.drawer.W(), p.drawer.H()-drawer.LineH, 0)
|
||||
//p.drawer.GetDisplay().FlushByMask(0b01110111)
|
||||
p.line1.SetStr("")
|
||||
p.line2.SetStr("")
|
||||
p.line3.SetStr("")
|
||||
switch screen {
|
||||
case 0:
|
||||
p.st.mutex.Lock()
|
||||
gpsData := &p.st.gpsData
|
||||
p.st.mutex.Unlock()
|
||||
p.drawCoords(gpsData)
|
||||
case 1:
|
||||
p.st.mutex.Lock()
|
||||
mpuData := &p.st.mpuData
|
||||
p.st.mutex.Unlock()
|
||||
p.drawMpu(mpuData)
|
||||
p.line1.SetStr("47 cameras connected")
|
||||
p.line3.SetStr("some other info...")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -50,7 +50,6 @@ func (t *text) Clear() {
|
||||
}
|
||||
|
||||
func (t *text) Draw() {
|
||||
t.Clear() // Assume that is draw is invoked string has been changed
|
||||
t.drawer.PutText(t.rect.x, t.rect.y, t.str)
|
||||
t.drawer.GetDisplay().FlushByMask(t.mask.bits)
|
||||
}
|
||||
@ -61,9 +60,18 @@ func (t *text) updateW() {
|
||||
|
||||
func (t *text) SetStr(str string) {
|
||||
t.str = str
|
||||
t.updateW()
|
||||
t.mask.Update(t.rect)
|
||||
newW := len(t.str)*(drawer.FontCharW+drawer.CharGap) - drawer.CharGap
|
||||
if t.rect.w-newW > 0 {
|
||||
// Need to clear some space
|
||||
t.Clear()
|
||||
t.drawer.FillBar(t.rect.x+newW, t.rect.y, t.rect.x+t.rect.w, t.rect.y+t.rect.h, 0)
|
||||
} else {
|
||||
t.rect.w = newW
|
||||
t.mask.Update(t.rect)
|
||||
}
|
||||
t.Draw()
|
||||
t.rect.w = newW
|
||||
t.mask.Update(t.rect)
|
||||
}
|
||||
|
||||
func (t *text) GetPos() (int, int) {
|
||||
|
4
main.go
4
main.go
@ -197,7 +197,7 @@ func mainE(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-time.After(20 * time.Millisecond):
|
||||
case <-time.After(60 * time.Millisecond):
|
||||
}
|
||||
pb.Checkpoint()
|
||||
}
|
||||
@ -214,7 +214,7 @@ func mainE(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-time.After(1000 * time.Millisecond):
|
||||
case <-time.After(1700 * time.Millisecond):
|
||||
// Random coords
|
||||
lat := float64(rand.Int()%10000) + float64(rand.Int()%1000000)/1000000
|
||||
log := float64(rand.Int()%100000) + float64(rand.Int()%1000000)/1000000
|
||||
|
Loading…
Reference in New Issue
Block a user