You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
6.4 KiB
253 lines
6.4 KiB
package yabl
|
|
|
|
import (
|
|
can "cli-mon/can"
|
|
"encoding/binary"
|
|
time "time"
|
|
)
|
|
|
|
type action struct {
|
|
object Action
|
|
fields []*field
|
|
interval uint
|
|
name AName
|
|
}
|
|
|
|
type field struct {
|
|
length uint8
|
|
fromBus func(frame uint64) any
|
|
toBus func(value any) uint64
|
|
value uint64
|
|
last *time.Time
|
|
name FName
|
|
}
|
|
|
|
type key struct {
|
|
canId uint32
|
|
unitId uint
|
|
}
|
|
|
|
type converter struct {
|
|
tag string
|
|
protocolMap map[key]*action
|
|
fromBus <-chan *can.CanFrame
|
|
fromProducer <-chan *Event
|
|
toBus chan<- *can.CanFrame
|
|
toConsumer chan<- *Event
|
|
cpuPresentEnergyArray [CONNECTOR_MAX]*CpuPresentEnergy
|
|
cpuPeripheryInstance *CpuPeriphery
|
|
cpuEnergySettingsArray [CONNECTOR_MAX]*CpuEnergySettings
|
|
cpuErrorsInstance *CpuErrors
|
|
cpuDebugInstance *CpuDebug
|
|
puPresentEnergyArray [CONNECTOR_MAX]*PuPresentEnergy
|
|
puPeripheryArray [CONNECTOR_MAX]*PuPeriphery
|
|
puErrorsArray [CONNECTOR_MAX]*PuErrors
|
|
puDebugArray [CONNECTOR_MAX]*PuDebug
|
|
seccTargetEnergyArray [CONNECTOR_MAX]*SeccTargetEnergy
|
|
seccErrorsArray [CONNECTOR_MAX]*SeccErrors
|
|
logicAuthArray [CONNECTOR_MAX]*LogicAuth
|
|
logicEnergyMode [CONNECTOR_MAX]*LogicEnergyMode
|
|
logicErrorsInstance *LogicErrors
|
|
logicWorkingMode [CONNECTOR_MAX]*LogicWorkingMode
|
|
LogicCommunicationMode [CONNECTOR_MAX]*LogicCommunicationMode
|
|
contactorInternalStateArray [CONTACTOR_MAX]*ContactorInternalState
|
|
contactorInternalErrorsInstance *ContactorInternalErrors
|
|
contactorsInternalForce *ContactorsInternalForce
|
|
contactorInternalDebugArray [CONTACTOR_MAX]*ContactorInternalDebug
|
|
peripheryStateArray [CONNECTOR_MAX]*PeripheryState
|
|
peripheryInfoArray [CONNECTOR_MAX]*PeripheryInfo
|
|
peripheryDebugArray [CONNECTOR_MAX]*PeripheryDebug
|
|
converterPresentEnergyArray [CONVERTERS_MAX]*ConverterPresentEnergy
|
|
converterErrorsArray [CONVERTERS_MAX]*ConverterErrors
|
|
converterDebugArray [CONVERTERS_MAX]*ConverterDebug
|
|
}
|
|
|
|
type Converter interface {
|
|
FramesFromEvents([]*Event) ([]*can.CanFrame, bool)
|
|
EventsFromFrame(*can.CanFrame) ([]*Event, bool)
|
|
CheckTimeouts(isOffline bool) ([]*Event, bool)
|
|
}
|
|
|
|
func NewProtocolConverter(tag string) Converter {
|
|
return newConverter(tag)
|
|
}
|
|
|
|
func newConverter(tag string) *converter {
|
|
c := &converter{tag: tag}
|
|
c.initialize()
|
|
return c
|
|
}
|
|
|
|
func (c *converter) EventsFromFrame(frame *can.CanFrame) ([]*Event, bool) {
|
|
if frame == nil {
|
|
return nil, false
|
|
}
|
|
var unitId = uint((frame.CanId & UNIT_ID_MASK) >> UNIT_ID_OFFSET)
|
|
var canId = frame.CanId & ACTION_ID_MASK
|
|
var k = key{canId: canId, unitId: unitId}
|
|
|
|
if param, ok := c.protocolMap[k]; ok {
|
|
result := make([]*Event, 0, len(param.fields))
|
|
|
|
var start uint8 = 0
|
|
for _, f := range param.fields {
|
|
if val, found := getValueFromBuf(start, f.length, frame.Payload); found {
|
|
f.last = frame.Date
|
|
if f.value != val { // value changed
|
|
f.value = val
|
|
obj := f.fromBus(val)
|
|
|
|
event := &Event{
|
|
Tag: &c.tag,
|
|
Field: &f.name,
|
|
ActionName: ¶m.name,
|
|
Object: ¶m.object,
|
|
unit: unit(unitId),
|
|
Updated: frame.Date,
|
|
Value: obj,
|
|
}
|
|
|
|
result = append(result, event)
|
|
}
|
|
}
|
|
start += f.length // next field
|
|
}
|
|
return result, len(result) > 0
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func getValueFromBuf(from uint8, size uint8, 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 (u unit) GetUnitId() uint32 {
|
|
return uint32(u)
|
|
}
|
|
|
|
func (c *converter) CheckTimeouts(isOffline bool) ([]*Event, bool) {
|
|
var multipler uint = 2
|
|
var events []*Event
|
|
|
|
for k, param := range c.protocolMap {
|
|
|
|
var now time.Time
|
|
if isOffline {
|
|
now = getMaxTime(&c.protocolMap)
|
|
} else {
|
|
now = time.Now()
|
|
}
|
|
|
|
deadline := now.Add(-time.Duration(param.interval*multipler) * time.Millisecond)
|
|
|
|
var from uint8 = 0
|
|
|
|
for _, fld := range param.fields {
|
|
if fld.last == nil {
|
|
continue
|
|
} else if deadline.After(*fld.last) {
|
|
if events == nil {
|
|
events = make([]*Event, 0, len(param.fields))
|
|
}
|
|
|
|
event := &Event{
|
|
Tag: &c.tag,
|
|
Field: &fld.name,
|
|
ActionName: ¶m.name,
|
|
Object: ¶m.object,
|
|
unit: unit(k.unitId),
|
|
Updated: &deadline,
|
|
Value: nil,
|
|
}
|
|
|
|
var mask uint64 = ^(ALL_BITS << fld.length)
|
|
mask = mask << from
|
|
fld.value = mask
|
|
|
|
events = append(events, event)
|
|
fld.last = nil
|
|
}
|
|
from += fld.length
|
|
}
|
|
}
|
|
return events, events != nil
|
|
}
|
|
|
|
func getMaxTime(protocolMap *map[key]*action) time.Time {
|
|
now := time.Unix(0, 0)
|
|
|
|
for _, param := range *protocolMap {
|
|
for _, fld := range param.fields {
|
|
if fld.last != nil && fld.last.After(now) {
|
|
now = *fld.last
|
|
}
|
|
}
|
|
}
|
|
return now
|
|
}
|
|
|
|
func (c *converter) FramesFromEvents(events []*Event) ([]*can.CanFrame, bool) {
|
|
var keys = make([]*key, len(events))
|
|
for _, event := range events {
|
|
unitId := event.GetUnitId()
|
|
|
|
actionId := event.ActionName
|
|
fieldId := event.Field
|
|
|
|
if ok, act, fld := c.findField(unitId, actionId, fieldId); ok {
|
|
keys = append(keys, act)
|
|
fld.value = fld.toBus(event.Value)
|
|
}
|
|
}
|
|
|
|
var frames = make([]*can.CanFrame, len(events))
|
|
for _, k := range keys {
|
|
act := c.protocolMap[*k]
|
|
canId := (act.object.GetUnitId() & (UNIT_ID_MASK >> UNIT_ID_OFFSET)) << UNIT_ID_OFFSET
|
|
canId = canId | k.canId
|
|
|
|
frame := &can.CanFrame{
|
|
CanId: canId,
|
|
Payload: fieldsToPayload(act.fields),
|
|
}
|
|
|
|
frames = append(frames, frame)
|
|
}
|
|
|
|
return frames, len(frames) > 0
|
|
}
|
|
|
|
func fieldsToPayload(fields []*field) *[]uint8 {
|
|
var val uint64 = 0
|
|
var offset uint8 = 0
|
|
for _, f := range fields {
|
|
val = val | (f.value << offset)
|
|
offset += f.length
|
|
}
|
|
|
|
var buf = make([]uint8, 8)
|
|
binary.PutUvarint(buf, val)
|
|
|
|
return &buf
|
|
}
|
|
|
|
func (c *converter) findField(unitId uint32, actionId *AName, fieldId *FName) (ok bool, key *key, fld *field) {
|
|
for k, a := range c.protocolMap {
|
|
for _, f := range a.fields {
|
|
if k.unitId == uint(unitId) && a.name == *actionId && f.name == *fieldId {
|
|
return true, &k, f
|
|
}
|
|
}
|
|
}
|
|
return false, nil, nil
|
|
}
|
|
|