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 }