Signed-off-by: didinst <9f6129a2ddff0d0c6d03169eaa75e3fea1c1770f>feature/evse-to-ev-win
commit
11f1f179d9
@ -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 |
@ -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 |
||||||
|
} |
@ -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) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -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 |
||||||
|
} |
@ -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 "" |
||||||
|
} |
@ -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 |
Loading…
Reference in new issue