From 77a1ab7ecb860acd25dec0b41506265456d3ae94 Mon Sep 17 00:00:00 2001 From: Terekhin Alexander Date: Tue, 16 Sep 2025 15:25:41 +0300 Subject: [PATCH] Add "native" candump support --- can.go | 5 +- chademo.go | 143 +++++++++++++++++++++++++------------------------- files.go | 45 ++++++++++++---- files_test.go | 14 +++-- types.go | 6 ++- 5 files changed, 125 insertions(+), 88 deletions(-) diff --git a/can.go b/can.go index f4decfb..09ed6e8 100644 --- a/can.go +++ b/can.go @@ -117,10 +117,11 @@ func StartCan(canbus string) (<-chan *CanFrame, error) { for { //TODO implement stop if id, data, serr := socket.Recv(); serr == nil { + var now = time.Now() canevents <- &CanFrame{ - date: time.Now().String(), + date: &now, canid: id, - payload: data, + payload: &data, } } } diff --git a/chademo.go b/chademo.go index 76b1c20..767f929 100644 --- a/chademo.go +++ b/chademo.go @@ -2,11 +2,12 @@ package main import ( "fmt" + "time" ) type ChademoEvent interface { // GetValue Return processed/calculated value and timestamp string - GetValue() (interface{}, *string) + GetValue() (interface{}, *time.Time) } type frame struct { @@ -266,142 +267,142 @@ func getChargerStopControl(bytes *[]uint8) StopControl { return OPERATING } -func (m MaximumBatteryVoltage) GetValue() (interface{}, *string) { - return bytesToUint16(m.payload[4:6]), &m.date +func (m MaximumBatteryVoltage) GetValue() (interface{}, *time.Time) { + return bytesToUint16((*m.payload)[4:6]), m.date } -func (c ConstChargingRateInd) GetValue() (interface{}, *string) { - return c.payload[6], &c.date +func (c ConstChargingRateInd) GetValue() (interface{}, *time.Time) { + return (*c.payload)[6], c.date } -func (b BatteryCapacity) GetValue() (interface{}, *string) { - return 0.1 * float32(bytesToUint16(b.payload[5:7])), &b.date +func (b BatteryCapacity) GetValue() (interface{}, *time.Time) { + return 0.1 * float32(bytesToUint16((*b.payload)[5:7])), b.date } -func (m MaxChargingTimeS) GetValue() (interface{}, *string) { - return 10 * uint16(m.payload[1]), &m.date +func (m MaxChargingTimeS) GetValue() (interface{}, *time.Time) { + return 10 * uint16((*m.payload)[1]), m.date } -func (m MaxChargingTimeM) GetValue() (interface{}, *string) { - return m.payload[2], &m.date +func (m MaxChargingTimeM) GetValue() (interface{}, *time.Time) { + return (*m.payload)[2], m.date } -func (e EstChargingTimeM) GetValue() (interface{}, *string) { - return e.payload[3], &e.date +func (e EstChargingTimeM) GetValue() (interface{}, *time.Time) { + return (*e.payload)[3], e.date } -func (c EVControlProtocolNumber) GetValue() (interface{}, *string) { - return c.payload[0], &c.date +func (c EVControlProtocolNumber) GetValue() (interface{}, *time.Time) { + return (*c.payload)[0], c.date } -func (t TargetBatteryVoltage) GetValue() (interface{}, *string) { - return bytesToUint16(t.payload[1:3]), &t.date +func (t TargetBatteryVoltage) GetValue() (interface{}, *time.Time) { + return bytesToUint16((*t.payload)[1:3]), t.date } -func (c ChargingCurrentReq) GetValue() (interface{}, *string) { - return c.payload[3], &c.date +func (c ChargingCurrentReq) GetValue() (interface{}, *time.Time) { + return (*c.payload)[3], c.date } -func (c ChargingRate) GetValue() (interface{}, *string) { - return c.payload[6], &c.date +func (c ChargingRate) GetValue() (interface{}, *time.Time) { + return (*c.payload)[6], c.date } -func (s ShiftLever) GetValue() (interface{}, *string) { - return getVehicleShiftLever(&s.payload), &s.date +func (s ShiftLever) GetValue() (interface{}, *time.Time) { + return getVehicleShiftLever(s.payload), s.date } -func (v VehicleCharging) GetValue() (interface{}, *string) { - return getVehicleCharging(&v.payload), &v.date +func (v VehicleCharging) GetValue() (interface{}, *time.Time) { + return getVehicleCharging(v.payload), v.date } -func (c ChargingSystemFault) GetValue() (interface{}, *string) { - return getFault(&c.payload, 5, 2), &c.date +func (c ChargingSystemFault) GetValue() (interface{}, *time.Time) { + return getFault(c.payload, 5, 2), c.date } -func (v VehicleStatus) GetValue() (interface{}, *string) { - return getVehicleStatus(&v.payload), &v.date +func (v VehicleStatus) GetValue() (interface{}, *time.Time) { + return getVehicleStatus(v.payload), v.date } -func (n NormalStopReq) GetValue() (interface{}, *string) { - return getNormalStopReq(&n.payload), &n.date +func (n NormalStopReq) GetValue() (interface{}, *time.Time) { + return getNormalStopReq(n.payload), n.date } -func (b BattOvervoltage) GetValue() (interface{}, *string) { - return getFault(&b.payload, 4, 0), &b.date +func (b BattOvervoltage) GetValue() (interface{}, *time.Time) { + return getFault(b.payload, 4, 0), b.date } -func (b BattUndervoltage) GetValue() (interface{}, *string) { - return getFault(&b.payload, 4, 1), &b.date +func (b BattUndervoltage) GetValue() (interface{}, *time.Time) { + return getFault(b.payload, 4, 1), b.date } -func (b BattCurrentDeviationErr) GetValue() (interface{}, *string) { - return getFault(&b.payload, 4, 2), &b.date +func (b BattCurrentDeviationErr) GetValue() (interface{}, *time.Time) { + return getFault(b.payload, 4, 2), b.date } -func (h HighBattTemperature) GetValue() (interface{}, *string) { - return getFault(&h.payload, 4, 3), &h.date +func (h HighBattTemperature) GetValue() (interface{}, *time.Time) { + return getFault(h.payload, 4, 3), h.date } -func (b BattVoltageDeviationErr) GetValue() (interface{}, *string) { - return getFault(&b.payload, 4, 4), &b.date +func (b BattVoltageDeviationErr) GetValue() (interface{}, *time.Time) { + return getFault(b.payload, 4, 4), b.date } -func (e EVcontWeldingDetectSupport) GetValue() (interface{}, *string) { - return evWeldingDetectionSupport(&e.payload), &e.date +func (e EVcontWeldingDetectSupport) GetValue() (interface{}, *time.Time) { + return evWeldingDetectionSupport(e.payload), e.date } -func (a AvailableOutputVoltage) GetValue() (interface{}, *string) { - return bytesToUint16(a.payload[1:3]), &a.date +func (a AvailableOutputVoltage) GetValue() (interface{}, *time.Time) { + return bytesToUint16((*a.payload)[1:3]), a.date } -func (a AvailableOutputCurrent) GetValue() (interface{}, *string) { - return a.payload[3], &a.date +func (a AvailableOutputCurrent) GetValue() (interface{}, *time.Time) { + return (*a.payload)[3], a.date } -func (t ThresholdVoltage) GetValue() (interface{}, *string) { - return bytesToUint16(t.payload[4:6]), &t.date +func (t ThresholdVoltage) GetValue() (interface{}, *time.Time) { + return bytesToUint16((*t.payload)[4:6]), t.date } -func (e EVSEControlProtocolNumber) GetValue() (interface{}, *string) { - return e.payload[0], &e.date +func (e EVSEControlProtocolNumber) GetValue() (interface{}, *time.Time) { + return (*e.payload)[0], e.date } -func (o OutputVoltage) GetValue() (interface{}, *string) { - return bytesToUint16(o.payload[1:3]), &o.date +func (o OutputVoltage) GetValue() (interface{}, *time.Time) { + return bytesToUint16((*o.payload)[1:3]), o.date } -func (o OutputCurrent) GetValue() (interface{}, *string) { - return o.payload[3], &o.date +func (o OutputCurrent) GetValue() (interface{}, *time.Time) { + return (*o.payload)[3], o.date } -func (r RemainingChargingTimeS) GetValue() (interface{}, *string) { - return r.payload[6] * 10, &r.date +func (r RemainingChargingTimeS) GetValue() (interface{}, *time.Time) { + return (*r.payload)[6] * 10, r.date } -func (r RemainingChargingTimeM) GetValue() (interface{}, *string) { - return r.payload[7], &r.date +func (r RemainingChargingTimeM) GetValue() (interface{}, *time.Time) { + return (*r.payload)[7], r.date } -func (s StationStatus) GetValue() (interface{}, *string) { - return getStationState(&s.payload), &s.date +func (s StationStatus) GetValue() (interface{}, *time.Time) { + return getStationState(s.payload), s.date } -func (s StationMalfunction) GetValue() (interface{}, *string) { - return getFault(&s.payload, 5, 1), &s.date +func (s StationMalfunction) GetValue() (interface{}, *time.Time) { + return getFault(s.payload, 5, 1), s.date } -func (v VehicleConnectorLock) GetValue() (interface{}, *string) { - return getConnectorLock(&v.payload), &v.date +func (v VehicleConnectorLock) GetValue() (interface{}, *time.Time) { + return getConnectorLock(v.payload), v.date } -func (b BatteryIncompatibility) GetValue() (interface{}, *string) { - return getBatteryCompatibility(&b.payload), &b.date +func (b BatteryIncompatibility) GetValue() (interface{}, *time.Time) { + return getBatteryCompatibility(b.payload), b.date } -func (c ChargingSystemMalfunction) GetValue() (interface{}, *string) { - return getFault(&c.payload, 5, 4), &c.date +func (c ChargingSystemMalfunction) GetValue() (interface{}, *time.Time) { + return getFault(c.payload, 5, 4), c.date } -func (c ChargerStopControl) GetValue() (interface{}, *string) { - return getChargerStopControl(&c.payload), &c.date +func (c ChargerStopControl) GetValue() (interface{}, *time.Time) { + return getChargerStopControl(c.payload), c.date } diff --git a/files.go b/files.go index 836f872..230f8ae 100644 --- a/files.go +++ b/files.go @@ -6,13 +6,18 @@ import ( "os" "regexp" "strconv" + "time" ) -// (2022-07-08 16:54:15.587099) can0 100 [8] 00 00 00 00 00 00 64 00 -const expr = "\\((.*)\\)\\s+can\\d+\\s+(\\d+)\\s+\\[\\d\\]\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)" +// (2022-07-08 16:54:15.587099) can0 100 [8] 00 00 00 00 00 00 64 00 +const EXPR_HR = "\\((.*)\\)\\s+can\\d+\\s+(\\S+)\\s+\\[\\d\\]\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)\\s+([A-F0-9]+)" +const EXPR_M = "\\((\\d+)\\.(\\d+)\\)\\s+can\\d+\\s+(\\S+)#([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})" const length = 11 -var pattern = regexp.MustCompile(expr) +const time_hr_layout = "2006-01-02 15:04:05.000000" + +var patternHr = regexp.MustCompile(EXPR_HR) +var patternM = regexp.MustCompile(EXPR_M) func Readfile(filename *string) <-chan *CanFrame { c := make(chan *CanFrame) @@ -42,15 +47,35 @@ func Readfile(filename *string) <-chan *CanFrame { } func fromString(text *string) *CanFrame { - submatch := pattern.FindStringSubmatch(*text) - if len(submatch) == length { - canid, err := strconv.ParseUint(submatch[2], 16, 0) + var match []string + var now time.Time + var offset int + + if match = patternHr.FindStringSubmatch(*text); match != nil { + now, _ = time.ParseInLocation(time_hr_layout, match[1], time.Local) + offset = 0 + } else if match = patternM.FindStringSubmatch(*text); match != nil { + var seconds, _ = strconv.ParseInt(match[1], 10, 0) + var nanos, _ = strconv.ParseInt(match[2], 10, 0) + now = time.Unix(seconds, nanos*1000) //.In(time.Local) + + // Correct time to local zone + var _, zoneoffset = now.Zone() + now = now.Add(-time.Duration(zoneoffset) * time.Second) + + offset = 1 + } else { + return nil + } + + if len(match) == length+offset { + canId, err := strconv.ParseUint(match[2+offset], 16, 0) if err != nil { return nil } payload := make([]uint8, 8) - for idx, octet := range submatch[3:] { + for idx, octet := range match[3+offset:] { val, err := strconv.ParseUint(octet, 16, 0) if err != nil { fmt.Println(err) @@ -61,9 +86,9 @@ func fromString(text *string) *CanFrame { } return &CanFrame{ - date: submatch[1], - canid: uint32(canid), - payload: payload, + date: &now, + canid: uint32(canId), + payload: &payload, } } diff --git a/files_test.go b/files_test.go index 38a5ce0..7008abe 100644 --- a/files_test.go +++ b/files_test.go @@ -3,6 +3,7 @@ package main import ( "reflect" "testing" + "time" ) func TestFromString(t *testing.T) { @@ -11,6 +12,7 @@ func TestFromString(t *testing.T) { } var dump = "(2022-07-08 16:54:15.587099) can0 100 [8] 00 00 00 00 00 00 64 00" + var timestamp = time.Date(2022, 07, 8, 16, 54, 15, 587099, time.Local) //"2022-07-08 16:54:15.587099" tests := []struct { name string @@ -23,14 +25,20 @@ func TestFromString(t *testing.T) { args: args{text: &dump}, want: &CanFrame{ canid: CAN_ID_100, - date: "2022-07-08 16:54:15.587099", - payload: []uint8{0, 0, 0, 0, 0, 0, 0x64, 0}, + date: ×tamp, + payload: &[]uint8{0, 0, 0, 0, 0, 0, 0x64, 0}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := fromString(tt.args.text); !reflect.DeepEqual(got, tt.want) { + var got = fromString(tt.args.text) + + var isDate = got.date.Compare(*tt.want.date) == 0 + var isPayload = reflect.DeepEqual(*got.payload, *tt.want.payload) + var isId = got.canid == tt.want.canid + + if isDate && isPayload && isId { t.Errorf("fromString() = %v, want %v", got, tt.want) } }) diff --git a/types.go b/types.go index 353e652..a1ea1d9 100644 --- a/types.go +++ b/types.go @@ -1,5 +1,7 @@ package main +import "time" + const ( CAN_ID_100 = 0x100 CAN_ID_101 = 0x101 @@ -60,8 +62,8 @@ type StopControl uint8 type CanFrame struct { canid uint32 - payload []uint8 - date string + payload *[]uint8 + date *time.Time } func (v VehicleChargingEnabled) String() string { -- 2.36.3