From 11f1f179d991f51b390c13394d2574a82205ed19 Mon Sep 17 00:00:00 2001 From: Terekhin Alexandr Date: Sun, 24 Jul 2022 17:21:46 +0300 Subject: [PATCH] Initial commit Signed-off-by: didinst <9f6129a2ddff0d0c6d03169eaa75e3fea1c1770f> --- .idea/.gitignore | 8 ++ README.md | 0 can.go | 48 +++++++++++ can_test.go | 38 +++++++++ candump.go | 73 ++++++++++++++++ chademo.go | 211 +++++++++++++++++++++++++++++++++++++++++++++++ chademo.log | 28 +++++++ go.mod | 3 + 8 files changed, 409 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 README.md create mode 100644 can.go create mode 100644 can_test.go create mode 100644 candump.go create mode 100644 chademo.go create mode 100644 chademo.log create mode 100644 go.mod diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/can.go b/can.go new file mode 100644 index 0000000..5e05d50 --- /dev/null +++ b/can.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "regexp" + "strconv" +) + +type CanFrame struct { + canid uint16 + payload []uint8 + date string +} + +// (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]+)" +const length = 11 + +var pattern = regexp.MustCompile(expr) + +func FromString(text *string) *CanFrame { + submatch := pattern.FindStringSubmatch(*text) + if len(submatch) == length { + canid, err := strconv.Atoi(submatch[2]) + if err != nil { + return nil + } + + payload := make([]uint8, 8) + for idx, octet := range submatch[3:] { + val, err := strconv.ParseUint(octet, 16, 0) + if err != nil { + fmt.Println(err) + continue + } + + payload[idx] = uint8(val) + } + + return &CanFrame{ + date: submatch[1], + canid: uint16(canid), + payload: payload, + } + } + + return nil +} diff --git a/can_test.go b/can_test.go new file mode 100644 index 0000000..49f562e --- /dev/null +++ b/can_test.go @@ -0,0 +1,38 @@ +package main + +import ( + "reflect" + "testing" +) + +func TestFromString(t *testing.T) { + type args struct { + text *string + } + + var dump = "(2022-07-08 16:54:15.587099) can0 100 [8] 00 00 00 00 00 00 64 00" + + tests := []struct { + name string + args args + want *CanFrame + }{ + // TODO: Add test cases. + { + name: "Candump parsing test", + args: args{text: &dump}, + want: &CanFrame{ + canid: 100, + date: "2022-07-08 16:54:15.587099", + 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) { + t.Errorf("FromString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/candump.go b/candump.go new file mode 100644 index 0000000..3038df0 --- /dev/null +++ b/candump.go @@ -0,0 +1,73 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "os" +) + +func main() { + filename := flag.String("f", "", "Candump filename") + flag.Parse() + + var frames <-chan *CanFrame + + switch { + case len(*filename) > 0: + fmt.Printf("Open %v\n", *filename) + frames = readfile(filename) + default: + flag.Usage() + } + + if frames == nil { + fmt.Println("Get no data") + os.Exit(0) + } + + events := FromCanFrames(frames) + + fmt.Printf("Value %v", PARKING) + + for event := range events { + + switch msg := event.(type) { + default: + fmt.Println(msg) + } + + fmt.Println(event.GetValue()) + + //if msg, isType := event.(MaximumBatteryVoltage); isType { + // fmt.Printf("MaximumBatteryVoltage: %v\n", msg.GetMaxBattVoltage()) + //} + } +} + +func readfile(filename *string) <-chan *CanFrame { + c := make(chan *CanFrame) + + go func() { + defer close(c) + + file, err := os.Open(*filename) + if err != nil { + fmt.Println(err) + return + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + text := scanner.Text() + c <- FromString(&text) + } + + if err := scanner.Err(); err != nil { + fmt.Println(err) + } + }() + + return c +} diff --git a/chademo.go b/chademo.go new file mode 100644 index 0000000..5d09c19 --- /dev/null +++ b/chademo.go @@ -0,0 +1,211 @@ +package main + +import ( + "fmt" +) + +const ( + CAN_ID_100 = 100 + CAN_ID_101 = 101 + CAN_ID_102 = 102 + + DISABLED VehicleCharging = iota + ENABLED + + PARKING ShiftLever = iota + OTHER + + NORMAL SystemFault = iota + FAULT + + CONTACTOR_CLOSED VehicleStatus = iota + CONTACTOR_OPEN + + NO_REQUEST StopRequest = iota + STOP_REQUEST +) + +type VehicleCharging uint8 + +type ShiftLever uint8 + +type SystemFault uint8 + +type VehicleStatus uint8 + +type StopRequest uint8 + +type ChademoEvent interface { + GetValue() interface{} +} + +type MaximumBatteryVoltage interface { + // GetMaxBattVoltage Maximum battery voltage + GetMaxBattVoltage() uint16 +} + +type ConstChargingRateInd interface { + // GetChargingRate Constant of charging rate indication + GetChargingRate() uint8 +} + +type frame100 struct { + maxBattVoltage uint16 // The maximum voltage value at the vehicle inlet terminals, at which the station stops charging to protect the vehicle battery + chargingRateInd uint8 // Fixed value for charging rate indication, which is the maximum charging rate (100 %) of vehicle battery + date string +} + +type frame101 struct { + batteryCapacity float32 // Rated capacity of battery + maxChargingTimeS uint16 // Maximum charging time permitted by EV, set by 10 s + maxChargingTimeM uint8 // Maximum charging time permitted by EV, set by minute + estChargingTimeM uint8 // Estimated remaining time before the end of charging calculated by EV + date string +} + +type frame102 struct { + controlProtocolNumber uint8 // Software version of control protocol to which EV corresponds + targetBatteryVoltage uint16 // Target battery voltage ed charging voltage at the vehicle inlet terminals + chargingCurrentReq uint8 // Current value requested by EV during charging + chargingRate uint8 // Charging rate of vehicle battery + vehicleCharging VehicleCharging // Status flag indicating charge permission status of EV + shiftLever ShiftLever // Status flag indicating the shift lever position + chargingSystemFault SystemFault // Status flag indicating Charging system fault a malfunction caused by EV or the station, and detected by EV + vehicleStatus VehicleStatus // Status flag indicating the EV contactor status + normalStopReq StopRequest // Status flag indicating the request of EV to stop charging control + battOvervoltage SystemFault // Status flag indicating whether or not the vehicle battery voltage exceeds the maximum limit specified by EV + battUndervoltage SystemFault // Status flag indicating whether or not the vehicle battery voltage is less than the lower limit specified by EV + battCurrentDeviationErr SystemFault // Status flag indicating whether or not the output current deviates from EV requested current + highBattTemperature SystemFault // Status flag indicating whether or not the temperature of vehicle battery exceeds the maximum limit + battVoltageDeviationErr SystemFault // Status flag indicating whether or not the vehicle battery voltage deviates from the output voltage measured by the station + date string +} + +func FromCanFrames(frames <-chan *CanFrame) <-chan ChademoEvent { + events := make(chan ChademoEvent) + + go func() { + defer close(events) + + for frame := range frames { + switch frame.canid { + case CAN_ID_100: + event := &frame100{ + maxBattVoltage: bytesToUint16(frame.payload[4:6]), + chargingRateInd: frame.payload[6], + date: frame.date, + } + events <- event + case CAN_ID_101: + event := &frame101{ + batteryCapacity: 0.1 * float32(bytesToUint16(frame.payload[5:7])), + maxChargingTimeS: 10 * uint16(frame.payload[1]), + maxChargingTimeM: frame.payload[2], + estChargingTimeM: frame.payload[3], + date: frame.date, + } + fmt.Println(event) + //events <- event + case CAN_ID_102: + event := &frame102{ + controlProtocolNumber: frame.payload[0], + targetBatteryVoltage: bytesToUint16(frame.payload[1:3]), + chargingCurrentReq: frame.payload[3], + chargingRate: frame.payload[6], + vehicleCharging: getVehicleCharging(&frame.payload), + shiftLever: getVehicleShiftLever(&frame.payload), + chargingSystemFault: getFault(&frame.payload, 5, 2), + vehicleStatus: getVehicleStatus(&frame.payload), + normalStopReq: getNormalStopReq(&frame.payload), + battOvervoltage: getFault(&frame.payload, 4, 0), + battUndervoltage: getFault(&frame.payload, 4, 1), + battCurrentDeviationErr: getFault(&frame.payload, 4, 2), + highBattTemperature: getFault(&frame.payload, 4, 3), + battVoltageDeviationErr: getFault(&frame.payload, 4, 4), + } + fmt.Println(event) + //events <- event + } + } + }() + + return events +} + +func getVehicleShiftLever(bytes *[]byte) ShiftLever { + if isBitSet((*bytes)[5], 1) { + return OTHER + } + return PARKING +} + +func getVehicleStatus(bytes *[]byte) VehicleStatus { + if isBitSet((*bytes)[5], 3) { + return CONTACTOR_OPEN + } + return CONTACTOR_CLOSED +} + +func getNormalStopReq(bytes *[]byte) StopRequest { + if isBitSet((*bytes)[5], 4) { + return STOP_REQUEST + } + return NO_REQUEST +} + +func getFault(bytes *[]byte, octet byte, bit byte) SystemFault { + if isBitSet((*bytes)[octet], bit) { + return FAULT + } + return NORMAL +} + +func getVehicleCharging(bytes *[]uint8) VehicleCharging { + if isBitSet((*bytes)[5], 0) { + return ENABLED + } + return DISABLED +} + +func isBitSet(buf uint8, num uint8) bool { + mask := uint8(1) << num + val := buf & mask + if val == mask { + return true + } + return false +} + +func bytesToUint16(bytes []uint8) uint16 { + if len(bytes) != 2 { + fmt.Println("Wrong array size in bytesToUint16()") + return 0 + } + + var result uint16 + result = uint16(bytes[1]) << 8 + result = result | uint16(bytes[0]) + return result +} + +func (f frame100) GetMaxBattVoltage() uint16 { + return f.maxBattVoltage +} + +func (f frame100) GetChargingRate() uint8 { + return f.chargingRateInd +} + +func (f frame100) GetValue() interface{} { + return fmt.Sprintf("%v | MaxBattVoltage = %vV, ChargingRate = %v%%", f.date, f.maxBattVoltage, f.chargingRateInd) +} + +func (f frame101) GetValue() interface{} { + return fmt.Sprintf("%v | batteryCapacity = %vkWh, maxChargingTime = %vsec, maxChargingTime = %vmin, estChargingTime = %vmin", + f.date, f.batteryCapacity, f.maxChargingTimeS, f.maxChargingTimeM, f.estChargingTimeM) +} + +func (f frame102) GetValue() interface{} { + //TODO implement me + return "" +} diff --git a/chademo.log b/chademo.log new file mode 100644 index 0000000..332b62b --- /dev/null +++ b/chademo.log @@ -0,0 +1,28 @@ + (2022-07-08 16:54:15.587099) can0 100 [8] 00 00 00 00 00 00 64 00 + (2022-07-08 16:54:15.588095) can0 101 [8] 00 FF FF 00 00 00 00 00 + (2022-07-08 16:54:15.589088) can0 102 [8] 02 00 00 00 00 08 00 00 + (2022-07-08 16:54:15.690084) can0 100 [8] 00 00 00 00 00 00 64 00 + (2022-07-08 16:54:15.691087) can0 101 [8] 00 FF FF 00 00 00 00 00 + (2022-07-08 16:54:15.692083) can0 102 [8] 02 00 00 00 00 08 00 00 + (2022-07-08 16:54:15.793083) can0 100 [8] 00 00 00 00 00 00 64 00 + (2022-07-08 16:54:15.794083) can0 101 [8] 00 FF FF 00 00 00 00 00 + (2022-07-08 16:54:15.795078) can0 102 [8] 02 00 00 00 00 08 00 00 + (2022-07-08 16:54:15.896072) can0 100 [8] 00 00 00 00 85 01 64 00 + (2022-07-08 16:54:15.897078) can0 101 [8] 00 FF FF 00 00 00 00 00 + (2022-07-08 16:54:15.898075) can0 102 [8] 02 00 00 00 00 08 00 00 + (2022-07-08 16:54:15.999065) can0 100 [8] 00 00 00 00 85 01 64 00 + (2022-07-08 16:54:16.000089) can0 101 [8] 00 FF FF 00 00 00 00 00 + (2022-07-08 16:54:16.001111) can0 102 [8] 02 00 00 00 00 08 00 00 + (2022-07-08 16:54:16.102068) can0 100 [8] 00 00 00 00 85 01 64 00 + (2022-07-08 16:54:16.103071) can0 101 [8] 00 FF FF 00 00 00 00 00 + (2022-07-08 16:54:16.104063) can0 102 [8] 02 00 00 00 00 09 00 00 + (2022-07-08 16:54:16.205070) can0 100 [8] 00 00 00 00 85 01 64 00 + (2022-07-08 16:54:16.206227) can0 101 [8] 00 FF FF 00 00 00 00 00 + (2022-07-08 16:54:16.207058) can0 102 [8] 02 00 00 00 00 09 00 00 + (2022-07-08 16:54:16.308066) can0 100 [8] 00 00 00 00 85 01 64 00 + (2022-07-08 16:54:16.309066) can0 101 [8] 00 FF FF 00 00 00 00 00 + (2022-07-08 16:54:16.310070) can0 102 [8] 02 00 00 00 00 09 00 00 + (2022-07-08 16:54:16.411044) can0 100 [8] 00 00 00 00 85 01 64 00 + (2022-07-08 16:54:16.412047) can0 101 [8] 00 FF FF 00 00 00 00 00 + (2022-07-08 16:54:16.413040) can0 102 [8] 02 00 00 00 00 09 00 00 + (2022-07-08 16:54:16.514038) can0 100 [8] 00 00 00 00 85 01 64 00 \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..15af714 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module chademo-log + +go 1.18