Compare commits

..

2 Commits

Author SHA1 Message Date
didinst 349050edb4
109 full impl 3 years ago
didinst 1a47ad1c7f
108, 109 codes partial impl 3 years ago
  1. 151
      can.go
  2. 6
      can_test.go
  3. 15
      can_win.go
  4. 45
      candump.go
  5. 168
      chademo.go
  6. 71
      files.go
  7. 2
      go.mod
  8. 175
      types.go

151
can.go

@ -1,133 +1,48 @@
//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"
"io"
"net"
"time"
"unsafe"
"golang.org/x/sys/unix"
"fmt"
"regexp"
"strconv"
)
// 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 {
return nil, err
}
return &Socket{dev: device{fd}}, nil
}
// Socket is a high-level representation of a CANBus socket.
type Socket struct {
iface *net.Interface
addr *unix.SockaddrCAN
dev device
}
// Close closes the CAN bus socket.
func (sck *Socket) Close() error {
return unix.Close(sck.dev.fd)
}
func (sck *Socket) Bind(addr string, filter []unix.CanFilter) error {
iface, err := net.InterfaceByName(addr)
if err != nil {
return err
}
sck.iface = iface
sck.addr = &unix.SockaddrCAN{Ifindex: sck.iface.Index}
//add filter
if filter != nil {
unix.SetsockoptCanRawFilter(sck.dev.fd, unix.SOL_CAN_RAW, unix.CAN_RAW_FILTER, filter)
}
return unix.Bind(sck.dev.fd, sck.addr)
}
// Recv receives data from the CAN socket.
// id is the CAN_frame id the data was originated from.
func (sck *Socket) Recv() (id uint32, data []byte, err error) {
var buf [frameSize]byte
n, err := io.ReadFull(sck.dev, buf[:])
if err != nil {
return id, data, err
}
if n != len(buf) {
return id, data, io.ErrUnexpectedEOF
}
id = binary.LittleEndian.Uint32(buf[:4])
id &= unix.CAN_SFF_MASK
data = make([]byte, buf[4])
copy(data, buf[8:])
return id, data, nil
}
type device struct {
fd int
}
func (d device) Read(data []byte) (int, error) {
return unix.Read(d.fd, data)
}
func (d device) Write(data []byte) (int, error) {
return unix.Write(d.fd, data)
type CanFrame struct {
canid uint16
payload []uint8
date string
}
const frameSize = unsafe.Sizeof(canframe{})
// (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
// frame is a can_frame.
type canframe struct {
ID uint32
Len byte
_ [3]byte
Data [8]byte
}
func StartCan(canbus string) (<-chan *CanFrame, error) {
var filter []unix.CanFilter = []unix.CanFilter{
{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 {
var pattern = regexp.MustCompile(expr)
if cerr := socket.Bind(canbus, filter); cerr == nil {
canevents := make(chan *CanFrame)
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
}
go func() {
defer close(canevents)
defer socket.Close()
payload := make([]uint8, 8)
for idx, octet := range submatch[3:] {
val, err := strconv.ParseUint(octet, 16, 0)
if err != nil {
fmt.Println(err)
continue
}
for { //TODO implement stop
if id, data, serr := socket.Recv(); serr == nil {
canevents <- &CanFrame{
date: time.Now().String(),
canid: id,
payload: data,
}
}
}
}()
payload[idx] = uint8(val)
}
return canevents, nil
return &CanFrame{
date: submatch[1],
canid: uint16(canid),
payload: payload,
}
}
return nil, errors.New("can't bind can socket")
return nil
}

@ -22,7 +22,7 @@ func TestFromString(t *testing.T) {
name: "Candump parsing test",
args: args{text: &dump},
want: &CanFrame{
canid: CAN_ID_100,
canid: 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)
}
})
}

@ -1,15 +0,0 @@
//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,6 +1,7 @@
package main
import (
"bufio"
"flag"
"fmt"
"os"
@ -9,8 +10,6 @@ import (
func main() {
filename := flag.String("f", "", "Candump filename")
canbus := flag.String("c", "", "CAN bus interface")
all := flag.Bool("a", false, "Show all packets")
flag.Parse()
var frames <-chan *CanFrame
@ -18,17 +17,8 @@ func main() {
switch {
case len(*filename) > 0:
fmt.Printf("Open file %v\n", *filename)
frames = Readfile(filename)
case len(*canbus) > 0:
fmt.Printf("Open device %v\n", canbus)
var err error
frames, err = StartCan(*canbus)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Open %v\n", *filename)
frames = readfile(filename)
default:
flag.Usage()
}
@ -47,7 +37,7 @@ func main() {
lastValue := last[eventType]
value, date := event.GetValue()
if lastValue != value || *all {
if lastValue != value {
fmt.Printf("%v | %s = %v\n", *date, eventType, value)
last[eventType] = value
}
@ -57,3 +47,30 @@ 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,6 +4,64 @@ import (
"fmt"
)
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 ChademoEvent interface {
// GetValue Return processed/calculated value and timestamp string
GetValue() (interface{}, *string)
@ -405,3 +463,113 @@ func (c ChargingSystemMalfunction) GetValue() (interface{}, *string) {
func (c ChargerStopControl) GetValue() (interface{}, *string) {
return getChargerStopControl(&c.payload), &c.date
}
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")
}
}

@ -1,71 +0,0 @@
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
}

@ -1,5 +1,3 @@
module chademo-log
go 1.18
require golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f

@ -1,175 +0,0 @@
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