private async Task ExecuteWriteSingleCommand(IWriteSingleFunction writeCommand) { string verboseMessage = $"{baseLogString} entering ExecuteWriteSingleCommand method, command's FunctionCode: {writeCommand.FunctionCode}."; Logger.LogVerbose(verboseMessage); ushort outputAddress = writeCommand.OutputAddress; int commandValue = writeCommand.CommandValue; if (outputAddress >= ushort.MaxValue || outputAddress == ushort.MinValue) { string message = $"{baseLogString} ExecuteWriteSingleCommand => Address is out of bound. Output address: {outputAddress}."; Logger.LogError(message); throw new ArgumentException(message); } if (writeCommand.FunctionCode != ModbusFunctionCode.WRITE_SINGLE_COIL && writeCommand.FunctionCode != ModbusFunctionCode.WRITE_SINGLE_REGISTER) { string errorMessage = $"{baseLogString} ExecuteWriteSingleCommand => function code is neither ModbusFunctionCode.READ_HOLDING_REGISTERS nor ModbusFunctionCode.READ_INPUT_REGISTERS"; Logger.LogError(errorMessage); throw new ArgumentException(errorMessage); } var modelReadAccessClient = ScadaModelReadAccessClient.CreateClient(); var modelUpdateAccessClient = ScadaModelUpdateAccessClient.CreateClient(); //LOGIC var addressToGidMap = await modelReadAccessClient.GetAddressToGidMap(); var pointType = writeCommand.FunctionCode == ModbusFunctionCode.WRITE_SINGLE_COIL ? PointType.DIGITAL_OUTPUT : PointType.ANALOG_OUTPUT; if (!addressToGidMap.ContainsKey((short)pointType)) { Logger.LogWarning($"{baseLogString} ExecuteAnalogReadCommand => Point type: {pointType} is not in the current addressToGidMap."); return; } if (addressToGidMap[(short)pointType].ContainsKey(outputAddress)) { long gid = addressToGidMap[(short)pointType][outputAddress]; CommandDescription commandDescription = new CommandDescription() { Gid = gid, Address = outputAddress, Value = commandValue, CommandOrigin = writeCommand.CommandOrigin, }; string debugMessage = $"{baseLogString} ExecuteWriteSingleCommand => About to send CommandDescription to CommandDescriptionCache. Gid: {commandDescription.Gid:X16}, Address: {commandDescription.Address}, Value: {commandDescription.Value}, CommandOrigin: {commandDescription.CommandOrigin}"; Logger.LogDebug(debugMessage); //LOGIC await modelUpdateAccessClient.AddOrUpdateCommandDescription(gid, commandDescription); string infoMessage = $"{baseLogString} ExecuteWriteSingleCommand => CommandDescription sent successfuly to CommandDescriptionCache. Gid: {commandDescription.Gid:X16}, Address: {commandDescription.Address}, Value: {commandDescription.Value}, CommandOrigin: {commandDescription.CommandOrigin}"; Logger.LogInformation(infoMessage); } if (writeCommand.FunctionCode == ModbusFunctionCode.WRITE_SINGLE_COIL) { bool booleanCommand; if (commandValue == 0) { booleanCommand = false; } else if (commandValue == 1) { booleanCommand = true; } else { string errorMessage = $"{baseLogString} ExecuteWriteSingleCommand => Non-boolean value in write single coil command parameter."; Logger.LogError(errorMessage); throw new ArgumentException(errorMessage); } string debugMessage = $"{baseLogString} ExecuteWriteSingleCommand => about to call ModbusClient.WriteSingleCoil({outputAddress - 1}, {booleanCommand}) method. OutputAddress: {outputAddress}"; Logger.LogDebug(debugMessage); //KEY LOGIC modbusClient.WriteSingleCoil(outputAddress - 1, booleanCommand); string infoMessage = $"{baseLogString} ExecuteWriteSingleCommand => ModbusClient.WriteSingleCoil({outputAddress - 1}, {booleanCommand}) method SUCCESSFULLY executed. OutputAddress: {outputAddress}"; Logger.LogInformation(infoMessage); } else if (writeCommand.FunctionCode == ModbusFunctionCode.WRITE_SINGLE_REGISTER) { string debugMessage = $"{baseLogString} ExecuteWriteSingleCommand => about to call ModbusClient.WriteSingleRegister({outputAddress - 1}, {commandValue}) method. OutputAddress: {outputAddress}"; Logger.LogDebug(debugMessage); //KEY LOGIC modbusClient.WriteSingleRegister(outputAddress - 1, commandValue); string infoMessage = $"{baseLogString} ExecuteWriteSingleCommand => ModbusClient.WriteSingleRegister({outputAddress - 1}, {commandValue}) method SUCCESSFULLY executed. OutputAddress: {outputAddress}"; Logger.LogInformation(infoMessage); } }
private async Task ExecuteWriteMultipleAnalogCommand(ushort startAddress, int[] commandValues, CommandOriginType commandOrigin) { StringBuilder commandValuesSB = new StringBuilder(); commandValuesSB.Append("[ "); foreach (int value in commandValues) { commandValuesSB.Append(value); commandValuesSB.Append(" "); } commandValuesSB.Append("]"); string verboseMessage = $"{baseLogString} entering ExecuteWriteMultipleAnalogCommand method, command's startAddress: {startAddress}, commandValues: {commandValuesSB}, commandOrigin: {commandOrigin}."; Logger.LogVerbose(verboseMessage); //LOGIC var modelReadAccessClient = ScadaModelReadAccessClient.CreateClient(); var modelUpdateAccessClient = ScadaModelUpdateAccessClient.CreateClient(); int quantity = commandValues.Length; var addressToGidMap = await modelReadAccessClient.GetAddressToGidMap(); //this.commandDescriptions = new Dictionary<long, CommandDescription>(); this.commandDescriptions.Clear(); //LOGIC for (ushort index = 0; index < quantity; index++) { ushort address = (ushort)(startAddress + index); var pointType = (short)PointType.ANALOG_OUTPUT; if (!addressToGidMap.ContainsKey(pointType)) { Logger.LogWarning($"{baseLogString} ExecuteWriteMultipleAnalogCommand => Point type: {pointType} is not in the current addressToGidMap."); continue; } if (addressToGidMap[pointType].ContainsKey(address)) { long gid = addressToGidMap[pointType][address]; CommandDescription commandDescription = new CommandDescription() { Gid = gid, Address = address, Value = commandValues[index], CommandOrigin = commandOrigin, }; //LOGIC this.commandDescriptions.Add(gid, commandDescription); string message = $"{baseLogString} ExecuteWriteMultipleAnalogCommand => CommandDescription added to the collection of commandDescriptions. Gid: {commandDescription.Gid:X16}, Address: {commandDescription.Address}, Value: {commandDescription.Value}, CommandOrigin: {commandDescription.CommandOrigin}"; Logger.LogInformation(message); } } string debugMessage = $"{baseLogString} ExecuteWriteMultipleAnalogCommand => About to send collection of CommandDescriptions to CommandDescriptionCache. collection count: {commandDescriptions.Count}"; Logger.LogDebug(debugMessage); //LOGIC await modelUpdateAccessClient.AddOrUpdateMultipleCommandDescriptions(this.commandDescriptions); debugMessage = $"{baseLogString} ExecuteWriteMultipleAnalogCommand => about to call ModbusClient.WriteMultipleRegisters({startAddress - 1}, {commandValuesSB}) method. StartAddress: {startAddress}, Quantity: {quantity}"; Logger.LogDebug(debugMessage); //KEY LOGIC modbusClient.WriteMultipleRegisters(startAddress - 1, commandValues); string infoMessage = $"{baseLogString} ExecuteWriteMultipleAnalogCommand => ModbusClient.WriteMultipleRegisters() method SUCCESSFULLY executed. StartAddress: {startAddress}, Quantity: {quantity}"; Logger.LogInformation(infoMessage); }
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); }