package main import ( "powerpod/gotool/pb" ) const ( lipoMinMv = 3000 lipoMaxMv = 4200 ) type lipoReadingJSON struct { Valid bool `json:"valid"` VoltageMv uint32 `json:"voltage_mv"` Percent int `json:"percent,omitempty"` } type batterySampleJSON struct { ClientID uint32 `json:"client_id"` Lipo1 lipoReadingJSON `json:"lipo1"` Lipo2 lipoReadingJSON `json:"lipo2"` AgeMs uint32 `json:"age_ms,omitempty"` } type batteryAPIRequest struct { ClientID uint32 `json:"client_id"` AllClients bool `json:"all_clients"` } type batteryAPIResponse struct { Type string `json:"type,omitempty"` // battery_status (WebSocket) Success bool `json:"success"` Samples []batterySampleJSON `json:"samples,omitempty"` Error string `json:"error,omitempty"` } func lipoPercent(mv uint32) int { if mv <= lipoMinMv { return 0 } if mv >= lipoMaxMv { return 100 } return int((mv - lipoMinMv) * 100 / (lipoMaxMv - lipoMinMv)) } func lipoFromPBMsg(l *pb.LipoReading) lipoReadingJSON { if l == nil { return lipoReadingJSON{} } return lipoFromPB(l.GetValid(), l.GetVoltageMv()) } func lipoFromPB(valid bool, mv uint32) lipoReadingJSON { out := lipoReadingJSON{Valid: valid, VoltageMv: mv} if valid { out.Percent = lipoPercent(mv) } return out } func batterySamplesFromPB(samples []*pb.BatterySample) []batterySampleJSON { out := make([]batterySampleJSON, 0, len(samples)) for _, s := range samples { out = append(out, batterySampleJSON{ ClientID: s.GetClientId(), Lipo1: lipoFromPBMsg(s.GetLipo1()), Lipo2: lipoFromPBMsg(s.GetLipo2()), AgeMs: s.GetAgeMs(), }) } return out } func applyBatteryStatus(link *managedSerial, in batteryAPIRequest) batteryAPIResponse { resp, err := link.BatteryStatus(&pb.BatteryStatusRequest{ ClientId: in.ClientID, AllClients: in.AllClients, }) if err != nil { return batteryAPIResponse{Error: err.Error()} } samples := batterySamplesFromPB(resp.GetSamples()) out := batteryAPIResponse{ Success: resp.GetSuccess() || len(samples) > 0, Samples: samples, } if len(samples) == 0 && out.Error == "" { out.Error = "battery status unavailable" } return out } func findBatterySample(samples []batterySampleJSON, clientID uint32) (batterySampleJSON, bool) { for _, s := range samples { if s.ClientID == clientID { return s, true } } return batterySampleJSON{}, false } // applyBatterySamplesToState merges UART/REST battery samples into dashboard views. func applyBatterySamplesToState(st *DashboardState, samples []batterySampleJSON) { if st == nil || len(samples) == 0 { return } if m, ok := findBatterySample(samples, 0); ok { st.Master.Lipo1 = m.Lipo1 st.Master.Lipo2 = m.Lipo2 st.Master.BatteryAgeMs = m.AgeMs } for i := range st.Clients { if s, ok := findBatterySample(samples, st.Clients[i].ID); ok { st.Clients[i].Lipo1 = s.Lipo1 st.Clients[i].Lipo2 = s.Lipo2 st.Clients[i].BatteryAgeMs = s.AgeMs } } }