コード例 #1
0
        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);
            }
        }
コード例 #3
0
        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}");
        }
コード例 #4
0
        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);
        }
コード例 #5
0
        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);
        }
コード例 #6
0
        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);
        }
コード例 #7
0
        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;
        }