Add: components, progress bar
This commit is contained in:
parent
f29bbf8345
commit
bad142a565
20
components/components.go
Normal file
20
components/components.go
Normal 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
117
components/progressbar.go
Normal 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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -11,6 +11,7 @@ const (
|
||||
LineGap = 1
|
||||
FontCharW = 5
|
||||
FontCharH = 7
|
||||
LineH = FontCharH + LineGap
|
||||
)
|
||||
|
||||
// Standard ASCII 5x7 font
|
||||
|
37
main.go
37
main.go
@ -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, "+ русский через жопу")
|
||||
|
Loading…
Reference in New Issue
Block a user