Пример #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;
        }
        public async Task <ScadaPublication> GetIntegrityUpdateForSpecificTopic(Topic topic)
        {
            while (!ReliableDictionariesInitialized)
            {
                await Task.Delay(1000);
            }

            if (GidToPointItemMap == null)
            {
                string message = $"GetIntegrityUpdate => GidToPointItemMap is null.";
                Logger.LogError(message);
                throw new InternalSCADAServiceException(message);
            }

            if (CommandDescriptionCache == null)
            {
                string message = $"GetIntegrityUpdate => CommandDescriptionCache is null.";
                Logger.LogError(message);
                throw new InternalSCADAServiceException(message);
            }

            Dictionary <long, AnalogModbusData>   analogModbusData   = new Dictionary <long, AnalogModbusData>();
            Dictionary <long, DiscreteModbusData> discreteModbusData = new Dictionary <long, DiscreteModbusData>();

            var enumerableGidToPointItemMap = await GidToPointItemMap.GetEnumerableDictionaryAsync();

            foreach (long gid in enumerableGidToPointItemMap.Keys)
            {
                CommandOriginType commandOrigin = CommandOriginType.UNKNOWN_ORIGIN;

                if (topic == Topic.MEASUREMENT && enumerableGidToPointItemMap[gid] is IAnalogPointItem analogPointItem)
                {
                    var result = await CommandDescriptionCache.TryGetValueAsync(gid);

                    if (result.HasValue && result.Value.Value == analogPointItem.CurrentRawValue)
                    {
                        commandOrigin = result.Value.CommandOrigin;
                    }

                    AnalogModbusData analogValue = new AnalogModbusData(analogPointItem.CurrentEguValue, analogPointItem.Alarm, gid, commandOrigin);
                    analogModbusData.Add(gid, analogValue);
                }
                else if (topic == Topic.SWITCH_STATUS && enumerableGidToPointItemMap[gid] is IDiscretePointItem discretePointItem)
                {
                    var result = await CommandDescriptionCache.TryGetValueAsync(gid);

                    if (result.HasValue && result.Value.Value == discretePointItem.CurrentValue)
                    {
                        commandOrigin = result.Value.CommandOrigin;
                    }


                    DiscreteModbusData discreteValue = new DiscreteModbusData(discretePointItem.CurrentValue, discretePointItem.Alarm, gid, commandOrigin);
                    discreteModbusData.Add(gid, discreteValue);
                }
            }

            ScadaPublication scadaPublication;

            if (topic == Topic.MEASUREMENT)
            {
                MultipleAnalogValueSCADAMessage analogValuesMessage = new MultipleAnalogValueSCADAMessage(analogModbusData);
                scadaPublication = new ScadaPublication(Topic.MEASUREMENT, analogValuesMessage);
            }
            else if (topic == Topic.SWITCH_STATUS)
            {
                MultipleDiscreteValueSCADAMessage discreteValuesMessage = new MultipleDiscreteValueSCADAMessage(discreteModbusData);
                scadaPublication = new ScadaPublication(Topic.SWITCH_STATUS, discreteValuesMessage);
            }
            else
            {
                string message = $"GetIntegrityUpdate => argument topic is neither Topic.MEASUREMENT nor Topic.SWITCH_STATUS.";
                Logger.LogError(message);
                throw new ArgumentException(message);
            }

            return(scadaPublication);
        }
        public bool EnqueueModelUpdateCommands(List <long> measurementGids)
        {
            bool   success;
            ushort length = 6;

            Dictionary <long, AnalogModbusData>   analogData   = new Dictionary <long, AnalogModbusData>();
            Dictionary <long, DiscreteModbusData> discreteData = new Dictionary <long, DiscreteModbusData>();

            MeasurementsCache.Clear();

            try
            {
                Dictionary <long, ISCADAModelPointItem> currentScadaModel = SCADAModel.CurrentScadaModel;

                foreach (long measurementGID in measurementGids)
                {
                    ISCADAModelPointItem scadaPointItem = currentScadaModel[measurementGID];
                    IWriteModbusFunction modbusFunction;

                    if (scadaPointItem is IAnalogSCADAModelPointItem analogSCADAModelPointItem)
                    {
                        modbusFunction = FunctionFactory.CreateWriteModbusFunction(new ModbusWriteCommandParameters(length,
                                                                                                                    (byte)ModbusFunctionCode.WRITE_SINGLE_REGISTER,
                                                                                                                    analogSCADAModelPointItem.Address,
                                                                                                                    analogSCADAModelPointItem.CurrentRawValue),
                                                                                   CommandOriginType.MODEL_UPDATE_COMMAND);

                        AnalogModbusData analogModbusData = new AnalogModbusData(analogSCADAModelPointItem.CurrentEguValue,
                                                                                 analogSCADAModelPointItem.Alarm,
                                                                                 measurementGID,
                                                                                 CommandOriginType.MODEL_UPDATE_COMMAND);
                        analogData.Add(measurementGID, analogModbusData);
                    }
                    else if (scadaPointItem is IDiscreteSCADAModelPointItem discreteSCADAModelPointItem)
                    {
                        modbusFunction = FunctionFactory.CreateWriteModbusFunction(new ModbusWriteCommandParameters(length,
                                                                                                                    (byte)ModbusFunctionCode.WRITE_SINGLE_COIL,
                                                                                                                    discreteSCADAModelPointItem.Address,
                                                                                                                    discreteSCADAModelPointItem.CurrentValue),
                                                                                   CommandOriginType.MODEL_UPDATE_COMMAND);

                        DiscreteModbusData discreteModbusData = new DiscreteModbusData(discreteSCADAModelPointItem.CurrentValue,
                                                                                       discreteSCADAModelPointItem.Alarm,
                                                                                       measurementGID,
                                                                                       CommandOriginType.MODEL_UPDATE_COMMAND);
                        discreteData.Add(measurementGID, discreteModbusData);
                    }
                    else
                    {
                        Logger.LogWarn("Unknown type of ISCADAModelPointItem.");
                        continue;
                    }

                    this.modelUpdateQueue.Enqueue(modbusFunction);
                }

                MakeAnalogEntryToMeasurementCache(analogData, true);
                MakeDiscreteEntryToMeasurementCache(discreteData, false);

                success = true;
                this.writeCommandQueue = new ConcurrentQueue <IWriteModbusFunction>();
                this.readCommandQueue  = new ConcurrentQueue <IReadModbusFunction>();
                this.commandEvent.Set();
            }
            catch (Exception e)
            {
                success = false;
                string message = "Exception caught in EnqueueModelUpdateCommands() method.";
                Logger.LogError(message, e);
            }

            return(success);
        }
        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);
            }

            int[] data = new int[0];

            try
            {
                if (modbusClient.Connected)
                {
                    data = modbusClient.ReadInputRegisters(startAddress - 1, quantity);
                }
                else
                {
                    Logger.LogError("modbusClient is disconected ");
                }
            }
            catch (Exception e)
            {
                Logger.LogError("Error on ReadInputRegisters()", e);
                throw e;
            }

            Data = new Dictionary <long, AnalogModbusData>(data.Length);

            var currentAddressToGidMap = SCADAModel.CurrentAddressToGidMap;
            var currentSCADAModel      = SCADAModel.CurrentScadaModel;
            var commandValuesCache     = SCADAModel.CommandedValuesCache;

            for (ushort i = 0; i < data.Length; i++)
            {
                ushort address  = (ushort)(startAddress + i);
                int    rawValue = data[i];

                //for commands enqueued during model update
                if (!currentAddressToGidMap[PointType.ANALOG_INPUT].ContainsKey(address))
                {
                    Logger.LogWarn($"ReadInputRegistersFunction execute => trying to read value on address {address}, Point type: {PointType.ANALOG_INPUT}, which is not in the current SCADA Model.");
                    continue;
                }

                long gid = currentAddressToGidMap[PointType.ANALOG_INPUT][address];

                //for commands enqueued during model update
                if (!currentSCADAModel.ContainsKey(gid))
                {
                    Logger.LogWarn($"ReadInputRegistersFunction 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 AnalogSCADAModelPointItem pointItem))
                {
                    string message = $"PointItem [Gid: 0x{gid:X16}] is not type AnalogSCADAModelPointItem.";
                    Logger.LogError(message);
                    throw new Exception(message);
                }

                float eguValue = pointItem.RawToEguValueConversion(rawValue);
                if (pointItem.CurrentEguValue != eguValue)
                {
                    pointItem.CurrentEguValue = eguValue;
                    Logger.LogInfo($"Alarm for Point [Gid: 0x{pointItem.Gid:X16}, Address: {pointItem.Address}] set to {pointItem.Alarm}.");
                }

                CommandOriginType commandOrigin = CommandOriginType.OTHER_COMMAND;

                if (commandValuesCache.ContainsKey(gid) && commandValuesCache[gid].Value == pointItem.CurrentRawValue)
                {
                    commandOrigin = commandValuesCache[gid].CommandOrigin;
                    commandValuesCache.Remove(gid);
                    Logger.LogDebug($"[ReadInputRegistersFunctions] Command origin of command address: {pointItem.Address} is set to {commandOrigin}.");
                }

                AnalogModbusData analogData = new AnalogModbusData(pointItem.CurrentEguValue, pointItem.Alarm, gid, commandOrigin);
                Data.Add(gid, analogData);
                //Logger.LogDebug($"ReadInputRegistersFunction execute => Current value: {pointItem.CurrentEguValue} from address: {address}, gid: 0x{gid:X16}.");
            }

            //Logger.LogDebug($"ReadInputRegistersFunction executed SUCCESSFULLY. StartAddress: {startAddress}, Quantity: {quantity}");
        }
        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);
        }
Пример #6
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;
        }