Add: status page screen change support
This commit is contained in:
		| @@ -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 | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user