Modbus Tcp Server编程(codesys2.3)(含完整源程序)

PLC_OPEN 是工业自动化编程领域的一个标准, Codesys是德国3S 公司开发的软件系统平台,它是完整支持PLC_OPEN标准的软件系统平台(即IEC61131-3标准)。支持标准IL 、ST、 FBD 、LD、 CFC、 SFC 六种PLC编程语言,用户可以在同一项目中选择不同的语言编辑子程序,功能模块等。

目前许多公司的自动化控制器都是基于这个平台开发的,例如施耐德,倍福,ABB,易福门,派芬,和利时等。

使用Codesys编程实现Modbus Tcp Server的功能,方便了客户使用带以太网接口的控制器和HMI或PC的通讯。降低了客户的成本,增强了系统使用的灵活性。

Modbus Tcp 应用层协议及数据帧介绍

Modbus-TCP已成为当今开放的互联网标准之一,并为互联网标准的组织IETF(互联网工程任务委员会)认可。由于Modbus协议部分未经改动,众所周知的Modbus服务和对象模式依然有效,只是将它的作为传输层协议移植到TCP/IP上。

Modbus Tcp应用层的协议是独立于通讯媒介,根据客户/服务器原理组织的。客户发送请求帧来请求服务,服务器回应响应帧。请求帧和响应帧包含了参数和/或数据。在图 1中显示了标准Modbus Tcp帧格式。标准的Modbus通讯中从站地址和CRC校验的处理优先于功能码,但在Modbus Tcp中地址和校验由底层的Tcp协议完成。

Modbus Tcp和Modbus_RTU 在数据报文的结构上存在的差异是报文帧头由MBAP 替换了Slave ID。

MBAP由7个字节组成,并且出现在每个Modbus Tcp 信息帧的头部,这7个字节分别由Transaction Identifier(2个字节,传输标志),Protocol Identifier(2个字节,协议标志),Length(2个字节),Unit Identifier(一个字节,设备站号)。

图 1: 使用Modbus-TCP,命令和用户数据可不经任何修改而封装在TCP/IP的数据容器中

图 2: Modbus-TCP ADU在经过以太网TCP/IP协议封装之后的结构

1. 实现MODBUS_TCP SERVER 的硬件及软件配置

1.1该项目选择LTI 公司的MOTION ONE PAC控制器

                 (型号:LACP242,INTEL_ATOM CPU ,1.1GHZ)

硬件资源:ETHERNET 10/100M 接口,软件CODESYS2.3 软件平台(图3) 

图3.Codesys2.3软件配置平台

1.2 选择3S公司CODESYS2.3软件平台内的TCP/IP通讯库函数SyslibSockets.lib ,同时主要使用到的功能函数如下:

  • SysSockInetAddr(ip):绑定指定的以太网接口的IP地址,控制器本体上的以太网端口的IP 地址

  • SysSockHtons(port):绑定以太网端口,MODBUS_TCP 通常使用502端口

  • SysSockCreate(SOCKET_AF_INET,SOCKET_STREAM,0):Tcp/IP Socket的创建

  • SysSockBind(socketId, ADR(), SIZEOF()):Tcp/IP Socket绑定指定端口和IP 地址

  • SysSockListen(socketId, 255):Tcp/IP Socket进行端口监听

  • SysSockSelect(SOCKET_FD_SETSIZE, ADR(), 0, 0, ADR()):TCP/IP client的选择

  • SysSockAccept(socketId, ADR(), ADR()):TCP/IP Socket 接受client 设备的链接

  • SysSockRecv(SocketHandle,ADR(),SIZEOF(),1):TCP/IP Socket 接收client 设备的数据包

  • SysSockSend(SocketHandle,ADR(),(),1):TCP/IP Socket 返回数据包,发送至Client设备

  • SysSockClose(socketId): 关闭TCP/IP Socket。

1.3 MODBUS_TCP server的创建过程

创建MODBUS_TCP server的过程主要分为以下几个步骤:

1.3.1 TCP/IP Socket库文件的安装

在CODESYS2.3软件平台的library manager 栏目下添加SyslibSockets.lib。之后则可以正常调用以太网通讯函数。

1.3.2   申明各种通讯参数变量及数据结构,例如MODBUS_TCP SERVER IP地址及端口,收发数据的数组:

 Var_Global

        addressPointer:POINTER TO SOCKADDRESS;

        address:SOCKADDRESS;

        ip:STRING:='192.168.39.100'; (*控制器本体 IP 地址*)

 ……

        port: WORD:=502;   (*MODBUS-TCP 端口*)

        objectArray:ARRAY[0..6] OF REAL;

        tcp_connect_state:BOOL;(*TCP/IP连接状态字*)

    protocol_id:INT; (*协议标志ID,modbus id=00 00*)

        device_id:INT; (*设备站号ID*)

END_VAR

1.3.3   MODBUS_TCP SERVER 主程序说明。

主程序结构框图及部分程序如下:

 

    

主程序通过调用SyslibSockets.lib库内部的Sockets函数完成端口绑定,并实施对应端口的监听,当Modbus Tcp客户端请求建立连接时候,服务器端建立连接,并进行数据的交换读写,为了便于判断客户端和服务器端的连接状态,程序做了实时的报文刷新,当通讯建立连接之后,服务器端没有接收到新的报文,且维持一段时间后,则按照通讯中断处理,关闭Sockets,并重新初始化参数,服务器端再次进入监听状态,这种控制模式可以辨识网络物理断线和客户端异常断开这些情况。

address.sin_addr:=SysSockInetAddr(ip);

IF terminate=FALSE THEN

CASE tcp_state OF

0:

  socketId := SysSockCreate(SOCKET_AF_INET,SOCKET_STREAM,0);

 IF socketId <> SOCKET_INVALID THEN

    SysSockSetOption(socketId, SOCKET_SOL, SOCKET_SO_REUSEADDR, ADR(dwValue), SIZEOF (dwValue));

 tcp_state := 10;

 END_IF

10:bResult := SysSockBind(socketId, ADR(address), SIZEOF(address));

IF bResult THEN

tcp_state := 20;

END_IF

……

50:

SocketHandle := SlaveSocketList.fd_array[TCPindex];

IF SocketHandle = socketId THEN

diSize := SIZEOF(Address);

SocketHandle:=SysSockAccept(socketId, ADR(Address), ADR(address));

tcp_state := 100;

END_IF

100:

 (* SysSockSend(socketId,ADR(send),SIZEOF(send),1);*)

SysSockRecv(SocketHandle,ADR(receive1),SIZEOF(receive1),1);

 ……

   IF protocol_id=0 AND  device_id>=0  AND receive1[1]<>0 THEN (*modbus_tcp,protocol_id=0*)

     frame_process();

     tcp_state := 120;

   END_IF;

 END_CASE;

END_IF;

1.3.4   创建MODBUS_TCP SERVER 报文处理程序(部分子程序)

当服务器端 接收到客户端的报文之后,经过了Modbus Tcp 协议ID和功能码有效性判断之后,调用报文处理程序,在报文处理程序中,主要是根据01,02,03,04,05,06,15,16 MODBU-TCP读写字,读写位功能码分别进行处理。例如进行写寄存器功能的处理时,先判断写入寄存器的起始地址和写入寄存器个数,再进行带地址偏移的赋值,在赋值过程中要进行高低字节的转换,以保证数据的正确性。

Frame_process (*MODBUS_TCP报文处理*)

   IF  (send_do =FALSE )THEN

   CASE input_byte1[2] OF

 

   03:(*读寄存器*)

        ……

        address_temp:=SHL(BYTE_TO_INT(input_byte1[3]),8) + ( BYTE_TO_INT(input_byte1[4]));

        ……

         FOR move_to_send:=address_temp TO  address_temp+(length_temp)*2  DO

           output_byte[4+move_to_send-address_temp]:=mw_area[move_to_send+address_temp+1];

         END_FOR;

         ……  

   END_CASE;

  END_IF;

2 .MODBUS_TCP server 的验证使用

在MODBUS_TCP sever通讯程序完成之后,通过wireshark以太网抓包软件分析MODBUS-TCP报文,并使用Easybuilder800触摸屏软件实现了触摸屏和PAC控制器的MODBUS_TCP通讯。

 图4. wireshark以太网抓包软件分析的MODBUS-TCP报文

图5. 使用Easybuilder800触摸屏软件配置通讯接口

图6. 使用Easybuilder800触摸屏软件监控控制器变量状态

结论

使用Codesys软件平台的sockets 编程可以实现PAC控制器和任意公开协议的以太网设备进行通讯,Codesys内部的库函数的功能丰富,PAC控制器通讯编程的灵活性远比PLC强大。

程序配置及源代码

MODBUS TCP SERVER 在LTI MOTION ONE 控制器上的实现 (Codesys)

一.功能简要概述:

由于客户的成本需求,客户想选择第三方的触摸屏幕和LTI 的MOTION ONE 进行通讯(以太网),通讯接口选择了控制器本体的以太网接口,由于MODBUS TCP SERVER 通讯软件功能块不是CODESYS内核自有的块,需要另外购买,所以我利用CODESYS内部原有的底层功能块(SYSockets.LIB)开发了一个MODBUS TCP SERVER DEMO 程序,这样MOTION ONE 就可以和市面上几乎所有的触摸屏进行以太网通讯了。

二.程序配置及源代码:

经过和MODSCAN和威伦触摸屏的实际MODBU-TCP通讯,验证了这个程序的功能,这个程序支持01,03,05,06,15,16 MODBU-TCP读写字,读写位功能码。
  VAR_GLOBAL
    VIRTUALPOS: LREAL;
    TEST_T:INT;
    can_encoder_pos:UDINT;
    input_byte:ARRAY[1..250] OF BYTE;
    input_byte1:ARRAY[1..250] OF BYTE;
    mw_area AT %MB1000:ARRAY[1..500] OF BYTE;  /***%mw交换区域***/
    mx_area  : ARRAY[1..512] OF BOOL;          /***%mx交换区域***/
    output_byte: ARRAY[1..250]OF BYTE;
    mw_temp AT%MW1000: ARRAY[1..500] OF INT;
    write_len:INT;
    send_do: BOOL;
    mw_area_use  AT %MW2000:ARRAY[1..500] OF INT;
    ton2: TON;
    test_trigger: BOOL;
    t1: TON;
    receive1_tmp:BYTE;
    flag:BOOL;
    test_lreal AT%MD1000 :LREAL;
    EN_READ: BOOL;
    en_camin: BOOL;
    EN_CAM: BOOL;
    addressPointer:POINTER TO SOCKADDRESS;
    address:SOCKADDRESS;
    ip:STRING:='192.168.39.100'; (*PLC IP ADDRESS*)
    send:STRING:='0001t';
    receive:STRING;
    receive1:ARRAY[1..300] OF BYTE;
    port: WORD:=502;   (*MODBUS——TCP 端口*)
    objectArray:ARRAY[0..6] OF REAL;
    tcp_connect_state:BOOL;
    tcp_connect_time1:TON;
    tcp_connect_time2:TON;
    get_last: BOOL;
    protocol_id:INT;
    device_id:INT;
    send1:ARRAY[1..300] OF BYTE;
    torque_real AT%MD2012:REAL;
    acttorque: LREAL;
    actvelocity:LREAL;
    virtual_speed AT %MD2016:DINT;
   tttt AT %MD2000:ARRAY[1..100] OF DINT;
   Master_axis_ref:axis_ref;
   switch_master:INT;
   start_syn:BOOL;
   in_syn:BOOL;
   end_of_profile:BOOL;

END_VAR

PROGRAM TCP_socket_test
VAR
    result: BOOL;
    socketId: DINT;
    terminate: BOOL;
    dwValue: DINT;
    bResult: BOOL;
    test_state: BOOL;
    send: STRING;
    error_now: BOOL;
    time_trigger: BOOL;
    SysSockGetLastError1:SysSockGetLastError;
    MasterSocketList:SOCKET_FD_SET;
    SlaveSocketList:SOCKET_FD_SET;
    SelectTimeOut:SOCKET_TIMEVAL;
    TCPindex: INT;
    diSize: DINT;
    TCPMax: DINT;

    tcp_state: INT;
    SocketHandle:DINT;
    move_ref: INT;
END_VAR

PROGRAM :TCP_socket_test

 address.sin_addr:=SysSockInetAddr(ip);//**初始化本机IP,端口//
 address.sin_family:=SOCKET_AF_INET;
 address.sin_port:=SysSockHtons(port);
 addressPointer:=ADR(address);//**地址指针赋值//

IF terminate=FALSE THEN
CASE tcp_state OF
0:
  socketId := SysSockCreate(SOCKET_AF_INET,SOCKET_STREAM,0);//**初始化sockets//
 IF socketId <> SOCKET_INVALID THEN
  dwValue := 1;
  (*Set the option that we can reuse 'old' port addresses *)
  SysSockSetOption(socketId, SOCKET_SOL, SOCKET_SO_REUSEADDR, ADR(dwValue), SIZEOF (dwValue));
  tcp_state := 10;
 END_IF

10:bResult := SysSockBind(socketId, ADR(address), SIZEOF(address));//**绑定端口//
  IF bResult THEN
   tcp_state := 20;
  END_IF

20:
bResult := SysSockListen(socketId, 255);//**监听端口状态//
 IF bResult THEN
  MasterSocketList.fd_count := 1;
  MasterSocketList.fd_array[0] := socketId;
  tcp_state := 30;
 END_IF

30:
 SlaveSocketList:=MasterSocketList;
 SelectTimeOut.tv_sec := 30;
 SelectTimeOut.tv_usec := 0;
 diSize := SysSockSelect(SOCKET_FD_SETSIZE, ADR(SlaveSocketList), 0, 0, ADR(SelectTimeOut));
IF diSize > 0 THEN
  tcp_state := 50;
  TCPindex := 0;
END_IF

50:
SocketHandle := SlaveSocketList.fd_array[TCPindex];
 IF SocketHandle = socketId THEN
   diSize := SIZEOF(Address);
   SocketHandle:=SysSockAccept(socketId, ADR(Address), ADR(address));
  IF SocketHandle <> SOCKET_INVALID THEN
    TCPMax := SocketHandle;
    MasterSocketList.fd_array[MasterSocketList.fd_count] := SocketHandle;
    MasterSocketList.fd_count := MasterSocketList.fd_count + 1;
   END_IF
    tcp_state := 100;
 END_IF

100:

 (* SysSockSend(socketId,ADR(send),SIZEOF(send),1);*)
SysSockRecv(SocketHandle,ADR(receive1),SIZEOF(receive1),1);
 FOR move_ref:=7 TO receive1[6]+7  DO
  input_byte1[move_ref-6] :=receive1[move_ref];
 END_FOR;
   protocol_id:=BYTE_TO_INT(receive1[4]);
   device_id:=BYTE_TO_INT(receive1[7]);
 IF protocol_id=0 AND  device_id>=0  AND receive1[1]<>0 THEN (*modbus_tcp,protocol_id=0*)
    frame_process();
    tcp_state := 120;
 END_IF;

120:
 IF send_do THEN
  send1[1]:=receive1[1];send1[2]:=receive1[2];
  send1[3]:=0;send1[4]:=0;
  send1[5]:=0;send1[6]:=INT_TO_BYTE(write_len-2);
 FOR move_ref:=7 TO 7+(write_len-2) DO
   send1[move_ref]:=output_byte[move_ref-6];
 END_FOR;

SysSockSend(SocketHandle,ADR(send1),(write_len-1+6),1);
receive1[1]:=0;
send_do:=FALSE;
END_IF;
  tcp_state := 100;
END_CASE;
END_IF;

IF terminate = TRUE THEN (* close socket *)
   SysSockClose(socketId);
   socketId := 0;
   tcp_state:=0;
   result := FALSE;
END_IF

PROGRAM frame_process
VAR
    length_check:INT;
    crc_out:WORD;
    crc_temp:WORD;
    address_temp:INT;
    move_to_send:INT;
    byte_len:INT;
    mod_state:INT;
    length_temp: INT;
    length_check1: INT;
    crc_out1: WORD;
    write_area:ARRAY[1..250] OF BYTE;
    x:INT;
    control_temp: WORD;
    offset_temp:INT;
END_VAR
Frame_process 
IF (( input_byte1[1]<16#FF) AND( input_byte1[1]>=0)) AND( (input_byte1[2]=01) OR (input_byte1[2]=05) OR( input_byte1[2]=03 ) OR( input_byte1[2]=6 )OR(input_byte1[2]=16)OR(input_byte1[2]=15)) THEN
   (*CRC16 check action   *)
    IF input_byte1[2]=03  OR  input_byte1[2]=06   OR input_byte1[2]=01 OR  input_byte1[2]=05 THEN
     length_check:=6;
   END_IF;

    IF input_byte1[2]=16  OR  input_byte1[2]=15 THEN
      length_check:=7+input_byte1[7];
    END_IF;

   (* crc_check( lengthcrc:= length_check,
                          inbyte:=input_byte1,
                          CRC16CVT=>crc_out  );
    crc_temp:=SHL(BYTE_TO_WORD(input_byte1[length_check+1] ),8) OR  BYTE_TO_WORD(input_byte1[length_check+2] );*)

  (* CRC16 check OK then the process is going on ,judge the function code to response *)
   IF (*  ( crc_out = crc_temp<>0) AND*) (send_do =FALSE )THEN
   CASE input_byte1[2] OF
   01:(*read the coils*) //**读取线圈状态**//
        output_byte[1]:=17; output_byte[2]:=01;
           address_temp:=SHL(BYTE_TO_INT(input_byte1[3]),8) + ( BYTE_TO_INT(input_byte1[4]));
            length_temp:=SHL(BYTE_TO_INT(input_byte1[5]),8) +(BYTE_TO_INT(input_byte1[6]));
            mod_state:=length_temp MOD 8;
              IF mod_state<>0 THEN
                 byte_len:=1+length_temp/8;
              ELSE
                  byte_len:=length_temp/8;
              END_IF;
            output_byte[3]:=INT_TO_BYTE(byte_len);
             FOR move_to_send:=4 TO byte_len+3 DO
             IF mod_state=0 THEN
             output_byte[move_to_send].0:=mx_area[address_temp+1+(move_to_send-4)*8]; output_byte[move_to_send].1:=mx_area[address_temp+(move_to_send-4)*8+2];
             output_byte[move_to_send].2:=mx_area[address_temp+(move_to_send-4)*8+3]; output_byte[move_to_send].3:=mx_area[address_temp+(move_to_send-4)*8+4];
             output_byte[move_to_send].4:=mx_area[address_temp+(move_to_send-4)*8+5]; output_byte[move_to_send].5:=mx_area[address_temp+(move_to_send-4)*8+6];
             output_byte[move_to_send].6:=mx_area[address_temp+(move_to_send-4)*8+7]; output_byte[move_to_send].7:=mx_area[address_temp+(move_to_send-4)*8+8];
             ELSE
               IF move_to_send=byte_len+3 THEN
               CASE mod_state OF
                1: output_byte[move_to_send].0:=mx_area[address_temp+(move_to_send-4)*8+1];output_byte[move_to_send]:=output_byte[move_to_send] AND 16#0001;
                2: output_byte[move_to_send].0:=mx_area[address_temp+(move_to_send-4)*8+1];output_byte[move_to_send].1:=mx_area[address_temp+(move_to_send-4)*8+2];
                   output_byte[move_to_send]:=output_byte[move_to_send] AND 16#0003;

                3: output_byte[move_to_send].0:=mx_area[address_temp+(move_to_send-4)*8+1];output_byte[move_to_send].1:=mx_area[address_temp+(move_to_send-4)*8+2];
                   output_byte[move_to_send].2:=mx_area[address_temp+(move_to_send-4)*8+3]; output_byte[move_to_send]:=output_byte[move_to_send] AND 16#0007;

                4: output_byte[move_to_send].0:=mx_area[address_temp+(move_to_send-4)*8+1];output_byte[move_to_send].1:=mx_area[address_temp+(move_to_send-4)*8+2];
                   output_byte[move_to_send].2:=mx_area[address_temp+(move_to_send-4)*8+3]; output_byte[move_to_send].3:=mx_area[address_temp+(move_to_send-4)*8+4];
                   output_byte[move_to_send]:=output_byte[move_to_send] AND 16#000F;

                5:output_byte[move_to_send].0:=mx_area[address_temp+(move_to_send-4)*8+1];output_byte[move_to_send].1:=mx_area[address_temp+(move_to_send-4)*8+2];
                  output_byte[move_to_send].2:=mx_area[address_temp+(move_to_send-4)*8+3]; output_byte[move_to_send].3:=mx_area[address_temp+(move_to_send-4)*8+4];
                  output_byte[move_to_send].4:=mx_area[address_temp+(move_to_send-4)*8+5]; output_byte[move_to_send]:=output_byte[move_to_send] AND 16#001F;

                6:output_byte[move_to_send].0:=mx_area[address_temp+(move_to_send-4)*8+1];output_byte[move_to_send].1:=mx_area[address_temp+(move_to_send-4)*8+2];
                  output_byte[move_to_send].2:=mx_area[address_temp+(move_to_send-4)*8+3]; output_byte[move_to_send].3:=mx_area[address_temp+(move_to_send-4)*8+4];
                  output_byte[move_to_send].4:=mx_area[address_temp+(move_to_send-4)*8+5]; output_byte[move_to_send].5:=mx_area[address_temp+(move_to_send-4)*8+6];
                  output_byte[move_to_send]:=output_byte[move_to_send] AND 16#003F;

                 7:output_byte[move_to_send].0:=mx_area[address_temp+(move_to_send-4)*8+1];output_byte[move_to_send].1:=mx_area[address_temp+(move_to_send-4)*8+2];
                   output_byte[move_to_send].2:=mx_area[address_temp+(move_to_send-4)*8+3]; output_byte[move_to_send].3:=mx_area[address_temp+(move_to_send-4)*8+4];
                   output_byte[move_to_send].4:=mx_area[address_temp+(move_to_send-4)*8+5]; output_byte[move_to_send].5:=mx_area[address_temp+(move_to_send-4)*8+6];
                   output_byte[move_to_send].6:=mx_area[address_temp+(move_to_send-4)*8+7]; output_byte[move_to_send]:=output_byte[move_to_send] AND 16#007F;

                END_CASE;
                 ELSE
                  output_byte[move_to_send].0:=mx_area[address_temp+1+(move_to_send-4)*8]; output_byte[move_to_send].1:=mx_area[address_temp+(move_to_send-4)*8+2];
                  output_byte[move_to_send].2:=mx_area[address_temp+(move_to_send-4)*8+3]; output_byte[move_to_send].3:=mx_area[address_temp+(move_to_send-4)*8+4];
                  output_byte[move_to_send].4:=mx_area[address_temp+(move_to_send-4)*8+5]; output_byte[move_to_send].5:=mx_area[address_temp+(move_to_send-4)*8+6];
                  output_byte[move_to_send].6:=mx_area[address_temp+(move_to_send-4)*8+7]; output_byte[move_to_send].7:=mx_area[address_temp+(move_to_send-4)*8+8];
               END_IF;
             END_IF;
              END_FOR;
          length_check1:=3+byte_len;
        (*  crc_check( lengthcrc:= length_check1,
                          inbyte:=output_byte,
                          CRC16CVT=>crc_out1 );
          output_byte[4+byte_len]:= WORD_TO_BYTE(SHR((crc_out1 AND 16#FF00 ),8));
          output_byte[5+byte_len]:= WORD_TO_BYTE(crc_out1 AND 16#FF );*)
           write_len:= 5+byte_len;send_do:=TRUE;

  15:(*force multi coils*)//**强制多线圈状态**//
             address_temp:=SHL(BYTE_TO_INT(input_byte1[3]),8) + ( BYTE_TO_INT(input_byte1[4]));
            length_temp:=SHL(BYTE_TO_INT(input_byte1[5]),8) +(BYTE_TO_INT(input_byte1[6]));

             mod_state:=length_temp MOD 8;

            FOR move_to_send:=address_temp TO address_temp+length_temp-1  BY 8 DO
                 offset_temp:=move_to_send-address_temp;
                 IF mod_state=0 THEN
                mx_area[move_to_send+1]:=input_byte[offset_temp/8+8].0;

mx_area[move_to_send+2]:=input_byte[offset_temp/8+8].1;
                mx_area[move_to_send+3]:=input_byte[offset_temp/8+8].2;

mx_area[move_to_send+4]:=input_byte[offset_temp/8+8].3;
                mx_area[move_to_send+5]:=input_byte[offset_temp/8+8].4;

mx_area[move_to_send+6]:=input_byte[offset_temp/8+8].5;
                mx_area[move_to_send+7]:=input_byte[offset_temp/8+8].6;

mx_area[move_to_send+8]:=input_byte[offset_temp/8+8].7;
                ELSE
                  IF move_to_send-address_temp-length_temp+1<=8 THEN
                   CASE mod_state OF
                  1:  mx_area[move_to_send+1]:=input_byte[offset_temp/8+8].0;
                  2:  mx_area[move_to_send+1]:=input_byte[offset_temp/8+8].0;

mx_area[move_to_send+2]:=input_byte[offset_temp/8+8].1;
                  3:  mx_area[move_to_send+1]:=input_byte[offset_temp/8+8].0;

mx_area[move_to_send+2]:=input_byte[offset_temp/8+8].1;
                mx_area[move_to_send+3]:=input_byte[offset_temp/8+8].2;

                  4: mx_area[move_to_send+1]:=input_byte[offset_temp/8+8].0;

mx_area[move_to_send+2]:=input_byte[offset_temp/8+8].1;
                mx_area[move_to_send+3]:=input_byte[offset_temp/8+8].2;

mx_area[move_to_send+4]:=input_byte[offset_temp/8+8].3;

                  5:  mx_area[move_to_send+1]:=input_byte[offset_temp/8+8].0;

mx_area[move_to_send+2]:=input_byte[offset_temp/8+8].1;
                mx_area[move_to_send+3]:=input_byte[offset_temp/8+8].2;

mx_area[move_to_send+4]:=input_byte[offset_temp/8+8].3;
                mx_area[move_to_send+5]:=input_byte[offset_temp/8+8].4;

                  6:mx_area[move_to_send+1]:=input_byte[offset_temp/8+8].0;

mx_area[move_to_send+2]:=input_byte[offset_temp/8+8].1;
                mx_area[move_to_send+3]:=input_byte[offset_temp/8+8].2;

mx_area[move_to_send+4]:=input_byte[offset_temp/8+8].3;
                mx_area[move_to_send+5]:=input_byte[offset_temp/8+8].4;

mx_area[move_to_send+6]:=input_byte[offset_temp/8+8].5;

                 7:  mx_area[move_to_send+1]:=input_byte[offset_temp/8+8].0;

mx_area[move_to_send+2]:=input_byte[offset_temp/8+8].1;
                mx_area[move_to_send+3]:=input_byte[offset_temp/8+8].2;

mx_area[move_to_send+4]:=input_byte[offset_temp/8+8].3;
                mx_area[move_to_send+5]:=input_byte[offset_temp/8+8].4;

mx_area[move_to_send+6]:=input_byte[offset_temp/8+8].5;
                mx_area[move_to_send+7]:=input_byte[offset_temp/8+8].6;
                    END_CASE;
                   ELSE
                mx_area[move_to_send+1]:=input_byte[offset_temp/8+8].0;

mx_area[move_to_send+2]:=input_byte[offset_temp/8+8].1;
                mx_area[move_to_send+3]:=input_byte[offset_temp/8+8].2;

mx_area[move_to_send+4]:=input_byte[offset_temp/8+8].3;
                mx_area[move_to_send+5]:=input_byte[offset_temp/8+8].4;

mx_area[move_to_send+6]:=input_byte[offset_temp/8+8].5;
                mx_area[move_to_send+7]:=input_byte[offset_temp/8+8].6;

mx_area[move_to_send+8]:=input_byte[offset_temp/8+8].7;

                   END_IF;

               END_IF;
             END_FOR;

          FOR move_to_send:=2 TO 6 DO
          output_byte[move_to_send]:=input_byte1[move_to_send];
          END_FOR;
          output_byte[1]:=17;
           length_check1:=6;
         (* crc_check( lengthcrc:= length_check1,
                          inbyte:=output_byte,
                          CRC16CVT=>crc_out1 );
          output_byte[7]:= WORD_TO_BYTE(SHR((crc_out1 AND 16#FF00 ),8));
          output_byte[8]:= WORD_TO_BYTE(crc_out1 AND 16#FF );*)
           write_len:= 8;send_do:=TRUE;

  05: (*force the coil*)//**读取线圈状态**//
          FOR move_to_send:=2 TO 8 DO
          output_byte[move_to_send]:=input_byte1[move_to_send];
          END_FOR;
           output_byte[1]:=17;
         address_temp:=SHL(BYTE_TO_INT(input_byte1[3]),8)+ BYTE_TO_INT(input_byte1[4]);
         control_temp:=INT_TO_WORD(SHL(BYTE_TO_INT(input_byte1[5]),8) +(BYTE_TO_INT(input_byte1[6])));
         IF control_temp=16#FF00 THEN
          mx_area[address_temp+1]:=TRUE;
         END_IF;
         IF control_temp=16#0000 THEN
          mx_area[address_temp+1]:=FALSE;
         END_IF;
          write_len:= 8;send_do:=TRUE;

   03:(*read the registers*)//**读取寄存器状态**//
        output_byte[1]:= 17; output_byte[2]:=03;
         address_temp:=SHL(BYTE_TO_INT(input_byte1[3]),8) + ( BYTE_TO_INT(input_byte1[4]));
         length_temp:=SHL(BYTE_TO_INT(input_byte1[5]),8) +(BYTE_TO_INT(input_byte1[6]));
         output_byte[3]:=INT_TO_BYTE((length_temp)*2);
         FOR move_to_send:=address_temp TO  address_temp+(length_temp)*2  DO
           output_byte[4+move_to_send-address_temp]:=mw_area[move_to_send+address_temp+1];
         END_FOR;
          length_check1:=3+(length_temp)*2;
         (* crc_check( lengthcrc:= length_check1,
                          inbyte:=output_byte,
                          CRC16CVT=>crc_out1 );
          output_byte[4+(length_temp)*2]:= WORD_TO_BYTE(SHR((crc_out1 AND 16#FF00 ),8));
          output_byte[5+(length_temp)*2]:= WORD_TO_BYTE(crc_out1 AND 16#FF );*)
           write_len:= 5+(length_temp)*2;send_do:=TRUE;

   16:(* write the registers*)//**写寄存器**//
          FOR move_to_send:=2 TO 6 DO
          output_byte[move_to_send]:=input_byte1[move_to_send];
          END_FOR;
          output_byte[1]:=17;
         address_temp:=SHL(BYTE_TO_INT(input_byte1[3]),8)+ BYTE_TO_INT(input_byte1[4]);
         length_temp:=SHL(BYTE_TO_INT(input_byte1[5]),8)+ BYTE_TO_INT(input_byte1[6]);
          FOR move_to_send:=address_temp TO  address_temp+(length_temp)*2-1  DO
           mw_area[move_to_send+address_temp+1]:=input_byte1[8+move_to_send-address_temp];
         END_FOR;
          length_check1:=6;
       (*  crc_check( lengthcrc:= length_check1,
                          inbyte:=output_byte,
                          CRC16CVT=>crc_out1 );
          output_byte[7]:= WORD_TO_BYTE(SHR((crc_out1 AND 16#FF00 ),8));
          output_byte[8]:= WORD_TO_BYTE(crc_out1 AND 16#FF );*)
           write_len:=8;send_do:=TRUE;

  06:(*force the single register*)//**强制单一寄存器**//
          FOR move_to_send:=2 TO 8 DO
          output_byte[move_to_send]:=input_byte1[move_to_send];
          END_FOR;
            output_byte[1]:=17;
           address_temp:=SHL(BYTE_TO_INT(input_byte1[3]),8)+ BYTE_TO_INT(input_byte1[4]);
           mw_area[address_temp*2+1]:=input_byte1[5];

mw_area[address_temp*2+2]:=input_byte1[6];
           write_len:= 8;send_do:=TRUE;

   END_CASE;

   END_IF;

END_IF;

作 者 简 介    

— 蒋勇飞 —

毕业于上海大学,2000年参加工作。从2000年到2007年在上海电气自动化设计研究所工作,主要从事隧道及污水处理工程项目的设计编程调试工作。

2007-2013年去了LTI德国伺服传动有限公司和施耐德研发中心,主要从事过伺服传动及运动控制项目的支持和项目实施,在施耐德研发部主要从事高中端PLC(昆腾系列及Premium/M340 PLC)的系统验证,自动化产品的 level3技术支持工作。

目前在交行数据中心从事环境监控的管理和改造工作。