plc4go/internal/cbus/CBusMessageMapper.go (532 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package cbus import ( "context" "fmt" "strconv" "strings" "github.com/pkg/errors" "github.com/rs/zerolog" apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" apiValues "github.com/apache/plc4x/plc4go/pkg/api/values" readWriteModel "github.com/apache/plc4x/plc4go/protocols/cbus/readwrite/model" "github.com/apache/plc4x/plc4go/spi/transactions" spiValues "github.com/apache/plc4x/plc4go/spi/values" ) func TagToCBusMessage( tag apiModel.PlcTag, value apiValues.PlcValue, alphaGenerator *AlphaGenerator, messageCodec *MessageCodec, ) (cBusMessage readWriteModel.CBusMessage, supportsRead, supportsWrite, supportsSubscribe bool, err error, ) { cbusOptions := messageCodec.cbusOptions requestContext := messageCodec.requestContext switch tagType := tag.(type) { case *statusTag: var statusRequest readWriteModel.StatusRequest switch tagType.statusRequestType { case StatusRequestTypeBinaryState: statusRequest = readWriteModel.NewStatusRequestBinaryState(0x7A, tagType.application) case StatusRequestTypeLevel: statusRequest = readWriteModel.NewStatusRequestLevel(0x73, tagType.application, *tagType.startingGroupAddressLabel) } var cbusCommand readWriteModel.CBusCommand cbusCommand, err = producePointToMultiPointCommandStatus(tagType.bridgeAddresses, tagType.application, statusRequest, cbusOptions) if err != nil { return nil, false, false, false, errors.Wrap(err, "error producing point to multipoint command") } request := readWriteModel.NewRequestCommand( readWriteModel.RequestType_REQUEST_COMMAND, nil, nil, readWriteModel.RequestType_EMPTY, readWriteModel.NewRequestTermination(), cbusCommand, nil, readWriteModel.NewAlpha(alphaGenerator.getAndIncrement()), cbusOptions, ) cBusMessage, supportsRead, supportsSubscribe = readWriteModel.NewCBusMessageToServer(request, requestContext, cbusOptions), true, true return case *calRecallTag: calData := readWriteModel.NewCALDataRecall( readWriteModel.CALCommandTypeContainer_CALCommandRecall, nil, tagType.parameter, tagType.count, requestContext, ) var command readWriteModel.CBusCommand command, err = producePointToPointCommand(tagType.unitAddress, tagType.bridgeAddresses, calData, cbusOptions) if err != nil { return nil, false, false, false, errors.Wrap(err, "error producing cal command") } request := readWriteModel.NewRequestCommand( readWriteModel.RequestType_REQUEST_COMMAND, nil, nil, readWriteModel.RequestType_EMPTY, readWriteModel.NewRequestTermination(), command, nil, readWriteModel.NewAlpha(alphaGenerator.getAndIncrement()), cbusOptions, ) cBusMessage, supportsRead = readWriteModel.NewCBusMessageToServer(request, requestContext, cbusOptions), true return case *calIdentifyTag: calData := readWriteModel.NewCALDataIdentify( readWriteModel.CALCommandTypeContainer_CALCommandIdentify, nil, tagType.attribute, requestContext, ) var command readWriteModel.CBusCommand command, err = producePointToPointCommand(tagType.unitAddress, tagType.bridgeAddresses, calData, cbusOptions) if err != nil { return nil, false, false, false, errors.Wrap(err, "error producing cal command") } request := readWriteModel.NewRequestCommand( readWriteModel.RequestType_REQUEST_COMMAND, nil, nil, readWriteModel.RequestType_EMPTY, readWriteModel.NewRequestTermination(), command, nil, readWriteModel.NewAlpha(alphaGenerator.getAndIncrement()), cbusOptions, ) cBusMessage, supportsRead = readWriteModel.NewCBusMessageToServer(request, requestContext, cbusOptions), true return case *calGetStatusTag: calData := readWriteModel.NewCALDataGetStatus( readWriteModel.CALCommandTypeContainer_CALCommandGetStatus, nil, tagType.parameter, tagType.count, requestContext, ) var command readWriteModel.CBusCommand command, err = producePointToPointCommand(tagType.unitAddress, tagType.bridgeAddresses, calData, cbusOptions) if err != nil { return nil, false, false, false, errors.Wrap(err, "error producing cal command") } request := readWriteModel.NewRequestCommand( readWriteModel.RequestType_REQUEST_COMMAND, nil, nil, readWriteModel.RequestType_EMPTY, readWriteModel.NewRequestTermination(), command, nil, readWriteModel.NewAlpha(alphaGenerator.getAndIncrement()), cbusOptions, ) cBusMessage, supportsRead = readWriteModel.NewCBusMessageToServer(request, requestContext, cbusOptions), true return case *salTag: var salCommand = tagType.salCommand if salCommand == "" { return nil, false, false, false, errors.New("Empty sal command not supported") } var salData readWriteModel.SALData switch tagType.application.ApplicationId() { case readWriteModel.ApplicationId_FREE_USAGE: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_TEMPERATURE_BROADCAST: var temperatureBroadcastData readWriteModel.TemperatureBroadcastData switch salCommand { case readWriteModel.TemperatureBroadcastCommandType_BROADCAST_EVENT.PLC4XEnumName(): if value == nil || !value.IsList() || len(value.GetList()) != 2 || !value.GetList()[0].IsByte() || !value.GetList()[1].IsByte() { return nil, false, false, false, errors.Errorf("%s requires exactly 2 arguments [temperatureGroup,temperatureByte]", salCommand) } commandTypeContainer := readWriteModel.TemperatureBroadcastCommandTypeContainer_TemperatureBroadcastCommandSetBroadcastEvent1_2Bytes temperatureGroup := value.GetList()[0].GetByte() temperatureByte := value.GetList()[1].GetByte() temperatureBroadcastData = readWriteModel.NewTemperatureBroadcastData(commandTypeContainer, temperatureGroup, temperatureByte) supportsWrite = true default: return nil, false, false, false, errors.Errorf("Unsupported command %s for %s", salCommand, tagType.application.ApplicationId()) } salData = readWriteModel.NewSALDataTemperatureBroadcast(nil, temperatureBroadcastData) case readWriteModel.ApplicationId_ROOM_CONTROL_SYSTEM: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_LIGHTING, readWriteModel.ApplicationId_VENTILATION, readWriteModel.ApplicationId_IRRIGATION_CONTROL, readWriteModel.ApplicationId_POOLS_SPAS_PONDS_FOUNTAINS_CONTROL, readWriteModel.ApplicationId_HEATING, readWriteModel.ApplicationId_AUDIO_AND_VIDEO, readWriteModel.ApplicationId_HVAC_ACTUATOR: var lightingData readWriteModel.LightingData switch salCommand { case readWriteModel.LightingCommandType_OFF.PLC4XEnumName(): commandTypeContainer := readWriteModel.LightingCommandTypeContainer_LightingCommandOff if value == nil || !value.IsByte() { return nil, false, false, false, errors.Errorf("%s requires exactly 1 arguments [group]", salCommand) } group := value.GetByte() lightingData = readWriteModel.NewLightingDataOff(commandTypeContainer, group) supportsWrite = true case readWriteModel.LightingCommandType_ON.PLC4XEnumName(): commandTypeContainer := readWriteModel.LightingCommandTypeContainer_LightingCommandOn if value == nil || (!value.IsByte() && (!value.IsList() || len(value.GetList()) != 1 || value.GetList()[0].IsByte())) { return nil, false, false, false, errors.Errorf("%s requires exactly 1 arguments [group]", salCommand) } group := value.GetByte() lightingData = readWriteModel.NewLightingDataOn(commandTypeContainer, group) supportsWrite = true case readWriteModel.LightingCommandType_RAMP_TO_LEVEL.PLC4XEnumName(): if value == nil || !value.IsList() || len(value.GetList()) != 3 || !value.GetList()[0].IsString() || !value.GetList()[1].IsByte() || !value.GetList()[2].IsByte() { return nil, false, false, false, errors.Errorf("%s requires exactly 3 arguments [delay,group,level]", salCommand) } commandTypeContainer, ok := readWriteModel.LightingCommandTypeContainerByName(fmt.Sprintf("LightingCommandRampToLevel_%s", value.GetList()[0].GetString())) if !ok { var possibleValues []string for _, v := range readWriteModel.LightingCommandTypeContainerValues { possibleValues = append(possibleValues, strings.TrimPrefix(v.String(), "LightingCommandRampToLevel_")) } return nil, false, false, false, errors.Errorf("No level found for %s. Possible values %s", value.GetList()[0].GetString(), possibleValues) } group := value.GetList()[1].GetByte() level := value.GetList()[2].GetByte() lightingData = readWriteModel.NewLightingDataRampToLevel(commandTypeContainer, group, level) supportsWrite = true case readWriteModel.LightingCommandType_TERMINATE_RAMP.PLC4XEnumName(): commandTypeContainer := readWriteModel.LightingCommandTypeContainer_LightingCommandTerminateRamp if value == nil || !value.IsByte() { return nil, false, false, false, errors.Errorf("%s requires exactly 1 arguments [group]", salCommand) } group := value.GetByte() lightingData = readWriteModel.NewLightingDataTerminateRamp(commandTypeContainer, group) supportsWrite = true case readWriteModel.LightingCommandType_LABEL.PLC4XEnumName(): err = errors.New("Not yet implemented") // TODO: implement return default: return nil, false, false, false, errors.Errorf("Unsupported command %s for %s", salCommand, tagType.application.ApplicationId()) } salData = readWriteModel.NewSALDataLighting(nil, lightingData) case readWriteModel.ApplicationId_AIR_CONDITIONING: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_TRIGGER_CONTROL: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_ENABLE_CONTROL: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_SECURITY: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_METERING: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_ACCESS_CONTROL: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_CLOCK_AND_TIMEKEEPING: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_TELEPHONY_STATUS_AND_CONTROL: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_MEASUREMENT: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_TESTING: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_MEDIA_TRANSPORT_CONTROL: err = errors.New("Not yet implemented") // TODO: implement return case readWriteModel.ApplicationId_ERROR_REPORTING: err = errors.New("Not yet implemented") // TODO: implement return default: return nil, false, false, false, errors.Errorf("No support for %s", tagType.application) } var cbusCommand readWriteModel.CBusCommand cbusCommand, err = producePointToMultiPointCommandNormal(tagType.bridgeAddresses, tagType.application, salData, cbusOptions) if err != nil { return nil, false, false, false, errors.Wrap(err, "error producing point to multipoint command") } request := readWriteModel.NewRequestCommand( readWriteModel.RequestType_REQUEST_COMMAND, nil, nil, readWriteModel.RequestType_EMPTY, readWriteModel.NewRequestTermination(), cbusCommand, nil, readWriteModel.NewAlpha(alphaGenerator.getAndIncrement()), cbusOptions, ) cBusMessage = readWriteModel.NewCBusMessageToServer(request, requestContext, cbusOptions) return default: return nil, false, false, false, errors.Errorf("Unsupported type %T", tagType) } } func producePointToPointCommand(unitAddress readWriteModel.UnitAddress, bridgeAddresses []readWriteModel.BridgeAddress, calData readWriteModel.CALData, cbusOptions readWriteModel.CBusOptions) (readWriteModel.CBusCommand, error) { if calData == nil { return nil, errors.New("cal data required") } var command readWriteModel.CBusPointToPointCommand numberOfBridgeAddresses := len(bridgeAddresses) if numberOfBridgeAddresses > 0 { if numberOfBridgeAddresses > 6 { return nil, errors.Errorf("Can't have a path longer than 6. Actuall path length = %d", numberOfBridgeAddresses) } networkRoute := readWriteModel.NewNetworkRoute(readWriteModel.NewNetworkProtocolControlInformation(uint8(numberOfBridgeAddresses), uint8(numberOfBridgeAddresses)), bridgeAddresses[1:]) command = readWriteModel.NewCBusPointToPointCommandIndirect(0x0000, calData, bridgeAddresses[0], networkRoute, unitAddress, cbusOptions) } else { command = readWriteModel.NewCBusPointToPointCommandDirect(0x0000, calData, unitAddress, cbusOptions) } header := readWriteModel.NewCBusHeader(readWriteModel.PriorityClass_Class4, false, 0, readWriteModel.DestinationAddressType_PointToPoint) return readWriteModel.NewCBusCommandPointToPoint(header, command, cbusOptions), nil } func producePointToMultiPointCommandStatus(bridgeAddresses []readWriteModel.BridgeAddress, application readWriteModel.ApplicationIdContainer, statusRequest readWriteModel.StatusRequest, cbusOptions readWriteModel.CBusOptions) (readWriteModel.CBusCommand, error) { numberOfBridgeAddresses := len(bridgeAddresses) if numberOfBridgeAddresses > 0 { if numberOfBridgeAddresses > 6 { return nil, errors.Errorf("Can't have a path longer than 6. Actuall path length = %d", numberOfBridgeAddresses) } networkRoute := readWriteModel.NewNetworkRoute(readWriteModel.NewNetworkProtocolControlInformation(uint8(numberOfBridgeAddresses), uint8(numberOfBridgeAddresses)), bridgeAddresses[1:]) command := readWriteModel.NewCBusPointToPointToMultiPointCommandStatus(bridgeAddresses[0], networkRoute, byte(application), statusRequest, cbusOptions) header := readWriteModel.NewCBusHeader(readWriteModel.PriorityClass_Class4, false, 0, readWriteModel.DestinationAddressType_PointToPointToMultiPoint) return readWriteModel.NewCBusCommandPointToPointToMultiPoint(header, command, cbusOptions), nil } command := readWriteModel.NewCBusPointToMultiPointCommandStatus(byte(application), statusRequest, cbusOptions) header := readWriteModel.NewCBusHeader(readWriteModel.PriorityClass_Class4, false, 0, readWriteModel.DestinationAddressType_PointToMultiPoint) return readWriteModel.NewCBusCommandPointToMultiPoint(header, command, cbusOptions), nil } func producePointToMultiPointCommandNormal(bridgeAddresses []readWriteModel.BridgeAddress, application readWriteModel.ApplicationIdContainer, salData readWriteModel.SALData, cbusOptions readWriteModel.CBusOptions) (readWriteModel.CBusCommand, error) { numberOfBridgeAddresses := len(bridgeAddresses) if numberOfBridgeAddresses > 0 { if numberOfBridgeAddresses > 6 { return nil, errors.Errorf("Can't have a path longer than 6. Actuall path length = %d", numberOfBridgeAddresses) } networkRoute := readWriteModel.NewNetworkRoute(readWriteModel.NewNetworkProtocolControlInformation(uint8(numberOfBridgeAddresses), uint8(numberOfBridgeAddresses)), bridgeAddresses[1:]) command := readWriteModel.NewCBusPointToPointToMultiPointCommandNormal(bridgeAddresses[0], networkRoute, byte(application), application, salData, cbusOptions) header := readWriteModel.NewCBusHeader(readWriteModel.PriorityClass_Class4, false, 0, readWriteModel.DestinationAddressType_PointToPointToMultiPoint) return readWriteModel.NewCBusCommandPointToPointToMultiPoint(header, command, cbusOptions), nil } command := readWriteModel.NewCBusPointToMultiPointCommandNormal(0x00, application, salData, cbusOptions) header := readWriteModel.NewCBusHeader(readWriteModel.PriorityClass_Class4, false, 0, readWriteModel.DestinationAddressType_PointToPoint) return readWriteModel.NewCBusCommandPointToMultiPoint(header, command, cbusOptions), nil } func MapEncodedReply(localLog zerolog.Logger, transaction transactions.RequestTransaction, encodedReply readWriteModel.EncodedReply, tagName string, addResponseCode func(name string, responseCode apiModel.PlcResponseCode), addPlcValue func(name string, plcValue apiValues.PlcValue)) error { switch reply := encodedReply.(type) { case readWriteModel.EncodedReplyCALReply: calData := reply.GetCalReply().GetCalData() addResponseCode(tagName, apiModel.PlcResponseCode_OK) switch calData := calData.(type) { case readWriteModel.CALDataStatus: application := calData.GetApplication() // TODO: verify application... this should be the same _ = application blockStart := calData.GetBlockStart() // TODO: verify application... this should be the same _ = blockStart statusBytes := calData.GetStatusBytes() addResponseCode(tagName, apiModel.PlcResponseCode_OK) plcListValues := make([]apiValues.PlcValue, len(statusBytes)*4) for i, statusByte := range statusBytes { plcListValues[i*4+0] = spiValues.NewPlcSTRING(statusByte.GetGav0().String()) plcListValues[i*4+1] = spiValues.NewPlcSTRING(statusByte.GetGav1().String()) plcListValues[i*4+2] = spiValues.NewPlcSTRING(statusByte.GetGav2().String()) plcListValues[i*4+3] = spiValues.NewPlcSTRING(statusByte.GetGav3().String()) } addPlcValue(tagName, spiValues.NewPlcStruct(map[string]apiValues.PlcValue{ "application": spiValues.NewPlcSTRING(application.PLC4XEnumName()), "blockStart": spiValues.NewPlcBYTE(blockStart), "values": spiValues.NewPlcList(plcListValues), })) case readWriteModel.CALDataStatusExtended: coding := calData.GetCoding() // TODO: verify coding... this should be the same _ = coding application := calData.GetApplication() // TODO: verify application... this should be the same _ = application blockStart := calData.GetBlockStart() // TODO: verify application... this should be the same _ = blockStart switch coding { case readWriteModel.StatusCoding_BINARY_BY_THIS_SERIAL_INTERFACE: fallthrough case readWriteModel.StatusCoding_BINARY_BY_ELSEWHERE: statusBytes := calData.GetStatusBytes() addResponseCode(tagName, apiModel.PlcResponseCode_OK) plcListValues := make([]apiValues.PlcValue, len(statusBytes)*4) for i, statusByte := range statusBytes { plcListValues[i*4+0] = spiValues.NewPlcSTRING(statusByte.GetGav0().String()) plcListValues[i*4+1] = spiValues.NewPlcSTRING(statusByte.GetGav1().String()) plcListValues[i*4+2] = spiValues.NewPlcSTRING(statusByte.GetGav2().String()) plcListValues[i*4+3] = spiValues.NewPlcSTRING(statusByte.GetGav3().String()) } addPlcValue(tagName, spiValues.NewPlcStruct(map[string]apiValues.PlcValue{ "application": spiValues.NewPlcSTRING(application.PLC4XEnumName()), "blockStart": spiValues.NewPlcBYTE(blockStart), "values": spiValues.NewPlcList(plcListValues), })) case readWriteModel.StatusCoding_LEVEL_BY_THIS_SERIAL_INTERFACE: fallthrough case readWriteModel.StatusCoding_LEVEL_BY_ELSEWHERE: levelInformation := calData.GetLevelInformation() addResponseCode(tagName, apiModel.PlcResponseCode_OK) plcListValues := make([]apiValues.PlcValue, len(levelInformation)) for i, levelInformation := range levelInformation { switch levelInformation := levelInformation.(type) { case readWriteModel.LevelInformationAbsent: plcListValues[i] = spiValues.NewPlcSTRING("is absent") case readWriteModel.LevelInformationCorrupted: plcListValues[i] = spiValues.NewPlcSTRING("corrupted") case readWriteModel.LevelInformationNormal: plcListValues[i] = spiValues.NewPlcUSINT(levelInformation.GetActualLevel()) default: return transaction.FailRequest(errors.Errorf("Impossible case %v", levelInformation)) } } addPlcValue(tagName, spiValues.NewPlcList(plcListValues)) } case readWriteModel.CALDataIdentifyReply: switch identifyReplyCommand := calData.GetIdentifyReplyCommand().(type) { case readWriteModel.IdentifyReplyCommandCurrentSenseLevels: addPlcValue(tagName, spiValues.NewPlcRawByteArray(identifyReplyCommand.GetCurrentSenseLevels())) case readWriteModel.IdentifyReplyCommandDelays: addPlcValue(tagName, spiValues.NewPlcStruct(map[string]apiValues.PlcValue{ "reStrikeDelay": spiValues.NewPlcUSINT(identifyReplyCommand.GetReStrikeDelay()), "terminalLevel": spiValues.NewPlcRawByteArray(identifyReplyCommand.GetTerminalLevels()), })) case readWriteModel.IdentifyReplyCommandDSIStatus: addPlcValue(tagName, spiValues.NewPlcStruct(map[string]apiValues.PlcValue{ "channelStatus1": spiValues.NewPlcSTRING(identifyReplyCommand.GetChannelStatus1().String()), "channelStatus2": spiValues.NewPlcSTRING(identifyReplyCommand.GetChannelStatus2().String()), "channelStatus3": spiValues.NewPlcSTRING(identifyReplyCommand.GetChannelStatus3().String()), "channelStatus4": spiValues.NewPlcSTRING(identifyReplyCommand.GetChannelStatus4().String()), "channelStatus5": spiValues.NewPlcSTRING(identifyReplyCommand.GetChannelStatus5().String()), "channelStatus6": spiValues.NewPlcSTRING(identifyReplyCommand.GetChannelStatus6().String()), "channelStatus7": spiValues.NewPlcSTRING(identifyReplyCommand.GetChannelStatus7().String()), "channelStatus8": spiValues.NewPlcSTRING(identifyReplyCommand.GetChannelStatus8().String()), "unitStatus": spiValues.NewPlcSTRING(identifyReplyCommand.GetUnitStatus().String()), "dimmingUCRevisionNumber": spiValues.NewPlcUSINT(identifyReplyCommand.GetDimmingUCRevisionNumber()), })) case readWriteModel.IdentifyReplyCommandExtendedDiagnosticSummary: addPlcValue(tagName, spiValues.NewPlcStruct(map[string]apiValues.PlcValue{ "lowApplication": spiValues.NewPlcSTRING(identifyReplyCommand.GetLowApplication().String()), "highApplication": spiValues.NewPlcSTRING(identifyReplyCommand.GetHighApplication().String()), "area": spiValues.NewPlcUSINT(identifyReplyCommand.GetArea()), "crc": spiValues.NewPlcUINT(identifyReplyCommand.GetCrc()), "serialNumber": spiValues.NewPlcUDINT(identifyReplyCommand.GetSerialNumber()), "networkVoltage": spiValues.NewPlcUSINT(identifyReplyCommand.GetNetworkVoltage()), "unitInLearnMode": spiValues.NewPlcBOOL(identifyReplyCommand.GetUnitInLearnMode()), "networkVoltageLow": spiValues.NewPlcBOOL(identifyReplyCommand.GetNetworkVoltageLow()), "networkVoltageMarginal": spiValues.NewPlcBOOL(identifyReplyCommand.GetNetworkVoltageMarginal()), "enableChecksumAlarm": spiValues.NewPlcBOOL(identifyReplyCommand.GetEnableChecksumAlarm()), "outputUnit": spiValues.NewPlcBOOL(identifyReplyCommand.GetOutputUnit()), "installationMMIError": spiValues.NewPlcBOOL(identifyReplyCommand.GetInstallationMMIError()), "EEWriteError": spiValues.NewPlcBOOL(identifyReplyCommand.GetEEWriteError()), "EEChecksumError": spiValues.NewPlcBOOL(identifyReplyCommand.GetEEChecksumError()), "EEDataError": spiValues.NewPlcBOOL(identifyReplyCommand.GetEEDataError()), "microReset": spiValues.NewPlcBOOL(identifyReplyCommand.GetMicroReset()), "commsTxError": spiValues.NewPlcBOOL(identifyReplyCommand.GetCommsTxError()), "internalStackOverflow": spiValues.NewPlcBOOL(identifyReplyCommand.GetInternalStackOverflow()), "microPowerReset": spiValues.NewPlcBOOL(identifyReplyCommand.GetMicroPowerReset()), })) case readWriteModel.IdentifyReplyCommandSummary: addPlcValue(tagName, spiValues.NewPlcStruct(map[string]apiValues.PlcValue{ "partName": spiValues.NewPlcSTRING(identifyReplyCommand.GetPartName()), "unitServiceType": spiValues.NewPlcUSINT(identifyReplyCommand.GetUnitServiceType()), "version": spiValues.NewPlcSTRING(identifyReplyCommand.GetVersion()), })) case readWriteModel.IdentifyReplyCommandFirmwareVersion: addPlcValue(tagName, spiValues.NewPlcSTRING(identifyReplyCommand.GetFirmwareVersion())) case readWriteModel.IdentifyReplyCommandGAVPhysicalAddresses: addPlcValue(tagName, spiValues.NewPlcRawByteArray(identifyReplyCommand.GetValues())) case readWriteModel.IdentifyReplyCommandGAVValuesCurrent: addPlcValue(tagName, spiValues.NewPlcRawByteArray(identifyReplyCommand.GetValues())) case readWriteModel.IdentifyReplyCommandGAVValuesStored: addPlcValue(tagName, spiValues.NewPlcRawByteArray(identifyReplyCommand.GetValues())) case readWriteModel.IdentifyReplyCommandLogicalAssignment: var plcValues []apiValues.PlcValue for _, logicAssigment := range identifyReplyCommand.GetLogicAssigment() { plcValues = append(plcValues, spiValues.NewPlcStruct(map[string]apiValues.PlcValue{ "greaterOfOrLogic": spiValues.NewPlcBOOL(logicAssigment.GetGreaterOfOrLogic()), "reStrikeDelay": spiValues.NewPlcBOOL(logicAssigment.GetReStrikeDelay()), "assignedToGav16": spiValues.NewPlcBOOL(logicAssigment.GetAssignedToGav16()), "assignedToGav15": spiValues.NewPlcBOOL(logicAssigment.GetAssignedToGav15()), "assignedToGav14": spiValues.NewPlcBOOL(logicAssigment.GetAssignedToGav14()), "assignedToGav13": spiValues.NewPlcBOOL(logicAssigment.GetAssignedToGav13()), })) } addPlcValue(tagName, spiValues.NewPlcList(plcValues)) case readWriteModel.IdentifyReplyCommandManufacturer: addPlcValue(tagName, spiValues.NewPlcSTRING(identifyReplyCommand.GetManufacturerName())) case readWriteModel.IdentifyReplyCommandMaximumLevels: addPlcValue(tagName, spiValues.NewPlcRawByteArray(identifyReplyCommand.GetMaximumLevels())) case readWriteModel.IdentifyReplyCommandMinimumLevels: addPlcValue(tagName, spiValues.NewPlcRawByteArray(identifyReplyCommand.GetMinimumLevels())) case readWriteModel.IdentifyReplyCommandNetworkTerminalLevels: addPlcValue(tagName, spiValues.NewPlcRawByteArray(identifyReplyCommand.GetNetworkTerminalLevels())) case readWriteModel.IdentifyReplyCommandNetworkVoltage: volts := identifyReplyCommand.GetVolts() voltsFloat, err := strconv.ParseFloat(volts, 0) if err != nil { addResponseCode(tagName, apiModel.PlcResponseCode_INTERNAL_ERROR) return transaction.FailRequest(errors.Wrap(err, "Error parsing volts")) } voltsDecimalPlace := identifyReplyCommand.GetVoltsDecimalPlace() voltsDecimalPlaceFloat, err := strconv.ParseFloat(voltsDecimalPlace, 0) if err != nil { addResponseCode(tagName, apiModel.PlcResponseCode_INTERNAL_ERROR) return transaction.FailRequest(errors.Wrap(err, "Error parsing volts decimal place")) } voltsFloat += voltsDecimalPlaceFloat / 10 addPlcValue(tagName, spiValues.NewPlcLREAL(voltsFloat)) case readWriteModel.IdentifyReplyCommandOutputUnitSummary: unitFlags := identifyReplyCommand.GetUnitFlags() structContent := map[string]apiValues.PlcValue{ "unitFlags": spiValues.NewPlcStruct(map[string]apiValues.PlcValue{ "assertingNetworkBurden": spiValues.NewPlcBOOL(unitFlags.GetAssertingNetworkBurden()), "restrikeTimingActive": spiValues.NewPlcBOOL(unitFlags.GetRestrikeTimingActive()), "remoteOFFInputAsserted": spiValues.NewPlcBOOL(unitFlags.GetRemoteOFFInputAsserted()), "remoteONInputAsserted": spiValues.NewPlcBOOL(unitFlags.GetRemoteONInputAsserted()), "localToggleEnabled": spiValues.NewPlcBOOL(unitFlags.GetLocalToggleEnabled()), "localToggleActiveState": spiValues.NewPlcBOOL(unitFlags.GetLocalToggleActiveState()), "clockGenerationEnabled": spiValues.NewPlcBOOL(unitFlags.GetClockGenerationEnabled()), "unitGeneratingClock": spiValues.NewPlcBOOL(unitFlags.GetUnitGeneratingClock()), }), "timeFromLastRecoverOfMainsInSeconds": spiValues.NewPlcUSINT(identifyReplyCommand.GetTimeFromLastRecoverOfMainsInSeconds()), } if gavStoreEnabledByte1 := identifyReplyCommand.GetGavStoreEnabledByte1(); gavStoreEnabledByte1 != nil { structContent["gavStoreEnabledByte1"] = spiValues.NewPlcUSINT(*gavStoreEnabledByte1) } if gavStoreEnabledByte2 := identifyReplyCommand.GetGavStoreEnabledByte2(); gavStoreEnabledByte2 != nil { structContent["gavStoreEnabledByte2"] = spiValues.NewPlcUSINT(*gavStoreEnabledByte2) } addPlcValue(tagName, spiValues.NewPlcStruct(structContent)) case readWriteModel.IdentifyReplyCommandTerminalLevels: addPlcValue(tagName, spiValues.NewPlcRawByteArray(identifyReplyCommand.GetTerminalLevels())) case readWriteModel.IdentifyReplyCommandType: addPlcValue(tagName, spiValues.NewPlcSTRING(identifyReplyCommand.GetUnitType())) default: addResponseCode(tagName, apiModel.PlcResponseCode_INVALID_DATA) return transaction.FailRequest(errors.Errorf("Unmapped type %T", identifyReplyCommand)) } default: wbpcb := spiValues.NewWriteBufferPlcValueBased() if err := calData.SerializeWithWriteBuffer(context.Background(), wbpcb); err != nil { localLog.Warn().Err(err).Type("calData", calData).Msg("Unmapped cal data type %T. Returning raw to string") addPlcValue(tagName, spiValues.NewPlcSTRING(fmt.Sprintf("%s", calData))) } else { addPlcValue(tagName, wbpcb.GetPlcValue()) } } default: return transaction.FailRequest(errors.Errorf("All types should be mapped here. Not mapped: %T", reply)) } return nil }