diff --git a/can.go b/can.go index ef5c4f0..f4decfb 100644 --- a/can.go +++ b/can.go @@ -1,61 +1,20 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris + package main import ( "encoding/binary" "errors" - "fmt" "io" "net" - "regexp" - "strconv" "time" "unsafe" "golang.org/x/sys/unix" ) -type CanFrame struct { - canid uint32 - 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.ParseUint(submatch[2], 16, 0) - 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: uint32(canid), - payload: payload, - } - } - - return nil -} - -// New returns a new CAN bus socket. +// NewCan New returns a new CAN bus socket. func NewCan() (*Socket, error) { fd, err := unix.Socket(unix.AF_CAN, unix.SOCK_RAW, unix.CAN_RAW) if err != nil { @@ -142,6 +101,8 @@ func StartCan(canbus string) (<-chan *CanFrame, error) { {Id: CAN_ID_100, Mask: 0xFFF}, {Id: CAN_ID_101, Mask: 0xFFF}, {Id: CAN_ID_102, Mask: 0xFFF}, + {Id: CAN_ID_108, Mask: 0xFFF}, + {Id: CAN_ID_109, Mask: 0xFFF}, } if socket, err := NewCan(); err == nil { @@ -156,8 +117,6 @@ func StartCan(canbus string) (<-chan *CanFrame, error) { for { //TODO implement stop if id, data, serr := socket.Recv(); serr == nil { - //log.Println("debug: data", data) - canevents <- &CanFrame{ date: time.Now().String(), canid: id, diff --git a/can_win.go b/can_win.go new file mode 100644 index 0000000..b27604d --- /dev/null +++ b/can_win.go @@ -0,0 +1,15 @@ +//go:build windows +// +build windows + +package main + +func StartCan(canbus string) (<-chan *CanFrame, error) { + return nil, &NotImplError{} +} + +type NotImplError struct { +} + +func (e *NotImplError) Error() string { + return "Not implemented for windows platform" +} diff --git a/candump.go b/candump.go index d2ab02b..1236263 100644 --- a/candump.go +++ b/candump.go @@ -1,7 +1,6 @@ package main import ( - "bufio" "flag" "fmt" "os" @@ -20,7 +19,7 @@ func main() { switch { case len(*filename) > 0: fmt.Printf("Open file %v\n", *filename) - frames = readfile(filename) + frames = Readfile(filename) case len(*canbus) > 0: fmt.Printf("Open device %v\n", canbus) var err error @@ -58,30 +57,3 @@ func main() { //} } } - -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 index dd94fed..76b1c20 100644 --- a/chademo.go +++ b/chademo.go @@ -4,37 +4,6 @@ import ( "fmt" ) -const ( - CAN_ID_100 = 256 - CAN_ID_101 = 257 - CAN_ID_102 = 258 - - DISABLED VehicleChargingEnabled = iota - ENABLED - - PARKING ShiftLeverPosition = iota - OTHER - - NORMAL SystemFault = iota - FAULT - - CONTACTOR_CLOSED ContactorVehicleStatus = iota - CONTACTOR_OPEN - - NO_REQUEST StopRequest = iota - STOP_REQUEST -) - -type VehicleChargingEnabled uint8 - -type ShiftLeverPosition uint8 - -type SystemFault uint8 - -type ContactorVehicleStatus uint8 - -type StopRequest uint8 - type ChademoEvent interface { // GetValue Return processed/calculated value and timestamp string GetValue() (interface{}, *string) @@ -62,8 +31,8 @@ type MaxChargingTimeM frame // EstChargingTimeM Estimated remaining time before the end of charging calculated by EV type EstChargingTimeM frame -// ControlProtocolNumber Software version of control protocol to which EV corresponds -type ControlProtocolNumber frame +// EVControlProtocolNumber Software version of control protocol to which EV corresponds +type EVControlProtocolNumber frame // TargetBatteryVoltage Target battery voltage ed charging voltage at the vehicle inlet terminals type TargetBatteryVoltage frame @@ -104,6 +73,53 @@ type HighBattTemperature frame // BattVoltageDeviationErr Status flag indicating whether or not the vehicle battery voltage deviates from the output voltage measured by the station type BattVoltageDeviationErr frame +// EVSE entity from here + +// EVcontWeldingDetectSupport Identifier indicating whether or not the station deals with EV contactor welding detection +type EVcontWeldingDetectSupport frame + +// AvailableOutputVoltage Maximum output voltage value at the vehicle connector terminals +type AvailableOutputVoltage frame + +// AvailableOutputCurrent Maximum output current value of the station +type AvailableOutputCurrent frame + +// ThresholdVoltage Threshold voltage to stop the charging process in order to protect vehicle battery +type ThresholdVoltage frame + +// EVSEControlProtocolNumber Software version number of control protocol or charging sequences that the station deals with +type EVSEControlProtocolNumber frame + +// OutputVoltage Supply voltage value of the output circuit in the station +type OutputVoltage frame + +// OutputCurrent Supply current value of the output circuit in the station +type OutputCurrent frame + +// RemainingChargingTimeS Remaining time before the end of charging (counted by 10 s) +type RemainingChargingTimeS frame + +// RemainingChargingTimeM Remaining time before the end of charging (counted by min) +type RemainingChargingTimeM frame + +// StationStatus Status flag indicating the energy transfer from the station +type StationStatus frame + +// StationMalfunction Status flag indicating whether or not there is a malfunction caused by the station +type StationMalfunction frame + +// VehicleConnectorLock Status flag indicating Vehicle connector lock the electromagnetic lock status of vehicle connector +type VehicleConnectorLock frame + +// BatteryIncompatibility Status flag indicating the compatibility of vehicle battery with the output voltage of station +type BatteryIncompatibility frame + +// ChargingSystemMalfunction Status flag indicating whether or not there is a problem with EV, such as improper connection +type ChargingSystemMalfunction frame + +// ChargerStopControl Status flag indicating whether or not the station proceeds with shutdown process +type ChargerStopControl frame + func FromCanFrames(frames <-chan *CanFrame) <-chan ChademoEvent { events := make(chan ChademoEvent) @@ -121,7 +137,7 @@ func FromCanFrames(frames <-chan *CanFrame) <-chan ChademoEvent { events <- &MaxChargingTimeM{frame} events <- &EstChargingTimeM{frame} case CAN_ID_102: - events <- &ControlProtocolNumber{frame} + events <- &EVControlProtocolNumber{frame} events <- &TargetBatteryVoltage{frame} events <- &ChargingCurrentReq{frame} events <- &ChargingRate{frame} @@ -135,6 +151,23 @@ func FromCanFrames(frames <-chan *CanFrame) <-chan ChademoEvent { events <- &BattCurrentDeviationErr{frame} events <- &HighBattTemperature{frame} events <- &BattVoltageDeviationErr{frame} + case CAN_ID_108: + events <- &EVcontWeldingDetectSupport{frame} + events <- &AvailableOutputVoltage{frame} + events <- &AvailableOutputCurrent{frame} + events <- &ThresholdVoltage{frame} + case CAN_ID_109: + events <- &EVSEControlProtocolNumber{frame} + events <- &OutputVoltage{frame} + events <- &OutputCurrent{frame} + events <- &RemainingChargingTimeS{frame} + events <- &RemainingChargingTimeM{frame} + events <- &StationStatus{frame} + events <- &StationMalfunction{frame} + events <- &VehicleConnectorLock{frame} + events <- &BatteryIncompatibility{frame} + events <- &ChargingSystemMalfunction{frame} + events <- &ChargerStopControl{frame} } } }() @@ -198,6 +231,41 @@ func bytesToUint16(bytes []uint8) uint16 { return result } +func evWeldingDetectionSupport(bytes *[]uint8) SuppVehicleWeldingDetection { + if (*bytes)[0] == 0 { + return VEHICLE_WELDING_DETECTION_NOT_SUPPORTED + } + return VEHICLE_WELDING_DETECTION_SUPPORTED +} + +func getStationState(bytes *[]uint8) StationState { + if isBitSet((*bytes)[5], 0) { + return CHARGING + } + return STANDBY +} + +func getConnectorLock(bytes *[]byte) ConnectorLock { + if isBitSet((*bytes)[5], 2) { + return CONNECTOR_LOCKED + } + return CONNECTOR_UNLOCKED +} + +func getBatteryCompatibility(bytes *[]uint8) BatteryCompatible { + if isBitSet((*bytes)[5], 3) { + return BATTERY_INCOMPATIBLE + } + return BATTERY_COMPATIBLE +} + +func getChargerStopControl(bytes *[]uint8) StopControl { + if isBitSet((*bytes)[5], 5) { + return SHUTDOWN_STOP + } + return OPERATING +} + func (m MaximumBatteryVoltage) GetValue() (interface{}, *string) { return bytesToUint16(m.payload[4:6]), &m.date } @@ -222,7 +290,7 @@ func (e EstChargingTimeM) GetValue() (interface{}, *string) { return e.payload[3], &e.date } -func (c ControlProtocolNumber) GetValue() (interface{}, *string) { +func (c EVControlProtocolNumber) GetValue() (interface{}, *string) { return c.payload[0], &c.date } @@ -278,57 +346,62 @@ func (b BattVoltageDeviationErr) GetValue() (interface{}, *string) { return getFault(&b.payload, 4, 4), &b.date } -func (v VehicleChargingEnabled) String() string { - switch v { - case DISABLED: - return "disabled" - case ENABLED: - return "enabled" - default: - panic("VehicleChargingEnabled not implemented") - } +func (e EVcontWeldingDetectSupport) GetValue() (interface{}, *string) { + return evWeldingDetectionSupport(&e.payload), &e.date } -func (s ShiftLeverPosition) String() string { - switch s { - case PARKING: - return "parking" - case OTHER: - return "other" - default: - panic("ShiftLeverPosition not implemented") - } +func (a AvailableOutputVoltage) GetValue() (interface{}, *string) { + return bytesToUint16(a.payload[1:3]), &a.date } -func (s SystemFault) String() string { - switch s { - case NORMAL: - return "normal" - case FAULT: - return "fault" - default: - panic("SystemFault not implemented") - } +func (a AvailableOutputCurrent) GetValue() (interface{}, *string) { + return a.payload[3], &a.date } -func (c ContactorVehicleStatus) String() string { - switch c { - case CONTACTOR_CLOSED: - return "contactor closed" - case CONTACTOR_OPEN: - return "contactor open" - default: - panic("ContactorVehicleStatus not implemented") - } +func (t ThresholdVoltage) GetValue() (interface{}, *string) { + return bytesToUint16(t.payload[4:6]), &t.date } -func (s StopRequest) String() string { - switch s { - case NO_REQUEST: - return "no request" - case STOP_REQUEST: - return "stop request" - default: - panic("StopRequest not implemented") - } +func (e EVSEControlProtocolNumber) GetValue() (interface{}, *string) { + return e.payload[0], &e.date +} + +func (o OutputVoltage) GetValue() (interface{}, *string) { + return bytesToUint16(o.payload[1:3]), &o.date +} + +func (o OutputCurrent) GetValue() (interface{}, *string) { + return o.payload[3], &o.date +} + +func (r RemainingChargingTimeS) GetValue() (interface{}, *string) { + return r.payload[6] * 10, &r.date +} + +func (r RemainingChargingTimeM) GetValue() (interface{}, *string) { + return r.payload[7], &r.date +} + +func (s StationStatus) GetValue() (interface{}, *string) { + return getStationState(&s.payload), &s.date +} + +func (s StationMalfunction) GetValue() (interface{}, *string) { + return getFault(&s.payload, 5, 1), &s.date +} + +func (v VehicleConnectorLock) GetValue() (interface{}, *string) { + return getConnectorLock(&v.payload), &v.date +} + +func (b BatteryIncompatibility) GetValue() (interface{}, *string) { + return getBatteryCompatibility(&b.payload), &b.date +} + +func (c ChargingSystemMalfunction) GetValue() (interface{}, *string) { + return getFault(&c.payload, 5, 4), &c.date +} + +func (c ChargerStopControl) GetValue() (interface{}, *string) { + return getChargerStopControl(&c.payload), &c.date } diff --git a/files.go b/files.go new file mode 100644 index 0000000..836f872 --- /dev/null +++ b/files.go @@ -0,0 +1,71 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "regexp" + "strconv" +) + +// (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 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 +} + +func fromString(text *string) *CanFrame { + submatch := pattern.FindStringSubmatch(*text) + if len(submatch) == length { + canid, err := strconv.ParseUint(submatch[2], 16, 0) + 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: uint32(canid), + payload: payload, + } + } + + return nil +} diff --git a/can_test.go b/files_test.go similarity index 80% rename from can_test.go rename to files_test.go index 49f562e..38a5ce0 100644 --- a/can_test.go +++ b/files_test.go @@ -22,7 +22,7 @@ func TestFromString(t *testing.T) { name: "Candump parsing test", args: args{text: &dump}, want: &CanFrame{ - canid: 100, + canid: CAN_ID_100, date: "2022-07-08 16:54:15.587099", payload: []uint8{0, 0, 0, 0, 0, 0, 0x64, 0}, }, @@ -30,8 +30,8 @@ func TestFromString(t *testing.T) { } 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) + if got := fromString(tt.args.text); !reflect.DeepEqual(got, tt.want) { + t.Errorf("fromString() = %v, want %v", got, tt.want) } }) } diff --git a/types.go b/types.go new file mode 100644 index 0000000..353e652 --- /dev/null +++ b/types.go @@ -0,0 +1,175 @@ +package main + +const ( + CAN_ID_100 = 0x100 + CAN_ID_101 = 0x101 + CAN_ID_102 = 0x102 + CAN_ID_108 = 0x108 + CAN_ID_109 = 0x109 + + DISABLED VehicleChargingEnabled = iota + ENABLED + + PARKING ShiftLeverPosition = iota + OTHER + + NORMAL SystemFault = iota + FAULT + + CONTACTOR_CLOSED ContactorVehicleStatus = iota + CONTACTOR_OPEN + + NO_REQUEST StopRequest = iota + STOP_REQUEST + + VEHICLE_WELDING_DETECTION_SUPPORTED SuppVehicleWeldingDetection = iota + VEHICLE_WELDING_DETECTION_NOT_SUPPORTED + + STANDBY StationState = iota + CHARGING + + CONNECTOR_LOCKED ConnectorLock = iota + CONNECTOR_UNLOCKED + + BATTERY_COMPATIBLE BatteryCompatible = iota + BATTERY_INCOMPATIBLE + + OPERATING StopControl = iota + SHUTDOWN_STOP +) + +type VehicleChargingEnabled uint8 + +type ShiftLeverPosition uint8 + +type SystemFault uint8 + +type ContactorVehicleStatus uint8 + +type StopRequest uint8 + +type SuppVehicleWeldingDetection uint8 + +type StationState uint8 + +type ConnectorLock uint8 + +type BatteryCompatible uint8 + +type StopControl uint8 + +type CanFrame struct { + canid uint32 + payload []uint8 + date string +} + +func (v VehicleChargingEnabled) String() string { + switch v { + case DISABLED: + return "disabled" + case ENABLED: + return "enabled" + default: + panic("VehicleChargingEnabled not implemented") + } +} + +func (s ShiftLeverPosition) String() string { + switch s { + case PARKING: + return "parking" + case OTHER: + return "other" + default: + panic("ShiftLeverPosition not implemented") + } +} + +func (s SystemFault) String() string { + switch s { + case NORMAL: + return "normal" + case FAULT: + return "fault" + default: + panic("SystemFault not implemented") + } +} + +func (c ContactorVehicleStatus) String() string { + switch c { + case CONTACTOR_CLOSED: + return "contactor closed" + case CONTACTOR_OPEN: + return "contactor open" + default: + panic("ContactorVehicleStatus not implemented") + } +} + +func (s StopRequest) String() string { + switch s { + case NO_REQUEST: + return "no request" + case STOP_REQUEST: + return "stop request" + default: + panic("StopRequest not implemented") + } +} + +func (s SuppVehicleWeldingDetection) String() string { + switch s { + case VEHICLE_WELDING_DETECTION_NOT_SUPPORTED: + return "not supported" + case VEHICLE_WELDING_DETECTION_SUPPORTED: + return "supported" + default: + panic("SuppVehicleWeldingDetection not implemented") + } +} + +func (s StationState) String() string { + switch s { + case CHARGING: + return "charging" + case STANDBY: + return "standby" + default: + panic("StationState not implemented") + } +} + +func (c ConnectorLock) String() string { + switch c { + case CONNECTOR_LOCKED: + return "locked" + case CONNECTOR_UNLOCKED: + return "unlocked" + default: + panic("ConnectorLock not implemented") + } +} + +func (b BatteryCompatible) String() string { + switch b { + case BATTERY_COMPATIBLE: + return "compatible" + case BATTERY_INCOMPATIBLE: + return "incompatible" + default: + panic("BatteryCompatible not implemented") + } +} + +func (s StopControl) String() string { + switch s { + case OPERATING: + return "operating" + case SHUTDOWN_STOP: + return "shutdown or stop charging" + default: + panic("StopControl not implemented") + } +}