Удобно то что добавлять и убирать опрашиваемые устройства можно в коде и гораздо проще чем через конфиг контроллера, хотя и сделал я это немного странновато, но работает. Удобно что в Кодесис есть экспорт программы, который экспортируется целиком в текстовом формате - можно экспортировать кусок программы, лишь один блок FB, что я и сделал и скопировал его в гитхаб гист. Этот блок вызывается из главной программы, и в него передается количество оправшиваемых модбас устройств, адреса которых записаны в отдельном массиве. Вообще там есть еще и множество глобальных переменных, но пока решил не добавлять
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* @NESTEDCOMMENTS := 'Yes' *) | |
(* @PATH := '\/Program' *) | |
(* @OBJECTFLAGS := '0, 8' *) | |
(* @SYMFILEFLAGS := '2048' *) | |
FUNCTION_BLOCK ModbusRead | |
VAR_INPUT | |
devnumb:BYTE; | |
END_VAR | |
VAR_OUTPUT | |
END_VAR | |
VAR | |
mbadr:BYTE; | |
mbdep:BYTE; | |
get_modbus: MB_RD_INP_REGS; (*функция 04 - чтение трех параметров типа INT*) | |
Buffer: ARRAY[0..255] OF BYTE; (* байтовый буфер данных*) | |
cmpl: BOOL; | |
TimeOut: TIME:=T#10ms;(*таймаут*) | |
Exception: BYTE; | |
DataSize: WORD; | |
master1: BYTE:= 0; | |
i:BYTE:=1; | |
END_VAR | |
(* @END_DECLARATION := '0' *) | |
IF enabl = FALSE THEN | |
enabl := TRUE; | |
END_IF | |
(*Устанавливаем настройки COM-порта*) | |
IF port_opened=0 THEN | |
Settings.Port:=com_num; (*номер COM-порта 0 – RS-485, 1 – RS-232*) | |
Settings.dwBaudRate:=115200; (*скорость*) | |
Settings.byParity:=0; | |
Settings.dwTimeout:=0; | |
Settings.byStopBits:=1; | |
Settings.dwBufferSize:=0; | |
Settings.dwScan:=0; | |
END_IF | |
(*Открываем COM-порт*) | |
COM_SERVICE1(Enable:=(port_opened=0), Settings:=Settings , Task:=OPEN_TSK ); | |
(*Если COM-порт открыт, то переходим к приему и передачи данных *) | |
IF COM_SERVICE1.ready THEN | |
port_opened:=2; | |
END_IF | |
IF port_opened=2 THEN (*Удачно проинициализировали*) | |
CASE master1 OF | |
0: | |
get_modbus( | |
Enable:=enabl , (* разрешение работы блока *) | |
Mode:=MB_RTU , (*режим передачи*) | |
DevAddr:=adrDep[i] , (*адрес*) | |
FirstAddr:=32000 , (*номер регистра*) | |
Quantity:=16, (*количество регистров*) | |
ComHandle:=Settings.Port ,(*номер COM-порта*) | |
TimeOut:=TimeOut , (*Таймаут T#50ms*) | |
Buffer:=Buffer , (* буфер данных *) | |
Complete=>cmpl , (* скопировать признак завершения операции *) | |
Exception=>err , (* скопировать регистр ошибок *) | |
ByteCnt=>DataSize ); (*кол-во считанных байтов *) | |
(*если установлен признак завершения операции, то *) | |
IF cmpl THEN | |
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа FLOAT*) | |
DEP[arrDep[i]].Ua:=GET_R(buffer[0],buffer[1],buffer[2],buffer[3]); | |
DEP[arrDep[i]].Ub:=GET_R(buffer[4],buffer[5],buffer[6],buffer[7]); | |
DEP[arrDep[i]].Uc:=GET_R(buffer[8],buffer[9],buffer[10],buffer[11]); | |
DEP[arrDep[i]].Ia:=GET_R(buffer[12],buffer[13],buffer[14],buffer[15]); | |
DEP[arrDep[i]].Ib:=GET_R(buffer[16],buffer[17],buffer[18],buffer[19]); | |
DEP[arrDep[i]].Ic:=GET_R(buffer[20],buffer[21],buffer[22],buffer[23]); | |
DEP[arrDep[i]].Freq:=GET_R(buffer[24],buffer[25],buffer[26],buffer[27]); | |
DEP[arrDep[i]].Ratio:=GET_R(buffer[28],buffer[29],buffer[30],buffer[31]); | |
comm_ok[arrDep[i]]:=1; | |
ELSE | |
comm_ok[arrDep[i]]:=0; | |
END_IF | |
master1:=1; (*переходим к выполнению следующего ФБ*) | |
END_IF | |
1: | |
get_modbus( | |
Enable:=enabl , (* разрешение работы блока *) | |
Mode:=MB_RTU , (*режим передачи*) | |
DevAddr:=adrDep[i], (*адрес*) | |
FirstAddr:=31000 , (*номер регистра*) | |
Quantity:=19, (*количество регистров*) | |
ComHandle:=Settings.Port ,(*номер COM-порта*) | |
TimeOut:=TimeOut , (*Таймаут T#50ms*) | |
Buffer:=Buffer , (* буфер данных *) | |
Complete=>cmpl , (* скопировать признак завершения | |
операции *) | |
Exception=>err , (* скопировать регистр ошибок *) | |
ByteCnt=>DataSize ); (*кол-во считанных байтов *) | |
(*если установлен признак завершения операции, то *) | |
IF cmpl THEN | |
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа BOOL*) | |
DEP[arrDep[i]].IN1:=buffer[1].0; | |
DEP[arrDep[i]].IN2:=buffer[3].0; | |
DEP[arrDep[i]].IN3:=buffer[5].0; | |
DEP[arrDep[i]].IN4:=buffer[7].0; | |
DEP[arrDep[i]].IN5:=buffer[9].0; | |
DEP[arrDep[i]].IN6:=buffer[11].0; | |
DEP[arrDep[i]].IN7:=buffer[13].0; | |
DEP[arrDep[i]].Enable:=buffer[15].0; | |
DEP[arrDep[i]].Ua_ON:=buffer[17].0; | |
DEP[arrDep[i]].Ub_ON:=buffer[19].0; | |
DEP[arrDep[i]].Uc_ON:=buffer[21].0; | |
DEP[arrDep[i]].phaseCross:=buffer[23].0; | |
DEP[arrDep[i]].MT3_phA:=buffer[25].0; | |
DEP[arrDep[i]].MT3_phB:=buffer[27].0; | |
DEP[arrDep[i]].MT3_phC:=buffer[29].0; | |
DEP[arrDep[i]].Cntr_CH_STAT:=buffer[31].0; | |
DEP[arrDep[i]].Cntr_CH_RSLT:=buffer[33].0; | |
DEP[arrDep[i]].power1:=buffer[35].0; | |
DEP[arrDep[i]].power2:=buffer[37].0; | |
comm_ok[arrDep[i]]:=1; | |
ELSE | |
comm_ok[arrDep[i]]:=0; | |
END_IF | |
master1:=2; (*переходим к выполнению следующего ФБ*) | |
END_IF | |
2: | |
get_modbus( | |
Enable:=enabl , (* разрешение работы блока *) | |
Mode:=MB_RTU , (*режим передачи*) | |
DevAddr:=adrDep[i] , (*адрес*) | |
FirstAddr:=33000 , (*номер регистра*) | |
Quantity:=14, (*количество регистров*) | |
ComHandle:=Settings.Port ,(*номер COM-порта*) | |
TimeOut:=TimeOut , (*Таймаут T#50ms*) | |
Buffer:=Buffer , (* буфер данных *) | |
Complete=>cmpl , (* скопировать признак завершения | |
операции *) | |
Exception=>err , (* скопировать регистр ошибок *) | |
ByteCnt=>DataSize ); (*кол-во считанных байтов *) | |
(*если установлен признак завершения операции, то *) | |
IF cmpl THEN | |
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа DWORD*) | |
DEP[arrDep[i]].c1:=GET_D(buffer[0],buffer[1],buffer[2],buffer[3]); | |
DEP[arrDep[i]].c2:=GET_D(buffer[4],buffer[5],buffer[6],buffer[7]); | |
DEP[arrDep[i]].c3:=GET_D(buffer[8],buffer[9],buffer[10],buffer[11]); | |
DEP[arrDep[i]].c4:=GET_D(buffer[12],buffer[13],buffer[14],buffer[15]); | |
DEP[arrDep[i]].c5:=GET_D(buffer[16],buffer[17],buffer[18],buffer[19]); | |
DEP[arrDep[i]].c6:=GET_D(buffer[20],buffer[21],buffer[22],buffer[23]); | |
DEP[arrDep[i]].c7:=GET_D(buffer[24],buffer[25],buffer[26],buffer[27]); | |
comm_ok[arrDep[i]]:=1; | |
ELSE | |
comm_ok[arrDep[i]]:=0; | |
END_IF | |
master1:=0; | |
i:=i+1; | |
IF i>devnumb THEN | |
i:=1; | |
END_IF | |
END_IF | |
END_CASE | |
IF err <> 0 THEN | |
enabl := FALSE; | |
END_IF | |
END_IF | |
END_FUNCTION_BLOCK |
Еще хотел бы написать еще об одной интересной штучке, подсмотрел на форуме, но хочу сохранить еще раз - логика для отображения времени скана программы:
Простейший принцип - фиксируется время каждого цикла и отнимается от времени предыдущего цикла - сработает на любом другом ПЛК, интересно даже проверить это потом как-нибудь.
Еще одна интересная фишка - есть и в документации и снова как всегда пишу тут чтобы не искать в документации:
Как видно на картинке можно задать разные цвета заливки для блока с восклицательным знаком - при разных условиях цвет будет разный. При этом удобно использовать массив цветов (в данном случае cell_err, причем обратить внимание на тип DINT - цвет требует больше памяти чем просто INT) и цикл для задания цветов для нескольких устройств. Конечно же даже если условий сработает несколько, то цвет останется в итоге по последнему условию, но в данном случае такое поведение не принципиально, потому что при любом раскладе требуется обратить внимание на состоянии ячейки. Чтобы при нормальной работе не отображать восклицательный знак - его показ можно скрыть по условию если цвет будет равен нулю, но в конструкции добавляется NOT - потому что свойство невидимости (или тогда <>0 без NOT)
.cell_err[1]=0
И еще кое-что. Для того чтобы реализовать мигающий красный восклицательный знак, пришлось добавить мигающий бит в программу. Сначала сделал на таймерах ,а потом увидел что есть готовый блок в библиотеке Utils:
Комментариев нет:
Отправить комментарий