From 9b2474c26130b5eddfcc9c75fccd10bcb56ade62 Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 21 Mar 2026 15:11:14 +0100 Subject: [PATCH] Improved GoTool --- goTool/main.go | 143 +++++--------------------------- goTool/ota.go | 66 +++++++++++---- goTool/state/state.go | 186 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+), 136 deletions(-) create mode 100644 goTool/state/state.go diff --git a/goTool/main.go b/goTool/main.go index 0f8754a..4521daf 100644 --- a/goTool/main.go +++ b/goTool/main.go @@ -11,24 +11,29 @@ import ( "alox.tool/api" "alox.tool/eventbus" "alox.tool/frontend" + "alox.tool/state" "alox.tool/testrunner" "alox.tool/uart" ) var ( - Tests bool + Tests bool + Baudrate uint ) func main() { flag.BoolVar(&Tests, "t", false, "Tests") + flag.UintVar(&Baudrate, "b", 921600, "Baudrate") // 115200 flag.Parse() + + log.Printf("Starting with Params %v", Baudrate) + config := Config{ Port: 8000, Host: "0.0.0.0", UartPort: "/dev/ttyUSB0", - //Baudrate: 115200, - Baudrate: 921600, + Baudrate: int(Baudrate), } if Tests { StartTests(config) @@ -51,13 +56,14 @@ func StartTests(config Config) { tr := testrunner.New(bus, com) - testTest1 := make(map[string]func() error) - testTest1["Echo Test"] = tr.RunEchoTest - testTest1["Version Test"] = tr.RunVersionTest - testTest1["Info Test"] = tr.RunClientInfoTest - testTest1["Input Test"] = tr.RunClientInputTest + testTest := make(map[string]func() error) + testTest["Echo Test"] = tr.RunEchoTest + testTest["Version Test"] = tr.RunVersionTest + testTest["Info Test"] = tr.RunClientInfoTest + testTest["Input Test"] = tr.RunClientInputTest + testTest["Rebuild Network"] = tr.RebuildNetwork - tr.RunTestSet(testTest1) + tr.RunTestSet(testTest) } func StartApp(config Config) { @@ -88,7 +94,8 @@ func StartApp(config Config) { oManager := NewOTAManager(bus, com, updateSlices) - StartMessageHandling(ctx, bus) + espHandle := state.New() + espHandle.Start(ctx, bus) oManager.StartUpdateHandler(ctx) time.Sleep(time.Millisecond * 5) @@ -98,127 +105,19 @@ func StartApp(config Config) { time.Sleep(time.Millisecond * 5) - com.Send(api.CmdEcho, make([]byte, 0)) - com.Send(api.CmdVersion, make([]byte, 0)) + //com.Send(api.CmdEcho, make([]byte, 0)) + //com.Send(api.CmdVersion, make([]byte, 0)) com.Send(api.CmdClientInfo, make([]byte, 0)) - com.Send(api.CmdClientInput, make([]byte, 0)) + //com.Send(api.CmdClientInput, make([]byte, 0)) //com.Send(api.CmdOtaStart, make([]byte, 0)) - com.Send(api.CmdOtaStartEspNow, make([]byte, 0)) + //com.Send(api.CmdOtaStartEspNow, make([]byte, 0)) url := fmt.Sprintf("%s:%d", config.Host, config.Port) fserver := frontend.New(bus) fserver.Start(url) } -func StartMessageHandling(ctx context.Context, bus eventbus.EventBus) { - - RXC := bus.Subscribe(api.TopicUARTRx) - defer bus.Unsubscribe(api.TopicUARTRx, RXC) - TXC := bus.Subscribe(api.TopicUARTTx) - defer bus.Unsubscribe(api.TopicUARTTx, TXC) - - go func() { - for { - select { - case <-ctx.Done(): - return - case _ = <-TXC: - //log.Printf("MSG[TX]: % X", msg) - case msg := <-RXC: - //log.Printf("MSG[RX]: % X", msg) - val, ok := msg.(api.Frame) - if !ok { - log.Printf("val is not type api.Frame its %T", val) - continue - } - log.Printf("[%d] Frame: %X, % X", val.Time, val.ID, val.Data) - - switch val.ID { - case api.CmdEcho: - log.Printf("Echo %v", val) - case api.CmdVersion: - v, err := uart.ParseFrameVersion(val) - if err != nil { - log.Printf("Could not Parse Version %v", err) - continue - } - log.Printf("Version Info %d %s", v.Version, v.Buildhash) - case api.CmdClientInfo: - v, err := uart.ParseFrameClientInfo(val) - if err != nil { - log.Printf("Could not Parse Client Info %v", err) - continue - } - for _, c := range v { - log.Printf("Client ID %d", c.ClientID) - log.Printf("\tIsAvailable %d", c.IsAvailable) - log.Printf("\tLastPing %d", c.LastPing) - log.Printf("\tLastSuccessfulPing %d", c.LastSuccessfulPing) - log.Printf("\tSlotIsUsed %d", c.SlotIsUsed) - log.Printf("\tVersion %d", c.Version) - log.Printf("\tMACAddr % X", c.MACAddr) - } - case api.CmdClientInput: - v, err := uart.ParseFrameClientInput(val) - if err != nil { - log.Printf("Could not Parse Client Input %v", err) - continue - } - for _, c := range v { - log.Printf("Client ID %d", c.ClientID) - log.Printf("\tX %f", c.X) - log.Printf("\tY %f", c.Y) - log.Printf("\tBitmask %08b", c.InputMask) - } - case api.CmdOtaPayload: - v, err := uart.ParseFrameOtaPayload(val) - if err != nil { - log.Printf("Could not Parse Client Input %v", err) - continue - } - bus.Publish(api.TopicOTA, v) - log.Printf("%v", v) - case api.CmdOtaStatus: - v, err := uart.ParseFrameOtaStatus(val) - if err != nil { - log.Printf("Could not Parse Client Input %v", err) - continue - } - bus.Publish(api.TopicOTA, v) - log.Printf("%v", v) - // Update State Machine - - case api.CmdOtaStart: - v, err := uart.ParseFrameOtaStart(val) - if err != nil { - log.Printf("Could not Parse Client Input %v", err) - continue - } - bus.Publish(api.TopicOTA, v) - log.Printf("%v", v) - case api.CmdOtaEnd: - v, err := uart.ParseFrameOtaEnd(val) - if err != nil { - log.Printf("Could not Parse Client Input %v", err) - continue - } - bus.Publish(api.TopicOTA, v) - log.Printf("%v", v) - case api.CmdOtaStartEspNow: - v, err := uart.ParseFrameOtaStartEspNow(val) - if err != nil { - log.Printf("Could not Parse Client Input %v", err) - continue - } - bus.Publish(api.TopicOTA, v) - log.Printf("%v", v) - } - } - } - }() -} - func SliceUpdate(update []byte, maxlen int) [][]byte { updateSlices := [][]byte{} for i := 0; i < len(update); i += 200 { diff --git a/goTool/ota.go b/goTool/ota.go index cd4720e..1d64e3c 100644 --- a/goTool/ota.go +++ b/goTool/ota.go @@ -30,27 +30,40 @@ func NewOTAManager(bus eventbus.EventBus, com *uart.Com, update [][]byte) OTAMan } func (om *OTAManager) StartUpdateHandler(ctx context.Context) { - OtaChanel := om.Bus.Subscribe(api.TopicOTA) + RXC := om.Bus.Subscribe(api.TopicUARTRx) + defer om.Bus.Unsubscribe(api.TopicUARTRx, RXC) go func() { for { select { case <-ctx.Done(): return - case msg := <-OtaChanel: - om.handleOtaMessage(msg) + case msg := <-RXC: + val, ok := msg.(api.Frame) + if !ok { + log.Printf("val is not type api.Frame its %T", val) + continue + } + log.Printf("[%d] Frame: %X, % X", val.Time, val.ID, val.Data) + om.processFrame(val) } } }() } -func (om *OTAManager) handleOtaMessage(msg any) { - switch msgT := msg.(type) { - case api.PayloadOtaStart: +func (om *OTAManager) processFrame(val api.Frame) { + switch val.ID { + case api.CmdOtaStart: + msgT, err := uart.ParseFrameOtaStart(val) + if err != nil { + log.Printf("Could not Parse Client Input %v", err) + return + } + // Send First Payload om.StartTime = time.Now() om.Partition = msgT.Parition - err := om.Com.Send(api.CmdOtaPayload, om.Update[om.CurrentSlice]) + err = om.Com.Send(api.CmdOtaPayload, om.Update[om.CurrentSlice]) if err != nil { log.Printf("Error Sending Update Step!: %v", err) return @@ -58,7 +71,12 @@ func (om *OTAManager) handleOtaMessage(msg any) { om.CurrentSlice = om.CurrentSlice + 1 log.Printf("First Update Step %d", om.CurrentSlice) log.Printf("%v", msgT) - case api.PayloadOtaPayload: + case api.CmdOtaPayload: + msgT, err := uart.ParseFrameOtaPayload(val) + if err != nil { + log.Printf("Could not Parse Client Input %v", err) + return + } // Send Next Payload until there is no more then send end package log.Printf("msgT %v", msgT) @@ -74,7 +92,7 @@ func (om *OTAManager) handleOtaMessage(msg any) { om.Com.Send(api.CmdOtaEnd, make([]byte, 1)) return } - err := om.Com.Send(api.CmdOtaPayload, om.Update[om.CurrentSlice]) + err = om.Com.Send(api.CmdOtaPayload, om.Update[om.CurrentSlice]) if err != nil { log.Printf("Error Sending Update Step!: %v", err) return @@ -84,15 +102,35 @@ func (om *OTAManager) handleOtaMessage(msg any) { log.Printf("UPDATE Part/WriteIndex %d/%d", msgT.SequenzCounter, msgT.WriteIndex) log.Printf("Progress: %05.2f%%", (float32(om.CurrentSlice)/float32(len(om.Update)))*100) - case api.PayloadOtaEnd: + case api.CmdOtaStatus: + v, err := uart.ParseFrameOtaStatus(val) + if err != nil { + log.Printf("Could not Parse Client Input %v", err) + return + } + log.Printf("%v", v) + // Update State Machine + + case api.CmdOtaEnd: + msgT, err := uart.ParseFrameOtaEnd(val) + if err != nil { + log.Printf("Could not Parse Client Input %v", err) + return + } + // End bestätigung om.EndTime = time.Now() duration := om.EndTime.Sub(om.StartTime) log.Printf("Partition %d Update done in %f.2s!", om.Partition, duration.Seconds()) log.Printf("%v", msgT) - case api.PayloadOtaStatus: - log.Printf("%v", msgT) - case api.PayloadOtaStartEspNow: - log.Printf("%v", msgT) + + case api.CmdOtaStartEspNow: + v, err := uart.ParseFrameOtaStartEspNow(val) + if err != nil { + log.Printf("Could not Parse Client Input %v", err) + return + } + //bus.Publish(api.TopicOTA, v) + log.Printf("%v", v) } } diff --git a/goTool/state/state.go b/goTool/state/state.go new file mode 100644 index 0000000..abd2f42 --- /dev/null +++ b/goTool/state/state.go @@ -0,0 +1,186 @@ +package state + +import ( + "context" + "log" + "sync" + + "alox.tool/api" + "alox.tool/eventbus" + "alox.tool/uart" +) + +type ESPStateHandler struct { + RWMutex sync.RWMutex + Master MasterESP +} + +type ESPVersion struct { + Version uint16 + Buildhash [7]byte +} + +type BaseESP struct { + ClientId byte + IsUpdating bool + UpdateProgress float32 + Version ESPVersion + RunningPartition int + MAC [6]byte +} + +type MasterESP struct { + BaseESP + Slaves map[byte]*SlaveESP +} + +type SlaveESP struct { + BaseESP + SlotAvailable bool + SlotUsed bool + Bitmask uint32 + LageX float32 + LageY float32 + LastPing uint32 + LastSuccessfullPing uint32 +} + +func New() *ESPStateHandler { + return &ESPStateHandler{ + RWMutex: sync.RWMutex{}, + Master: NewMasterESP(), + } +} + +func NewMasterESP() MasterESP { + return MasterESP{ + BaseESP: BaseESP{ + ClientId: 0, + IsUpdating: false, + UpdateProgress: 0, + Version: ESPVersion{ + Version: 0, + Buildhash: [7]byte{}, + }, + RunningPartition: 0, + MAC: [6]byte{}, + }, + Slaves: map[byte]*SlaveESP{}, + } +} + +func NewSlaveESP() *SlaveESP { + return &SlaveESP{ + BaseESP: BaseESP{ + ClientId: 0, + IsUpdating: false, + UpdateProgress: 0, + Version: ESPVersion{ + Version: 0, + Buildhash: [7]byte{}, + }, + RunningPartition: 0, + MAC: [6]byte{}, + }, + SlotAvailable: false, + SlotUsed: false, + Bitmask: 0, + LageX: 0, + LageY: 0, + LastPing: 0, + LastSuccessfullPing: 0, + } +} + +func (s *ESPStateHandler) Start(ctx context.Context, bus eventbus.EventBus) { + go func() { + RXC := bus.Subscribe(api.TopicUARTRx) + defer bus.Unsubscribe(api.TopicUARTRx, RXC) + + for { + select { + case <-ctx.Done(): + return + case msg := <-RXC: + val, ok := msg.(api.Frame) + if !ok { + log.Printf("val is not type api.Frame its %T", val) + continue + } + log.Printf("[%d] Frame: %X, % X", val.Time, val.ID, val.Data) + s.processFrame(val) + } + } + }() +} + +func (s *ESPStateHandler) processFrame(val api.Frame) { + s.RWMutex.Lock() + defer s.RWMutex.Unlock() + + switch val.ID { + case api.CmdEcho: + log.Printf("Echo %v", val) + case api.CmdVersion: + v, err := uart.ParseFrameVersion(val) + if err != nil { + log.Printf("Could not Parse Version %v", err) + return + } + log.Printf("Version Info %d %s", v.Version, v.Buildhash) + + // Update State + s.Master.Version.Version = v.Version + s.Master.Version.Buildhash = v.Buildhash + + case api.CmdClientInfo: + v, err := uart.ParseFrameClientInfo(val) + if err != nil { + log.Printf("Could not Parse Client Info %v", err) + return + } + for _, c := range v { + log.Printf("Client ID %d", c.ClientID) + log.Printf("\tIsAvailable %d", c.IsAvailable) + log.Printf("\tLastPing %d", c.LastPing) + log.Printf("\tLastSuccessfulPing %d", c.LastSuccessfulPing) + log.Printf("\tSlotIsUsed %d", c.SlotIsUsed) + log.Printf("\tVersion %d", c.Version) + log.Printf("\tMACAddr % X", c.MACAddr) + + _, ok := s.Master.Slaves[c.ClientID] + if !ok { + s.Master.Slaves[c.ClientID] = NewSlaveESP() + } + + s.Master.Slaves[c.ClientID].SlotAvailable = c.IsAvailable != 0 + s.Master.Slaves[c.ClientID].SlotUsed = c.SlotIsUsed != 0 + s.Master.Slaves[c.ClientID].LastPing = c.LastPing + s.Master.Slaves[c.ClientID].LastSuccessfullPing = c.LastSuccessfulPing + s.Master.Slaves[c.ClientID].Version.Version = c.Version + s.Master.Slaves[c.ClientID].MAC = c.MACAddr + } + + case api.CmdClientInput: + v, err := uart.ParseFrameClientInput(val) + if err != nil { + log.Printf("Could not Parse Client Input %v", err) + return + } + for _, c := range v { + log.Printf("Client ID %d", c.ClientID) + log.Printf("\tX %f", c.X) + log.Printf("\tY %f", c.Y) + log.Printf("\tBitmask %08b", c.InputMask) + + _, ok := s.Master.Slaves[c.ClientID] + if !ok { + s.Master.Slaves[c.ClientID] = NewSlaveESP() + } + + s.Master.Slaves[c.ClientID].LageX = c.X + s.Master.Slaves[c.ClientID].LageY = c.Y + s.Master.Slaves[c.ClientID].Bitmask = c.InputMask + } + } +}