Add: components, progress bar

This commit is contained in:
Andrey Egorov 2024-09-22 21:53:22 +03:00
parent f29bbf8345
commit bad142a565
10 changed files with 227 additions and 28 deletions

20
components/components.go Normal file
View File

@ -0,0 +1,20 @@
package components
// Some general types
type rect struct {
x int
y int
w int
h int
}
type RectGetter interface {
GetPos() (int, int)
GetSize() (int, int)
}
type RectSetter interface {
SetPos(x, y int)
SetSize(w, h int)
}

117
components/progressbar.go Normal file
View File

@ -0,0 +1,117 @@
package components
import (
"fmt"
"io"
"gitea.unprism.ru/yotia/display-test/drawer"
)
type progressBar struct {
drawer drawer.Drawer // Drawer with dysplay
mask uint32 // Flush mask
progress float64
rect rect // Rect
}
type ProgressBar interface {
RectGetter
SetProgress(coef float64) // Value from 0 to 1
Draw() // For any cases
Clear() // Clear space of bar
Flush() // For those times like after SetPos
io.Closer
}
// Creation function
// Now the only way to chage shape is to recreate component
func NewProgressBar(drawer drawer.Drawer, x, y, w, h int) (ProgressBar, error) {
if err := drawer.GetDisplay().IsReady(); err != nil {
return nil, fmt.Errorf("display is ready: %w", err)
}
// Check for correct sizes
if w < 4 {
return nil, fmt.Errorf("invalid width: below 4")
}
if h < 4 {
return nil, fmt.Errorf("invalid height: below 4")
}
pb := progressBar{
drawer: drawer,
rect: rect{x, y, w, h},
}
pb.updateMask()
pb.Draw()
pb.Flush()
return &pb, nil
}
func (pb *progressBar) Flush() {
pb.drawer.GetDisplay().FlushByMask(pb.mask)
}
func (pb *progressBar) updateMask() {
pb.mask = 0
// Implementation dependent !!!
// Now for mt12232
y0 := min(3, max(0, int(pb.rect.y/8)))
y1 := min(3, max(0, int((pb.rect.y+pb.rect.h-1)/8)))
x0 := min(1, max(0, int(pb.rect.x/61)))
x1 := min(1, max(0, int((pb.rect.x+pb.rect.w-1)/61)))
//log.Println(x0, y0, x1, y1)
for y := y0; y <= y1; y++ {
for x := x0; x <= x1; x++ {
pb.mask |= (1 << (x*4 + y))
}
}
}
func (pb *progressBar) Close() error {
return nil
}
func (pb *progressBar) Clear() {
pb.drawer.FillBar(pb.rect.x, pb.rect.y, pb.rect.x+pb.rect.w, pb.rect.y+pb.rect.h, 0x00)
}
// Draw whole bar
func (pb *progressBar) Draw() {
// Shape:
// Bounds - 1px
// Padding - 1px
// Progress - the rest space
pb.Clear()
pb.drawer.PutBar(pb.rect.x, pb.rect.y, pb.rect.x+pb.rect.w, pb.rect.y+pb.rect.h, 0xFF)
pb.drawBar()
}
// Draw only bar(without frames)
func (pb *progressBar) drawBar() {
w := int(float64(pb.rect.w-4) * pb.progress)
pb.drawer.FillBar(pb.rect.x+2, pb.rect.y+2, pb.rect.x+2+w, pb.rect.y+pb.rect.h-2, 0xFF)
}
func (pb *progressBar) SetProgress(coef float64) {
pb.progress = max(0, min(1, coef))
pb.drawBar()
pb.Flush()
}
func (pb *progressBar) GetPos() (int, int) {
return pb.rect.x, pb.rect.y
}
func (pb *progressBar) GetSize() (int, int) {
return pb.rect.w, pb.rect.h
}

View File

@ -9,7 +9,11 @@ import (
)
type Display interface {
IsReady() error // Nil if it is ready, error with some info otherwise
Flush(crystal, page byte)
FlushByMask(mask uint32)
GetFlushMaskBit(crystal, page byte) uint32
// Image functions
GetImg() *image.Gray
@ -17,7 +21,7 @@ type Display interface {
UnlockImg()
GetBounds() image.Rectangle
Test(ctx context.Context) error
Test(ctx context.Context) error // DEBUG ONLY
io.Closer
}

View File

@ -19,11 +19,16 @@ const (
mt12232aW = 122
mt12232aH = 32
flushChanCap = 24 // Flush channel capacity
flushUpdateTimeout = 5 * time.Millisecond
)
type displayMt12232a struct {
logger *log.Logger
// Some state flags
isTurnedOn bool
// Image
img *image.Gray
imgMutex sync.Mutex
@ -145,6 +150,14 @@ func (d *displayMt12232a) powerOn() error {
if d.dev.ReadStatus(1)&0x20 != 0 {
return fmt.Errorf("Right cristal is off")
}
d.isTurnedOn = true
return nil
}
func (d *displayMt12232a) IsReady() error {
if !d.isTurnedOn {
return fmt.Errorf("display is turned off")
}
return nil
}
@ -217,12 +230,22 @@ func (d *displayMt12232a) Flush(crystal, page byte) {
d.pagesFlushFlags.Store(d.pagesFlushFlags.Load() | bit)
}
func (d *displayMt12232a) FlushByMask(mask uint32) {
// !!! TODO Need to update GO to 1.23 to use .Or !!!
d.pagesFlushFlags.Store(d.pagesFlushFlags.Load() | mask)
}
func (d *displayMt12232a) GetFlushMaskBit(crystal, page byte) uint32 {
return uint32(1 << (crystal*4 + page))
}
func (d *displayMt12232a) Close() error {
if d.flushCancel != nil {
d.flushCancel()
d.flushCancel = nil
<-d.flushDone
}
d.isTurnedOn = false
return d.dev.Close()
}
@ -233,10 +256,10 @@ func (d *displayMt12232a) flushLoop(ctx context.Context) {
case <-ctx.Done():
close(d.flushDone)
return
case <-time.After(time.Millisecond):
case <-time.After(flushUpdateTimeout):
forUpdate := d.pagesFlushFlags.Swap(0)
checkBit := uint32(1)
st := time.Now()
//st := time.Now()
d.LockImg()
for p := byte(0); p < 4; p++ {
if forUpdate&(checkBit) != 0 {
@ -257,7 +280,7 @@ func (d *displayMt12232a) flushLoop(ctx context.Context) {
}
checkBit = checkBit << 1
}
d.logger.Printf("%08b - %s\n", forUpdate, time.Since(st))
//d.logger.Printf("%08b - %s\n", forUpdate, time.Since(st))
for p := byte(0); p < 4; p++ {
if forUpdate&(checkBit) != 0 {
d.dev.WriteCode(1, (3-p)|0xB8)

View File

@ -37,7 +37,8 @@ func newMt12864a(logger *log.Logger) (Display, error) {
d.test()
d.logger.Println("Draw finished")
return &d, nil
//return &d, nil
return nil, fmt.Errorf("IMPLEMENTATION COMMENTED")
}
func (d *displayMt12864a) Test(ctx context.Context) error {
@ -145,6 +146,9 @@ func (d *displayMt12864a) GetBounds() image.Rectangle {
func (d *displayMt12864a) Flush(crystal, page byte) {
}
func (d *displayMt12864a) FlushByMask(mask uint32) {
}
func (d *displayMt12864a) Close() error {
return rpio.Close()
}

View File

@ -42,11 +42,13 @@ func newSsd1306(logger *log.Logger) (Display, error) {
return nil, fmt.Errorf("create i2c: %w", err)
}
return &displaySsd1306{
_ = displaySsd1306{
logger: logger,
bus: bus,
dev: dev,
}, nil
}
return nil, fmt.Errorf("IMPLEMENTATION COMMENTED")
}
func (d *displaySsd1306) GetBounds() image.Rectangle {
@ -61,6 +63,10 @@ func (d *displaySsd1306) Flush(crystal, page byte) {
//return d.dev.Draw(img.Bounds(), d.img, image.Point{})
}
func (d *displaySsd1306) FlushByMask(mask uint32) {
//return d.dev.Draw(img.Bounds(), d.img, image.Point{})
}
func (d *displaySsd1306) Close() error {
if err := d.bus.Close(); err != nil {
d.logger.Println("ERROR: close i2c bus:", err.Error())

View File

@ -2,7 +2,7 @@ package drawer
import "math"
func (d *drawer) FillBar(sx, sy, ex, ey, color int) {
func (d *drawer) FillBar(sx, sy, ex, ey int, color byte) {
d.dev.LockImg()
defer d.dev.UnlockImg()
@ -18,12 +18,12 @@ func (d *drawer) FillBar(sx, sy, ex, ey, color int) {
addr := lineaddr
le := addr + w
for ; addr < le; addr++ {
d.img.Pix[addr] = 255
d.img.Pix[addr] = color
}
}
}
func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
func (d *drawer) PutBar(sx, sy, ex, ey int, color byte) {
d.dev.LockImg()
defer d.dev.UnlockImg()
@ -36,7 +36,7 @@ func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
x1 := int(math.Min(float64(ex), float64(bounds.Dx())))
for addr := sy*bounds.Dx() + x0; addr < sy*bounds.Dx()+x1; addr++ {
d.img.Pix[addr] = 255
d.img.Pix[addr] = color
}
}
// Bottom
@ -45,7 +45,7 @@ func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
x1 := int(math.Min(float64(ex), float64(bounds.Dx())))
for addr := (ey-1)*bounds.Dx() + x0; addr < (ey-1)*bounds.Dx()+x1; addr++ {
d.img.Pix[addr] = 255
d.img.Pix[addr] = color
}
}
// Left
@ -54,7 +54,7 @@ func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
y1 := int(math.Min(float64(ey), float64(bounds.Dy())))
for addr := y0*bounds.Dx() + sx; addr < y1*bounds.Dx()+sx; addr += bounds.Dx() {
d.img.Pix[addr] = 255
d.img.Pix[addr] = color
}
}
// Right
@ -63,7 +63,7 @@ func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
y1 := int(math.Min(float64(ey), float64(bounds.Dy())))
for addr := y0*bounds.Dx() + (ex - 1); addr < y1*bounds.Dx()+(ex-1); addr += bounds.Dx() {
d.img.Pix[addr] = 255
d.img.Pix[addr] = color
}
}
}

View File

@ -15,13 +15,16 @@ type drawer struct {
type Drawer interface {
GetDisplay() display.Display
W() int
H() int
Clear()
Flush()
PutText(x, y int, text string)
PutBar(x0, y0, x1, y1, color int)
FillBar(x0, y0, x1, y1, color int)
PutBar(x0, y0, x1, y1 int, color byte)
FillBar(x0, y0, x1, y1 int, color byte)
}
func New(dev display.Display) Drawer {
@ -37,6 +40,14 @@ func (d *drawer) GetDisplay() display.Display {
return d.dev
}
func (d *drawer) W() int {
return d.img.Rect.Dx()
}
func (d *drawer) H() int {
return d.img.Rect.Dy()
}
func (d *drawer) Clear() {
d.dev.LockImg()
defer d.dev.UnlockImg()

View File

@ -11,6 +11,7 @@ const (
LineGap = 1
FontCharW = 5
FontCharH = 7
LineH = FontCharH + LineGap
)
// Standard ASCII 5x7 font

37
main.go
View File

@ -4,12 +4,12 @@ import (
"context"
"fmt"
"log"
"math"
"os"
"os/signal"
"syscall"
"time"
"gitea.unprism.ru/yotia/display-test/components"
"gitea.unprism.ru/yotia/display-test/display"
"gitea.unprism.ru/yotia/display-test/drawer"
@ -94,22 +94,35 @@ func Init(ctx context.Context) error {
d.Clear()
//d.FillBar(0, 0, 10, 10, 1)
stTime := time.Now()
for {
d.PutText(2, 0, "12345678901234567890")
d.PutText(2, drawer.LineH, "WWWWWWWWWWWWWWWWWWWWWWW")
d.PutText(1, drawer.LineH*2, "Progress bar")
d.Flush()
time.Sleep(time.Second)
pb, err := components.NewProgressBar(d, 0, drawer.LineH*3, d.W(), drawer.LineH)
time.Sleep(time.Second)
if err != nil {
return fmt.Errorf("create progress bar: %w", err)
}
for x := 0; x < 100; x++ {
select {
case <-ctx.Done():
return nil
default:
t := float64(time.Since(stTime).Milliseconds()) / 1000
x := math.Sin(t)*10 + 5
y := math.Cos(t)*10 + 5
d.Clear()
d.PutText(int(x), int(y), "CGSG forever")
stT := time.Now()
d.Flush()
logger.Println("Iteration time:", time.Since(stT))
case <-time.After(10 * time.Millisecond):
pb.SetProgress(float64(x) / 100)
}
}
pb.Clear()
pb.Close()
d.FillBar(0, drawer.LineH*2, d.W(), drawer.LineH*3, 0)
d.PutText(1, drawer.LineH*2, "Done")
d.Flush()
time.Sleep(100 * time.Millisecond)
//d.PutText(0, 0, "можно 4 строки впихнуть")
//d.PutText(0, drawer.FontCharH+drawer.LineGap, "каждая буква 5x7")
//d.PutText(0, (drawer.FontCharH+drawer.LineGap)*2, "+ русский через жопу")