Initial commit

fix/target-energy
Terekhin Alexandr 3 years ago
commit 268e784cb5
Signed by: didinst
GPG Key ID: D2EF94423C23BF12
  1. 4
      .gitignore
  2. 8
      .idea/.gitignore
  3. 141
      can/can.go
  4. 96
      can/input.go
  5. 44
      cli-mon.go
  6. 14
      go.mod
  7. 10
      go.sum
  8. 43
      ui/helper.go
  9. 81
      ui/model.go
  10. 88
      ui/ui.go
  11. 224
      ui/view.go
  12. 280
      yabl/protocol.go
  13. 129
      yabl/strings.go

4
.gitignore vendored

@ -0,0 +1,4 @@
/cli-mon
/.idea/cli-mon.iml
/.idea/modules.xml
/.idea/vcs.xml

8
.idea/.gitignore vendored

@ -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,141 @@
package can
import (
"encoding/binary"
"errors"
"io"
"net"
"time"
"unsafe"
"golang.org/x/sys/unix"
)
const CAN_ID_071 = 0x071
const CAN_ID_073 = 0x073
const CAN_ID_021 = 0x021
const CAN_ID_022 = 0x022
type CanFrame struct {
CanId uint32
Payload []uint8
Date string
}
// 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])
//TODO make correct switch betwin EFF and SFF
id &= unix.CAN_EFF_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)
}
const frameSize = unsafe.Sizeof(canframe{})
// 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_071, Mask: 0xFFF},
{Id: CAN_ID_073, Mask: 0xFFF},
{Id: CAN_ID_021, Mask: 0xFFF},
{Id: CAN_ID_022, Mask: 0xFFF},
}
if socket, err := NewCan(); err == nil {
if cerr := socket.Bind(canbus, filter); cerr == nil {
canevents := make(chan *CanFrame)
go func() {
defer close(canevents)
defer socket.Close()
for { //TODO implement stop
if id, data, serr := socket.Recv(); serr == nil {
canevents <- &CanFrame{
Date: time.Now().String(),
CanId: id,
Payload: data,
}
}
}
}()
return canevents, nil
}
}
return nil, errors.New("can't bind can socket")
}

@ -0,0 +1,96 @@
package can
import (
"bufio"
"fmt"
"os"
"regexp"
"strconv"
)
// (2022-07-08 16:54:15.587099) can0 100 [8] 00 00 00 00 00 00 64 00
// (1670578900.771868) can0 00004021#00000000FCFFFFFF
const EXPR_HR = "\\((.*)\\)\\s+can\\d+\\s+(\\S+)\\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 EXPR_M = "\\((.*)\\)\\s+can\\d+\\s+(\\S+)#([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})"
const length = 11
var patternHr = regexp.MustCompile(EXPR_HR)
var patternM = regexp.MustCompile(EXPR_M)
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)
os.Exit(1)
}
defer file.Close()
scanner := bufio.NewScanner(file)
process(scanner, c)
}()
return c
}
func process(scanner *bufio.Scanner, ch chan<- *CanFrame) {
for scanner.Scan() {
text := scanner.Text()
if frame := fromString(&text); frame != nil {
ch <- frame
}
}
if err := scanner.Err(); err != nil {
fmt.Println(err)
}
}
func ReadStdin() <-chan *CanFrame {
c := make(chan *CanFrame)
go func() {
scanner := bufio.NewScanner(os.Stdin)
process(scanner, c)
}()
return c
}
func fromString(text *string) *CanFrame {
var submatch []string
if submatch = patternHr.FindStringSubmatch(*text); submatch == nil {
if submatch = patternM.FindStringSubmatch(*text); submatch == nil {
return nil
}
}
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
}

@ -0,0 +1,44 @@
package main
import (
"cli-mon/can"
"cli-mon/ui"
"cli-mon/yabl"
"flag"
"fmt"
"os"
)
func main() {
filename := flag.String("f", "", "Candump filename")
canbus := flag.String("i", "", "CAN bus interface")
stdin := flag.Bool("s", false, "Read from stdin")
flag.Parse()
var frames <-chan *can.CanFrame
switch {
case *stdin:
frames = can.ReadStdin()
case len(*filename) > 0:
frames = can.Readfile(filename)
case len(*canbus) > 0:
var err error
frames, err = can.StartCan(*canbus)
if err != nil {
fmt.Println(err)
return
}
default:
flag.Usage()
}
if frames == nil {
fmt.Println("Get no data")
os.Exit(0)
}
var messages = yabl.StartProtocolParsing(frames)
ui.InitCliApp(messages)
}

@ -0,0 +1,14 @@
module cli-mon
go 1.19
require (
github.com/gizak/termui/v3 v3.1.0
golang.org/x/sys v0.3.0
)
require (
github.com/mattn/go-runewidth v0.0.2 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
)

@ -0,0 +1,10 @@
github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc=
github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY=
github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

@ -0,0 +1,43 @@
package ui
import "cli-mon/yabl"
var fNewCIS = func(packet *yabl.ContactorInternalState) *contactorType {
return &contactorType{state: packet}
}
var fGetCIS = func(c *contactorType) *yabl.ContactorInternalState {
return c.state
}
var fSetCIS = func(c *contactorType, packet *yabl.ContactorInternalState) {
c.state = packet
}
var fNewCES = func(packet *yabl.ContactorInternalErrors) *contactorType {
return &contactorType{errors: packet}
}
var fGetCES = func(c *contactorType) *yabl.ContactorInternalErrors {
return c.errors
}
var fSetCES = func(c *contactorType, packet *yabl.ContactorInternalErrors) {
c.errors = packet
}
var fNewPPE = func(packet *yabl.PuPresentEnergy) *connectorType {
return &connectorType{energy: packet}
}
var fGetPPE = func(c *connectorType) *yabl.PuPresentEnergy {
return c.energy
}
var fSetPPE = func(c *connectorType, packet *yabl.PuPresentEnergy) {
c.energy = packet
}
var fNewPP = func(packet *yabl.PuPeriphery) *connectorType {
return &connectorType{state: packet}
}
var fGetPP = func(c *connectorType) *yabl.PuPeriphery {
return c.state
}
var fSetPP = func(c *connectorType, packet *yabl.PuPeriphery) {
c.state = packet
}

@ -0,0 +1,81 @@
package ui
import (
"cli-mon/yabl"
"github.com/gizak/termui/v3/widgets"
"reflect"
)
type connectorType struct {
energy *yabl.PuPresentEnergy
state *yabl.PuPeriphery
}
type contactorType struct {
state *yabl.ContactorInternalState
errors *yabl.ContactorInternalErrors
}
type modelTypes interface {
*connectorType | *contactorType
}
type dataTypes interface {
*yabl.PuPresentEnergy | *yabl.PuPeriphery | *yabl.ContactorInternalState | *yabl.ContactorInternalErrors
}
type fNewType[T modelTypes, V dataTypes] func(e V) T
type fGetType[T modelTypes, V dataTypes] func(m T) V
type fSetType[T modelTypes, V dataTypes] func(m T, e V)
var connectors = make([]*connectorType, connectorsCount+1)
var contactors = make([]*contactorType, contactorsCount+1)
var connUI [connectorsCount + 1]*widgets.Paragraph
var contUI [contactorsCount + 1]*widgets.Paragraph
var contactorsErr *widgets.Paragraph
func process(message yabl.Packet) {
if message == nil {
return
}
unitId := message.GetUnitId()
switch msg := message.(type) {
case *yabl.ContactorInternalState:
if updateOrNew(contactors, unitId, msg, fNewCIS, fGetCIS, fSetCIS) {
updateContactorsView(unitId)
}
case *yabl.ContactorInternalErrors:
if updateOrNew(contactors, unitId, msg, fNewCES, fGetCES, fSetCES) {
updateContactorsStateView()
}
case *yabl.PuPeriphery:
if updateOrNew(connectors, unitId, msg, fNewPP, fGetPP, fSetPP) {
updateConnectorsView(unitId)
}
case *yabl.PuPresentEnergy:
if updateOrNew(connectors, unitId, msg, fNewPPE, fGetPPE, fSetPPE) {
updateConnectorsView(unitId)
}
}
}
func updateOrNew[T modelTypes, V dataTypes](array []T, id uint, msg V, fNew fNewType[T, V], fGet fGetType[T, V], fSet fSetType[T, V]) bool {
if model := array[id]; model != nil {
if element := fGet(model); !reflect.DeepEqual(element, msg) {
fSet(model, msg)
return true
}
} else {
model = fNew(msg)
array[id] = model
return true
}
return false
}

@ -0,0 +1,88 @@
package ui
import (
"cli-mon/yabl"
ui "github.com/gizak/termui/v3"
"github.com/gizak/termui/v3/widgets"
"log"
"time"
)
const headerHeight int = 4
const contactorsCount = 18
const connectorsCount = 6
func InitCliApp(events <-chan yabl.Packet) {
if err := ui.Init(); err != nil {
log.Fatalf("failed to initialize termui: %v", err)
}
defer ui.Close()
var connectorsTab *ui.Grid
//var contactorsErr *widgets.Paragraph
var contactorsStateTab *ui.Grid
var header *widgets.Paragraph
tabpane := widgets.NewTabPane("Connectors", "Contactors States", "Contactors Errors")
tabpane.SetRect(0, 1, 150, 4)
tabpane.Border = true
header = newHeader()
connectorsTab = newConnectorsView()
contactorsStateTab = newContactorsView()
contactorsErr = newContactorsErrView()
renderTab := func() {
switch tabpane.ActiveTabIndex {
case 0:
termWidth, termHeight := ui.TerminalDimensions()
connectorsTab.SetRect(0, headerHeight, termWidth, termHeight)
ui.Render(connectorsTab)
case 1:
termWidth, termHeight := ui.TerminalDimensions()
contactorsStateTab.SetRect(0, headerHeight, termWidth, termHeight)
ui.Render(contactorsStateTab)
case 2:
ui.Render(contactorsErr)
}
}
ui.Render(header, tabpane, connectorsTab)
uiEvents := ui.PollEvents()
ticker := time.NewTicker(2 * time.Second).C
for {
select {
case e := <-uiEvents:
switch e.ID { // event string/identifier
case "q", "<C-c>": // press 'q' or 'C-c' to quit
return
case "<MouseLeft>":
//payload := e.Payload.(ui.Mouse)
//x, y := payload.X, payload.Y
//p.Text = fmt.Sprintf("X = %d, Y = %d", x, y)
case "<Left>":
tabpane.FocusLeft()
ui.Clear()
ui.Render(header, tabpane)
renderTab()
case "<Right>":
tabpane.FocusRight()
ui.Clear()
ui.Render(header, tabpane)
renderTab()
}
switch e.Type {
case ui.KeyboardEvent: // handle all key presses
//eventID := e.ID // keypress string
//p.Text = eventID
}
// use Go's built-in tickers for updating and drawing data
case <-ticker:
renderTab()
case message := <-events:
process(message)
}
}
}

@ -0,0 +1,224 @@
package ui
import (
"cli-mon/yabl"
"fmt"
ui "github.com/gizak/termui/v3"
"github.com/gizak/termui/v3/widgets"
)
func newHeader() *widgets.Paragraph {
widget := widgets.NewParagraph()
widget.Text = "Press q to quit, Press <Left> or <Right> to switch tabs"
widget.SetRect(0, 0, 50, 1)
widget.Border = false
widget.TextStyle.Bg = ui.ColorBlue
return widget
}
func newConnectorsView() *ui.Grid {
grid := ui.NewGrid()
termWidth, termHeight := ui.TerminalDimensions()
grid.SetRect(0, headerHeight, termWidth, termHeight)
var conn [connectorsCount + 1]*widgets.Paragraph //ui.GridItem
for i := 0; i <= connectorsCount; i++ {
p := widgets.NewParagraph()
p.Text = fmt.Sprintf("No data: #%2.d", i)
p.Title = fmt.Sprintf("Connector: #%2.d", i)
//p.SetRect(0, 0, 50, 30)
p.TextStyle.Bg = ui.ColorBlue
p.TextStyle.Fg = ui.ColorWhite
p.BorderStyle.Fg = ui.ColorYellow
p.Border = true
connUI[i] = p
conn[i] = p //ui.NewCol(1.0/6, p)
}
grid.Set(
ui.NewRow(
1.0/2,
ui.NewCol(1.0/4, conn[1]),
ui.NewCol(1.0/4, conn[2]),
ui.NewCol(1.0/4, conn[3]),
ui.NewCol(1.0/4, conn[4]),
),
ui.NewRow(
1.0/2,
ui.NewCol(1.0/4, conn[5]),
ui.NewCol(1.0/4, conn[6]),
ui.NewCol(1.0/4, conn[0]),
),
)
return grid
}
func newContactorsView() *ui.Grid {
grid := ui.NewGrid()
termWidth, termHeight := ui.TerminalDimensions()
grid.SetRect(0, headerHeight, termWidth, termHeight)
var cont [contactorsCount + 1]ui.GridItem
//var contactors = make([]ui.GridItem, contactorsCount)
for i := 1; i <= contactorsCount; i++ {
p := widgets.NewParagraph()
p.Text = fmt.Sprintf("No data: #%2.d", i)
p.Title = fmt.Sprintf("Contactor: #%2.d", i)
p.Border = false
p.TextStyle.Bg = ui.ColorBlue
p.TextStyle.Fg = ui.ColorWhite
p.BorderStyle.Fg = ui.ColorYellow
p.Border = true
contUI[i] = p
cont[i] = ui.NewCol(1.0/6, p)
}
grid.Set(
ui.NewRow(
1.0/3,
cont[1],
cont[2],
cont[3],
cont[4],
cont[5],
cont[6],
),
ui.NewRow(
1.0/3,
cont[7],
cont[8],
cont[9],
cont[10],
cont[11],
cont[12],
),
ui.NewRow(
1.0/3,
cont[13],
cont[14],
cont[15],
cont[16],
cont[17],
cont[18],
))
//ui.NewRow(1, ui.InterfaceSlice(contactors)...)
grid.Set()
return grid
}
func newContactorsErrView() *widgets.Paragraph {
p := widgets.NewParagraph()
p.Text = "No data\n"
p.Title = "Contactors Errors"
p.SetRect(5, 5, 55, 15)
p.BorderStyle.Fg = ui.ColorYellow
return p
}
func updateContactorsView(unitId uint) {
p := contUI[unitId]
s := contactors[unitId].state
if s == nil || p == nil {
return
}
p.Text = fmt.Sprintf(
"BoardReady: %s\nContactorOn: %s\nUnexpectedState: %s\nIsolated: %s\nDebug: %s",
s.ContactorReady,
s.ContactorOn,
s.UnexpectedState,
s.Isolated,
s.DebugEnabled)
if s.ContactorReady == yabl.BOARD_READY_OK {
p.TextStyle.Bg = ui.ColorClear
} else {
p.TextStyle.Bg = ui.ColorRed
}
if s.ContactorOn == yabl.ON {
p.TitleStyle.Bg = ui.ColorBlue
} else {
p.TitleStyle.Bg = ui.ColorClear
}
}
func updateConnectorsView(unitId uint) {
p := connUI[unitId]
s := connectors[unitId].state
e := connectors[unitId].energy
if s == nil || e == nil {
return
}
fstring := "ConnectorInsert %s\n" +
"ContactorOn %s\n" +
"ConnectorLocked %s\n" +
"LineLevel %s\n" +
"IsolationState %s\n" +
"ChargingAllowed %s\n" +
"PwmEnabled %s\n" +
"CpLineVoltage %s\n" +
"V2GMode %s\n" +
"VoltageBefore %s\n" +
"VoltageAfter %s\n" +
"PresentCurrent %s"
p.Text = fmt.Sprintf(
fstring,
s.ConnectorInsert,
s.ContactorOn,
s.ConnectorLocked,
s.CpLineLevel,
s.IsolationState,
s.ChargingAllowed,
s.PwmEnabled,
s.CpLineVoltage,
e.V2GMode,
e.VoltageBefore,
e.VoltageAfter,
e.PresentCurrent)
}
func updateContactorsStateView() {
p := contactorsErr
if contactors[1] == nil {
return
}
e := contactors[1].errors
if e == nil {
return
}
p.Text = fmt.Sprintf(
"BoardReady: %s\nOtherError: %s\nGroupChanged: %s\nUnexpectedFormation: %s\nCpuNotReady: %s\nPuNotReady: %s\nDebug: %s",
e.BoardReady,
e.OtherError,
e.ContactorGroupChanged,
e.UnexpectedFormation,
e.CpuNotReady,
e.PuNotReady,
e.Debug,
)
if e.BoardReady == yabl.BOARD_READY_OK {
p.TextStyle.Bg = ui.ColorClear
} else {
p.TextStyle.Bg = ui.ColorRed
}
}

@ -0,0 +1,280 @@
package yabl
import (
can "cli-mon/can"
"encoding/binary"
)
const (
CONTACTOR_MAX = 18 + 1
CONNECTOR_MAX = 6 + 1
ALL_BITS = 0xFFFFFFFFFFFFFFFF
UNIT_ID_OFFSET = 12
UNIT_ID_MASK = 0b111111111111 << UNIT_ID_OFFSET
ACTION_ID_MASK = 0b111111111111
BOARD_READY_OK BoardReadyType = 0
BOARD_READY_INFO BoardReadyType = 1
BOARD_READY_WARNING BoardReadyType = 2
BOARD_READY_DEBUG BoardReadyType = 3
BOARD_READY_DEP_ERROR BoardReadyType = 4
BOARD_READY_ERROR BoardReadyType = 5
BOARD_READY_CRITICAL BoardReadyType = 6
OFF BooleanType = 0
ON BooleanType = 1
NO_ERROR ErrorType = 0
ERROR ErrorType = 1
OTHER_NO_ERROR ContactorInternalOtherErrorType = 0
NO_PENDING_CHANGES ContactorGroupChangedType = 0
INITIALIZED_CHANGE ContactorGroupChangedType = 1
CHANGE_IN_PROGRESS ContactorGroupChangedType = 2
V2G_MODE_G2V V2GModeType = 0
V2G_MODE_V2G V2GModeType = 1
V2G_MODE_INV V2GModeType = 2
NOT_ENABLE CpLineLevelType = 0
TWELVE CpLineLevelType = 1
NINE CpLineLevelType = 2
SIX CpLineLevelType = 3
THREE CpLineLevelType = 4
MINUS_TWELVE CpLineLevelType = 5
UNKNOWN IsolationStateType = 0
ONGOING IsolationStateType = 1
PASSED IsolationStateType = 2
WARNING IsolationStateType = 3
FAILED IsolationStateType = 4
)
type Packet interface {
GetUnitId() uint
}
type ContactorInternalState struct {
UnitId uint
ContactorReady BoardReadyType
ContactorOn BooleanType
UnexpectedState ErrorType
Isolated ErrorType
DebugEnabled BooleanType
}
type ContactorInternalErrors struct {
UnitId uint
BoardReady BoardReadyType
OtherError ContactorInternalOtherErrorType
ContactorGroupChanged ContactorGroupChangedType
UnexpectedFormation ContactorInternalOtherErrorType
CpuNotReady ErrorType
PuNotReady ErrorType
Debug BooleanType
}
type PuPresentEnergy struct {
UnitId uint
V2GMode V2GModeType
VoltageBefore Voltage11BitType
VoltageAfter Voltage11BitType
PresentCurrent Current10BitType
}
type PuPeriphery struct {
UnitId uint
ConnectorInsert BooleanType
ContactorOn BooleanType
ConnectorLocked BooleanType
CpLineLevel CpLineLevelType
IsolationState IsolationStateType
ChargingAllowed BooleanType
PwmEnabled BooleanType
CpLineVoltage Voltage9BitType
}
type BoardReadyType uint
type BooleanType uint
type ErrorType uint
type ContactorInternalOtherErrorType uint
type ContactorGroupChangedType uint
type V2GModeType uint
type Voltage11BitType uint
type Current10BitType uint
type CpLineLevelType uint
type IsolationStateType uint
type Voltage9BitType float32
func StartProtocolParsing(frames <-chan *can.CanFrame) <-chan Packet {
result := make(chan Packet)
go func() {
defer close(result)
var contactorsFirstState [CONTACTOR_MAX]bool
var contactorsFirstErr [CONTACTOR_MAX]bool
var puFirst [CONNECTOR_MAX]bool
var puPeripheryFirst [CONNECTOR_MAX]bool
for frame := range frames {
if frames == nil {
continue
}
var unitId uint = uint((frame.CanId & UNIT_ID_MASK) >> UNIT_ID_OFFSET)
switch {
case frame.CanId&ACTION_ID_MASK == can.CAN_ID_071:
changed := false
var model = ContactorInternalState{UnitId: unitId}
if val, ok := get(0, 3, frame.Payload); ok && model.ContactorReady != BoardReadyType(val) {
model.ContactorReady = BoardReadyType(val)
changed = true
}
if val, ok := get(3, 2, frame.Payload); ok && model.ContactorOn != BooleanType(val) {
model.ContactorOn = BooleanType(val)
changed = true
}
if val, ok := get(5, 2, frame.Payload); ok && model.UnexpectedState != ErrorType(val) {
model.UnexpectedState = ErrorType(val)
changed = true
}
if val, ok := get(7, 2, frame.Payload); ok && model.Isolated != ErrorType(val) {
model.Isolated = ErrorType(val)
changed = true
}
if val, ok := get(9, 2, frame.Payload); ok && model.DebugEnabled != BooleanType(val) {
model.DebugEnabled = BooleanType(val)
changed = true
}
if changed || !contactorsFirstState[unitId] {
contactorsFirstState[unitId] = true
result <- &model
}
case frame.CanId&ACTION_ID_MASK == can.CAN_ID_073:
changed := false
model := ContactorInternalErrors{UnitId: unitId}
if val, ok := get(0, 3, frame.Payload); ok && model.BoardReady != BoardReadyType(val) {
model.BoardReady = BoardReadyType(val)
changed = true
}
if val, ok := get(3, 4, frame.Payload); ok && model.OtherError != ContactorInternalOtherErrorType(val) {
model.OtherError = ContactorInternalOtherErrorType(val)
changed = true
}
if val, ok := get(7, 3, frame.Payload); ok && model.ContactorGroupChanged != ContactorGroupChangedType(val) {
model.ContactorGroupChanged = ContactorGroupChangedType(val)
changed = true
}
if val, ok := get(10, 4, frame.Payload); ok && model.UnexpectedFormation != ContactorInternalOtherErrorType(val) {
model.UnexpectedFormation = ContactorInternalOtherErrorType(val)
changed = true
}
if val, ok := get(14, 2, frame.Payload); ok && model.CpuNotReady != ErrorType(val) {
model.CpuNotReady = ErrorType(val)
changed = true
}
if val, ok := get(16, 2, frame.Payload); ok && model.PuNotReady != ErrorType(val) {
model.PuNotReady = ErrorType(val)
changed = true
}
if val, ok := get(18, 2, frame.Payload); ok && model.Debug != BooleanType(val) {
model.Debug = BooleanType(val)
changed = true
}
if changed || !contactorsFirstErr[unitId] {
contactorsFirstErr[unitId] = true
result <- &model
}
case frame.CanId&ACTION_ID_MASK == can.CAN_ID_021:
changed := false
model := PuPresentEnergy{UnitId: unitId}
if val, ok := get(0, 2, frame.Payload); ok && model.V2GMode != V2GModeType(val) {
model.V2GMode = V2GModeType(val)
changed = true
}
if val, ok := get(2, 11, frame.Payload); ok && model.VoltageBefore != Voltage11BitType(val) {
model.VoltageBefore = Voltage11BitType(val)
changed = true
}
if val, ok := get(13, 11, frame.Payload); ok && model.VoltageAfter != Voltage11BitType(val) {
model.VoltageAfter = Voltage11BitType(val)
changed = true
}
if val, ok := get(24, 10, frame.Payload); ok && model.PresentCurrent != Current10BitType(val) {
model.PresentCurrent = Current10BitType(val)
changed = true
}
if changed || !puFirst[unitId] {
puFirst[unitId] = true
result <- &model
}
case frame.CanId&ACTION_ID_MASK == can.CAN_ID_022:
changed := false
model := PuPeriphery{UnitId: unitId}
if val, ok := get(0, 2, frame.Payload); ok && model.ConnectorInsert != BooleanType(val) {
model.ConnectorInsert = BooleanType(val)
changed = true
}
if val, ok := get(2, 2, frame.Payload); ok && model.ContactorOn != BooleanType(val) {
model.ContactorOn = BooleanType(val)
changed = true
}
if val, ok := get(4, 2, frame.Payload); ok && model.ConnectorLocked != BooleanType(val) {
model.ConnectorLocked = BooleanType(val)
changed = true
}
if val, ok := get(6, 3, frame.Payload); ok && model.CpLineLevel != CpLineLevelType(val) {
model.CpLineLevel = CpLineLevelType(val)
changed = true
}
if val, ok := get(9, 3, frame.Payload); ok && model.IsolationState != IsolationStateType(val) {
model.IsolationState = IsolationStateType(val)
changed = true
}
if val, ok := get(12, 2, frame.Payload); ok && model.ChargingAllowed != BooleanType(val) {
model.ChargingAllowed = BooleanType(val)
changed = true
}
if val, ok := get(14, 2, frame.Payload); ok && model.PwmEnabled != BooleanType(val) {
model.PwmEnabled = BooleanType(val)
changed = true
}
if val, ok := get(16, 9, frame.Payload); ok && model.CpLineVoltage != (Voltage9BitType(val)*0.1-15) {
model.CpLineVoltage = Voltage9BitType(val)*0.1 - 15
changed = true
}
if changed || !puPeripheryFirst[unitId] {
puPeripheryFirst[unitId] = true
result <- &model
}
}
}
}()
return result
}
func get(from uint, size uint, buffer []byte) (uint64, bool) {
value := binary.LittleEndian.Uint64(buffer)
var mask uint64 = ^(ALL_BITS << size)
mask = mask << from
selected := mask & value
if selected == mask {
return 0, false
} else {
return selected >> from, true
}
}
func (t ContactorInternalState) GetUnitId() uint {
return t.UnitId
}
func (t ContactorInternalErrors) GetUnitId() uint {
return t.UnitId
}
func (t PuPresentEnergy) GetUnitId() uint {
return t.UnitId
}
func (t PuPeriphery) GetUnitId() uint {
return t.UnitId
}

@ -0,0 +1,129 @@
package yabl
import "fmt"
func (t BooleanType) String() string {
switch t {
case ON:
return "ON"
case OFF:
return "OFF"
default:
panic("BooleanType not defended")
}
}
func (t ErrorType) String() string {
switch t {
case NO_ERROR:
return "OK"
case ERROR:
return "ERROR"
default:
panic("ErrorType not defended")
}
}
func (t BoardReadyType) String() string {
switch t {
case BOARD_READY_OK:
return "OK"
case BOARD_READY_INFO:
return "INFO"
case BOARD_READY_WARNING:
return "WARNING"
case BOARD_READY_DEBUG:
return "DEBUG"
case BOARD_READY_DEP_ERROR:
return "DEP_ERROR"
case BOARD_READY_ERROR:
return "ERROR"
case BOARD_READY_CRITICAL:
return "CRITICAL"
default:
panic("BoardReadyType not defended")
}
}
func (t ContactorInternalOtherErrorType) String() string {
switch t {
case OTHER_NO_ERROR:
return "OK"
default:
return fmt.Sprintf("ERROR_CODE_%d", t)
}
}
func (t ContactorGroupChangedType) String() string {
switch t {
case NO_PENDING_CHANGES:
return "NO_PENDING"
case INITIALIZED_CHANGE:
return "INITIALIZED"
case CHANGE_IN_PROGRESS:
return "IN_PROGRESS"
default:
panic("ContactorGroupChangedType not defended")
}
}
func (t V2GModeType) String() string {
switch t {
case V2G_MODE_G2V:
return "G2V"
case V2G_MODE_V2G:
return "V2G"
case V2G_MODE_INV:
return "INV"
default:
panic("V2GModeType not defended")
}
}
func (t Voltage11BitType) String() string {
return fmt.Sprintf("%dV", t)
}
func (t Current10BitType) String() string {
return fmt.Sprintf("%dA", t)
}
func (t CpLineLevelType) String() string {
switch t {
case NOT_ENABLE:
return "NOT_ENABLE"
case TWELVE:
return "12V_NOT_CONNECTED"
case NINE:
return "9V_CONNECTED"
case SIX:
return "6V_CHARGING"
case THREE:
return "3V"
case MINUS_TWELVE:
return "-12V"
default:
panic("CpLineLevelType not defended")
}
}
func (t IsolationStateType) String() string {
switch t {
case UNKNOWN:
return "UNKNOWN"
case ONGOING:
return "ONGOING"
case PASSED:
return "PASSED"
case WARNING:
return "WARNING"
case FAILED:
return "FAILED"
default:
panic("IsolationStateType not defended")
}
}
func (t Voltage9BitType) String() string {
return fmt.Sprintf("%.1fV", t)
}
Loading…
Cancel
Save