/// <summary> /// This is the main entry point for your service instance. /// </summary> /// <param name="cancellationToken">Canceled when Service Fabric needs to shut down this service instance.</param> protected override async Task RunAsync(CancellationToken cancellationToken) { AcquisitionCycle acquisitionCycle; IScadaConfigData configData; cancellationToken.ThrowIfCancellationRequested(); try { acquisitionCycle = new AcquisitionCycle(); var readAccessClient = ScadaModelReadAccessClient.CreateClient(); configData = await readAccessClient.GetScadaConfigData(); string message = $"{baseLogString} RunAsync => AcquisitionCycle initialized."; Logger.LogInformation(message); } catch (Exception e) { string errMessage = $"{baseLogString} RunAsync => Exception caught: {e.Message}."; Logger.LogError(errMessage, e); throw e; } int acquisitionCycleCount = 0; while (true) { string message = $"{baseLogString} RunAsync => AcquisitionCycleCount: {acquisitionCycleCount}"; if (acquisitionCycleCount % 100 == 0) { Logger.LogInformation(message); } else if (acquisitionCycleCount % 10 == 0) { Logger.LogDebug(message); } else { Logger.LogVerbose(message); } try { await acquisitionCycle.Start(); message = $"{baseLogString} RunAsync => AcquisitionCycle executed."; Logger.LogVerbose(message); } catch (Exception e) { string errMessage = $"{baseLogString} RunAsync => Exception caught: {e.Message}"; Logger.LogError(errMessage, e); } await Task.Delay(TimeSpan.FromMilliseconds(configData.AcquisitionInterval), cancellationToken); acquisitionCycleCount++; } }
/// <summary> /// This is the main entry point for your service instance. /// </summary> /// <param name="cancellationToken">Canceled when Service Fabric needs to shut down this service instance.</param> protected override async Task RunAsync(CancellationToken cancellationToken) { IScadaConfigData configData; try { InitializeReliableCollections(); string debugMessage = $"{baseLogString} RunAsync => ReliableDictionaries initialized."; Logger.LogDebug(debugMessage); IScadaModelReadAccessContract readAccessClient = ScadaModelReadAccessClient.CreateClient(); configData = await readAccessClient.GetScadaConfigData(); string infoMessage = $"{baseLogString} RunAsync => FunctionExecutorCycle initialized."; Logger.LogInformation(infoMessage); } catch (Exception e) { string errorMessage = $"{baseLogString} RunAsync => exception {e.Message}"; Logger.LogError(errorMessage, e); throw e; } var functionExecutionCycleCount = 0; while (true) { string message = $"{baseLogString} RunAsync => FunctionExecutionCycleCount: {functionExecutionCycleCount}"; if (functionExecutionCycleCount % 100 == 0) { Logger.LogInformation(message); } else if (functionExecutionCycleCount % 10 == 0) { Logger.LogDebug(message); } else { Logger.LogVerbose(message); } try { await functionExecutorCycle.Start(); string verboseMessage = $"{baseLogString} RunAsync => FunctionExecutorCycle executed."; Logger.LogVerbose(verboseMessage); } catch (Exception e) { string errorMessage = $"{baseLogString} RunAsync => exception {e.Message}"; Logger.LogError(errorMessage, e); } await Task.Delay(TimeSpan.FromMilliseconds(configData.FunctionExecutionInterval), cancellationToken); functionExecutionCycleCount++; } }
public async Task <bool> SendMultipleAnalogCommand(Dictionary <long, float> commandingValues, CommandOriginType commandOriginType) { if (commandingValues.Count == 0) { string warnMessage = $"{baseLogString} SendMultipleAnalogCommand => commandingValues is empty and thus aborting the call."; Logger.LogWarning(warnMessage); return(false); } #region Log commanding values var sb = new StringBuilder(); foreach (var kvp in commandingValues) { sb.AppendLine($"Gid: 0x{kvp.Key:X16} | Value: {kvp.Value}"); } Logger.LogInformation($"{baseLogString} SendMultipleAnalogCommand => Origin: {commandOriginType} Values {Environment.NewLine}{sb}"); #endregion Log commanding values ushort startAddress = 1; //EasyModbus spec IScadaModelReadAccessContract scadaModelReadAccessClient = ScadaModelReadAccessClient.CreateClient(); Dictionary <long, IScadaModelPointItem> gidToPointItemMap = await scadaModelReadAccessClient.GetGidToPointItemMap(); Dictionary <short, Dictionary <ushort, long> > addressToGidMap = await scadaModelReadAccessClient.GetAddressToGidMap(); if (gidToPointItemMap == null) { string message = $"{baseLogString} SendMultipleAnalogCommand => SCADA model is null."; Logger.LogError(message); return(false); } int analogOutputCount = addressToGidMap[(short)PointType.ANALOG_OUTPUT].Count; int[] multipleCommandingValues = new int[addressToGidMap[(short)PointType.ANALOG_OUTPUT].Count]; foreach (ushort address in addressToGidMap[(short)PointType.ANALOG_OUTPUT].Keys) { long gid = addressToGidMap[(short)PointType.ANALOG_OUTPUT][address]; if (!gidToPointItemMap.ContainsKey(gid)) { string message = $"{baseLogString} SendMultipleAnalogCommand => Entity with gid: 0x{gid:X16} does not exist in current SCADA model."; Logger.LogError(message); return(false); } else if (!(gidToPointItemMap[gid] is IAnalogPointItem analogPointItem)) { string message = $"{baseLogString} SendMultipleAnalogCommand => Entity with gid: 0x{gid:X16} does not implement IAnalogPointItem interface."; Logger.LogError(message); return(false); }
public async Task Start() { string verboseMessage = $"{baseLogString} entering Start method."; Logger.LogVerbose(verboseMessage); try { var commandEnqueuerClient = ReadCommandEnqueuerClient.CreateClient(); var readAccessClient = ScadaModelReadAccessClient.CreateClient(); verboseMessage = $"{baseLogString} Start => Trying to get AddressToGidMap."; Logger.LogVerbose(verboseMessage); //LOGIC addressToGidMap.Clear(); addressToGidMap = await readAccessClient.GetAddressToGidMap(); verboseMessage = $"{baseLogString} Start => AddressToGidMap received, Count: {addressToGidMap.Count}."; Logger.LogVerbose(verboseMessage); foreach (var kvp in addressToGidMap) { if ((PointType)kvp.Key == PointType.HR_LONG) { continue; } verboseMessage = $"{baseLogString} Start => AddressToGidMap value for key {kvp.Key} is dictionary with Count: {kvp.Value.Count}."; Logger.LogVerbose(verboseMessage); if (TryCreateModbusFunction(kvp, out IReadModbusFunction modbusFunction)) { //KEY LOGIC await commandEnqueuerClient.EnqueueReadCommand(modbusFunction); verboseMessage = $"{baseLogString} Start => Modbus function enquided. Point type is {kvp.Key}, FunctionCode: {modbusFunction.FunctionCode}, StartAddress: {modbusFunction.StartAddress}, Quantity: {modbusFunction.Quantity}."; Logger.LogVerbose(verboseMessage); } } } catch (Exception e) { string message = $"{baseLogString} Start => Exception caught."; Logger.LogError(message, e); throw e; } }
public async Task <bool> SendMultipleAnalogCommand(Dictionary <long, float> commandingValues, CommandOriginType commandOriginType) { if (commandingValues.Count == 0) { string warnMessage = $"{baseLogString} SendMultipleAnalogCommand => commandingValues is empty and thus aborting the call."; Logger.LogWarning(warnMessage); return(false); } ushort startAddress = 1; //EasyModbus spec IScadaModelReadAccessContract scadaModelReadAccessClient = ScadaModelReadAccessClient.CreateClient(); Dictionary <long, IScadaModelPointItem> gidToPointItemMap = await scadaModelReadAccessClient.GetGidToPointItemMap(); Dictionary <short, Dictionary <ushort, long> > addressToGidMap = await scadaModelReadAccessClient.GetAddressToGidMap(); if (gidToPointItemMap == null) { string message = $"{baseLogString} SendMultipleAnalogCommand => SCADA model is null."; Logger.LogError(message); //throw new InternalSCADAServiceException(message); return(false); } int analogOutputCount = addressToGidMap[(short)PointType.ANALOG_OUTPUT].Count; int[] multipleCommandingValues = new int[addressToGidMap[(short)PointType.ANALOG_OUTPUT].Count]; //for (ushort address = 1; address <= analogOutputCount; address++) //{ foreach (ushort address in addressToGidMap[(short)PointType.ANALOG_OUTPUT].Keys) { long gid = addressToGidMap[(short)PointType.ANALOG_OUTPUT][address]; if (!gidToPointItemMap.ContainsKey(gid)) { string message = $"{baseLogString} SendMultipleAnalogCommand => Entity with gid: 0x{gid:X16} does not exist in current SCADA model."; Logger.LogError(message); //throw new ArgumentException(message); return(false); } else if (!(gidToPointItemMap[gid] is IAnalogPointItem analogPointItem)) { string message = $"{baseLogString} SendMultipleAnalogCommand => Entity with gid: 0x{gid:X16} does not implement IAnalogPointItem interface."; Logger.LogError(message); //throw new InternalSCADAServiceException(message); return(false); }
private async Task InitializeModbusClient() { try { var modelReadAccessClient = ScadaModelReadAccessClient.CreateClient(); this.configData = await modelReadAccessClient.GetScadaConfigData(); this.modbusClient = new ModbusClient(configData.IpAddress.ToString(), configData.TcpPort); } catch (Exception e) { string message = $"{baseLogString} InitializeModbusClient => Exception: {e.Message}."; Logger.LogError(message, e); } if (modbusClient == null) { string message = $"{baseLogString} InitializeModbusClient => ModbusClient is null"; Logger.LogError(message); throw new Exception(message); } }
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 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 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 async Task <bool> SendSingleAnalogCommand(long gid, float commandingValue, CommandOriginType commandOriginType) { string verboseMessage = $"{baseLogString} SendSingleAnalogCommand method called. gid: {gid:X16}, commandingValue: {commandingValue}, commandOriginType: {commandOriginType}"; Logger.LogVerbose(verboseMessage); IScadaModelReadAccessContract scadaModelReadAccessClient = ScadaModelReadAccessClient.CreateClient(); Dictionary <long, IScadaModelPointItem> gidToPointItemMap = await scadaModelReadAccessClient.GetGidToPointItemMap(); if (gidToPointItemMap == null) { string message = $"{baseLogString} SendSingleAnalogCommand => SendSingleAnalogCommand => SCADA model is null."; Logger.LogError(message); //throw new InternalSCADAServiceException(message); return(false); } if (!gidToPointItemMap.ContainsKey(gid)) { string message = $"{baseLogString} SendSingleAnalogCommand => Entity with gid: 0x{gid:X16} does not exist in current SCADA model."; Logger.LogError(message); //throw new ArgumentException(message); return(false); } IScadaModelPointItem pointItem = gidToPointItemMap[gid]; if (!(pointItem is IAnalogPointItem analogPointItem && pointItem.RegisterType == PointType.ANALOG_OUTPUT)) { string message = $"{baseLogString} SendSingleAnalogCommand => Either RegistarType of entity with gid: 0x{gid:X16} is not ANALOG_OUTPUT or entity does not implement IAnalogPointItem interface."; Logger.LogError(message); //throw new ArgumentException(message); return(false); } try { if (!analogPointItem.Initialized) { string errorMessage = $"{baseLogString} SendSingleAnalogCommand => PointItem was initialized. Gid: 0x{analogPointItem.Gid:X16}, Addres: {analogPointItem.Address}, Name: {analogPointItem.Name}, RegisterType: {analogPointItem.RegisterType}, Initialized: {analogPointItem.Initialized}"; Logger.LogError(errorMessage); } //LOGIC int modbusValue = analogPointItem.EguToRawValueConversion(commandingValue); string debugMessage = $"{baseLogString} SendSingleAnalogCommand => Calling SendSingleCommand({pointItem}, {modbusValue}, {commandOriginType})"; Logger.LogDebug(verboseMessage); //KEY LOGIC await SendSingleCommand(pointItem, modbusValue, commandOriginType); debugMessage = $"{baseLogString} SendSingleAnalogCommand => SendSingleCommand() executed SUCCESSFULLY"; Logger.LogDebug(debugMessage); return(true); } catch (Exception e) { string message = $"{baseLogString} SendSingleAnalogCommand => Exception in SendAnalogCommand() method."; Logger.LogError(message, e); //throw new InternalSCADAServiceException(message, e); return(false); } }