Exemple #1
0
        public static byte[] OpenCnlReq(int devAddr, string uroven, string pass)
        {
            byte[] temp_pass = new byte[4];
            byte[] buf_pass  = Encoding.ASCII.GetBytes(pass);

            for (int f = 0; f < 6; f++)
            {
                Array.Copy(buf_pass, f, temp_pass, 0, 1);
                int temp_int = BitConverter.ToInt32(temp_pass, 0) - 48;
                temp_pass = BitConverter.GetBytes(temp_int);
                Array.Copy(temp_pass, 0, buf_pass, f, 1);
            }

            byte[] openCnl = new byte[11];
            openCnl[0] = (byte)devAddr;
            openCnl[1] = 0x01;                                      //команда запроса на открытие канала
            openCnl[2] = Convert.ToByte(Convert.ToInt16(uroven));   // Ввод уровня доступа пока без проверки, по умолчанию 1.... 0x01; //Уровень доступа 1

            Array.Copy(buf_pass, 0, openCnl, 3, 6);

            res = CrcFunc.CalcCRC16(openCnl, 9);                //получить контрольную сумму
            openCnl[openCnl.Length - 2] = (byte)(res % 256);    //Добавить контрольную сумму к буферу посылки
            openCnl[openCnl.Length - 1] = (byte)(res / 256);
            return(openCnl);
        }
Exemple #2
0
        public static byte[] DataReq(int devAddr, string Param, int bwri)
        {
            byte[] data  = new byte[6];
            int    bwrim = bwri & 0xf0;

            data[0] = (byte)devAddr;
            data[1] = 0x08;                               //команда чтения зафиксированных данных
            if (Param == "14h")
            {
                data[2] = 0x14;
                data[3] = (byte)bwri;                    //параметр зафиксированных данных
            }
            else
            {
                if (bwrim == 0xf0)
                {
                    data[1] = 0x05;                        // Переход на функцию 0x05 для чтения энергии от сброса при использовании чтения счетчика кодом 0x08 и параметром 0x16
                    data[2] = 0x00;
                    data[3] = Convert.ToByte(bwri & 0x0f); // Запись # тарифа
                }
                else
                {
                    data[2] = 0x16;
                    data[3] = Convert.ToByte(bwri);         // Запись BWRI кода
                }
            }
            res = CrcFunc.CalcCRC16(data, 4);               //получить контрольную сумму
            data[data.Length - 2] = (byte)(res % 256);      //Добавить контрольную сумму к буферу посылки
            data[data.Length - 1] = (byte)(res / 256);
            return(data);
        }
Exemple #3
0
        public static byte[] ReadRomReq(int devAddr, int energy, int numRom, int startAddr, int Quantity)
        {
            byte NumRom = 0;
            byte Energy = 0;

            byte[] readrom = new byte[8];
            if (numRom == 3)
            {
                Energy = (byte)((energy & 0x07) << 4);
                NumRom = startAddr > 0xffff ? (byte)((numRom & 0x0f) | 0x80) : (byte)(numRom & 0x0f);
            }
            else
            {
                Energy = 0x00;
            }
            readrom[0] = (byte)devAddr;
            readrom[1] = 0x06;                                  // 2.4 Ускоренный режим чтения по физическим адресам памяти

            readrom[2] = (byte)(NumRom | Energy);               // Вид энергии, номер памяти
            readrom[3] = (byte)(startAddr / 256);               // Старший байт адреса                            TEST
            readrom[4] = (byte)(startAddr % 256);               // Младший байт адреса                            TEST
            readrom[5] = (byte)Quantity;                        // количество байт                                TEST
            res        = CrcFunc.CalcCRC16(readrom, 6);         //получить контрольную сумму
            readrom[readrom.Length - 2] = (byte)(res % 256);    //Добавить контрольную сумму к буферу посылки
            readrom[readrom.Length - 1] = (byte)(res / 256);
            return(readrom);
        }
Exemple #4
0
        public static ushort res;  //резервирование ответа контрольной суммы

        public static byte[] TestCnlReq(int devAddr)
        {
            byte[] testCnl = new byte[4];
            testCnl[0] = (byte)devAddr;
            testCnl[1] = 0x00;                                  //команда запроса на тестирование канала
            res        = CrcFunc.CalcCRC16(testCnl, 2);         //получить контрольную сумму
            testCnl[testCnl.Length - 2] = (byte)(res % 256);    //Добавить контрольную сумму к буферу посылки
            testCnl[testCnl.Length - 1] = (byte)(res / 256);
            return(testCnl);
        }
Exemple #5
0
 public static byte[] KuiReq(int devAddr)
 {
     byte[] kui = new byte[5];
     kui[0] = (byte)devAddr;
     kui[1] = 0x08;                              // 2.3 Запрос на чтение параметров
     kui[2] = 0x02;                              // 2.3.3 Прочитать коэффициент трансформации счетчика
     res    = CrcFunc.CalcCRC16(kui, 3);         //получить контрольную сумму
     kui[kui.Length - 2] = (byte)(res % 256);    //Добавить контрольную сумму к буферу посылки
     kui[kui.Length - 1] = (byte)(res / 256);
     return(kui);
 }
Exemple #6
0
 public static byte[] FixDataReq(int devAddr)
 {
     byte[] fixData = new byte[5];
     fixData[0] = (byte)devAddr;
     fixData[1] = 0x03;                                  //команда записи
     fixData[2] = 0x08;                                  //параметр фиксации данных
     res        = CrcFunc.CalcCRC16(fixData, 3);         //получить контрольную сумму
     fixData[fixData.Length - 2] = (byte)(res % 256);    //Добавить контрольную сумму к буферу посылки
     fixData[fixData.Length - 1] = (byte)(res / 256);
     return(fixData);
 }
Exemple #7
0
 public static byte[] CurTimeReq(int devAddr)
 {
     byte[] curtime = new byte[5];
     curtime[0] = (byte)devAddr;
     curtime[1] = 0x04;                                  // 2.1 Запросы на чтение массивов времен (код 0x04)
     curtime[2] = 0x00;                                  // Запрос на чтение текущего времени (параметр 0x00)
     res        = CrcFunc.CalcCRC16(curtime, 3);         //получить контрольную сумму
     curtime[curtime.Length - 2] = (byte)(res % 256);    //Добавить контрольную сумму к буферу посылки
     curtime[curtime.Length - 1] = (byte)(res / 256);
     return(curtime);
 }
Exemple #8
0
 public static byte[] InfoReq(int devAddr)
 {
     byte[] info = new byte[5];
     info[0] = (byte)devAddr;
     info[1] = 0x08;                             // 2.3.2 Ускоренный режим чтения индивидуальных параметров
     info[2] = 0x01;                             // Серийный номер, дата выпуска, версия ПО, вариант исполнения
     res     = CrcFunc.CalcCRC16(info, 3);       //получить контрольную сумму
     info[info.Length - 2] = (byte)(res % 256);  //Добавить контрольную сумму к буферу посылки
     info[info.Length - 1] = (byte)(res / 256);
     return(info);
 }
Exemple #9
0
 public static byte[] EnergyPReq(int devAddr, int tarif)
 {
     byte[] energy = new byte[6];
     energy[0] = (byte)devAddr;
     energy[1] = 0x05;                                   // 2.2 Запросы на чтение массивов регистров накопленной энергии
     energy[2] = 0x60;                                   // Параметр чтения накопленной энергии A+ от сброса по фазам
     energy[3] = (byte)tarif;                            // Номер тарифа
     res       = CrcFunc.CalcCRC16(energy, 4);           //получить контрольную сумму
     energy[energy.Length - 2] = (byte)(res % 256);      //Добавить контрольную сумму к буферу посылки
     energy[energy.Length - 1] = (byte)(res / 256);
     return(energy);
 }
Exemple #10
0
        /// <summary>
        /// Отправка команды без параметров
        /// </summary>
        /// <param name="devAddr"></param>
        /// <param name="Com"></param>
        /// <param name="Data"></param>
        /// <returns></returns>
        public static byte[] WriteComReq(int devAddr, int Com, byte[] Data = null)
        {
            int cnt = 2;

            if (Data != null)
            {
                cnt = 2 + Data.Length;
            }
            byte[] com = new byte[cnt + 2];
            com[0] = (byte)devAddr;
            com[1] = (byte)Com;                             // Отправка команды без параметров
            if (Data != null)
            {
                Array.Copy(Data, 0, com, 2, Data.Length);   // Копируем блок данных при его наличии
            }
            res = CrcFunc.CalcCRC16(com, cnt);              // получить контрольную сумму
            com[com.Length - 2] = (byte)(res % 256);        // добавить контрольную сумму к буферу посылки
            com[com.Length - 1] = (byte)(res / 256);
            return(com);
        }
Exemple #11
0
        /// <summary>
        /// Отправка команды с параметрами
        /// </summary>
        /// <param name="devAddr"></param>
        /// <param name="Com"></param>
        /// <param name="Par"></param>
        /// <param name="Data"></param>
        /// <returns></returns>
        public static byte[] WriteCompReq(int devAddr, int Com, int Par, byte[] Data = null) //, bool dataYes = false
        {
            int cnt = 3;

            if (Data != null)
            {
                cnt = 3 + Data.Length;
            }
            byte[] comp = new byte[cnt + 2];
            comp[0] = (byte)devAddr;
            comp[1] = (byte)Com;                            // Отправка команды c параметром
            comp[2] = (byte)Par;                            // Отправка команды c параметром
            if (Data != null)
            {
                Array.Copy(Data, 0, comp, 3, Data.Length);  // Копируем блок данных при его наличии
            }
            res = CrcFunc.CalcCRC16(comp, cnt);             // получить контрольную сумму
            comp[comp.Length - 2] = (byte)(res % 256);      // добавить контрольную сумму к буферу посылки
            comp[comp.Length - 1] = (byte)(res / 256);
            return(comp);
        }
Exemple #12
0
        public override void SendCmd(Command cmd)
        {
            base.SendCmd(cmd);
            lastCommSucc = false;

            bool WriteOk = false;               // Идентификатор успешной записи

            mask_ch_wr  = 0;                    // переменная для параметра MASK_CH записи данных каналов (Регистратор импульсов)
            mask_chv_wr = 0;                    // Переменная для параметра MASK_CH записи Веса импульсов (Регистратор импульсов)

            byte cmdCode = 0x00;                // переменная для байта запроса CmdCode - параметр F протокола (номера для записи)

            byte[] byteData = new byte[1];      // Буфер для значения переменной
            double cmdVal   = cmd.CmdVal;
            int    cmdNum   = cmd.CmdNum;

            int cmdCnl = ActiveCmd[cmdNum];     // Чтение индекса команды по ключу из Словаря

            if (cmd.CmdTypeID == BaseValues.CmdTypes.Standard)
            {
                byte[] cmdcode = ScadaUtils.HexToBytes(devTemplate.CmdGroups[cmdCnl].CmdCode, true); // Чтение строки HEX из параметра CmdCode
                cmdCode = cmdcode[0];

                string cmdtype = devTemplate.CmdGroups[cmdCnl].CmdType; // Чтение строки Типа переменной команды

                // Определив диапазон проверяем к какому из них относятся Текущие параметры и Веса импульса для составления маски

                if ((cmdNum >= startCnl && cmdNum <= maxch) || (cmdNum >= startCnlv && cmdNum <= maxchv))
                {
                    if ((cmdNum >= startCnl && cmdNum <= maxch) && !(cmdNum >= startCnlv && cmdNum <= maxchv))
                    {
                        mask_ch_wr = BitFunc.SetBit(mask_ch_wr, cmdNum - startCnl, true);       // Если каналы относятся к Текущим данным, то формируем маску для  записи маски текущих данных
                    }
                    else
                    {
                        mask_chv_wr = BitFunc.SetBit(mask_chv_wr, cmdNum - startCnlv, true);    // Иначе для записи маски Весов импульсов
                    }
                }
                if (cmdtype == "uint16")
                {
                    Array.Resize(ref byteData, 2);
                    byteData = BitConverter.GetBytes(Convert.ToUInt16(cmdVal));
                }
                else if (cmdtype == "float")
                {
                    Array.Resize(ref byteData, 4);
                    byteData = BitConverter.GetBytes(Convert.ToSingle(cmdVal));
                }
                else if (cmdtype == "double")
                {
                    Array.Resize(ref byteData, 8);
                    byteData = BitConverter.GetBytes(cmdVal);
                }
                else if (cmdtype == "DateTime")
                {
                    Array.Resize(ref byteData, 6);
                    DateTime dateTime = DateTime.FromOADate(cmdVal);
                    byteData[0] = Convert.ToByte(dateTime.Year - 2000);
                    byteData[1] = Convert.ToByte(dateTime.Month);
                    byteData[2] = Convert.ToByte(dateTime.Day);
                    byteData[3] = Convert.ToByte(dateTime.Hour);
                    byteData[4] = Convert.ToByte(dateTime.Minute);
                    byteData[5] = Convert.ToByte(dateTime.Second);
                }

                if (cmdCode == 0x0B)
                {
                    Array.Resize(ref byteData, 8);                                                                                     // Увеличить размер буфера до 8 байт записываемого параметра F=0x0B PARAM_VAL_NEW
                }
                Buf_Out(cmdCnl, cmdCode, byteData, false);                                                                             // отправить в функцию Номер индекса команды управления и Байт запроса

                Connection.Write(buf_out, 0, buf_out.Length, CommUtils.ProtocolLogFormats.Hex, out logText);                           //послать запрос в порт
                ExecWriteToLog(logText);                                                                                               // вывести запрос в Журнал линии связи

                readcnt = Connection.Read(buf_in, 0, buf_in.Length, ReqParams.Timeout, CommUtils.ProtocolLogFormats.Hex, out logText); //считать значение из порта
                ExecWriteToLog(logText);                                                                                               // вывести запрос в Журнал линии связи

                // Проверка выполнения команды прибором - определяется по ответу прибора на запись команды

                if (readcnt == buf_in.Length || readcnt == 11)
                {
                    crc = CrcFunc.CalcCRC16(buf_in, readcnt);           // Рассчет CRC16 полученного ответа, при совпадении должно вернуть 0 при расчете CRC16(Modbus) и полного буфера вместе с CRC
                    byte fCode = buf_in[4];                             // Чтение кода команды F
                    Array.Copy(buf_in, readcnt - 4, byteIDres, 0, 2);

                    if (!(crc == 0 & fCode != 0 & byteID.SequenceEqual(byteIDres)))                    // Проверка CRC, параметра F и ID запроса
                    {
                        if (crc != 0)
                        {
                            ExecWriteToLog(CommPhrases.ResponseCrcError);
                        }
                        else if (fCode == 0)
                        {
                            string err = Error_code(buf_in[6]);
                            ExecWriteToLog(CommPhrases.IncorrectCmdData + " - " + err);                // При некорректном запросе F будет равен 0x00
                        }
                        else if (!byteID.SequenceEqual(byteIDres))
                        {
                            ExecWriteToLog("ID ответа не совпадает с ID запроса");                     // При несовпадении ID
                        }
                        FinishRequest();
                    }
                    else
                    {
                        if (fCode == 0x03 || fCode == 0x08)
                        {
                            byte[] maskchRes = new byte[4];
                            Array.Copy(buf_in, 6, maskchRes, 0, 4);
                            if (maskch.SequenceEqual(maskchRes))
                            {
                                WriteOk = true;
                            }
                        }
                        if (fCode == 0x05)
                        {
                            if (buf_in[6] != 0)
                            {
                                WriteOk = true;
                            }
                        }
                        if (fCode == 0x0B)
                        {
                            UInt16 Result_WR = BitConverter.ToUInt16(buf_in, 6);
                            if (Result_WR == 0)
                            {
                                WriteOk = true;
                            }
                        }

                        if (WriteOk)
                        {
                            lastCommSucc = true;
                            string nameCnl = ActiveCnl.Find(c => c.Cnl == cmdNum).Name;
                            ExecWriteToLog($"Запись команды {nameCnl} - " + CommPhrases.ResponseOK);
                        }
                        else
                        {
                            ExecWriteToLog(CommPhrases.WriteDataError);
                        }
                        FinishRequest();
                    }
                }
                else
                {
                    if (readcnt == 0)
                    {
                        ExecWriteToLog(CommPhrases.ResponseError);              // Нет ответа по Timeout - Ошибка связи!
                    }
                    else
                    {
                        ExecWriteToLog(CommPhrases.IncorrectResponseLength);    // Некорректная длина ответа
                    }
                    FinishRequest();
                }
            }
            else
            {
                WriteToLog(CommPhrases.IllegalCommand);
            }

            CalcCmdStats();
        }
Exemple #13
0
        // Сессия опроса ------------------------------------------------------------------------------------------------------------------------------------
        public override void Session()
        {
            base.Session();             // Опрос должен происходить согласно активности списка запросов по Словарю ActiveSnd
            if (!fileyes)               // Если конфигурация не была загружена, выставляем все теги в невалидное состояние и выходим
            {
                InvalidateCurData();
                return;
            }

            for (int i = 0; i < ActiveSnd.Count; i++)
            {
                int sndCnt_ = ActiveSnd.Values.ElementAt(i);                                          // Выполняем запросы поочередно по индексам из словаря Активных запросов

                byte[] sndcode = ScadaUtils.HexToBytes(devTemplate.SndGroups[sndCnt_].SndCode, true); // Чтение строки HEX из параметра SndCode
                sndcode_ = sndcode[0];

                // ------------------  Тут вызвать формирование буфера запроса --------------------------------------
                Buf_Out(sndCnt_, sndcode_, null, true);                                             // отправить в функцию Номер и Байт запроса

                if (lastCommSucc)
                {
                    lastCommSucc = false;
                    int tryNum = 0; // Счетчик для корректных ответов

                    // Выполняем опрос если был загружен файл конфигурации
                    while (RequestNeeded(ref tryNum))
                    {
                        Connection.Write(buf_out, 0, buf_out.Length, CommUtils.ProtocolLogFormats.Hex, out logText);                           //послать запрос в порт
                        ExecWriteToLog(logText);                                                                                               // вывести запрос в Журнал линии связи

                        readcnt = Connection.Read(buf_in, 0, buf_in.Length, ReqParams.Timeout, CommUtils.ProtocolLogFormats.Hex, out logText); //считать значение из порта
                        ExecWriteToLog(logText);                                                                                               // вывести запрос в Журнал линии связи

                        // ------------------------------Тут проверка на корректность ответа - ID запроса и CRC -------------------------------------------------------------------
                        var valCnt_ = devTemplate.Values.FindIndex(x => x.ValCnt == ActiveSnd.ElementAt(i).Key); // Разбираем ответ поочередно по индексам из Списка Активных запросов

                        if (readcnt == buf_in.Length || readcnt == 11)
                        {
                            crc = CrcFunc.CalcCRC16(buf_in, readcnt);           // Рассчет CRC16 полученного ответа, при совпадении должно вернуть 0 при расчете CRC16(Modbus) и полного буфера вместе с CRC
                            byte fCode = buf_in[4];
                            Array.Copy(buf_in, readcnt - 4, byteIDres, 0, 2);

                            if (!(crc == 0 & fCode != 0 & byteID.SequenceEqual(byteIDres)))                // Проверка CRC, параметра F и ID запроса
                            {
                                if (crc != 0)
                                {
                                    ExecWriteToLog(CommPhrases.ResponseCrcError);
                                }
                                else if (fCode == 0)
                                {
                                    string err = Error_code(buf_in[6]);
                                    ExecWriteToLog(CommPhrases.IncorrectCmdData + " - " + err);                // При некорректном запросе F будет равен 0x00
                                }
                                else if (!byteID.SequenceEqual(byteIDres))
                                {
                                    ExecWriteToLog("ID ответа не совпадает с ID запроса");                     // При несовпадении ID
                                }
                                FinishRequest();
                                invalidData(valCnt_);                                                          // выставить сигналы в невалидное состояние
                            }
                            else
                            {
                                int index_bufin = 6;                                                                               // Индекс первой переменной в ответе прибора

                                for (int sig = 0; sig < devTemplate.Values[valCnt_].Vals.Count; sig++)                             // Разбор по количеству переменных Vals в ответе
                                {
                                    if (devTemplate.Values[valCnt_].Vals[sig].SigActive)                                           // Если переменная активна, читаем и разбираем ее
                                    {
                                        string sig_type = devTemplate.Values[valCnt_].Vals[sig].SigType;                           // читаем тип переменной
                                        double range    = devTemplate.Values[valCnt_].Vals[sig].Range;                             // читаем множитель (мало ли, вдруг пригодится) :)

                                        int k = ActiveCnl.Find(s => s.Cnl == devTemplate.Values[valCnt_].Vals[sig].SigCnl).IdxTag; // Находим в списке Индекс переменной и Указываем индекс Тега

                                        if (sig_type == "float")
                                        {
                                            SetCurData(k, BitConverter.ToSingle(buf_in, index_bufin) * range, 1);       // Конвертируем буфер байт в переменную float
                                        }
                                        else if (sig_type == "double")
                                        {
                                            SetCurData(k, BitConverter.ToDouble(buf_in, index_bufin) * range, 1);       // Конвертируем буфер байт в переменную double
                                        }
                                        else if (sig_type == "uint16")
                                        {
                                            SetCurData(k, BitConverter.ToUInt16(buf_in, index_bufin) * range, 1);       // Конвертируем буфер байт в переменную UInt16
                                        }
                                        else if (sig_type == "uint32")
                                        {
                                            SetCurData(k, BitConverter.ToUInt32(buf_in, index_bufin) * range, 1);       // Конвертируем буфер байт в переменную UInt32
                                        }
                                        else if (sig_type == "DateTime")                                                // Определяем системное время и конвертируем в double для Scada
                                        {
                                            if (!myTagId.ContainsKey(devTemplate.Values[valCnt_].Vals[sig].SigCnl))     // Указываем номер сигнала для преобразования в текстовую строку
                                            {                                                                           // в окне Данных КП Коммуникатора
                                                myTagId.Add(devTemplate.Values[valCnt_].Vals[sig].SigCnl, "DateTime");
                                            }

                                            int      year     = Convert.ToInt32(buf_in[index_bufin]) + 2000;          // Читаем из ответа переменные года
                                            int      month    = Convert.ToInt32(buf_in[index_bufin + 1]);             // месяца
                                            int      day      = Convert.ToInt32(buf_in[index_bufin + 2]);             // дня
                                            int      hour     = Convert.ToInt32(buf_in[index_bufin + 3]);             // часа
                                            int      minute   = Convert.ToInt32(buf_in[index_bufin + 4]);             // минут
                                            int      second   = Convert.ToInt32(buf_in[index_bufin + 5]);             // секунд
                                            DateTime dateTime = new DateTime(year, month, day, hour, minute, second); //  формируем переменную времени в формате DateTime
                                            SetCurData(k, dateTime.ToOADate(), 1);
                                        }

                                        if (devTemplate.Values[valCnt_].ValCnt == xValCnt01 || devTemplate.Values[valCnt_].ValCnt == xValCnt07)
                                        {
                                            if (sig_type == "float" || sig_type == "uint32") // Найденная ошибка, не учитывалось увеличение индекса при наличии uint32 переменной
                                            {
                                                index_bufin = index_bufin + 4;               // Увеличиваем индекс переменной для следующего текущего параметра для float
                                            }
                                            else if (sig_type == "double")
                                            {
                                                index_bufin = index_bufin + 8;           // Увеличиваем индекс переменной для следующего текущего параметра для double
                                            }
                                        }
                                    }
                                }
                                ExecWriteToLog(CommPhrases.ResponseOK);

                                lastCommSucc = true;
                                FinishRequest();
                            }
                        }
                        else
                        {
                            if (readcnt == 0)
                            {
                                ExecWriteToLog(CommPhrases.ResponseError);              // Нет ответа по Timeout - Ошибка связи!
                            }
                            else
                            {
                                ExecWriteToLog(CommPhrases.IncorrectResponseLength);    // Некорректная длина ответа
                            }
                            FinishRequest();
                            invalidData(valCnt_);                           // выставить сигналы в невалидное состояние
                        }
                        // завершение запроса
                        tryNum++;
                    }
                }
            }
            CalcSessStats(); // расчёт статистики
        }
Exemple #14
0
        // --------------------------------------------- Формирование буфера для команд чтения и команд записи
        private void Buf_Out(int Num, byte Fcode, byte[] bData, bool read) // формирование буфера отправки в порт Num = Номер индекса запроса или команды
        {                                                                  // Fcode = параметр команды SndCode или CmdCode, read = true - чтение, выполняются запросы Snd или read = false, выполняются команды
            if (read)
            {                                                              // Тут собраны команды чтения
                if (Fcode == 0x01 || Fcode == 0x07)                        // Если код равен F=0x01 - Текущие параметры или F=0x07 - Вес омпульсов
                {
                    Array.Resize(ref buf_out, 14);                         // Меняем размер буфера для запроса Текущих параметров и Веса импульсов
                    if (Fcode == 0x01)
                    {
                        maskch = BitConverter.GetBytes(mask_ch);            // запись битовой маски Текущих параметров в массив байт
                        Array.Resize(ref buf_in, col * res_ch + 10);        // длина ответа 4 * n каналов (или 8 * n каналов) + 10 байт
                    }
                    else if (Fcode == 0x07)
                    {
                        maskch = BitConverter.GetBytes(mask_chv);           // запись битовой маски Веса импульсов в массив байт
                        Array.Resize(ref buf_in, 4 * res_chv + 10);         // длина ответа 4 * n каналов + 10 байт
                    }

                    Array.Copy(maskch, 0, buf_out, 6, maskch.Length);                                 // Копирование маски в буфер запроса
                }
                else if (Fcode == 0x04)                                                               // Если код равен F=0x04 - Системное время
                {
                    Array.Resize(ref buf_out, 10);                                                    // Меняем размер буфера для запроса Системного времмени
                    Array.Resize(ref buf_in, 16);                                                     // длина ответа 16 байт
                }
                else if (Fcode == 0x0A)                                                               // Если код равен F=0x0A - Параметры прибора
                {
                    Array.Resize(ref buf_out, 12);                                                    // Меняем размер буфера для запроса Параметров прибора
                    byte[] snddata = ScadaUtils.HexToBytes(devTemplate.SndGroups[Num].SndData, true); // Чтение строки HEX из параметра SndData
                    buf_out[6] = snddata[0];                                                          // требуется 1 байт, код параметра
                    buf_out[7] = 0x00;                                                                // второй байт будет со значением 0
                    Array.Resize(ref buf_in, 18);                                                     // длина ответа 18 байт
                }
            }
            else                                                                                                     // Тут собраны команды записи
            {
                if (Fcode == 0x03 || Fcode == 0x08)                                                                  // Если код равен F=0x03 – код функции записи текущих показаний
                {                                                                                                    // Или F=0x08 - Вес импульса для Регистраторов импульса
                    Array.Resize(ref buf_out, 0x0E + bData.Length);                                                  // Меняем размер буфера для запроса Текущих параметров
                    maskch = Fcode == 0x03 ? BitConverter.GetBytes(mask_ch_wr) : BitConverter.GetBytes(mask_chv_wr); // запись битовой маски редактируемого канала в массив байт
                    Array.Copy(maskch, 0, buf_out, 6, maskch.Length);                                                // Копирование маски в буфер запроса
                    Array.Copy(bData, 0, buf_out, 10, bData.Length);                                                 // Копируем значение cmdVal в буфер запроса
                    Array.Resize(ref buf_in, 14);                                                                    // длина ответа 14 байт
                }
                else if (Fcode == 0x05)                                                                              // Запись времени в прибор
                {
                    Array.Resize(ref buf_out, 10 + bData.Length);
                    Array.Copy(bData, 0, buf_out, 6, bData.Length);
                    Array.Resize(ref buf_in, 14);                                                                       // длина ответа 14 байт
                }
                else if (Fcode == 0x0B)                                                                                 // Запись параметров в прибор
                {
                    Array.Resize(ref buf_out, 12 + bData.Length);
                    Array.Copy(bData, 0, buf_out, 8, bData.Length);
                    byte[] cmddata = ScadaUtils.HexToBytes(devTemplate.CmdGroups[Num].CmdData, true);       // Чтение строки HEX из параметра SndData
                    buf_out[6] = cmddata[0];                                                                // требуется 1 байт, код параметра
                    buf_out[7] = 0x00;                                                                      // второй байт будет со значением 0
                    Array.Resize(ref buf_in, 12);                                                           // длина ответа 12 байт
                }
            }

            buf_out[4] = Fcode;                                             // Копируем в буфер код запроса F
            buf_out[5] = Convert.ToByte(buf_out.Length);                    // Запись длины массива запроса - параметр L
            idBCD      = BitConverter.GetBytes(ConvFunc.DecToBCD(Address)); // Преобразование адреса в BCD формат
            ConvFunc.Reverse_array(idBCD, false);                           // Переворот буфера старшим байтом вперед
            Array.Copy(idBCD, 0, buf_out, 0, idBCD.Length);                 // Копирование адреса в буфер запроса

            byteID = BitConverter.GetBytes((ushort)rnd.Next(1, 65535));     // Сформировать случайный ID запроса
            buf_out[buf_out.Length - 4] = byteID[0];
            buf_out[buf_out.Length - 3] = byteID[1];

            crc = CrcFunc.CalcCRC16(buf_out, buf_out.Length - 2);       // Рассчет контрольной суммы CRC16
            buf_out[buf_out.Length - 2] = (byte)(crc % 256);            // Запись младшего байта контрольной суммы в буфер
            buf_out[buf_out.Length - 1] = (byte)(crc / 256);            // Запись старшего байта контрольной суммы в буфер
        }