Как я писал библиотеку под МЭК 870-5-104 на Arduino при помощи Wireshark

от автора

В этой статье я хотел бы рассказать о своем знакомстве с протоком передачи данных МЭК 870-5-104 со стороны контролируемого (slave) устройства путем написания простой библиотеки на Arduino.

Что такое МЭК 870-5-104 это и где применяется?

МЭК 60870-5-104 – протокол телемеханики, предназначенный для передачи сигналов ТМ в АСТУ, регламентирующий использование сетевого доступа по протоколу TCP/IP. Чаще всего применяется в энергетике для информационного обмена между энергосистемами, а также для получения данных от измерительных преобразователей (вольтметры, счетчики электроэнергии и прочее).

Стэк протокола МЭК 670-5-104:


Используемые материалы

  • плата Arduino UNO;
  • Ethernet shield (HR911105a);
  • в роли мастера МЭК 60870-5-104 будет выступать MicroScada от ABB;
  • Wireshark для анализа трафика.

Краткое описание этапов работы

  1. Установка TCP/IP соединение по 2404 порту;
  2. Подтверждение запроса на передачу данных (STARTDT act/con);
  3. Запрос на общий опрос станции;
  4. Подготовка и передача данных контролирующей (master) станции;
  5. Процедуры тестирования.

Подготовка

  • Подключена плата Arduino к ПК;
  • Настроен соответствующим образом сетевой интерфейс;
  • Настроена контролирующая (master) станция (добавлена 104 линия и добавлено контролируемое (slave) устройство).

Термины и сокращения

APCI — Управляющая Информация Прикладного Уровня может применяться как самостоятельный управляющий кадр (кадр U или кадр S).
ASDU — Блоки данных прикладного уровня, состоит из идентификатора блока данных и одного или более объектов информации, каждый из которых включает в себя один или более однородных элементов информации (либо комбинаций элементов информации).
APDU — Протокольный блок данных прикладного уровня.
ТС — телесигнализация.
ТИ — телеизмерения.
ТУ — телеуправление.

1. Установка TCP/IP соединение порт 2404

Контролирующая (master) станция инициализирует установку TCP соединения путем посылки TCP пакета с флагом (SYS). Соединение считается установленным, если в течение контрольного времени (t0) контролируемая станция (slave) выдала на свой уровень TCP/IP подтверждение «активного открытия» (SYS ACK). Контрольное время t0 называется «Тайм-аут установки соединения». Таймер t0 определяет, когда открытие отменяется и не определяет начало новой попытки соединения.

Взаимодействие с транспортным уровнем выполняет стандартная библиотека для плат Arduino «Ethernet.h». То есть первым делом необходимо установить TCP/IP соединение между контролируемой и контролирующей станциями. Для этого необходимо в скетче Arduino инициализировать устройство и создать сервер который будет ожидать входящие соединения через указанный порт.

Скетч

#include <Ethernet.h> byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xB7 };//мак адрес  IPAddress ip(172, 16, 7, 1);// ip адрес контролируемого устройства IPAddress gateway(172, 16,7, 0);//шлюз IPAddress subnet(255, 255, 0, 0);//маска EthernetClient client; EthernetServer iec104Server(2404);// для МЭК 670-5-104- порт- 2404 void setup() {   Ethernet.begin(mac, ip, gateway, subnet); // инициализация Ethernet-устройства } void loop() {   client = iec104Server.available();//подсоединение клиентов  }  

Если загрузить этот скетч то будет происходить следующее:

Установка соединения, далее приходит пока неизвестный для Arduino пакет STARTDT act и по истечении определенного времени рвется соединение. Далее необходимо разобраться что такое STARTDT act.

2. Подтверждение запроса на передачу данных (STARTDT act/con)

В МЭК 670-5-104 существует 3 типа формата для передачи:

  • I-формат для передачи данных телеметрии;
  • S-формат для передачи квитанций;
  • U-формат для передачи посылок установления связи и тестирования канала связи.

После успешного «тройного рукопожатия» контролирующая (master) станция посылает APDU STARTDT (старт передачи данных). STARTDT инициирует для контролируемой (Slavе) станции разрешение передачи блоков ASDU (кадров I) в направлении контролирующей (master), для продолжения работы необходимо подтвердить STARTDT, если контролируемая (Slavе) станция готова к передаче блоков данных. Если контролируемая (slave) станция не подтверждает выполнение STARTDT то контролирующая (master) станция вызывает обязательное закрытие IP- соединения.

Картинка

Таким образом далее необходимо считать байты полученные от контролирующей (master) станции и разобрать их.

Скетч

uint8_t iec104ReciveArray[128];//массив для приема  EthernetClient client = iec104Server.available();  if(client.available())   {     delay(100);     int i = 0; while(client.available())  {     iec104ReciveArray[i] = client.read();//записываем в буфер приема данные     i++;  } 

Прочитав данные необходимо разобрать их и сформировать ответ.

Wireshark

Вот как выглядит посылка содержащая блок STARTDT в программе Wireshark, APDU блок U-формата, который состоит только из APCI.
APCIУправляющая Информация Прикладного Уровня может применяться как самостоятельный управляющий кадр (кадр U или кадр S).

Вкратце можно сказать, что блок APCI определяет тип блока APDU и его длину. APCI состоит из следующих шести байтов:

1. Признак инициализации блока APDU переменной длины, начинающийся байтом START2 68h;
2. Длин APDU, в данном примере равна четырем байтам;
3. Байт управления в котором определяется тип APDU, в данном примере записано значение равное семи, что означает запрос на передачу данных;
4,5,6 Не используются.

Исходя из вышеописанного, перед тем как ответить, не мешало бы определить какой тип APDU нам послала контролирующая станция. Зная, что тип APDU записан третьим по порядку чтения блока APCI байтом, сохраню его в целочисленную переменную. Из рисунка выше видно, что тип APDU соответствующий значению 7 это STARTDT act соответственно ответить необходимо таким же по структуре пакетом только значение типа должно иметь значение 11 (0b), что соответствует STARTDT con.

Скетч

#include <Ethernet.h> byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xB7 }; IPAddress ip(172, 16, 7, 1); IPAddress gateway(172, 16,7, 0); IPAddress subnet(255, 255, 0, 0); EthernetClient client; EthernetServer iec104Server(2404); int TypeQuerry, MessageLength;// тип APDU и длина посылки uint8_t iec104ReciveArray[128];//буфер приема APDU void setup() {   Ethernet.begin(mac, ip, gateway, subnet); } void loop() {   client = iec104Server.available();   if(client.available())//клиент подсоединен    {      delay(100);      int i = 0;      while(client.available())//чтение байтов      {        iec104ReciveArray[i] = client.read();//записываем в буфер приема данные        i++;      }     TypeQuerry= iec104ReciveArray[2];//определяем тип APDU     switch(TypeQuerry)   {        case 07:// если пришел тип STARTDT        iec104ReciveArray[0]=iec104ReciveArray[0];// START2 = 68h;        iec104ReciveArray[1]=iec104ReciveArray[1];//длина APDU         iec104ReciveArray[2] = iec104ReciveArray[2]+4; //тип APDU        iec104ReciveArray[3]=0;        iec104ReciveArray[4]=0;        iec104ReciveArray[5]=0;        MessageLength = iec104ReciveArray[1]+2;//длина сообщения + 2 байта Start and Lenght APCI        delay(100);        client.write(iec104ReciveArray, MessageLength);//передача обратно     break;   }  } } 

После обновления скетча наблюдаем следующий порядок обмена:

Установку соединения, запрос на передачу данных, подтверждение запроса и еще один новый пока неизвестный APDU формата I типа 1 C_IC_NA Act.

3. Запрос на общий опрос станции

При инициализации оборудования формируется общий опрос станции кадр c идентификатором <100> C_IC_NA_1.

Wireshark

APDU <100> C_IC_NA_1 кроме блока APCI так же имеет блок ASDU (блок данных прикладного уровня), которые вместе формируют Протокольный Блок Данных Прикладного Уровня APDU.

Рассмотрим более подробно полученный APDU.

APCI:

  • В первом байте указан тип 0 означающий, что это команда опроса;
  • Во втором длина APDU 14 байт;

ASDU:

  • Первый байт в блоке ASDU определяет тип объекта информации, в данном случае <100> C_IC_NA_1 (общий опрос станции);
  • Второй структуру блока данных;
  • Третий причину передачи (CauseTx), значение шесть означает запрос на активацию;
  • Четвертый общий адрес стануии;
  • Пятый адрес контролируемой (slave) станции;
  • С шестого по восьмой адрес объекта информации равен нулю;
  • Девятый информационный байт — QOI — описатель запроса, имеющий следующие значения:

В ответ на <100> C_IC_NA_1 необходимо ответить подтверждением и завершением активации. Для этого необходимо немного изменить принятый блок ASDU. Для отправки подтверждения необходимо записать в байт указывающий на причину передачи (CauseTX) значение равное 7. Для оправки завершения активации необходимо записать в байт указывающий на причину передачи (CauseTX) значение равное 10.

Скетч

#include <Ethernet.h> byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xB7 }; IPAddress ip(172, 16, 7, 1); IPAddress gateway(172, 16,7, 0); IPAddress subnet(255, 255, 0, 0); EthernetClient client; EthernetServer iec104Server(2404); int TypeQuerry, MessageLength;// тип APDU и длина посылки uint8_t iec104ReciveArray[128];//буфер приема APDU void setup() {   Ethernet.begin(mac, ip, gateway, subnet); } void loop() {   client = iec104Server.available();   if(client.available())//клиент подсоединен    {      delay(100);      int i = 0;      while(client.available())//чтение байтов      {        iec104ReciveArray[i] = client.read();//записываем в буфер приема данные        i++;      }     TypeQuerry= iec104ReciveArray[2];//определяем тип APDU     switch(TypeQuerry)   {        case 07:// если пришел тип STARTDT        iec104ReciveArray[0]=iec104ReciveArray[0];// START2 = 68h;        iec104ReciveArray[1]=iec104ReciveArray[1];//длина APDU         iec104ReciveArray[2] = iec104ReciveArray[2]+4; //тип APDU        iec104ReciveArray[3]=0;        iec104ReciveArray[4]=0;        iec104ReciveArray[5]=0;        MessageLength = iec104ReciveArray[1]+2;//длина сообщения + 2 байта Start and Lenght APCI        delay(100);        client.write(iec104ReciveArray, MessageLength);//передача обратно     break; case 00://команда опроса значение указателя опроса QOI <20> - опрос станции (общий) 	delay(100); //подтверждение активации 	iec104ReciveArray[0]=iec104ReciveArray[0];// START2 = 68h; 	iec104ReciveArray[1]=iec104ReciveArray[1];//длина APDU 	iec104ReciveArray[2]=iec104ReciveArray[4];//TX  	iec104ReciveArray[3]=iec104ReciveArray[5];//TX  	iec104ReciveArray[4]=iec104ReciveArray[2];//RX  	iec104ReciveArray[5]=iec104ReciveArray[3];//RX  	iec104ReciveArray[6]=100;//тип ASDU 	iec104ReciveArray[7]=01;//SQ 	iec104ReciveArray[8]=07;//причина передачи 	iec104ReciveArray[9]=00;//AO 	iec104ReciveArray[10]=01;//Adress 	iec104ReciveArray[11]=00;//Adress 	iec104ReciveArray[12]=00;//IOA 	iec104ReciveArray[13]=00;//IOA 	iec104ReciveArray[14]=00;//IOA 	iec104ReciveArray[15]=00;// QOI 	MessageLength = iec104ReciveArray[1]+2;//16 bytes  =APDU LENGHT+2 	client.write(iec104ReciveArray, MessageLength); 	delay(100); //завершение активации 	iec104ReciveArray[0]=iec104ReciveArray[0];// START2 = 68h; 	iec104ReciveArray[1]=iec104ReciveArray[1];//длина APDU 	iec104ReciveArray[2]=iec104ReciveArray[4];//TX  	iec104ReciveArray[3]=iec104ReciveArray[5];//TX  	iec104ReciveArray[4]=iec104ReciveArray[2];//RX  	iec104ReciveArray[5]=iec104ReciveArray[3];//RX  	iec104ReciveArray[6]=100;//тип ASDU 	iec104ReciveArray[7]=01;//SQ 	iec104ReciveArray[8]=10;//причина передачи 	iec104ReciveArray[9]=00;//AO 	iec104ReciveArray[10]=01;//Adress 	iec104ReciveArray[11]=00;//Adress 	iec104ReciveArray[12]=00;//IOA 	iec104ReciveArray[13]=00;//IOA 	iec104ReciveArray[14]=00;//IOA 	iec104ReciveArray[15]=00;// QOI 	MessageLength = iec104ReciveArray[1]+2;//16 bytes 	client.write(iec104ReciveArray, MessageLength);  	break; }}} 

После обновления скетча наблюдаем следующий порядок обмена:

Установку соединения, запрос на передачу данных, подтверждение, общий опрос станции, подтверждение запроса и пока что неизвестные APDU формата S и спустя некоторое время U TESTFR.

4. Подготовка и передача данных

APDU блок формата S, состоящий только из APCI предназначен для подтверждения принятого APDU I формата. Для S-формата 7 старших бит служебного поля байта 1 и байт 2 не задействованы, а байт 3 (7 старших бит) и байт 4 определяют текущий номер принятой посылки.

В данном случае блок S указывает на то, что контролирующая (master) станция готова к приему данных в течении определенного времени, не превышающего, тайм-аут t3 определенного на стороне контролирующей (master) станции. То есть контролирующая (master) станция говорит нам «я готова к приему данных!». Далее необходимо позаботиться о том какие данные передавать и откуда их брать.

Что можно передавать? Существует несколько видов информации определённых в МЭК 870-5- 104:

  • Контрольная;
  • Управляющая;
  • Параметры;
  • Передача файлов.

Картинка

В данном примере рассматривается передача контрольной информации на примере 1, 11 и 13 функций (одноэлементная, измерение масштабируемое, измерение короткий формат с плавающей запятой). Данные формируются рандомно. Также необходимо учитывать, что у каждого передаваемого сигнала имеется байт качества.

Простой алгоритм определения качества сигнала:

  • Если используется замещение действующего сигнала то выставляются флаги BL(блокировка) и SB(замещение);
  • Если значение сигнала не изменялось в течении контрольного промежутка времени то выставляется флаг NT(не актуальное);
  • Если имеется признак неработоспособности узла или устройства более нижнего уровня (датчик или прочее) то выставляется флаг IV(не достоверное значение).

Скетч

void SetQDS(int currvalue, int i,bool zam)//определение качества сигнала { if (zam==0)//замещение? {   if (currvalue==previusValue[i])//значение не изменялось?     {       previusValue[i]=currvalue;       counter[i]+=1;       if (counter[i]>=1000)         {           qds[i]=64;// NT            counter[i]=0;         }     }     else     {         qds[i]=0;         counter[i]=0;         previusValue[i]=currvalue;     } } else    {     qds[i]=48;// SB, BL      } } 

Так же необходимо учесть, что для каждого сигнала имеется уникальный идентификатор IOA, в энергетике принято распределять эти адреса следующим образом:

  • ТС-начиная с 4096;
  • ТИ-начиная с 8192;
  • ТУ-начиная с 20480.

Для передачи значения сигналов в массив для отправки используется библиотека EEPROM:

Скетч

void EEPROM_float_write(int addr, float val,int IOA,int number,bool subs) // начальный адрес в EEPROM, значение сигнала, адрес сигнала, порядковый номер измеряемого сигнала, замещение {     SetQDS(val,number, subs);//установка качества сигнала   byte *x = (byte *)&val;//float -->byte   byte *xxx = (byte *)&IOA;//запись адреса IOA   for(int jj = 0; jj <2; jj++)     {       EEPROM.write(addr,xxx[jj]);//сохранение в EEPROM адреса блока данных в 2 байтах       addr+=1;     }   for(byte i = 0; i < 4; i++) //запись формата float в 4 байтах     {       EEPROM.write(addr, x[i]); //запись формата float в 4 байтах       addr+=1;     }   EEPROM.write(addr, qds[number]);//запись информации о качестве сигнала   if (addr == EEPROM.length())     {       addr = 0;     } } 

И так получив APDU подтверждение S можно начать передавать имеющиеся у нас в распоряжении данные, не забывая увеличивать номер передаваемого кадра.

Скетч

#include <Ethernet.h> #include <eeprom.h> byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xB7 }; IPAddress ip(172, 16, 7, 1); IPAddress gateway(172, 16,7, 0); IPAddress subnet(255, 255, 0, 0); EthernetClient client; EthernetServer iec104Server(2404); int TypeQuerry, MessageLength; uint8_t iec104ReciveArray[128]; int counter[6]; int qds[6]; int previusValue[6]; word iecData[256]; int txcnt; void setup() {   Ethernet.begin(mac, ip, gateway, subnet); }  void EEPROM_float_write(int addr, float val,int IOA,int number,bool zam)  {     SetQDS(val,number,zam);     byte *x = (byte *)&val;   byte *xxx = (byte *)&IOA;   for(int jj = 0; jj <2; jj++)     {       EEPROM.write(addr,xxx[jj]);       addr+=1;       }   for(byte i = 0; i < 4; i++)   {       EEPROM.write(addr, x[i]);       addr+=1;   }   EEPROM.write(addr, qds[number]);  if (addr == EEPROM.length())    { 	  addr = 0;    } } void EEPROM_byte_write(int addr, bool val,int IOA,int number,bool zam)  {     SetQDS(val,number,zam);     byte c=val+qds[number];   byte *x = (byte *)&c;   byte *xxxx = (byte *)&IOA;   for(int jj = 0; jj <2; jj++) //IOA     {        EEPROM.write(addr,xxxx[jj]);       addr+=1;       }   for(byte i = 0; i < 1; i++) //Data   {   EEPROM.write(addr, x[i]);   }   if (addr == EEPROM.length()) {     addr = 0;   } } void EEPROM_int_write(int addr, int val, int IOA,int number,bool zam)  {       SetQDS(val,number,zam);    byte *x = (byte *)&val;   byte *xx = (byte *)&IOA;   for(int jj = 0; jj <2; jj++)     {       EEPROM.write(addr,xx[jj]);       addr+=1;       }     for(byte i = 0; i < 2; i++) 	  { 	   EEPROM.write(addr, x[i]); 	   addr+=1;  	  }    EEPROM.write(addr, qds[number]);      if (addr == EEPROM.length()) {     addr = 0;   } } void SetQDS(int currvalue, int i,bool zam)// качество сигнала { if (zam==0)//замещение? {   if (currvalue==previusValue[i])//значение не изменялось?     {       previusValue[i]=currvalue;       counter[i]+=1;       if (counter[i]>=1000)         {           qds[i]=64;           counter[i]=0;         }       }     else       {         qds[i]=0;         counter[i]=0;         previusValue[i]=currvalue;       } } else    {           qds[i]=48;       }  } void loop() {    EEPROM_byte_write(0,0,4096,0,0);    EEPROM_byte_write(3,random(0, 2),4097,1,1);//bl    EEPROM_int_write(6,  67,8192,2,1);//float data     EEPROM_int_write(11, random(10, 20),8193,3,0);// int data +5    EEPROM_float_write(16, random(-1000, 2000),8194,4,1);//float data+7    EEPROM_float_write(23, 78.66f,8195,5,1);//float data+7   client = iec104Server.available(); if(client.available())   {     delay(100);     int i = 0;     while(client.available())     {       iec104ReciveArray[i] = client.read(); 	  i++;     }     TypeQuerry= iec104ReciveArray[2];//определяем тип посылки   switch(TypeQuerry)   {     case 07:        iec104ReciveArray[0]=iec104ReciveArray[0];//кадр переменной длины, начинающийся байтом START2 = 68h;        iec104ReciveArray[1]=iec104ReciveArray[1];//длина APDU LENGHT        iec104ReciveArray[2] = iec104ReciveArray[2]+4; //TYPE        iec104ReciveArray[3]=0;        iec104ReciveArray[4]=0;        iec104ReciveArray[5]=0;        MessageLength = iec104ReciveArray[1]+2;//определение длины сообщения + 2 байта Start and Lenght        delay(100);        client.write(iec104ReciveArray, MessageLength);     break;     case 00:       delay(100);       iec104ReciveArray[0]=iec104ReciveArray[0];//кадр переменной длины, начинающийся байтом START2 = 68h;       iec104ReciveArray[1]=iec104ReciveArray[1];//длина       iec104ReciveArray[2]=iec104ReciveArray[4];//тип, TX H       iec104ReciveArray[3]=iec104ReciveArray[5];//TX L       iec104ReciveArray[4]=iec104ReciveArray[2];//RX H       iec104ReciveArray[5]=iec104ReciveArray[3];//RX L       iec104ReciveArray[6]=100;//type       iec104ReciveArray[7]=01;//sq       iec104ReciveArray[8]=07;//cause con       iec104ReciveArray[9]=00;//AO       iec104ReciveArray[10]=01;//Adress       iec104ReciveArray[11]=00;//Adress       iec104ReciveArray[12]=00;//IOA       iec104ReciveArray[13]=00;//IOA       iec104ReciveArray[14]=00;//IOA       iec104ReciveArray[15]=00;//IOA, QOI       MessageLength = iec104ReciveArray[1]+2;//16 bytes  =APDU LENGHT+2 BAIT       client.write(iec104ReciveArray, MessageLength);       delay(100);       iec104ReciveArray[0]=iec104ReciveArray[0];//кадр переменной длины, начинающийся байтом START2 = 68h;       iec104ReciveArray[1]=iec104ReciveArray[1];//длина       iec104ReciveArray[2]=02;//тип, TX H       iec104ReciveArray[3]=iec104ReciveArray[5];//TX L       iec104ReciveArray[4]=iec104ReciveArray[2];//RX H       iec104ReciveArray[5]=iec104ReciveArray[3];//RX L       iec104ReciveArray[6]=100;//type       iec104ReciveArray[7]=01;//sq       iec104ReciveArray[8]=10;//cause con       iec104ReciveArray[9]=00;//AO       iec104ReciveArray[10]=01;//Adress       iec104ReciveArray[11]=00;//Adress       iec104ReciveArray[12]=00;//IOA       iec104ReciveArray[13]=00;//IOA       iec104ReciveArray[14]=00;//IOA       iec104ReciveArray[15]=20;//IOA, QOI       MessageLength = iec104ReciveArray[1]+2;//16 bytes       client.write(iec104ReciveArray, MessageLength);      break;     case 01:       txcnt=word(iec104ReciveArray[5],iec104ReciveArray[4]);       iec104ReciveArray[0]=iec104ReciveArray[0];//кадр переменной длины, начинающийся байтом START2 = 68h;       iec104ReciveArray[1]=14;//длина  APDU=4 APCI+ (6 ASDU + 8 DATA*2)        iec104ReciveArray[2]=lowByte(txcnt);//TX       iec104ReciveArray[3]=highByte(txcnt);// iec104ReciveArray[3];//TX       iec104ReciveArray[4]=0;//RX       iec104ReciveArray[5]=0;//RX       iec104ReciveArray[6]=1;//type 1       iec104ReciveArray[7]=01;//sq       iec104ReciveArray[8]=01;//cause       iec104ReciveArray[9]=00;//AO       iec104ReciveArray[10]=01;//Adress       iec104ReciveArray[11]=00;//Adress       iec104ReciveArray[12]=iecData[0];//IOA       iec104ReciveArray[13]=iecData[1];//IOA       iec104ReciveArray[14]=0;//IOA       iec104ReciveArray[15]=iecData[2];//lowByte(iecData);//value [DATA 1]       MessageLength = iec104ReciveArray[1]+2;       client.write(iec104ReciveArray, MessageLength);        delay(5);       txcnt=txcnt+2;       iec104ReciveArray[0]=iec104ReciveArray[0];//кадр переменной длины, начинающийся байтом START2 = 68h;       iec104ReciveArray[1]=14;//длина  APDU=4 APCI+ (6 ASDU + 8 DATA*2)        iec104ReciveArray[2]=lowByte(txcnt);//TX       iec104ReciveArray[3]=highByte(txcnt);// iec104ReciveArray[3];//TX       iec104ReciveArray[4]=0;//RX       iec104ReciveArray[5]=0;//RX       iec104ReciveArray[6]=1;//type 13       iec104ReciveArray[7]=01;//sq       iec104ReciveArray[8]=01;//cause       iec104ReciveArray[9]=00;//AO       iec104ReciveArray[10]=01;//Adress       iec104ReciveArray[11]=00;//Adress       iec104ReciveArray[12]=iecData[3];//IOA       iec104ReciveArray[13]=iecData[4];//IOA       iec104ReciveArray[14]=0;//IOA       iec104ReciveArray[15]=iecData[5];//iecData[11];       MessageLength = iec104ReciveArray[1]+2;       client.write(iec104ReciveArray, MessageLength);        delay(5);       txcnt=txcnt+2;       iec104ReciveArray[0]=iec104ReciveArray[0];// START2 = 68h;       iec104ReciveArray[1]=22;//длина  APDU=4 APCI+ (6 ASDU + 8 DATA*2)        iec104ReciveArray[2]=lowByte(txcnt);       iec104ReciveArray[3]=highByte(txcnt);       iec104ReciveArray[4]=0;//RX       iec104ReciveArray[5]=0;//RX       iec104ReciveArray[6]=11;//type 11       iec104ReciveArray[7]=02;//sq       iec104ReciveArray[8]=01;//cause       iec104ReciveArray[9]=00;//AO       iec104ReciveArray[10]=01;//Adress       iec104ReciveArray[11]=00;//Adress       iec104ReciveArray[12]=iecData[6];//IOA       iec104ReciveArray[13]=iecData[7];//IOA       iec104ReciveArray[14]=0;//IOA       iec104ReciveArray[15]=iecData[8];//value  [DATA 2]       iec104ReciveArray[16]=iecData[9];//value  [DATA 2]       iec104ReciveArray[17]=iecData[10];//QDS        iec104ReciveArray[18]=iecData[11];//IOA       iec104ReciveArray[19]=iecData[12];//OA       iec104ReciveArray[20]=0;//IOA       iec104ReciveArray[21]=iecData[13];//value  [DATA 2]       iec104ReciveArray[22]=iecData[14];//value  [DATA 2]       iec104ReciveArray[23]=iecData[15];//IOA QDS        MessageLength = iec104ReciveArray[1]+2;       client.write(iec104ReciveArray, MessageLength);       delay(5);       txcnt=txcnt+2;       iec104ReciveArray[0]=iec104ReciveArray[0];       iec104ReciveArray[1]=26;       iec104ReciveArray[2]=lowByte(txcnt);       iec104ReciveArray[3]=highByte(txcnt);       iec104ReciveArray[4]=0;       iec104ReciveArray[5]=0;       iec104ReciveArray[6]=13;       iec104ReciveArray[7]=02;       iec104ReciveArray[8]=01;       iec104ReciveArray[9]=00;       iec104ReciveArray[10]=01;       iec104ReciveArray[11]=00;       iec104ReciveArray[12]=iecData[16];       iec104ReciveArray[13]=iecData[17];       iec104ReciveArray[14]=0;       iec104ReciveArray[15]=iecData[18];       iec104ReciveArray[16]=iecData[19];       iec104ReciveArray[17]=iecData[20];       iec104ReciveArray[18]=iecData[21];       iec104ReciveArray[19]=iecData[22];       iec104ReciveArray[20]=iecData[23];       iec104ReciveArray[21]=iecData[24];       iec104ReciveArray[22]=0;       iec104ReciveArray[23]=iecData[25];       iec104ReciveArray[24]=iecData[26];       iec104ReciveArray[25]=iecData[27];       iec104ReciveArray[26]=iecData[28];       iec104ReciveArray[27]=iecData[29];       MessageLength = iec104ReciveArray[1]+2;       client.write(iec104ReciveArray, MessageLength);     break;   }  } } 

Загрузив скетч в Wireshark наблюдаем, что наконец-то началась передача данных.

Далее привожу описание структуры ASDU <100>M_SP_NA_1 одноэлементная индикация.

TypeId — вид информации.
SQ — классификатора переменной структуры.

Предусматриваются две структуры блоков данных:

1. Блок, содержащий i объектов информации, каждый из которых содержит по одному элементу информации (или по одной комбинации элементов); старший бит классификатора переменной структуры SQ (single/sequence) равен 0, остальные 7 битов задают число i.

2. Блок, содержащий один объект информации, который содержит j элементов либо одинаковых комбинаций элементов информации; старший бит (27 = 80h) классификатора SQ равен 1, остальные 7 битов задают число j.

Картинка

CauseTx — причина передачи.

Картинка

Addr — адрес слэйва (указывается при конфигурировании мастера).
IOA — адрес объекта информации, по этому адресу контролирующая станция будет привязывать свой тэг
SIQ — показатель качества передаваемого сигнала.

Структура ASDU блока функции <11>M_ME_NB_1:

Wireshark

В ответ на полученные данные master будет отправлять блоки формата S и процесс зациклится до тех пор пока контролируемое(slave) устройство не перестанет передавать кадры.

5. Процедуры тестирования

Процедуры тестирования применяются с целью контроля за работоспособностью транспортных соединений. Процедура выполняется независимо от «активности» IP-соединения, если в течение контрольного времени t3 не было принято ни одного кадра (I, U, S). Время t3 является предметом согласования и называется «Тайм-аут для посылки блоков тестирования в случае долгого простоя». Процедура тестирования реализуется путем посылки тестового APDU (TESTFR =act), которое подтверждается принимаемой станцией с помощью APDU (TESTFR =con).

Картинка

Wireshark

Если от контролирующей (master) станции придет APDU у которого в байте отвечающего за тип APDU значение равно шестидесяти сети (TESTFR) это говорит о том, что в течении времени t3 от контролируемой станции не было принято ни одного кадра (I, U, S), и если в течении времени t1 не ответить подтверждением то соединение будет разорвано.

Скетч

case 67:       iec104ReciveArray[0]=iec104ReciveArray[0];//кадр переменной длины, начинающийся байтом START2 = 68h;       iec104ReciveArray[1]=iec104ReciveArray[1];//длина APDU LENGHT       iec104ReciveArray[2] = 131; //TESTDT con       iec104ReciveArray[3] =0;       iec104ReciveArray[4] =0;       iec104ReciveArray[5] =0;       MessageLength = iec104ReciveArray[1]+2;//определение длины сообщения + 2 байта Start68H and Lenght       delay(10);       client.write(iec104ReciveArray, MessageLength); 

Wireshark

На этом всё, если кому-нибудь интересно то в следующей статье я рассмотрю протокол МЭК 670-5-104 со стороны контролирующей (master) станции на примере Arduino.

ссылка на оригинал статьи https://habrahabr.ru/post/280818/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *