public WriteMultipleFunction(ModbusFunctionCode functionCode, ushort startAddress, int[] commandValues, CommandOriginType commandOrigin) : base(functionCode) { StartAddress = startAddress; CommandValues = commandValues; CommandOrigin = commandOrigin; }
public AnalogModbusData(float value, AlarmType alarm, long measurementGid, CommandOriginType commandOrigin) { MeasurementGid = measurementGid; Value = value; Alarm = alarm; CommandOrigin = commandOrigin; }
public async Task <bool> EnqueuePotentialOutageCommand(long elementGid, CommandOriginType commandOriginType, NetworkType networkType) { Logger.LogDebug($"{baseLogString} EnqueuePotentialOutageCommand method started. Element gid: {elementGid}, command origin type: {commandOriginType}, network type {networkType}"); while (!ReliableDictionariesInitialized) { await Task.Delay(1000); } try { var command = new PotentialOutageCommand() { ElementGid = elementGid, CommandOriginType = commandOriginType, NetworkType = networkType, }; await PotentialOutagesQueue.EnqueueAsync(command); return(true); } catch (Exception e) { string message = "EnqueuePotentialOutageCommand => exception caught"; Logger.LogError(message, e); return(false); } }
public DiscreteModbusData(ushort value, AlarmType alarm, long measurementGid, CommandOriginType commandOrigin) { MeasurementGid = measurementGid; Value = value; Alarm = alarm; CommandOrigin = commandOrigin; }
public CommandOriginData(CommandOriginType type, UUID uuid, string requestId, long entityUniqueId) { Type = type; UUID = uuid; RequestId = requestId; EntityUniqueId = entityUniqueId; }
public void Notify(IPublishableMessage msg) { Console.WriteLine("Message from PubSub: " + msg); if (msg is MultipleAnalogValueSCADAMessage multipleAnalogValue) { foreach (long gid in multipleAnalogValue.Data.Keys) { double currentValue = multipleAnalogValue.Data[gid].Value; AlarmType alarm = multipleAnalogValue.Data[gid].Alarm; CommandOriginType commandOrigin = multipleAnalogValue.Data[gid].CommandOrigin; Console.WriteLine($"Analog => Gid: 0x{gid:X16} Value: {currentValue}, Alarm: {alarm}, Origin: {commandOrigin}"); } } else if (msg is MultipleDiscreteValueSCADAMessage multipleDiscreteValue) { foreach (long gid in multipleDiscreteValue.Data.Keys) { ushort currentValue = multipleDiscreteValue.Data[gid].Value; AlarmType alarm = multipleDiscreteValue.Data[gid].Alarm; CommandOriginType commandOrigin = multipleDiscreteValue.Data[gid].CommandOrigin; Console.WriteLine($"Discrete => Gid: 0x{gid:X16}, Value: {currentValue}, Alarm: {alarm}, Origin: {commandOrigin}"); } } else if (msg is ActiveOutageMessage activeOutage) { Console.WriteLine($"Active outage id: {activeOutage.OutageId}, affected consumers:{activeOutage.AffectedConsumers}, affected element: 0x{activeOutage.OutageElementGid:x16}"); } }
public WriteSingleFunction(ModbusFunctionCode functionCode, ushort outputAddress, int commandValue, CommandOriginType commandOrigin) : base(functionCode) { OutputAddress = outputAddress; CommandValue = commandValue; CommandOrigin = commandOrigin; }
public WriteSingleRegisterFunction(ModbusCommandParameters commandParameters, CommandOriginType commandOrigin) : base(commandParameters) { CheckArguments(MethodBase.GetCurrentMethod(), typeof(ModbusWriteCommandParameters)); CommandOrigin = commandOrigin; ModbusWriteCommandParameters = commandParameters as IModbusWriteCommandParameters; }
private void CommandToRecloser(long measurementGid, int value, CommandOriginType originType, ITopologyElement recloser) { Thread.Sleep(10000); if (!((Recloser)recloser).IsReachedMaximumOfTries()) { scadaCommanding.SendDiscreteCommand(measurementGid, value, originType); ((Recloser)recloser).NumberOfTry++; } }
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 bool ReportPotentialOutage(long elementGid, CommandOriginType commandOriginType) { bool success; try { success = Channel.ReportPotentialOutage(elementGid, commandOriginType); } catch (Exception e) { string message = "Exception in ReportPotentialOutage() proxy method."; LoggerWrapper.Instance.LogError(message, e); throw e; } return(success); }
private async Task <bool> CheckPreconditions(long elementGid, CommandOriginType commandOriginType, List <long> affectedConsumersGids, IHistoryDBManagerContract historyDBManagerClient) { if (this.ignorableCommandOriginTypes.Contains(commandOriginType)) { Logger.LogDebug($"{baseLogString} CheckPreconditions => ignorable command origin type: {commandOriginType}"); return(false); } if (await ElementsToBeIgnoredInReportPotentialOutage.ContainsKeyAsync(elementGid)) { Logger.LogWarning($"{baseLogString} CheckPreconditions => ElementGid 0x{elementGid:X16} found in '{ReliableDictionaryNames.ElementsToBeIgnoredInReportPotentialOutage}'."); if ((await ElementsToBeIgnoredInReportPotentialOutage.TryRemoveAsync(elementGid)).HasValue) { Logger.LogDebug($"{baseLogString} CheckPreconditions => ElementGid 0x{elementGid:X16} removed form '{ReliableDictionaryNames.ElementsToBeIgnoredInReportPotentialOutage}'"); } return(false); } if (await OptimumIsolationPoints.ContainsKeyAsync(elementGid)) { Logger.LogWarning($"{baseLogString} CheckPreconditions => ElementGid 0x{elementGid:X16} found in '{ReliableDictionaryNames.OptimumIsolationPoints}'."); return(false); } if (affectedConsumersGids.Count == 0) { await OnZeroAffectedConsumersCase(elementGid, historyDBManagerClient); Logger.LogWarning($"{baseLogString} ReportPotentialOutage => There is no affected consumers => outage report is not valid. ElementGid: 0x{elementGid:X16}, CommandOriginType: {commandOriginType}"); return(false); } var outageModelAccessClient = OutageModelAccessClient.CreateClient(); var activeOutages = await outageModelAccessClient.GetAllActiveOutages(); if (activeOutages.Any(active => (active.OutageState == OutageState.CREATED && active.OutageElementGid == elementGid) || (active.OutageState != OutageState.CREATED && active.DefaultIsolationPoints.Any(point => point.EquipmentId == elementGid)))) { Logger.LogWarning($"{baseLogString} ReportPotentialOutage => duplicate... ElementGID: 0x{elementGid:X16}"); return(false); } return(true); }
public bool SendDiscreteCommand(long gid, ushort commandingValue, CommandOriginType commandOriginType) { bool success; try { success = Channel.SendDiscreteCommand(gid, commandingValue, commandOriginType); } catch (Exception e) { string message = "Exception in SendDiscreteCommand() proxy method."; LoggerWrapper.Instance.LogError(message, e); throw e; } return(success); }
public bool ReportPotentialOutage(long elementGid, CommandOriginType commandOriginType) { bool result; try { outageModel.PotentialOutage.Enqueue(new Tuple <long, CommandOriginType>(elementGid, commandOriginType)); //result = reportOutageService.ReportPotentialOutage(elementGid, commandOriginType); //TODO: enum (error, noAffectedConsumers, success,...) result = true; } catch (Exception e) { result = false; string message = "ReportPotentialOutage => exception caught"; Logger.LogError(message, e); //todo throw; } return(result); }
private async Task CommandToRecloser(long measurementGid, int value, CommandOriginType originType, ITopologyElement recloser) { string verboseMessage = $"{baseLogString} CommandToRecloser method called."; Logger.LogVerbose(verboseMessage); if (recloser == null) { string message = $"{baseLogString} CommandToRecloser => NULL value has been passed instead of element."; Logger.LogError(message); throw new Exception(message); } if (!(recloser is Recloser)) { string message = $"{baseLogString} CommandToRecloser => Element with GID {recloser.Id:X16} is not a recloser."; Logger.LogError(message); throw new Exception(message); } Logger.LogDebug($"{baseLogString} CommandToRecloser => Enetring in sleep for 20 seconds."); await Task.Delay(recloserInterval); Logger.LogDebug($"{baseLogString} CommandToRecloser => Waking up after 20 seconds."); var topologyProviderClient = TopologyProviderClient.CreateClient(); int counter = await topologyProviderClient.GetRecloserCount(recloser.Id); if (((Recloser)recloser).MaxNumberOfTries > counter) { topologyProviderClient = TopologyProviderClient.CreateClient(); await topologyProviderClient.RecloserOpened(recloser.Id); Logger.LogDebug($"{baseLogString} CommandToRecloser => Calling SendDiscreteCommand method from measurement provider. Measurement GID: {measurementGid:X16}, Value: {value}, OriginType {originType}."); var measurementProviderClient = MeasurementProviderClient.CreateClient(); await measurementProviderClient.SendSingleDiscreteCommand(measurementGid, value, originType); Logger.LogDebug($"{baseLogString} CommandToRecloser => SendDiscreteCommand method has been successfully called."); } }
public void SendAnalogCommand(long measurementGid, float commandingValue, CommandOriginType commandOrigin) { try { ProxyFactory proxyFactory = new ProxyFactory(); using (SCADACommandProxy proxy = proxyFactory.CreateProxy <SCADACommandProxy, ISCADACommand>(EndpointNames.SCADACommandService)) { if (proxy == null) { string message = "SendDiscreteCommand => SCADACommandProxy is null."; logger.LogError(message); throw new NullReferenceException(message); } proxy.SendAnalogCommand(measurementGid, commandingValue, commandOrigin); } } catch (Exception) { throw; } }
private async Task <bool> CheckPreconditions(long elementGid, CommandOriginType commandOriginType, List <long> affectedConsumersGids, IHistoryDBManagerContract historyDBManagerClient) { if (this.ignorableCommandOriginTypes.Contains(commandOriginType)) { Logger.LogDebug($"{baseLogString} CheckPreconditions => ignorable command origin type: {commandOriginType}"); return(false); } var enumerableStartedAlgorithms = await StartedIsolationAlgorithms.GetEnumerableDictionaryAsync(); var enumerableOptimumIsolationPoints = await OptimumIsolationPoints.GetEnumerableDictionaryAsync(); if (enumerableStartedAlgorithms.Values.Any(algorithm => algorithm.ElementsCommandedInCurrentCycle.Contains(elementGid)) || enumerableOptimumIsolationPoints.ContainsKey(elementGid)) { Logger.LogWarning($"{baseLogString} CheckPreconditions => ElementGid 0x{elementGid:X16} found in elements commanded in current isolating algorithm cycle or in optimumIsolationPoints."); return(false); } if (affectedConsumersGids.Count == 0) { await OnZeroAffectedConsumersCase(elementGid, historyDBManagerClient); Logger.LogWarning($"{baseLogString} ReportPotentialOutage => There is no affected consumers => outage report is not valid. ElementGid: 0x{elementGid:X16}, CommandOriginType: {commandOriginType}"); return(false); } var outageModelAccessClient = OutageModelAccessClient.CreateClient(); var activeOutages = await outageModelAccessClient.GetAllActiveOutages(); if (activeOutages.Any(active => (active.OutageState == OutageState.CREATED && active.OutageElementGid == elementGid) || (active.OutageState != OutageState.CREATED && active.DefaultIsolationPoints.Any(point => point.EquipmentId == elementGid)))) { Logger.LogWarning($"{baseLogString} ReportPotentialOutage => duplicate... ElementGID: 0x{elementGid:X16}"); return(false); } return(true); }
public void SendDiscreteCommand(long measurementGid, int value, CommandOriginType commandOrigin) { try { if (Provider.Instance.MeasurementProvider.TryGetDiscreteMeasurement(measurementGid, out DiscreteMeasurement measurement) && !(measurement is ArtificalDiscreteMeasurement)) { ProxyFactory proxyFactory = new ProxyFactory(); using (SCADACommandProxy proxy = proxyFactory.CreateProxy <SCADACommandProxy, ISCADACommand>(EndpointNames.SCADACommandService)) { if (proxy == null) { string message = "SendDiscreteCommand => SCADACommandProxy is null."; logger.LogError(message); throw new NullReferenceException(message); } proxy.SendDiscreteCommand(measurementGid, (ushort)value, commandOrigin); } } else { //TOOD: DiscreteModbusData prilikom prijema sa skade prepakovati u model podataka koji ce se cuvati na CE, u prilogy AlarmType.NO_ALARM, nije validna stvar, navodi se da se //DiscreteModbusData data = new DiscreteModbusData((ushort)value, AlarmType.NO_ALARM, measurementGid, commandOrigin); Dictionary <long, DiscreteModbusData> data = new Dictionary <long, DiscreteModbusData>(1) { { measurementGid, new DiscreteModbusData((ushort)value, AlarmType.NO_ALARM, measurementGid, commandOrigin) } }; Provider.Instance.MeasurementProvider.UpdateDiscreteMeasurement(data); //Provider.Instance.MeasurementProvider.UpdateDiscreteMeasurement(data.MeasurementGid, data.Value, data.CommandOrigin); } } catch (Exception ex) { logger.LogError($"Sending discrete command for measurement with GID 0x{measurementGid.ToString("X16")} failed. Exception: {ex.Message}"); } }
public bool SendAnalogCommand(long gid, float commandingValue, CommandOriginType commandOriginType) { bool success; if (CommandService.scadaModel == null) { string message = $"SendAnalogCommand => SCADA model is null."; Logger.LogError(message); throw new InternalSCADAServiceException(message); } var currentScadaModel = CommandService.scadaModel.CurrentScadaModel; if (!currentScadaModel.ContainsKey(gid)) { string message = $"Entity with gid: 0x{gid:X16} does not exist in current SCADA model."; Logger.LogError(message); throw new ArgumentException(message); } ISCADAModelPointItem pointItem = currentScadaModel[gid]; if (pointItem is IAnalogSCADAModelPointItem analogPointItem && pointItem.RegisterType == PointType.ANALOG_OUTPUT) { try { int modbusValue = analogPointItem.EguToRawValueConversion(commandingValue); success = SendCommand(pointItem, modbusValue, commandOriginType); } catch (Exception e) { string message = $"Exception in SendAnalogCommand() method."; Logger.LogError(message, e); throw new InternalSCADAServiceException(message, e); } }
public Task <bool> SendMultipleDiscreteCommand(Dictionary <long, ushort> commandingValues, CommandOriginType commandOriginType) { return(InvokeWithRetryAsync(client => client.Channel.SendMultipleDiscreteCommand(commandingValues, commandOriginType))); }
public Task <bool> SendSingleDiscreteCommand(long gid, ushort commandingValue, CommandOriginType commandOriginType) { return(InvokeWithRetryAsync(client => client.Channel.SendSingleDiscreteCommand(gid, commandingValue, commandOriginType))); }
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 <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); }
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 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); } }
public async Task <bool> ReportPotentialOutage(long elementGid, CommandOriginType commandOriginType, NetworkType networkType) { Logger.LogVerbose($"{baseLogString} ReportPotentialOutage method started. ElementGid: 0x{elementGid:X16}, CommandOriginType: {commandOriginType}, NetworkType: {networkType}"); while (!ReliableDictionariesInitialized) { await Task.Delay(1000); } try { #region Preconditions var ceModelProviderClient = CeModelProviderClient.CreateClient(); if (await ceModelProviderClient.IsRecloser(elementGid)) { Logger.LogWarning($"{baseLogString} ReportPotentialOutage => Element with gid 0x{elementGid:X16} is a Recloser. Call to ReportPotentialOutage aborted."); return(false); } var enumerableTopology = await OutageTopologyModel.GetEnumerableDictionaryAsync(); if (!enumerableTopology.ContainsKey(ReliableDictionaryNames.OutageTopologyModel)) { Logger.LogError($"{baseLogString} ReportPotentialOutage => Topology not found in Rel Dictionary: {ReliableDictionaryNames.OutageTopologyModel}."); return(false); } var topology = enumerableTopology[ReliableDictionaryNames.OutageTopologyModel]; var affectedConsumersGids = lifecycleHelper.GetAffectedConsumers(elementGid, topology, networkType); var historyDBManagerClient = HistoryDBManagerClient.CreateClient(); if (!(await CheckPreconditions(elementGid, commandOriginType, affectedConsumersGids, historyDBManagerClient))) { Logger.LogWarning($"{baseLogString} ReportPotentialOutage => Parameters do not satisfy required preconditions. ElementId: 0x{elementGid:X16}, CommandOriginType: {commandOriginType}"); return(false); } #endregion Preconditions Logger.LogInformation($"{baseLogString} ReportPotentialOutage => Reporting outage for gid: 0x{elementGid:X16}, CommandOriginType: {commandOriginType}"); var result = await StoreActiveOutage(elementGid, affectedConsumersGids, topology); if (!result.HasValue) { Logger.LogError($"{baseLogString} ReportPotentialOutage => Storing outage on element 0x{elementGid:X16} FAILED."); return(false); } var createdOutage = result.Value; Logger.LogInformation($"{baseLogString} ReportPotentialOutage => Outage on element with gid: 0x{createdOutage.OutageElementGid:x16} is successfully stored in database."); //await historyDBManagerClient.OnSwitchOpened(elementGid, createdOutage.OutageId); //await historyDBManagerClient.OnConsumerBlackedOut(affectedConsumersGids, createdOutage.OutageId); return(await lifecycleHelper.PublishOutageAsync(Topic.ACTIVE_OUTAGE, outageMessageMapper.MapOutageEntity(createdOutage))); } catch (Exception e) { string message = $"{baseLogString} ReportPotentialOutage => exception: {e.Message}"; Logger.LogError(message, e); return(false); } }
public async Task <bool> SendMultipleScadaCommandAsync(Dictionary <long, DiscreteCommandingType> elementGidCommandMap, Dictionary <long, CommandedElement> commandedElements, CommandOriginType commandOriginType) { var measurementMapClient = MeasurementMapClient.CreateClient(); var elementToMeasurementMap = await measurementMapClient.GetElementToMeasurementMap(); var commands = new Dictionary <long, int>(); foreach (var elementGid in elementGidCommandMap.Keys) { var discreteCommandingType = elementGidCommandMap[elementGid]; int reTryCount = 30; while (commandedElements.ContainsKey(elementGid) && commandedElements[elementGid].CommandingType == discreteCommandingType) { Logger.LogDebug($"{baseLogString} SendMultipleScadaCommandAsync => Trying to send duplicate command. Entering delay for 1000 ms and retrying the call."); await Task.Delay(1000); if (--reTryCount <= 0) { Logger.LogError($"{baseLogString} SendMultipleScadaCommandAsync => Trying to send duplicate command. ReTryCount reached 60 calls."); return(false); } } if (!elementToMeasurementMap.TryGetValue(elementGid, out List <long> measurementGids) || measurementGids.Count == 0) { Logger.LogWarning($"{baseLogString} SendMultipleScadaCommandAsync => Element with gid: 0x{elementGid:X16} has no measurements."); return(false); } var measurementGid = measurementGids.FirstOrDefault(); if (measurementGid == 0) { Logger.LogWarning($"{baseLogString} SendMultipleScadaCommandAsync => Measurement gid is 0."); return(false); } commands.Add(measurementGid, (int)elementGidCommandMap[elementGid]); } var measurementProviderClient = MeasurementProviderClient.CreateClient(); return(await measurementProviderClient.SendMultipleDiscreteCommand(commands, commandOriginType)); }
public async Task <bool> SendSingleScadaCommandAsync(long breakerGid, DiscreteCommandingType discreteCommandingType, Dictionary <long, CommandedElement> commandedElements, CommandOriginType commandOriginType) { if (commandedElements.ContainsKey(breakerGid) && commandedElements[breakerGid].CommandingType == discreteCommandingType) { Logger.LogDebug($"{baseLogString} SendSingleScadaCommandAsync => Trying to send duplicate command. Aborting call."); return(false); } var measurementMapClient = MeasurementMapClient.CreateClient(); List <long> measurementGids = await measurementMapClient.GetMeasurementsOfElement(breakerGid); if (measurementGids.Count == 0) { Logger.LogWarning($"{baseLogString} SendSingleScadaCommandAsync => Element with gid: 0x{breakerGid:X16} has no measurements."); return(false); } var measurementGid = measurementGids.FirstOrDefault(); if (measurementGid == 0) { Logger.LogWarning($"{baseLogString} SendSingleScadaCommandAsync => Measurement gid is 0."); return(false); } var switchStatusCommandingClient = SwitchStatusCommandingClient.CreateClient(); bool sendCommandSuccess = false; if (discreteCommandingType == DiscreteCommandingType.OPEN) { sendCommandSuccess = await switchStatusCommandingClient.SendOpenCommand(measurementGid); } else if (discreteCommandingType == DiscreteCommandingType.CLOSE) { sendCommandSuccess = await switchStatusCommandingClient.SendCloseCommand(measurementGid); } return(sendCommandSuccess); }
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); }
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); }