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; }
private async Task UpdateMonitoredPoints(long measurementGid, Dictionary <long, MonitoredIsolationPoint> enumerableMonitoredPoints, ushort scadaDataValue) { var monitoredPoint = enumerableMonitoredPoints[measurementGid]; var monitoredPointData = monitoredPoint.DiscreteModbusData; if (scadaDataValue != monitoredPointData.Value) { var newDiscreteModbusData = new DiscreteModbusData(scadaDataValue, monitoredPointData.Alarm, monitoredPointData.MeasurementGid, monitoredPointData.CommandOrigin); monitoredPoint.DiscreteModbusData = newDiscreteModbusData; await MonitoredIsolationPoints.SetAsync(measurementGid, monitoredPoint); } }
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); } bool[] data = new bool[0]; try { if (modbusClient.Connected) { data = modbusClient.ReadDiscreteInputs(startAddress - 1, quantity); } else { Logger.LogError("modbusClient is disconected "); } } catch (Exception e) { Logger.LogError("Error on ReadDiscreteInputs()", e); throw e; } Data = new Dictionary <long, DiscreteModbusData>(data.Length); var currentSCADAModel = SCADAModel.CurrentScadaModel; var currentAddressToGidMap = SCADAModel.CurrentAddressToGidMap; var commandValuesCache = SCADAModel.CommandedValuesCache; for (ushort i = 0; i < data.Length; i++) { ushort address = (ushort)(startAddress + i); ushort value = (ushort)(data[i] ? 1 : 0); //for commands enqueued during model update if (!currentAddressToGidMap[PointType.DIGITAL_INPUT].ContainsKey(address)) { Logger.LogWarn($"ReadDiscreteInputsFunction execute => trying to read value on address {address}, Point type: {PointType.DIGITAL_INPUT}, which is not in the current SCADA Model."); continue; } long gid = currentAddressToGidMap[PointType.DIGITAL_INPUT][address]; //for commands enqueued during model update if (!currentSCADAModel.ContainsKey(gid)) { Logger.LogWarn($"ReadDiscreteInputsFunction 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 DiscreteSCADAModelPointItem pointItem)) { string message = $"PointItem [Gid: 0x{gid:X16}] is not type DiscreteSCADAModelPointItem."; Logger.LogError(message); throw new Exception(message); } if (pointItem.CurrentValue != value) { pointItem.CurrentValue = value; Logger.LogInfo($"Alarm for Point [Gid: 0x{pointItem.Gid:X16}, Point type: {PointType.DIGITAL_INPUT}, Address: {pointItem.Address}] set to {pointItem.Alarm}."); } CommandOriginType commandOrigin = CommandOriginType.OTHER_COMMAND; if (commandValuesCache.ContainsKey(gid) && commandValuesCache[gid].Value == value) { commandOrigin = commandValuesCache[gid].CommandOrigin; commandValuesCache.Remove(gid); Logger.LogDebug($"[ReadDiscreteInputsFunction] Command origin of command address: {pointItem.Address} is set to {commandOrigin}."); } DiscreteModbusData digitalData = new DiscreteModbusData(value, pointItem.Alarm, gid, commandOrigin); Data.Add(gid, digitalData); //Logger.LogDebug($"ReadDiscreteInputsFunction execute => Current value: {value} from address: {address}, point type: {PointType.DIGITAL_INPUT}, gid: 0x{gid:X16}."); } //Logger.LogDebug($"ReadDiscreteInputsFunction executed SUCCESSFULLY. StartAddress: {startAddress}, Quantity: {quantity}"); }
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); }
private async Task ExecuteDiscreteReadCommand(ModbusFunctionCode functionCode, ushort startAddress, ushort quantity) { string verboseMessage = $"{baseLogString} entering ExecuteDiscreteReadCommand method, command's functionCode: {functionCode}, startAddress: {startAddress}, quantity:{quantity}."; Logger.LogVerbose(verboseMessage); bool[] data; PointType pointType; if (functionCode == ModbusFunctionCode.READ_COILS) { verboseMessage = $"{baseLogString} ExecuteDiscreteReadCommand => about to call ModbusClient.ReadCoils({startAddress - 1}, {quantity}) method."; Logger.LogVerbose(verboseMessage); //KEY LOGIC pointType = PointType.DIGITAL_OUTPUT; data = modbusClient.ReadCoils(startAddress - 1, quantity); verboseMessage = $"{baseLogString} ExecuteDiscreteReadCommand => ModbusClient.ReadCoils({startAddress - 1}, {quantity}) method SUCCESSFULLY executed. Resulting data count: {data.Length}."; Logger.LogVerbose(verboseMessage); } else if (functionCode == ModbusFunctionCode.READ_DISCRETE_INPUTS) { verboseMessage = $"{baseLogString} ExecuteDiscreteReadCommand => about to call ModbusClient.ReadDiscreteInputs({startAddress - 1}, {quantity}) method."; Logger.LogVerbose(verboseMessage); //KEY LOGIC pointType = PointType.DIGITAL_INPUT; data = modbusClient.ReadDiscreteInputs(startAddress - 1, quantity); verboseMessage = $"{baseLogString} ExecuteDiscreteReadCommand => ModbusClient.ReadDiscreteInputs({startAddress - 1}, {quantity}) method SUCCESSFULLY executed. Resulting data count: {data.Length}."; Logger.LogVerbose(verboseMessage); } else { string errorMessage = $"{baseLogString} ExecuteDiscreteReadCommand => function code is neither ModbusFunctionCode.READ_COILS nor ModbusFunctionCode.READ_DISCRETE_INPUTS"; Logger.LogError(errorMessage); throw new ArgumentException(errorMessage); } //this.discreteMeasurementCache = new Dictionary<long, DiscreteModbusData>(data.Length); this.discreteMeasurementCache.Clear(); var modelReadAccessClient = ScadaModelReadAccessClient.CreateClient(); var modelUpdateAccessClient = ScadaModelUpdateAccessClient.CreateClient(); var currentSCADAModel = await modelReadAccessClient.GetGidToPointItemMap(); var currentAddressToGidMap = await modelReadAccessClient.GetAddressToGidMap(); var commandValuesCache = await modelReadAccessClient.GetCommandDescriptionCache(); for (ushort i = 0; i < data.Length; i++) { ushort address = (ushort)(startAddress + i); ushort value = (ushort)(data[i] ? 1 : 0); if (!currentAddressToGidMap.ContainsKey((short)pointType)) { Logger.LogWarning($"{baseLogString} ExecuteDiscreteReadCommand => Point type: {pointType} is not in the current addressToGidMap."); continue; } //for commands enqueued during model update, that are not valid if (!currentAddressToGidMap[(short)pointType].ContainsKey(address)) { Logger.LogWarning($"{baseLogString} ExecuteDiscreteReadCommand => trying to read value on address {address}, Point type: {pointType}, which is not in the current SCADA Model."); continue; } long gid = currentAddressToGidMap[(short)pointType][address]; //for commands enqueued during model update, that are not valid if (!currentSCADAModel.ContainsKey(gid)) { Logger.LogWarning($"{baseLogString} ExecuteDiscreteReadCommand => trying to read value for measurement with gid: 0x{gid:X16}, which is not in the current SCADA Model."); continue; } if (!(currentSCADAModel[gid] is IDiscretePointItem pointItem)) { string message = $"{baseLogString} ExecuteDiscreteReadCommand => PointItem [Gid: 0x{gid:X16}] does not implement {typeof(IDiscretePointItem)}."; Logger.LogError(message); throw new InternalSCADAServiceException(message); } //KEY LOGIC if (pointItem.CurrentValue != value) { pointItem = (IDiscretePointItem)(await modelUpdateAccessClient.UpdatePointItemRawValue(pointItem.Gid, value)); Logger.LogInformation($"{baseLogString} ExecuteDiscreteReadCommand => Alarm for Point [Gid: 0x{pointItem.Gid:X16}, Address: {pointItem.Address}] set to {pointItem.Alarm}."); } //LOGIC CommandOriginType commandOrigin = CommandOriginType.UNKNOWN_ORIGIN; if (commandValuesCache.ContainsKey(gid) && commandValuesCache[gid].Value == value) { commandOrigin = commandValuesCache[gid].CommandOrigin; await modelUpdateAccessClient.RemoveCommandDescription(gid); Logger.LogDebug($"{baseLogString} ExecuteDiscreteReadCommand => Command origin of command address: {pointItem.Address} is set to {commandOrigin}."); //LOGIC DiscreteModbusData digitalData = new DiscreteModbusData(value, pointItem.Alarm, gid, commandOrigin); this.discreteMeasurementCache.Add(gid, digitalData); verboseMessage = $"{baseLogString} ExecuteDiscreteReadCommand => DiscreteModbusData added to measurementCache. MeasurementGid: {digitalData.MeasurementGid:X16}, Value: {digitalData.Value}, Alarm: {digitalData.Alarm}, CommandOrigin: {digitalData.CommandOrigin} ."; Logger.LogVerbose(verboseMessage); } } //LOGIC await modelUpdateAccessClient.MakeDiscreteEntryToMeasurementCache(this.discreteMeasurementCache, true); verboseMessage = $"{baseLogString} ExecuteDiscreteReadCommand => MakeDiscreteEntryToMeasurementCache method called. measurementCache count: {this.discreteMeasurementCache.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; }