public Dictionary<Topic, SCADAPublication> GetIntegrityUpdate() { if (IntegrityUpdateService.scadaModel == null) { string message = $"GetIntegrityUpdate => SCADA model is null."; Logger.LogError(message); throw new InternalSCADAServiceException(message); } var currentScadaModel = IntegrityUpdateService.scadaModel.CurrentScadaModel; var commandValuesCache = IntegrityUpdateService.scadaModel.CommandedValuesCache; Dictionary<long, AnalogModbusData> analogModbusData = new Dictionary<long, AnalogModbusData>(); Dictionary<long, DiscreteModbusData> discreteModbusData = new Dictionary<long, DiscreteModbusData>(); foreach(long gid in currentScadaModel.Keys) { CommandOriginType commandOrigin = CommandOriginType.OTHER_COMMAND; if (currentScadaModel[gid] is AnalogSCADAModelPointItem analogPointItem) { if (commandValuesCache.ContainsKey(gid) && commandValuesCache[gid].Value == analogPointItem.CurrentRawValue) { commandOrigin = commandValuesCache[gid].CommandOrigin; } AnalogModbusData analogValue = new AnalogModbusData(analogPointItem.CurrentEguValue, analogPointItem.Alarm, gid, commandOrigin); analogModbusData.Add(gid, analogValue); } else if(currentScadaModel[gid] is DiscreteSCADAModelPointItem discretePointItem) { if (commandValuesCache.ContainsKey(gid) && commandValuesCache[gid].Value == discretePointItem.CurrentValue) { commandOrigin = commandValuesCache[gid].CommandOrigin; } DiscreteModbusData discreteValue = new DiscreteModbusData(discretePointItem.CurrentValue, discretePointItem.Alarm, gid, commandOrigin); discreteModbusData.Add(gid, discreteValue); } } MultipleAnalogValueSCADAMessage analogValuesMessage = new MultipleAnalogValueSCADAMessage(analogModbusData); SCADAPublication measurementPublication = new SCADAPublication(Topic.MEASUREMENT, analogValuesMessage); MultipleDiscreteValueSCADAMessage discreteValuesMessage = new MultipleDiscreteValueSCADAMessage(discreteModbusData); SCADAPublication switchStatusPublication = new SCADAPublication(Topic.SWITCH_STATUS, discreteValuesMessage); Dictionary<Topic, SCADAPublication> scadaPublications = new Dictionary<Topic, SCADAPublication> { { Topic.MEASUREMENT, measurementPublication }, { Topic.SWITCH_STATUS, switchStatusPublication }, }; return scadaPublications; }
public async Task <ScadaPublication> GetIntegrityUpdateForSpecificTopic(Topic topic) { while (!ReliableDictionariesInitialized) { await Task.Delay(1000); } if (GidToPointItemMap == null) { string message = $"GetIntegrityUpdate => GidToPointItemMap is null."; Logger.LogError(message); throw new InternalSCADAServiceException(message); } if (CommandDescriptionCache == null) { string message = $"GetIntegrityUpdate => CommandDescriptionCache is null."; Logger.LogError(message); throw new InternalSCADAServiceException(message); } Dictionary <long, AnalogModbusData> analogModbusData = new Dictionary <long, AnalogModbusData>(); Dictionary <long, DiscreteModbusData> discreteModbusData = new Dictionary <long, DiscreteModbusData>(); var enumerableGidToPointItemMap = await GidToPointItemMap.GetEnumerableDictionaryAsync(); foreach (long gid in enumerableGidToPointItemMap.Keys) { CommandOriginType commandOrigin = CommandOriginType.UNKNOWN_ORIGIN; if (topic == Topic.MEASUREMENT && enumerableGidToPointItemMap[gid] is IAnalogPointItem analogPointItem) { var result = await CommandDescriptionCache.TryGetValueAsync(gid); if (result.HasValue && result.Value.Value == analogPointItem.CurrentRawValue) { commandOrigin = result.Value.CommandOrigin; } AnalogModbusData analogValue = new AnalogModbusData(analogPointItem.CurrentEguValue, analogPointItem.Alarm, gid, commandOrigin); analogModbusData.Add(gid, analogValue); } else if (topic == Topic.SWITCH_STATUS && enumerableGidToPointItemMap[gid] is IDiscretePointItem discretePointItem) { var result = await CommandDescriptionCache.TryGetValueAsync(gid); if (result.HasValue && result.Value.Value == discretePointItem.CurrentValue) { commandOrigin = result.Value.CommandOrigin; } DiscreteModbusData discreteValue = new DiscreteModbusData(discretePointItem.CurrentValue, discretePointItem.Alarm, gid, commandOrigin); discreteModbusData.Add(gid, discreteValue); } } ScadaPublication scadaPublication; if (topic == Topic.MEASUREMENT) { MultipleAnalogValueSCADAMessage analogValuesMessage = new MultipleAnalogValueSCADAMessage(analogModbusData); scadaPublication = new ScadaPublication(Topic.MEASUREMENT, analogValuesMessage); } else if (topic == Topic.SWITCH_STATUS) { MultipleDiscreteValueSCADAMessage discreteValuesMessage = new MultipleDiscreteValueSCADAMessage(discreteModbusData); scadaPublication = new ScadaPublication(Topic.SWITCH_STATUS, discreteValuesMessage); } else { string message = $"GetIntegrityUpdate => argument topic is neither Topic.MEASUREMENT nor Topic.SWITCH_STATUS."; Logger.LogError(message); throw new ArgumentException(message); } return(scadaPublication); }
public bool EnqueueModelUpdateCommands(List <long> measurementGids) { bool success; ushort length = 6; Dictionary <long, AnalogModbusData> analogData = new Dictionary <long, AnalogModbusData>(); Dictionary <long, DiscreteModbusData> discreteData = new Dictionary <long, DiscreteModbusData>(); MeasurementsCache.Clear(); try { Dictionary <long, ISCADAModelPointItem> currentScadaModel = SCADAModel.CurrentScadaModel; foreach (long measurementGID in measurementGids) { ISCADAModelPointItem scadaPointItem = currentScadaModel[measurementGID]; IWriteModbusFunction modbusFunction; if (scadaPointItem is IAnalogSCADAModelPointItem analogSCADAModelPointItem) { modbusFunction = FunctionFactory.CreateWriteModbusFunction(new ModbusWriteCommandParameters(length, (byte)ModbusFunctionCode.WRITE_SINGLE_REGISTER, analogSCADAModelPointItem.Address, analogSCADAModelPointItem.CurrentRawValue), CommandOriginType.MODEL_UPDATE_COMMAND); AnalogModbusData analogModbusData = new AnalogModbusData(analogSCADAModelPointItem.CurrentEguValue, analogSCADAModelPointItem.Alarm, measurementGID, CommandOriginType.MODEL_UPDATE_COMMAND); analogData.Add(measurementGID, analogModbusData); } else if (scadaPointItem is IDiscreteSCADAModelPointItem discreteSCADAModelPointItem) { modbusFunction = FunctionFactory.CreateWriteModbusFunction(new ModbusWriteCommandParameters(length, (byte)ModbusFunctionCode.WRITE_SINGLE_COIL, discreteSCADAModelPointItem.Address, discreteSCADAModelPointItem.CurrentValue), CommandOriginType.MODEL_UPDATE_COMMAND); DiscreteModbusData discreteModbusData = new DiscreteModbusData(discreteSCADAModelPointItem.CurrentValue, discreteSCADAModelPointItem.Alarm, measurementGID, CommandOriginType.MODEL_UPDATE_COMMAND); discreteData.Add(measurementGID, discreteModbusData); } else { Logger.LogWarn("Unknown type of ISCADAModelPointItem."); continue; } this.modelUpdateQueue.Enqueue(modbusFunction); } MakeAnalogEntryToMeasurementCache(analogData, true); MakeDiscreteEntryToMeasurementCache(discreteData, false); success = true; this.writeCommandQueue = new ConcurrentQueue <IWriteModbusFunction>(); this.readCommandQueue = new ConcurrentQueue <IReadModbusFunction>(); this.commandEvent.Set(); } catch (Exception e) { success = false; string message = "Exception caught in EnqueueModelUpdateCommands() method."; Logger.LogError(message, e); } return(success); }
public override void Execute(ModbusClient modbusClient) { ModbusReadCommandParameters mdb_read_comm_pars = this.CommandParameters as ModbusReadCommandParameters; ushort startAddress = mdb_read_comm_pars.StartAddress; ushort quantity = mdb_read_comm_pars.Quantity; if (quantity <= 0) { string message = $"Reading Quantity: {quantity} does not make sense."; Logger.LogError(message); throw new Exception(message); } if (startAddress + quantity >= ushort.MaxValue || startAddress + quantity == ushort.MinValue || startAddress == ushort.MinValue) { string message = $"Address is out of bound. Start address: {startAddress}, Quantity: {quantity}"; Logger.LogError(message); throw new Exception(message); } int[] data = new int[0]; try { if (modbusClient.Connected) { data = modbusClient.ReadInputRegisters(startAddress - 1, quantity); } else { Logger.LogError("modbusClient is disconected "); } } catch (Exception e) { Logger.LogError("Error on ReadInputRegisters()", e); throw e; } Data = new Dictionary <long, AnalogModbusData>(data.Length); var currentAddressToGidMap = SCADAModel.CurrentAddressToGidMap; var currentSCADAModel = SCADAModel.CurrentScadaModel; var commandValuesCache = SCADAModel.CommandedValuesCache; for (ushort i = 0; i < data.Length; i++) { ushort address = (ushort)(startAddress + i); int rawValue = data[i]; //for commands enqueued during model update if (!currentAddressToGidMap[PointType.ANALOG_INPUT].ContainsKey(address)) { Logger.LogWarn($"ReadInputRegistersFunction execute => trying to read value on address {address}, Point type: {PointType.ANALOG_INPUT}, which is not in the current SCADA Model."); continue; } long gid = currentAddressToGidMap[PointType.ANALOG_INPUT][address]; //for commands enqueued during model update if (!currentSCADAModel.ContainsKey(gid)) { Logger.LogWarn($"ReadInputRegistersFunction execute => trying to read value for measurement with gid: 0x{gid:X16}, which is not in the current SCADA Model."); continue; } if (!(currentSCADAModel[gid] is AnalogSCADAModelPointItem pointItem)) { string message = $"PointItem [Gid: 0x{gid:X16}] is not type AnalogSCADAModelPointItem."; Logger.LogError(message); throw new Exception(message); } float eguValue = pointItem.RawToEguValueConversion(rawValue); if (pointItem.CurrentEguValue != eguValue) { pointItem.CurrentEguValue = eguValue; Logger.LogInfo($"Alarm for Point [Gid: 0x{pointItem.Gid:X16}, Address: {pointItem.Address}] set to {pointItem.Alarm}."); } CommandOriginType commandOrigin = CommandOriginType.OTHER_COMMAND; if (commandValuesCache.ContainsKey(gid) && commandValuesCache[gid].Value == pointItem.CurrentRawValue) { commandOrigin = commandValuesCache[gid].CommandOrigin; commandValuesCache.Remove(gid); Logger.LogDebug($"[ReadInputRegistersFunctions] Command origin of command address: {pointItem.Address} is set to {commandOrigin}."); } AnalogModbusData analogData = new AnalogModbusData(pointItem.CurrentEguValue, pointItem.Alarm, gid, commandOrigin); Data.Add(gid, analogData); //Logger.LogDebug($"ReadInputRegistersFunction execute => Current value: {pointItem.CurrentEguValue} from address: {address}, gid: 0x{gid:X16}."); } //Logger.LogDebug($"ReadInputRegistersFunction executed SUCCESSFULLY. StartAddress: {startAddress}, Quantity: {quantity}"); }
private async Task ExecuteAnalogReadCommand(ModbusFunctionCode functionCode, ushort startAddress, ushort quantity) { string verboseMessage = $"{baseLogString} entering ExecuteAnalogReadCommand method, command's functionCode: {functionCode}, startAddress: {startAddress}, quantity:{quantity}."; Logger.LogVerbose(verboseMessage); int[] data; PointType pointType; if (functionCode == ModbusFunctionCode.READ_HOLDING_REGISTERS) { verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => about to call ModbusClient.ReadHoldingRegisters({startAddress - 1}, {quantity}) method."; Logger.LogVerbose(verboseMessage); //KEY LOGIC pointType = PointType.ANALOG_OUTPUT; data = modbusClient.ReadHoldingRegisters(startAddress - 1, quantity); verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => ModbusClient.ReadHoldingRegisters({startAddress - 1}, {quantity}) method SUCCESSFULLY executed. Resulting data count: {data.Length}."; Logger.LogVerbose(verboseMessage); } else if (functionCode == ModbusFunctionCode.READ_INPUT_REGISTERS) { verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => about to call ModbusClient.ReadInputRegisters({startAddress - 1}, {quantity}) method."; Logger.LogVerbose(verboseMessage); //KEY LOGIC pointType = PointType.ANALOG_INPUT; data = modbusClient.ReadInputRegisters(startAddress - 1, quantity); verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => ModbusClient.ReadInputRegisters({startAddress - 1}, {quantity}) method SUCCESSFULLY executed. Resulting data count: {data.Length}."; Logger.LogVerbose(verboseMessage); } else { string message = $"{baseLogString} ExecuteAnalogReadCommand => function code is neither ModbusFunctionCode.READ_HOLDING_REGISTERS nor ModbusFunctionCode.READ_INPUT_REGISTERS"; Logger.LogError(message); throw new ArgumentException(message); } //this.analogMeasurementCache = new Dictionary<long, AnalogModbusData>(data.Length); this.analogMeasurementCache.Clear(); var modelReadAccessClient = ScadaModelReadAccessClient.CreateClient(); var modelUpdateAccessClient = ScadaModelUpdateAccessClient.CreateClient(); var gidToPointItemMap = await modelReadAccessClient.GetGidToPointItemMap(); var addressToGidMap = await modelReadAccessClient.GetAddressToGidMap(); var commandDescriptionCache = await modelReadAccessClient.GetCommandDescriptionCache(); for (ushort i = 0; i < data.Length; i++) { ushort address = (ushort)(startAddress + i); int rawValue = data[i]; if (!addressToGidMap.ContainsKey((short)pointType)) { Logger.LogWarning($"{baseLogString} ExecuteAnalogReadCommand => Point type: {pointType} is not in the current addressToGidMap."); continue; } //for commands enqueued during model update, that are not valid if (!addressToGidMap[(short)pointType].ContainsKey(address)) { Logger.LogWarning($"{baseLogString} ExecuteAnalogReadCommand => trying to read value on address {address}, Point type: {pointType}, which is not in the current addressToGidMap."); continue; } long gid = addressToGidMap[(short)pointType][address]; //for commands enqueued during model update, that are not valid if (!gidToPointItemMap.ContainsKey(gid)) { Logger.LogWarning($"{baseLogString} ExecuteAnalogReadCommand => trying to read value for measurement with gid: 0x{gid:X16}, which is not in the current SCADA Model."); continue; } if (!(gidToPointItemMap[gid] is IAnalogPointItem pointItem)) { string message = $"{baseLogString} ExecuteAnalogReadCommand => PointItem [Gid: 0x{gid:X16}] does not implement {typeof(IAnalogPointItem)}."; Logger.LogError(message); throw new Exception(message); } //KEY LOGIC if (pointItem.CurrentRawValue != rawValue) { pointItem = (IAnalogPointItem)(await modelUpdateAccessClient.UpdatePointItemRawValue(pointItem.Gid, rawValue)); Logger.LogInformation($"{baseLogString} ExecuteAnalogReadCommand => Alarm for Point [Gid: 0x{pointItem.Gid:X16}, Address: {pointItem.Address}] set to {pointItem.Alarm}."); } //LOGIC CommandOriginType commandOrigin = CommandOriginType.UNKNOWN_ORIGIN; if (commandDescriptionCache.ContainsKey(gid) && commandDescriptionCache[gid].Value == pointItem.CurrentRawValue) { commandOrigin = commandDescriptionCache[gid].CommandOrigin; await modelUpdateAccessClient.RemoveCommandDescription(gid); Logger.LogDebug($"{baseLogString} ExecuteAnalogReadCommand => Command origin of command address: {pointItem.Address} is set to {commandOrigin}."); //LOGIC AnalogModbusData analogData = new AnalogModbusData(pointItem.CurrentEguValue, pointItem.Alarm, gid, commandOrigin); this.analogMeasurementCache.Add(gid, analogData); verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => AnalogModbusData added to measurementCache. MeasurementGid: {analogData.MeasurementGid:X16}, Value: {analogData.Value}, Alarm: {analogData.Alarm}, CommandOrigin: {analogData.CommandOrigin} ."; Logger.LogVerbose(verboseMessage); } } //LOGIC await modelUpdateAccessClient.MakeAnalogEntryToMeasurementCache(this.analogMeasurementCache, true); verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => MakeAnalogEntryToMeasurementCache method called. measurementCache count: {this.analogMeasurementCache.Count}."; Logger.LogVerbose(verboseMessage); }
public SCADAPublication GetIntegrityUpdateForSpecificTopic(Topic topic) { if (IntegrityUpdateService.scadaModel == null) { string message = $"GetIntegrityUpdate => SCADA model is null."; Logger.LogError(message); throw new InternalSCADAServiceException(message); } SCADAPublication scadaPublication; var currentScadaModel = IntegrityUpdateService.scadaModel.CurrentScadaModel; var commandValuesCache = IntegrityUpdateService.scadaModel.CommandedValuesCache; Dictionary<long, AnalogModbusData> analogModbusData = new Dictionary<long, AnalogModbusData>(); Dictionary<long, DiscreteModbusData> discreteModbusData = new Dictionary<long, DiscreteModbusData>(); foreach (long gid in currentScadaModel.Keys) { CommandOriginType commandOrigin = CommandOriginType.OTHER_COMMAND; if (topic == Topic.MEASUREMENT && currentScadaModel[gid] is AnalogSCADAModelPointItem analogPointItem) { if (commandValuesCache.ContainsKey(gid) && commandValuesCache[gid].Value == analogPointItem.CurrentRawValue) { commandOrigin = commandValuesCache[gid].CommandOrigin; } AnalogModbusData analogValue = new AnalogModbusData(analogPointItem.CurrentEguValue, analogPointItem.Alarm, gid, commandOrigin); analogModbusData.Add(gid, analogValue); } else if (topic == Topic.SWITCH_STATUS && currentScadaModel[gid] is DiscreteSCADAModelPointItem discretePointItem) { if (commandValuesCache.ContainsKey(gid) && commandValuesCache[gid].Value == discretePointItem.CurrentValue) { commandOrigin = commandValuesCache[gid].CommandOrigin; } DiscreteModbusData discreteValue = new DiscreteModbusData(discretePointItem.CurrentValue, discretePointItem.Alarm, gid, commandOrigin); discreteModbusData.Add(gid, discreteValue); } } if(topic == Topic.MEASUREMENT) { MultipleAnalogValueSCADAMessage analogValuesMessage = new MultipleAnalogValueSCADAMessage(analogModbusData); scadaPublication = new SCADAPublication(Topic.MEASUREMENT, analogValuesMessage); } else if(topic == Topic.SWITCH_STATUS) { MultipleDiscreteValueSCADAMessage discreteValuesMessage = new MultipleDiscreteValueSCADAMessage(discreteModbusData); scadaPublication = new SCADAPublication(Topic.SWITCH_STATUS, discreteValuesMessage); } else { string message = $"GetIntegrityUpdate => argument topic is neither Topic.MEASUREMENT nor Topic.SWITCH_STATUS."; Logger.LogError(message); throw new ArgumentException(message); } return scadaPublication; }