Я писал когда-то инструкцию (сделанную по видео из интернета, которое в свою очередь было сделано на основе инструкций от Сименса) по настройке связи по TCP для ПЛК Сименс. Там использовался язык laddar diagram и стандартные блоки. Но проблема в том что при добавлении в новый проект такого функционала эти блоки снова надо накидывать по одному, а хочется просто в пару кликов добавить готовый FB блок и уже с ним работать. Благо такое уже придумали, нужно просто получить такой блок. Но проблема оказалась в том что с сайта сименса заполучить его та еще проблема, у меня были такие блоки давным давно скачены, поэтому я поделюсь ими и здесь. Но и это еще не все. Сименсовские блоки показались мне перегружены лишними входями и не очевидными настройками для IP адресов (но если читать инструкции то все очевидно), поэтому я решил сделать что-то свое что мне кажется интереснее. Мало того для генерации пульсов я добавил таймер, а не как это обычно принято в сименсе программистами специальный тактовый бит - мне кажется это правильнее. Весь блок пишется в SCL и тогда его можно легко экспортировать как Source файл (исходник). Дело в том что если накитывать все это в LADDER то он не дает экспортировать в исходник потому что используются блоки для коммуникации которые защищены ноухау защитой - а в scl формате такого сообщения нет.
Чем удобно scl еще в совремнных реалиях - его очень хорошо понимают нейронки и могут по коду дать кучу замечаний и даже исправить их. Я так делал - просил придумать более правильные названия для переменных (результат не сильно понравился ну да ладно), просил добавить комменты, перевести их на русский. Свой последний вариант скормил Квену - и он нашел еще 23 замечания. Некоторые не существенные, некоторые интересные, если делать по уму, то пожалуй надо сделать и их, но мне уже стало лень, хочется просто поделиться наработками.
Ну и да. Чтобы мой блок или сименс блок встроить в проект, нужно экспортировать исходный текст и сгенерировать блок, а чтобы использовать, перенести этот блок в свою программу и заполнить входа (даже выхода не обязательно):
А это сам код моего блочка, не думаю что нужны пояснения, но если вдруг нужны... спросите у ИИ - он прям очень четко объяснит, лучше меня)
| FUNCTION_BLOCK "UDP_SEND_v2" { S7_Optimized_Access := 'TRUE' } VERSION : 2.2 VAR_INPUT startConn : Bool; // Активация соединения и отправки данных connectionId : Word; // Уникальный ID соединения remoteIpOctet1 : Byte; // IP-адрес удаленного устройства, октет 1 remoteIpOctet2 : Byte; // IP-адрес удаленного устройства, октет 2 remoteIpOctet3 : Byte; // IP-адрес удаленного устройства, октет 3 remoteIpOctet4 : Byte; // IP-адрес удаленного устройства, октет 4 remotePort : Word; // Порт удаленного устройства timeInterval : Time := T#2S; // Интервал циклической отправки данных dataLen : UInt; // Количество байт для отправки (0 = отправить всё) END_VAR VAR_OUTPUT connDone : Bool; // Соединение успешно установлено connBusy : Bool; // Процесс установки соединения в процессе errorSum : Bool; // Общий сигнал ошибки блока sendStat : Word; // Код статуса выполнения TUSEND sendCounter : Byte; // Счетчик циклов отправки END_VAR VAR_IN_OUT dataToSend : Variant; // Массив или структура с данными для отправки END_VAR VAR fbTcon : TCON; statusWords : Struct // Диагностические слова ошибок связи conStatus : Word; sendStatus : Word; rcvStatus : Word; disconStatus : Word; END_STRUCT; tconParam : TCON_IP_v4; // Настройки IPv4 соединения isConnected : Bool; // Флаг установленного соединения sendAddrParam : TADDR_Param; // Параметры адреса назначения UDP fbTusend : TUSEND; fbTdiscon : TDISCON; isTimerDone : Bool; statSendDone : Bool; statSendBusy : Bool; statSendError : Bool; internalSendStat : Word; tonSendTimer : TON_TIME; internalError : Bool; oldStartConn : Bool; END_VAR VAR_TEMP tempTconStat : Word; tempTconDone : Bool; tempTconBusy : Bool; tempTconError : Bool; tempTdisconError : Bool; tempTdisconStat : Word; sendReqPulse : Bool; actualTimeInterval : Time; END_VAR BEGIN // Инициализация параметров соединения #tconParam.InterfaceId := 64; #tconParam.ID := #connectionId; #tconParam.ConnectionType := 19; // 19 = UDP, 11 = TCP/IP #tconParam.ActiveEstablished := FALSE; // FALSE для UDP (неустановленное соединение) #tconParam.RemotePort := #remotePort; #tconParam.LocalPort := 2000; // Заполнение структуры удаленного IP-адреса и порта #sendAddrParam.REM_IP_ADDR[1] := #remoteIpOctet1; #sendAddrParam.REM_IP_ADDR[2] := #remoteIpOctet2; #sendAddrParam.REM_IP_ADDR[3] := #remoteIpOctet3; #sendAddrParam.REM_IP_ADDR[4] := #remoteIpOctet4; #sendAddrParam.REM_PORT_NR := #remotePort; // Сброс статусных слов при новом старте блока IF #startConn AND NOT #oldStartConn THEN #statusWords.conStatus := 16#0; #statusWords.sendStatus := 16#0; END_IF; #oldStartConn := #startConn; // Управление установкой соединения #fbTcon(REQ := #startConn, DONE => #tempTconDone, STATUS => #tempTconStat, BUSY => #tempTconBusy, ERROR => #tempTconError, CONNECT := #tconParam, ID := #tconParam.ID); // Фиксация статуса соединения (для UDP выход DONE держится только 1 цикл) IF #tempTconDone THEN #isConnected := TRUE; ELSIF NOT #startConn OR #tempTconError THEN #isConnected := FALSE; END_IF; // Валидация заданного интервала времени IF #timeInterval <= T#0MS THEN #actualTimeInterval := T#2S; ELSE #actualTimeInterval := #timeInterval; END_IF; // Таймер интервала циклической отправки данных #tonSendTimer(IN := #startConn AND NOT #isTimerDone, PT := #actualTimeInterval); #isTimerDone := #tonSendTimer.Q; // Формируем импульс отправки, только если предыдущая отправка завершена #sendReqPulse := #isTimerDone AND NOT #statSendBusy; IF #sendReqPulse THEN #sendCounter := #sendCounter + 1; END_IF; // Выполнение отправки данных по UDP #fbTusend(REQ := #sendReqPulse, ID := #tconParam.ID, DONE => #statSendDone, BUSY => #statSendBusy, ERROR => #statSendError, STATUS => #internalSendStat, ADDR := #sendAddrParam, LEN := #dataLen, DATA := #dataToSend); #sendStat := #internalSendStat; // Управление разрывом соединения #fbTdiscon(ID := #tconParam.ID, ERROR => #tempTdisconError, STATUS => #tempTdisconStat, REQ := NOT #startConn); // Логирование диагностических кодов при ошибках IF #tempTconError THEN #statusWords.conStatus := #tempTconStat; END_IF; IF #statSendError THEN #statusWords.sendStatus := #internalSendStat; END_IF; #internalError := (#tempTconError OR #statSendError); // Привязка внутренних флагов к выходным переменным блока #connDone := #isConnected; #errorSum := #internalError; #connBusy := #tempTconBusy; END_FUNCTION_BLOCK DATA_BLOCK "Test_data_to_send" { S7_Optimized_Access := 'TRUE' } VERSION : 0.1 NON_RETAIN VAR "Array" : Array[0..5] of Byte; // Test byte array container IntArr : Array[0..5] of Int; // Test integer array container data : Struct // Test structured data container int1 : Int; "byte" : Byte; "string" : String; END_STRUCT; END_VAR BEGIN data.int1 := 123; data."byte" := 16#00AD; data."string" := 'Hello'; END_DATA_BLOCK |
| FUNCTION_BLOCK "T_COMM" TITLE = TCP communication function block { S7_Optimized_Access := 'TRUE' } AUTHOR : Siemens VERSION : 0.1 //Send and receive data over a TCP connection. Configurable as client or server and for sending and/or receiving data. VAR_INPUT Enable { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Enable communication ConnId { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : CONN_OUC; // Connection ID InterfaceId { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : HW_ANY := 64; // Interface ID ServerIP {InstructionName := 'IP_V4'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : IP_V4; // Server IP Address CommPort { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt; // Port to be used for communication IsServer { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Act as Server IsClient { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Act as Client DataLen { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt; // Bytes to send / receive RecData { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Enable receiving data SendData { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Enable sending data Continuous { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool := TRUE; // Continuous sending and receiving data WritePausePre { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Time := T#1S; // Pause between sending data END_VAR VAR_OUTPUT ConDn : Bool; // Connected ConBusy : Bool; // Waiting for connection Err : Bool; // Collective error signal END_VAR VAR_IN_OUT DataSend : Array[0..99] of Byte; // Byte array with user data to send DataRec : Array[0..99] of Byte; // Byte array with user data received END_VAR VAR TCon {InstructionName := 'TCON'; LibVersion := '4.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : TCON; // Establish communication connection TConParam {InstructionName := 'TCON_IP_v4'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : TCON_IP_v4; // Connection parameters to establish communication connection TSend {InstructionName := 'TSEND'; LibVersion := '4.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : TSEND; // Send data via communication connection TRcv {InstructionName := 'TRCV'; LibVersion := '4.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : TRCV; // Receive data via communication connection TDiscon {InstructionName := 'TDISCON'; LibVersion := '2.1'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : TDISCON; // Terminate communication connection TSendDnTof {InstructionName := 'TOF_TIME'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : TOF_TIME; // Off-Delay Timer send done TSendDelay { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Delay after sending active LocalEnable { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Enable logic FB internal Reset { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Reset communication Error { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Error present Connected { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Connection made Send1Time { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Send data only one time Rec1Time { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Receive data only one time TSendDn { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Sending done TRcvNdr { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // New data received SingleDataPulse { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Send / Receive one time - positive edge signal SingleDataMem { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Send / Receive one time - positive edge signal memory bit SingleData { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Send / Receive one time (continuous inverted) StatusWords { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct // Status words of communication instructions ConStatus { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Word; // Error status word of TCon SendStatus { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Word; // Error status word of TSend RcvStatus { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Word; // Error status word of TRcv DisconStatus { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Word; // Error status word of TDiscon END_STRUCT; END_VAR VAR_TEMP EnSend : Bool; // Enable TSend instruction EnRcv : Bool; // Enable TRcv instruction TConBusy : Bool; // TCon busy status TConDn : Bool; // TCon done status TConError : Bool; // TCon error status TConStat : Word; // TCon status word TSendBusy : Bool; // TSend busy status TSendError : Bool; // TSend error status TSendStatus : Word; // TSend status word TRcvBusy : Bool; // TRcv busy status TRcvError : Bool; // TRcv error status TRcvStatus : Word; // TRcv status word TRcvRcvdLen : UDInt; // TRcv data length received TDisconError : Bool; // TDiscon error status TDisconStatus : Word; // TDiscon status word END_VAR BEGIN //*** Connect Configuration #TConParam.InterfaceId := #InterfaceId; #TConParam.ID := #ConnId; #TConParam.ConnectionType := 11; //11 = TCP/IP #TConParam.ActiveEstablished := (#IsClient AND NOT #IsServer); //FALSE = Server; TRUE = Client #TConParam.RemoteAddress := #ServerIP; #TConParam.LocalPort := #CommPort; #TConParam.RemotePort := #TConParam.LocalPort; //*** End of connection configuration //Set a signal that data should be send and receive only one time #SingleData := #Enable AND NOT #Continuous; #SingleDataPulse := #SingleData AND NOT #SingleDataMem; #SingleDataMem := #SingleData; //Signal that data should only received one time (TRUE = receiving pending; FALSE = data received) #Rec1Time := #Enable AND ((#RecData AND #SingleDataPulse) OR (#Rec1Time AND NOT #TRcvNdr)); //Signal that data should only send one time (TRUE = sending pending; FALSE = data sent) #Send1Time := #Enable AND ((#SendData AND #SingleDataPulse) OR (#Send1Time AND NOT #TSendDn)); //Enable logic with enable input if other checks are OK #LocalEnable := #Enable AND NOT #Error AND (#Continuous OR #Rec1Time OR #Send1Time); //Enable Signal switched off #Reset := NOT #LocalEnable; //Connect #TCon(REQ := #LocalEnable, DONE => #TConDn, STATUS => #TConStat, BUSY => #TConBusy, ERROR => #TConError, CONNECT := #TConParam, ID := #TConParam.ID); //Connection status #Connected := #TConDn OR (#Connected AND #LocalEnable); //Enable TSEND instruction #EnSend := #Connected AND #SendData AND (#Send1Time OR #Continuous) AND NOT #TSendDelay; //Send data #TSend(ID := #TCon.ID, LEN := #DataLen, DATA := #DataSend, REQ := #EnSend, ERROR => #TSendError, STATUS => #TSendStatus, DONE => #TSendDn, BUSY => #TSendBusy); //Delay between sending data #TSendDnTof(IN := #TSendDn, PT := #WritePausePre, Q => #TSendDelay); //Enable TRCV instruction #EnRcv := #Connected AND #RecData AND (#Rec1Time OR #Continuous); //Receive data #TRcv(ID := #TCon.ID, LEN := #DataLen, DATA := #DataRec, ERROR => #TRcvError, EN_R := #EnRcv, STATUS => #TRcvStatus, NDR => #TRcvNdr, BUSY => #TRcvBusy, RCVD_LEN => #TRcvRcvdLen); // //Disconnect #TDiscon(ID := #TCon.ID, ERROR => #TDisconError, STATUS => #TDisconStatus, REQ := #Reset); //Fault signal - collect all fault from all T_ instructions #Error := (#TConError OR #TSendError OR #TRcvError OR #TDisconError) OR (#Error AND #Enable); //In case of error, store the status word IF #TConError THEN #StatusWords.ConStatus := #TConStat; END_IF; //In case of error, store the status word IF #TSendError THEN #StatusWords.SendStatus := #TSendStatus; END_IF; //In case of error, store the status word IF #TRcvError THEN #StatusWords.RcvStatus := #TRcvStatus; END_IF; //In case of error, store the status word IF #TDisconError THEN #StatusWords.DisconStatus := #TDisconStatus; END_IF; //Status Outputs #ConDn := #Connected; #Err := #Error; #ConBusy := #TConBusy; END_FUNCTION_BLOCK DATA_BLOCK "DB_DATA" { S7_Optimized_Access := 'TRUE' } VERSION : 0.1 NON_RETAIN VAR UserDataSend : Array[0..99] of Byte; // User Data to send UserDataRec : Array[0..99] of Byte; // User Data received END_VAR VAR RETAIN ServerIP {InstructionName := 'IP_V4'; LibVersion := '1.0'} : IP_V4; // Server IP Address CommPort : UInt; END_VAR BEGIN ServerIP.ADDR[1] := 0; ServerIP.ADDR[2] := 0; ServerIP.ADDR[3] := 0; ServerIP.ADDR[4] := 0; CommPort := 2001; END_DATA_BLOCK |

Комментариев нет:
Отправить комментарий