Merge pull request 'EVSE to EV, refactor' (#1) from feature/refactor into master

Reviewed-on: #1
master rev0.1
Terekhin Alexandr 3 years ago
commit 0bd594d32d
  1. 53
      can.go
  2. 15
      can_win.go
  3. 30
      candump.go
  4. 233
      chademo.go
  5. 71
      files.go
  6. 6
      files_test.go
  7. 175
      types.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,

@ -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"
}

@ -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
}

@ -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
}

@ -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
}

@ -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)
}
})
}

@ -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")
}
}
Loading…
Cancel
Save