/// <summary>
        /// получение таблицы с абонентами
        /// </summary>
        /// <returns></returns>
        private IList <Meter> GetMeters()
        {
            int totalRows     = 0;
            int processedRows = 0;

            Model.WorkTask workTask = new("обработка картотеки");
            this.workTasksProgressViewModel.WorkTasks.Add(workTask);

            // таблица с абонентами
            totalRows = this.collectionKARTAB.Length;

            System.Collections.Concurrent.ConcurrentBag <Meter> metersList = new System.Collections.Concurrent.ConcurrentBag <Meter>();

            string s = string.Empty;

            workTask.UpdateStatus($"количество строк в таблице абонентов: {totalRows:N0}");
            processedRows = 0;
            workTask.StartProcessing();

            Parallel.ForEach(this.collectionKARTAB, (abonent) =>
            {
                ICollection <KARTSCH> abonentMeters = this.GetDictionaryValue(this.dictionaryKARTSCH, abonent.LIC_SCH);

                if (abonentMeters != null && abonentMeters.Count != 0)
                {
                    var rowsGroupedByMeter = abonentMeters
                                             .GroupBy(
                        i => i.N_SCH.ToString().Trim(),
                        (meterNumber, list) => new { MeterNumber = meterNumber, List = list.ToArray() });

                    foreach (var group in rowsGroupedByMeter)
                    {
                        KARTSCH meterDataRow = group.List[0];
                        Meter meter          = this.ParseDataRowAndGetMeter(meterDataRow, abonent);
                        meter.Тарифов        = group.List.Length > 1 ? (byte)group.List.Length : (byte)1;
                        metersList.Add(meter);
                    }
                }
                else
                {
                    Meter meter   = this.ParseDataRowAndGetMeter(null, abonent);
                    meter.Тарифов = 0;
                    metersList.Add(meter);
                }

                workTask.UpdateUI(++processedRows, totalRows);
            });

            // fix
            workTask.UpdateUI(totalRows, totalRows);

            return(this.SortData(metersList, workTask));
        }
        private Meter ParseDataRowAndGetMeter(KARTSCH meterData, KARTAB abonent)
        {
            Meter meter = new();

            try
            {
                #region Контактные данные

                meter.Удалён = abonent.IsDeleted;

                meter.Фамилия  = abonent.FAM;
                meter.Имя      = abonent.NAME;
                meter.Отчество = abonent.OTCH;

                meter.SMS      = abonent.SMS;
                meter.Телефоны = abonent.TELEF;

                if (this.dictionaryKartTn.ContainsKey(abonent.COD_TN))
                {
                    KartTn town     = this.dictionaryKartTn[abonent.COD_TN];
                    string townName = town.TOWN;
                    string province = NOTFOUNDED;
                    if (this.dictionaryKartSs.ContainsKey(town.COD_SS))
                    {
                        province = this.dictionaryKartSs[town.COD_SS].СЕЛЬСОВЕТ;

                        if (string.IsNullOrEmpty(province) && townName != null && townName.StartsWith("г.", AppSettings.StringComparisonMethod))
                        {
                            province = "город";
                        }
                    }
                    else
                    {
                        if (string.Equals(province, NOTFOUNDED) && townName != null && townName.StartsWith("г.", AppSettings.StringComparisonMethod))
                        {
                            province = "город";
                        }
                    }

                    KartSt street = this.GetDictionaryValue(this.dictionaryKartSt, abonent.COD_ST);

                    Address address = new(
                        townName,
                        street?.STREET,
                        abonent.HOME,
                        abonent.KV,
                        province);
                    meter.Адрес = address;
                }
                else
                {
                    string code = abonent.COD_TN;
                    this.errors.Add($"Не найден населенный пункт с кодом {code}");
                }

                #endregion

                #region Абонент

                meter.Категория = this.GetDictionaryValue(this.dictionaryKartKat, abonent.COD_KAT)?.KATEGAB;

                meter.Коментарий = abonent.KOMENT;

                meter.ДатаУведомления = ConvertToDateOnly(abonent.ДАТА_ОТКПЛ);

                meter.ДатаОтключения         = ConvertToDateOnly(abonent.ДАТА_ОТКФК);
                meter.ПоказаниеПриОтключении = ConvertToUInt(abonent.ПОКАЗАНИЯ);

                meter.Задолженник = abonent.PR_ZD;

                meter.КолвоЧеловек = ConvertToByte(abonent.ЧЛЕНОВ);

                #endregion

                #region Счётчик

                meter.ШифрТарифа = abonent.ШИФР;
                meter.Контролёр  = this.GetDictionaryValue(this.dictionaryASKONTR, abonent.КОД_КОН)?.ФАМИЛИЯ;

                #region Оплата

                meter.ПериодПослОплаты = ConvertToDateOnly(abonent.YEARMON);
                meter.Среднее          = abonent.СРЕДНЕЕ;
                meter.Месяц            = abonent.МЕСЯЦ;

                meter.ДатаОплаты  = ConvertToDateOnly(abonent.DATE_R);
                meter.СуммаОплаты = ConvertToFloat(abonent.SUMMA_KN + abonent.SUMMA_KC);

                meter.ДолгРуб = ConvertToFloat(abonent.ERRSUM);

                meter.ErrSumN = ConvertToFloat(abonent.ERRSUMN);
                meter.ErrSumV = ConvertToFloat(abonent.ERRSUMV);

                meter.ДатаОбхода = ConvertToDateOnly(abonent.DATE_KON);

                #endregion

                #region Счётчик-признаки

                meter.асположение   = this.GetDictionaryValue(this.dictionaryKartTpr, abonent.COD_TPR)?.TPRIEM;
                meter.Использование = this.GetDictionaryValue(this.dictionaryKartIsp, abonent.COD_ISP)?.ISPIEM;

                #endregion

                #region Привязка

                if (this.dictionaryKartfid.ContainsKey(abonent.FIDER10))
                {
                    Kartfid fider10 = this.dictionaryKartfid[abonent.FIDER10];
                    string  s       = fider10.ФИДЕР;
                    meter.Фидер10    = string.IsNullOrWhiteSpace(s) ? string.Empty : fider10.НАИМЕНОВ + "-" + s;
                    meter.Подстанция = this.GetDictionaryValue(this.dictionaryKartps, fider10.ПОДСТАНЦИЯ)?.НАИМЕНОВ;
                }
                else
                {
                    this.errors.Add($"Не найдена информация по фидеру 10 кВ: л/с {meter.Лицевой}, код фидера {abonent.FIDER10}");
                }

                if (int.TryParse(abonent.НОМЕР_ТП, out int n) == false && abonent.IsDeleted == false)
                {
                    this.errors.Add($"Ошибка в номере ТП (поле НОМЕР_ТП) '{abonent.НОМЕР_ТП}' (Лицевой счет абонента={abonent.LIC_SCH})");
                }

                if (this.dictionaryKartktp.ContainsKey(n))
                {
                    string nameTp   = this.GetDictionaryValue(this.dictionaryKartktp, n)?.НАИМ_ТП;
                    int?   numberTp = this.GetDictionaryValue(this.dictionaryKartktp, n)?.НОМЕР_ТП;
                    string ss       = this.GetDictionaryValue(this.dictionaryKartktp, n)?.PR_GS;
                    meter.ТП = new TransformerSubstation(nameTp, numberTp.GetValueOrDefault(), ss);
                }

                meter.Фидер04 = ConvertToByte(abonent.ФИДЕР);
                meter.Опора   = abonent.НОМЕР_ОПОР;

                #endregion

                #region Признаки

                string priznak = string.IsNullOrWhiteSpace(abonent.PRIZNAK) ? string.Empty : abonent.PRIZNAK;
                if (priznak.Length is > 0 and not 15)
                {
                    System.Diagnostics.Debugger.Break();
                }

                BitArray bitArray = new BitArray(15);
                for (byte index = 0; index < priznak.Length; index++)
                {
                    char bit = priznak[index];

                    if (bit == '1')
                    {
                        meter.Signs = (MeterSigns)((int)meter.Signs | (1 << index));
                    }
                }

                meter.Договор      = string.IsNullOrWhiteSpace(abonent.DOG) == false && (abonent.DOG == "1" || abonent.DOG == "д");
                meter.ДатаДоговора = abonent.DATE_DOG.HasValue ? ConvertToDateOnly(abonent.DATE_DOG) : null;

                meter.ПринадлежностьРуп = abonent.PR_MO;
                meter.Льгота            = (ushort)abonent.ЛЬГОТА;

                meter.аботникБелЭнерго = abonent.RABOT;

                #endregion

                #region Счёт

                meter.Лицевой    = ConvertToULong(abonent.LIC_SCH);
                meter.ЛицевойЖКХ = ConvertToULong(abonent.GKH_L1);

                #endregion

                if (meterData != null)
                {
                    KARTTSCH meterInfo = this.GetDictionaryValue(this.dictionaryKARTTSCH, meterData.COD_TSCH);
                    if (meterInfo != null)
                    {
                        meter.ТипСчетчика   = meterInfo.NAME;
                        meter.Ампераж       = meterInfo.TOK;
                        meter.ПериодПоверки = ConvertToByte(meterInfo.PERIOD, 0, 1, 0);
                        meter.Фаз           = (byte)meterInfo.ФАЗ;
                        meter.Принцип       = meterInfo.TIP == "И" ? "индукционный" : (meterInfo.TIP == "Э" ? "электронный" : "неизвестный");
                        meter.азрядность    = ConvertToByte(meterInfo.ЗНАК);
                    }

                    meter.ПоследнееОплаченноеПоказание = ConvertToUInt(meterData.DATA_NEW);
                    meter.ПредыдущеОплаченноеПоказание = ConvertToUInt(meterData.DATA_OLD);

                    meter.асчПоказание        = abonent.RACHPOK;
                    meter.ПослПоказаниеОбхода = ConvertToUInt(abonent.DATA_KON);

                    meter.НомерСчетчика = meterData.N_SCH;
                    string god = meterData.GODVYPUSKA.ToString();
                    meter.ГодВыпуска = ConvertToUShort(god);

                    meter.ДатаУстановки         = ConvertToDateOnly(meterData.DUSTAN);
                    meter.ПоказаниеПриУстановке = ConvertToUInt(meterData.PUSTAN);

                    meter.НомераПломб = meterData.N_PLOMB + "; " + meterData.PLOMB_GS;
                    meter.Мощность    = ConvertToFloat(meterData.POWERS);

                    #region Счётчик-признаки

                    meter.МестоУстановки = this.GetDictionaryValue(this.dictionaryASVIDYST, meterData.COD_SS)?.MESTO;
                    meter.Аскуэ          = abonent.ASKUE;

                    #endregion

                    #region Поверка

                    meter.КварталПоверки = ConvertToByte(meterData.G_PROV, 0, 1, 1);
                    meter.ГодПоверки     = ConvertToByte(meterData.G_PROV, 2, 2, 0);

                    #endregion
                }
                else
                {
                    meter.ТипСчетчика    = UNKNOWN_STR;
                    meter.Ампераж        = UNKNOWN_STR;
                    meter.ПериодПоверки  = 0;
                    meter.Фаз            = 1;
                    meter.Принцип        = UNKNOWN_STR;
                    meter.НомерСчетчика  = UNKNOWN_STR;
                    meter.МестоУстановки = UNKNOWN_STR;
                }

                #endregion
            }
            catch (Exception ex)
            {
                logger?.Error($">>> TMP.WORK.AramisChetchiki.Repository>GetSelectedDepartamentData->AbonentsTable\n{meterData}\n>>>: {TMPApp.GetExceptionDetails(ex)}");
            }

            return(meter);
        }
        /// <summary>
        /// Парсинг записи о замене счетчика
        /// </summary>
        /// <param name="assmena"></param>
        /// <returns></returns>
        private ChangeOfMeter ParseChangesOfMetersRecord(Assmena assmena)
        {
            ChangeOfMeter change = new();

            try
            {
                KARTTSCH meterType = this.GetDictionaryValue(this.dictionaryKARTTSCH, assmena.ТИП_СЧЕТЧ);
                ICollection <KARTSCH> meterInfos = this.dictionaryKARTSCH[assmena.ЛИЦ_СЧЕТ];

                KARTAB abonent            = new KARTAB();
                IEnumerable <KARTAB> list = this.collectionKARTAB.Where(i => i.LIC_SCH == assmena.ЛИЦ_СЧЕТ && i.IsDeleted == false);
                if (list.Any())
                {
                    if (list.Count() > 1)
                    {
                        ;
                    }

                    abonent = this.collectionKARTAB.Where(i => i.LIC_SCH == assmena.ЛИЦ_СЧЕТ && i.IsDeleted == false).First();
                }
                else
                {
                    return(null);
                }

                ICollection <KARTSCH> old_meter = this.GetDictionaryValue(this.dictionaryKARTSCHRemoved, assmena.ЛИЦ_СЧЕТ);

                change.ТипСнятогоСчетчика = meterType == null ? NOTFOUNDED : meterType.NAME;
                change.СнятЭлектронный    = meterType == null ? false : meterType.TIP == "Э";

                if (meterInfos != null && meterInfos.Count != 0)
                {
                    KARTSCH meterInfo1 = meterInfos.First();
                    change.ГодВыпускаУстановленного = ConvertToUShort(meterInfo1.GODVYPUSKA);

                    KARTTSCH meterInfos1 = this.GetDictionaryValue(this.dictionaryKARTTSCH, meterInfo1.COD_TSCH);
                    if (meterInfos1 != null)
                    {
                        change.ТипУстановленногоСчетчика = meterInfos1.NAME;
                        change.УстановленЭлектронный     = meterInfos1.TIP == "Э";

                        change.азрядностьУстановленного = (byte)meterInfos1.ЗНАК;
                    }
                }

                if (old_meter != null && old_meter.Count != 0)
                {
                    KARTSCH  meterInfo3  = old_meter.First();
                    KARTTSCH meterInfos3 = this.GetDictionaryValue(this.dictionaryKARTTSCH, meterInfo3.COD_TSCH);

                    change.КварталПоверкиСнятого = ConvertToByte(meterInfo3.G_PROV, 0, 1, 1);
                    change.ГодПоверкиСнятого     = ConvertToByte(meterInfo3.G_PROV, 2, 2, 0);
                    change.ГодВыпускаСнятого     = ConvertToUShort(meterInfo3.GODVYPUSKA);
                    change.ДатаУстановкиСнятого  = ConvertToDateOnly(meterInfo3.DATE_UST);

                    if (meterInfos3 != null)
                    {
                        change.азрядностьСнятого = (byte)meterInfos3.ЗНАК;
                    }
                }

                KartTn town     = this.dictionaryKartTn[abonent.COD_TN];
                string townName = town.TOWN;
                string province = NOTFOUNDED;
                if (this.dictionaryKartSs.ContainsKey(town.COD_SS))
                {
                    province = this.dictionaryKartSs[town.COD_SS].СЕЛЬСОВЕТ;

                    if (string.IsNullOrEmpty(province) && townName != null && townName.StartsWith("г.", AppSettings.StringComparisonMethod))
                    {
                        province = "город";
                    }
                }
                else
                {
                    if (string.Equals(province, NOTFOUNDED) && townName != null && townName.StartsWith("г.", AppSettings.StringComparisonMethod))
                    {
                        province = "город";
                    }
                }

                KartSt street = this.GetDictionaryValue(this.dictionaryKartSt, abonent.COD_ST);

                change.Адрес = new Address(
                    townName,
                    street?.STREET,
                    abonent.HOME,
                    abonent.KV,
                    province);

                string name = abonent.NAME;
                string otch = abonent.OTCH;
                change.Фио = abonent.FAM + " " + (string.IsNullOrEmpty(name) ? string.Empty : name[0] + ".") + (string.IsNullOrEmpty(otch) ? string.Empty : otch[0] + ".");
                change.Фио.Trim();

                if (this.dictionaryKartfid.ContainsKey(abonent.FIDER10))
                {
                    Kartfid fider10 = this.dictionaryKartfid[abonent.FIDER10];
                    string  s       = fider10.ФИДЕР;
                    change.Фидер10    = string.IsNullOrWhiteSpace(s) ? string.Empty : fider10.НАИМЕНОВ + "-" + s;
                    change.Подстанция = this.GetDictionaryValue(this.dictionaryKartps, fider10.ПОДСТАНЦИЯ)?.НАИМЕНОВ;
                }

                if (int.TryParse(abonent.НОМЕР_ТП, out int n) == false)
                {
                    this.errors.Add($"Ошибка в номере ТП (поле НОМЕР_ТП) '{abonent.НОМЕР_ТП}' (Лицевой счет абонента={abonent.LIC_SCH})");
                }

                if (this.dictionaryKartktp.ContainsKey(n) == false)
                {
                    this.errors.Add($"Справочник ТП не содержит записи (поле НОМЕР_ТП) с номером ТП '{n}' (Лицевой счет абонента={abonent.LIC_SCH})");
                }
                else
                {
                    string typeTp   = this.dictionaryKartktp[n].НАИМ_ТП;
                    string nameTp   = this.dictionaryKartktp[n].PR_GS;
                    int?   numberTp = this.dictionaryKartktp[n].НОМЕР_ТП;
                    change.НомерТП        = numberTp.GetValueOrDefault().ToString();
                    change.НаименованиеТП = typeTp + " " + nameTp;
                }

                change.Фидер04 = ConvertToByte(abonent.ФИДЕР);
                change.Опора   = abonent.НОМЕР_ОПОР;

                change.Лицевой = ConvertToULong(assmena.ЛИЦ_СЧЕТ);
                change.НомерСнятогоСчетчика = assmena.НОМЕР_СНЯТ;
                change.ПоказаниеСнятого     = ConvertToUInt(assmena.ПОКАЗ_СНЯТ);

                change.НомерУстановленногоСчетчика = assmena.НОМЕР_УСТ;
                change.LastReading = ConvertToUInt(assmena.ПОКАЗ_УСТ);
                change.ДатаЗамены  = ConvertToDateOnly(assmena.ДАТА_ЗАМЕН);
                change.НомерАкта   = ConvertToUInt(assmena.НОМЕР_АКТА);
                change.Фамилия     = assmena.ФАМИЛИЯ;
                change.Причина     = assmena.ПРИЧИНА;
            }
            catch (Exception ex)
            {
                logger?.Error($">>> ChangesOfMeters ParseRecord >>>: {TMPApp.GetExceptionDetails(ex)}");
            }

            return(change);
        }