private async Task SendModelUpdateCommands()
        {
            var scadaCommandingClient           = ScadaCommandingClient.CreateClient();
            var enumerableAddressToGidMapResult = await AddressToGidMap.GetEnumerableDictionaryAsync();

            var tasks = new List <Task>()
            {
                Task.Run(async() =>
                {
                    var key = (short)PointType.ANALOG_OUTPUT;
                    if (!enumerableAddressToGidMapResult.ContainsKey(key))
                    {
                        return;
                    }

                    var analogItemsAddressToGidMap = enumerableAddressToGidMapResult[key];
                    var analogCommandingValues     = new Dictionary <long, float>(analogItemsAddressToGidMap.Count);

                    foreach (long gid in analogItemsAddressToGidMap.Values)
                    {
                        var result          = await GidToPointItemMap.TryGetValueAsync(gid);
                        var analogPointItem = result.Value as IAnalogPointItem;

                        analogCommandingValues.Add(gid, analogPointItem.CurrentEguValue);
                    }

                    if (analogCommandingValues.Count > 0)
                    {
                        await scadaCommandingClient.SendMultipleAnalogCommand(analogCommandingValues, CommandOriginType.MODEL_UPDATE_COMMAND);
                    }
                }),

                Task.Run(async() =>
                {
                    var key = (short)PointType.DIGITAL_OUTPUT;
                    if (!enumerableAddressToGidMapResult.ContainsKey(key))
                    {
                        return;
                    }

                    var discreteItemsAddressToGidMap = enumerableAddressToGidMapResult[key];
                    var discreteCommandingValues     = new Dictionary <long, ushort>(discreteItemsAddressToGidMap.Count);

                    foreach (long gid in discreteItemsAddressToGidMap.Values)
                    {
                        var result            = await GidToPointItemMap.TryGetValueAsync(gid);
                        var discretePointItem = result.Value as IDiscretePointItem;

                        discreteCommandingValues.Add(gid, discretePointItem.CurrentValue);
                    }

                    if (discreteCommandingValues.Count > 0)
                    {
                        await scadaCommandingClient.SendMultipleDiscreteCommand(discreteCommandingValues, CommandOriginType.MODEL_UPDATE_COMMAND);
                    }
                }),
            };

            Task.WaitAll(tasks.ToArray());
        }
        public async Task <bool> ImportModel(bool isRetry = false)
        {
            bool success;

            await GidToPointItemMap.ClearAsync();

            var enumerableCurrentAddressToGidMap = await AddressToGidMap.GetEnumerableDictionaryAsync();

            foreach (var key in enumerableCurrentAddressToGidMap.Keys)
            {
                var dictionary = enumerableCurrentAddressToGidMap[key];
                dictionary.Clear();

                await AddressToGidMap.SetAsync(key, dictionary);
            }

            string message = $"{baseLogString} ImportModel => Importing analog measurements started...";

            Logger.LogInformation(message);

            bool analogImportSuccess = await ImportAnalog();

            message = $"{baseLogString} ImportModel =>Importing analog measurements finished. ['success' value: {analogImportSuccess}]";
            Logger.LogInformation(message);

            message = $"{baseLogString} ImportModel => Importing discrete measurements started...";
            Logger.LogInformation(message);

            bool discreteImportSuccess = await ImportDiscrete();

            message = $"{baseLogString} ImportModel => Importing discrete measurements finished. ['success' value: {discreteImportSuccess}]";
            Logger.LogInformation(message);

            success = analogImportSuccess && discreteImportSuccess;

            if (!success)
            {
                await GidToPointItemMap.ClearAsync();

                enumerableCurrentAddressToGidMap = await AddressToGidMap.GetEnumerableDictionaryAsync();

                foreach (var key in enumerableCurrentAddressToGidMap.Keys)
                {
                    var dictionary = enumerableCurrentAddressToGidMap[key];
                    dictionary.Clear();

                    await AddressToGidMap.SetAsync(key, dictionary);
                }
            }

            return(success);
        }
        public async Task <Dictionary <short, Dictionary <ushort, long> > > GetAddressToGidMap()
        {
            string verboseMessage = $"{baseLogString} entering GetAddressToGidMap method.";

            Logger.LogVerbose(verboseMessage);

            while (!ReliableDictionariesInitialized)
            {
                await Task.Delay(1000);
            }

            verboseMessage = $"{baseLogString} GetAddressToGidMap => about to execut AddressToGidMap.GetDataCopy().";
            Logger.LogVerbose(verboseMessage);

            var copy = await AddressToGidMap.GetDataCopyAsync();

            verboseMessage = $"{baseLogString} GetAddressToGidMap => AddressToGidMap.GetDataCopy() SUCCESSFULLY executed. Returning the collection with {copy.Count} elements.";
            Logger.LogVerbose(verboseMessage);

            return(copy);
        }
        private async Task <bool> ImportDiscrete()
        {
            bool             success;
            int              numberOfResources = 1000;
            List <ModelCode> props             = modelResourceDesc.GetAllPropertyIds(ModelCode.DISCRETE);

            try
            {
                var nmsGdaClient = NetworkModelGdaClient.CreateClient();
                int iteratorId   = await nmsGdaClient.GetExtentValues(ModelCode.DISCRETE, props);

                int resourcesLeft = await nmsGdaClient.IteratorResourcesLeft(iteratorId);

                while (resourcesLeft > 0)
                {
                    List <ResourceDescription> rds = await nmsGdaClient.IteratorNext(numberOfResources, iteratorId);

                    for (int i = 0; i < rds.Count; i++)
                    {
                        if (rds[i] == null)
                        {
                            continue;
                        }

                        long      gid  = rds[i].Id;
                        ModelCode type = modelResourceDesc.GetModelCodeFromId(gid);

                        DiscretePointItem discretePoint = new DiscretePointItem(AlarmConfigDataHelper.GetAlarmConfigData());

                        string debugMessage = $"{baseLogString} ImportDiscrete => Before Initialization => Gid: 0x{discretePoint.Gid:X16}, Address: {discretePoint.Address}, CurrentValue: {discretePoint.CurrentValue}, Alarm: {discretePoint.Alarm}, AbnormalValue: {discretePoint.AbnormalValue}, DiscreteType: {discretePoint.DiscreteType}, MinValue: {discretePoint.MinValue}, MaxValue: {discretePoint.MaxValue}, NormalValue: {discretePoint.NormalValue}, RegisterType: {discretePoint.RegisterType}, Name: {discretePoint.Name}, Initialized: {discretePoint.Initialized}";
                        Logger.LogDebug(debugMessage);

                        pointItemHelper.InitializeDiscretePointItem(discretePoint, rds[i].Properties, ModelCode.DISCRETE, enumDescs);

                        debugMessage = $"{baseLogString} ImportDiscrete => After Initialization => Gid: 0x{discretePoint.Gid:X16}, Address: {discretePoint.Address}, CurrentValue: {discretePoint.CurrentValue}, Alarm: {discretePoint.Alarm}, AbnormalValue: {discretePoint.AbnormalValue}, DiscreteType: {discretePoint.DiscreteType}, MinValue: {discretePoint.MinValue}, MaxValue: {discretePoint.MaxValue}, NormalValue: {discretePoint.NormalValue}, RegisterType: {discretePoint.RegisterType}, Name: {discretePoint.Name}, Initialized: {discretePoint.Initialized}";
                        Logger.LogDebug(debugMessage);

                        if (await GidToPointItemMap.ContainsKeyAsync(gid))
                        {
                            string errorMessage = $"{baseLogString} ImportDiscrete => SCADA model is invalid => Gid: 0x{gid:X16} belongs to more than one entity.";
                            Logger.LogError(errorMessage);
                            throw new InternalSCADAServiceException(errorMessage);
                        }

                        await GidToPointItemMap.SetAsync(gid, discretePoint);

#if DEBUG
                        var pointItemResult = await GidToPointItemMap.TryGetValueAsync(gid);

                        if (pointItemResult.HasValue)
                        {
                            DiscretePointItem controlPointItem = pointItemResult.Value as DiscretePointItem;
                            debugMessage = $"{baseLogString} ImportDiscrete => Control after CurrentGidToPointItemMap.SetAsync => Gid: 0x{controlPointItem.Gid:X16}, Address: {controlPointItem.Address}, CurrentValue: {controlPointItem.CurrentValue}, Alarm: {controlPointItem.Alarm}, AbnormalValue: {controlPointItem.AbnormalValue}, DiscreteType: {controlPointItem.DiscreteType}, MinValue: {controlPointItem.MinValue}, MaxValue: {controlPointItem.MaxValue}, NormalValue: {controlPointItem.NormalValue}, RegisterType: {controlPointItem.RegisterType}, Name: {controlPointItem.Name}, Initialized: {controlPointItem.Initialized}";
                            Logger.LogDebug(debugMessage);
                        }
                        else
                        {
                            string warningMessage = $"{baseLogString} ImportDiscrete => Control after CurrentGidToPointItemMap.SetAsync => Gid: 0x{gid:X16} was not found in reliable collection '{ReliableDictionaryNames.GidToPointItemMap}' after the value was supposedly set.";
                            Logger.LogWarning(warningMessage);
                        }
#endif
                        short registerType = (short)discretePoint.RegisterType;
                        if (!(await AddressToGidMap.ContainsKeyAsync(registerType)))
                        {
                            await AddressToGidMap.SetAsync(registerType, new Dictionary <ushort, long>());
                        }

                        var addressToGidDictionaryResult = await AddressToGidMap.TryGetValueAsync(registerType);

                        if (!addressToGidDictionaryResult.HasValue)
                        {
                            string message = $"{baseLogString} ImportDiscrete => reliable collection '{ReliableDictionaryNames.AddressToGidMap}' is not initialized properly.";
                            Logger.LogError(message);
                            throw new InternalSCADAServiceException(message);
                        }

                        var addressToGidDictionary = addressToGidDictionaryResult.Value;

                        if (addressToGidDictionary.ContainsKey(discretePoint.Address))
                        {
                            string errorMessage = $"{baseLogString} ImportDiscrete => SCADA model is invalid => Address: {discretePoint.Address} (RegType: {registerType}) belongs to more than one entity.";
                            Logger.LogError(errorMessage);
                            throw new InternalSCADAServiceException(errorMessage);
                        }

                        addressToGidDictionary.Add(discretePoint.Address, rds[i].Id);
                        await AddressToGidMap.SetAsync(registerType, addressToGidDictionary);

                        debugMessage = $"{baseLogString} ImportDiscrete => ANALOG measurement added to SCADA model [Gid: 0x{gid:X16}, Address: {discretePoint.Address}]";
                        Logger.LogDebug(debugMessage);
                    }

                    resourcesLeft = await nmsGdaClient.IteratorResourcesLeft(iteratorId);
                }

                await nmsGdaClient.IteratorClose(iteratorId);

                success = true;
            }
            catch (Exception ex)
            {
                success = false;
                string errorMessage = $"{baseLogString} ImportDiscrete => failed with error: {ex.Message}";
                Console.WriteLine(errorMessage);
                Logger.LogError(errorMessage, ex);
            }

            return(success);
        }