forked from xuos/xiuos
				
			add frermodbustcp and CIP protocol; add three PLC test demo and readme, include ab L30、850and abb PM5630
it is OK
This commit is contained in:
		
						commit
						bca78a3133
					
				|  | @ -18,6 +18,7 @@ | |||
| * @date:    2023/2/17 | ||||
| */ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <transform.h> | ||||
| #ifdef ADD_XIZI_FEATURES | ||||
|  | @ -54,7 +55,7 @@ void TestCAN(void) | |||
|     } | ||||
|     printf("CAN configure successful!\n"); | ||||
| 
 | ||||
|     uint8_t data_buff[64u] = {1,2,3,4,4,3,2,1}; | ||||
|     uint8_t data_buff[64u] = "12344321"; | ||||
|     struct CanSendConfigure frame_send; | ||||
|     frame_send.ide=0; | ||||
|     frame_send.stdid = 0x55; | ||||
|  | @ -62,7 +63,7 @@ void TestCAN(void) | |||
|     frame_send.data_lenth=8; | ||||
| 
 | ||||
|     struct CanSendConfigure frame_recv; | ||||
|     uint8_t recv_buff[65U] = {0}; | ||||
|     uint8_t recv_buff[64u] = {}; | ||||
|     frame_recv.data = recv_buff; | ||||
| 
 | ||||
|     // CAN write
 | ||||
|  |  | |||
|  | @ -15,6 +15,10 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) | |||
| 		SRC_DIR += socket_demo | ||||
| 	endif | ||||
| 
 | ||||
| 	ifeq ($(CONFIG_CONNECTION_ADAPTER_FREEMODBUSTCP),y) | ||||
| 		SRC_DIR += freemodbus_tcp_slave | ||||
| 	endif | ||||
| 
 | ||||
|     include $(KERNEL_ROOT)/compiler.mk | ||||
| endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| SRC_FILES := tcpserver_sample.c  | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
|  | @ -0,0 +1,259 @@ | |||
| /*
 | ||||
|  * FreeModbus Libary: Win32 Demo Application | ||||
|  * Copyright (C) 2006 Christian Walter <wolti@sil.at> | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  * | ||||
|  * File: $Id$ | ||||
|  */ | ||||
| 
 | ||||
|  /**********************************************************
 | ||||
|  *	Linux TCP support. | ||||
|  *	Based on Walter's project.  | ||||
|  *	Modified by Steven Guo <gotop167@163.com> | ||||
|  ***********************************************************/ | ||||
| 
 | ||||
| /* ----------------------- Standard C Libs includes --------------------------*/ | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <errno.h> | ||||
| #include <pthread.h> | ||||
| #include <signal.h> | ||||
| #include <transform.h> | ||||
| #include "lwip/sys.h" | ||||
| #include "lwip/sockets.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbport.h" | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define PROG            "freemodbus" | ||||
| 
 | ||||
| #define REG_INPUT_START 1000 | ||||
| #define REG_INPUT_NREGS 4 | ||||
| #define REG_HOLDING_START 2000 | ||||
| #define REG_HOLDING_NREGS 10 | ||||
| 
 | ||||
| /* ----------------------- Static variables ---------------------------------*/ | ||||
| static USHORT   usRegInputStart = REG_INPUT_START; | ||||
| static USHORT   usRegInputBuf[REG_INPUT_NREGS]; | ||||
| static USHORT   usRegHoldingStart = REG_HOLDING_START; | ||||
| static USHORT   usRegHoldingBuf[REG_HOLDING_NREGS]; | ||||
| static pthread_mutex_t xLock; | ||||
| static enum ThreadState | ||||
| { | ||||
|     STOPPED, | ||||
|     RUNNING, | ||||
|     SHUTDOWN | ||||
| } ePollThreadState; | ||||
| 
 | ||||
| /* ----------------------- Static functions ---------------------------------*/ | ||||
| static BOOL     bCreatePollingThread( void ); | ||||
| static enum ThreadState eGetPollingThreadState( void ); | ||||
| static void     eSetPollingThreadState( enum ThreadState eNewState ); | ||||
| static void* pvPollingThread( void *pvParameter ); | ||||
| int LWIPConnectSocket(uint16_t port); | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| int MBSlave() | ||||
| { | ||||
|     int             iExitCode; | ||||
|     CHAR            cCh; | ||||
|     BOOL            bDoExit; | ||||
|     usRegHoldingBuf[5] = 123; | ||||
|     usRegHoldingBuf[7] = 234; | ||||
| 
 | ||||
|     printf("%s ip %d.%d.%d.%d mask %d.%d.%d.%d gw %d.%d.%d.%d\n", __func__,  | ||||
|     192, 168, 250, 233, | ||||
|     255, 255, 255, 255, | ||||
|     192, 168, 250, 1); | ||||
|     uint8_t local_ip[4] = {192,168,250,233}; | ||||
|     uint8_t gateway[4] = {192,168,250,1}; | ||||
|     uint8_t netmask[4] = {255,255,255,0}; | ||||
|     lwip_config_tcp(0, local_ip, netmask, gateway); | ||||
|     printf("%s LWIPInit done\n", __func__); | ||||
| 
 | ||||
|     if( eMBTCPInit( MB_TCP_PORT_USE_DEFAULT ) != MB_ENOERR ) | ||||
|     { | ||||
|         fprintf( stderr, "%s: can't initialize modbus stack!\r\n", PROG ); | ||||
|         iExitCode = EXIT_FAILURE; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         eSetPollingThreadState( STOPPED ); | ||||
|         /* CLI interface. */ | ||||
|         if( bCreatePollingThread(  ) != TRUE ) | ||||
|         { | ||||
|             printf(  "Can't start protocol stack! Already running?\r\n"  ); | ||||
|         } | ||||
|     } | ||||
|     printf("%d %d %s\n",sizeof(usRegHoldingBuf),__LINE__,__func__); | ||||
|      | ||||
|     while(1) | ||||
|     { | ||||
|         for(int i =0; i<sizeof(usRegHoldingBuf)/2;i++) | ||||
|         { | ||||
|             printf("poll recv is %3d\n", usRegHoldingBuf[i]); | ||||
|             MdelayKTask(100); | ||||
|         } | ||||
|     } | ||||
|     return iExitCode; | ||||
| } | ||||
| 
 | ||||
| PRIV_SHELL_CMD_FUNCTION(MBSlave, a Mtcp server Demo, PRIV_SHELL_CMD_MAIN_ATTR);   | ||||
| 
 | ||||
| 
 | ||||
| BOOL bCreatePollingThread( void ) | ||||
| { | ||||
|     BOOL            bResult; | ||||
| 	pthread_t       xThread; | ||||
|     if( eGetPollingThreadState(  ) == STOPPED ) | ||||
|     { | ||||
|         if( pthread_create( &xThread, NULL, pvPollingThread, NULL ) != 0 ) | ||||
|         { | ||||
|             /* Can't create the polling thread. */ | ||||
|             bResult = FALSE; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             bResult = TRUE; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         bResult = FALSE; | ||||
|     } | ||||
|     return bResult; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void* pvPollingThread( void *pvParameter ) | ||||
| { | ||||
|     eSetPollingThreadState( RUNNING ); | ||||
| 
 | ||||
|     if( eMBEnable(  ) == MB_ENOERR ) | ||||
|     { | ||||
|         do | ||||
|         { | ||||
|             if( eMBPoll(  ) != MB_ENOERR ) | ||||
|                 break; | ||||
|         } | ||||
|         while( eGetPollingThreadState(  ) != SHUTDOWN ); | ||||
|     } | ||||
| 
 | ||||
|     ( void )eMBDisable(  ); | ||||
| 
 | ||||
|     eSetPollingThreadState( STOPPED ); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| enum ThreadState eGetPollingThreadState(  ) | ||||
| { | ||||
|     enum ThreadState eCurState; | ||||
| 
 | ||||
|     ( void )pthread_mutex_lock( &xLock ); | ||||
|     eCurState = ePollThreadState; | ||||
|     ( void )pthread_mutex_unlock( &xLock ); | ||||
| 
 | ||||
|     return eCurState; | ||||
| } | ||||
| 
 | ||||
| void eSetPollingThreadState( enum ThreadState eNewState ) | ||||
| { | ||||
|     ( void )pthread_mutex_lock( &xLock ); | ||||
|     ePollThreadState = eNewState; | ||||
|     ( void )pthread_mutex_unlock( &xLock ); | ||||
| } | ||||
| 
 | ||||
| eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
|     int             iRegIndex; | ||||
| 
 | ||||
|     if( ( usAddress >= REG_INPUT_START ) | ||||
|         && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) ) | ||||
|     { | ||||
|         iRegIndex = ( int )( usAddress - usRegInputStart ); | ||||
|         while( usNRegs > 0 ) | ||||
|         { | ||||
|             *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 ); | ||||
|             *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF ); | ||||
|             iRegIndex++; | ||||
|             usNRegs--; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         eStatus = MB_ENOREG; | ||||
|     } | ||||
| 
 | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
|     int             iRegIndex; | ||||
| 
 | ||||
|     if( ( usAddress >= REG_HOLDING_START ) && | ||||
|         ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) ) | ||||
|     { | ||||
|         iRegIndex = ( int )( usAddress - usRegHoldingStart ); | ||||
|         switch ( eMode ) | ||||
|         { | ||||
|             /* Pass current register values to the protocol stack. */ | ||||
|         case MB_REG_READ: | ||||
|             while( usNRegs > 0 ) | ||||
|             { | ||||
|                 *pucRegBuffer++ = ( UCHAR ) ( usRegHoldingBuf[iRegIndex] >> 8 ); | ||||
|                 *pucRegBuffer++ = ( UCHAR ) ( usRegHoldingBuf[iRegIndex] & 0xFF ); | ||||
|                 iRegIndex++; | ||||
|                 usNRegs--; | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
|             /* Update current register values with new values from the
 | ||||
|              * protocol stack. */ | ||||
|         case MB_REG_WRITE: | ||||
|             while( usNRegs > 0 ) | ||||
|             { | ||||
|                 usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8; | ||||
|                 usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++; | ||||
|                 iRegIndex++; | ||||
|                 usNRegs--; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         eStatus = MB_ENOREG; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) | ||||
| { | ||||
|     return MB_ENOREG; | ||||
| } | ||||
| 
 | ||||
| eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) | ||||
| { | ||||
|     return MB_ENOREG; | ||||
| } | ||||
|  | @ -1,3 +1,3 @@ | |||
| SRC_DIR := advantech beckhoff br delta mitsubishi omron schneider siemens ge xinje inovance keyence | ||||
| SRC_DIR := advantech beckhoff br delta mitsubishi omron schneider siemens ge xinje inovance keyence ab abb | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| SRC_FILES := ab_l30erm.c ab_micro850.c | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
|  | @ -0,0 +1,91 @@ | |||
| # AB_850通信测试 | ||||
| 
 | ||||
| [TOC] | ||||
| 
 | ||||
| ## 通信接线及参数设置 | ||||
| 
 | ||||
| * 网口 | ||||
| 
 | ||||
|   *Mosbus TCP协议,IP:192.168.250.56,Port:502 | ||||
| 
 | ||||
| ## 存储区 | ||||
| 
 | ||||
| - 存储区D区 | ||||
| 
 | ||||
| ## JSON配方设计 | ||||
| 
 | ||||
| * AB_850类型PLC需要配置控制器映射 | ||||
| 
 | ||||
|    | ||||
|    | ||||
| * 共测试Word和real共2种类型数据,real型数据有2个Word组成,以下为JSON文件解释。 | ||||
| 
 | ||||
|   - ```json | ||||
|     { | ||||
|         "device_id": 1,                     //设备ID默认是1,此参数无效 | ||||
|         "device_name": "AB_850",            //设备名称,自定义 | ||||
|         "communication_type": 0,            //通讯协议类型 0是以太网,1是串口  | ||||
|         "socket_config": {                  //以太网配置 | ||||
|             "plc_ip": "192.168.250.56",     //PLC的IP地址 | ||||
|             "local_ip": "192.168.250.233",   //矽达通IP地址设定 | ||||
|             "gateway": "192.168.250.1",     //矽达通的网关地址设定 | ||||
|             "netmask": "255.255.255.0",     //矽达通子网掩码设定 | ||||
|             "port":502                      //端口号设定 | ||||
|             }, | ||||
|         "protocol_type": 2,                 //通讯协议,2代表modbus-tcp协议 | ||||
|         "read_period": 100,                 //交互周期ms | ||||
|         "read_item_list": [ | ||||
|             { | ||||
|                 "value_name": "CON_DATA[0]",  //变量名称,自定义 | ||||
|                 "value_type": 1,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 1,   //功能码。1是读线圈 | ||||
|             	"start_address": 0,   //起始地址 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "CON_DATA[1]",  //变量名称,自定义 | ||||
|                 "value_type": 1,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 1,   //功能码。1是读 | ||||
|             	"start_address": 1,   //起始地址偏移1位106*8+1=849 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "CON_INT",  //变量名称,自定义 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 3,   //功能码。3是读 | ||||
|             	"start_address": 2,   //起始地址偏移2位 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "CON_ARRAY[0]_1",  //变量名称,自定义 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 3,   //功能码。3是读 | ||||
|             	"start_address": 3,   //起始地址偏移3位 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "CON_ARRAY[0]_2",  //变量名称,自定义,CON_ARRAY[0]_1和CON_ARRAY[0]_2组成real型数据 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 3,   //功能码。3是读 | ||||
|             	"start_address": 4,   //起始地址偏移4位 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
|     ``` | ||||
| 
 | ||||
| ## 通信测试 | ||||
| 
 | ||||
|  (1) 新增1个通信demo,命名为ab_micro850.c; | ||||
| 
 | ||||
|  (2) 复制modbus_tcp样例代码程序到ab_micro850.c文件中; | ||||
| 
 | ||||
|  (3) void **ControlAB850Test**(void) 更改函数名; | ||||
| 
 | ||||
|  (4) PRIV_SHELL_CMD_FUNCTION(**ControlAB850Test**, AB Plc micro850 Demo**, PRIV_SHELL_CMD_MAIN_ATTR);更改测试指令; | ||||
| 
 | ||||
|  (5) 剪裁配置完成后,用过烧写器下载至矽数通中,重启后完成测试。 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
|  | @ -0,0 +1,71 @@ | |||
| # AB_L30ERM通信测试 | ||||
| 
 | ||||
| [TOC] | ||||
| 
 | ||||
| ## 通信接线及参数设置 | ||||
| 
 | ||||
| * 网口 | ||||
| 
 | ||||
|   *Ethernet/ip协议,IP:192.168.250.57,Port:44818 | ||||
| 
 | ||||
| ## 存储区 | ||||
| 
 | ||||
| - Ethernet/ip协议是根据变量名称搜索寄存器地址 | ||||
| 
 | ||||
| ## JSON配方设计 | ||||
| 
 | ||||
| * 本实例共测试Word和real共2种类型数据,以下为JSON文件解释。 | ||||
| 
 | ||||
|   - ```json | ||||
|     { | ||||
|         "device_id": “ab_l30”,              // | ||||
|         "device_name": "robot",            //设备名称,自定义 | ||||
|         "communication_type": 0,            //通讯协议类型 0是以太网,1是串口  | ||||
|         "socket_config": {                  //以太网配置 | ||||
|             "plc_ip": "192.168.250.37",     //PLC的IP地址 | ||||
|             "local_ip": "192.168.250.123",   //矽达通IP地址设定 | ||||
|             "gateway": "192.168.250.1",     //矽达通的网关地址设定 | ||||
|             "netmask": "255.255.255.0",     //矽达通子网掩码设定 | ||||
|             "port":502                      //端口号设定 | ||||
|             }, | ||||
|         "protocol_type": 12,                 //通讯协议,12代表ethernet/ip协议 | ||||
|         "read_period": 100,                 //交互周期ms | ||||
|         "read_item_list": [ | ||||
|             { | ||||
|                 "value_name": "L30_SPEED",  //变量名称,自定义 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|             	"wordlen": "WORD",    //以WORD方式传输 | ||||
|                 "amount": 1           //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "L30_TORQUE",  //变量名称,自定义 | ||||
|                 "value_type": 9,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|             	"wordlen": 2,        //以WORD方式传输 | ||||
|                 "amount": 1          //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "D",     //变量名称,自定义 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|             	"wordlen": 3,         //以WORD方式传输 | ||||
|                 "amount": 1           //默认是1,代表读取1个数据类型长度 | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
|     ``` | ||||
|    | ||||
| 
 | ||||
| ## 通信测试 | ||||
| 
 | ||||
|  (1) 新增1个通信demo,命名为ab_l30erm.c; | ||||
| 
 | ||||
|  (2) 复制modbus_tcp样例代码程序到ab_l30erm.c文件中; | ||||
| 
 | ||||
|  (3) void **ControlABL30Test**(void) 更改函数名; | ||||
| 
 | ||||
|  (4) PRIV_SHELL_CMD_FUNCTION(**ControlABL30Test**, AB Plc l30ermDemo**, PRIV_SHELL_CMD_MAIN_ATTR);更改测试指令; | ||||
| 
 | ||||
|  (5) 剪裁配置完成后,用过烧写器下载至矽数通中,重启后完成测试。 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
|  | @ -0,0 +1,48 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022 AIIT XUOS Lab | ||||
|  * XiUOS is licensed under Mulan PSL v2. | ||||
|  * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||||
|  * You may obtain a copy of Mulan PSL v2 at: | ||||
|  *        http://license.coscl.org.cn/MulanPSL2
 | ||||
|  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||||
|  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||||
|  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||||
|  * See the Mulan PSL v2 for more details. | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @file ab_l30.c | ||||
|  * @brief PLC ABB L30 app | ||||
|  * @version 3.0 | ||||
|  * @author AIIT XUOS Lab | ||||
|  * @date 2023.8.27 | ||||
|  */ | ||||
| 
 | ||||
| #include <control.h> | ||||
| 
 | ||||
| void ControlABL30Test(void) | ||||
| { | ||||
|     int i = 0; | ||||
|     uint16_t read_data_length = 0; | ||||
|     uint8_t read_data[1024] = {0}; | ||||
|     ControlProtocolType CIP_protocol = ControlProtocolFind(); | ||||
|     if (NULL == CIP_protocol) { | ||||
|         printf("%s get CIP protocol %p failed\n", __func__, CIP_protocol); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     printf("%s get CIP protocol %p successfull\n", __func__, CIP_protocol); | ||||
|     if (CONTROL_REGISTERED == CIP_protocol->protocol_status) { | ||||
|         ControlProtocolOpen(CIP_protocol); | ||||
| 
 | ||||
|         for (;;) { | ||||
|             read_data_length = ControlProtocolRead(CIP_protocol, read_data, sizeof(read_data)); | ||||
|             printf("%s read [%d] CIP data %d using receipe file\n", __func__, i, read_data_length); | ||||
|             i++; | ||||
|             PrivTaskDelay(1000); | ||||
|         } | ||||
| 
 | ||||
|         //ControlProtocolClose(CIP_protocol);
 | ||||
|     } | ||||
| } | ||||
| PRIV_SHELL_CMD_FUNCTION(ControlABL30Test, Ab Plc CIP Demo, PRIV_SHELL_CMD_MAIN_ATTR); | ||||
|  | @ -0,0 +1,52 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022 AIIT XUOS Lab | ||||
|  * XiUOS is licensed under Mulan PSL v2. | ||||
|  * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||||
|  * You may obtain a copy of Mulan PSL v2 at: | ||||
|  *        http://license.coscl.org.cn/MulanPSL2
 | ||||
|  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||||
|  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||||
|  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||||
|  * See the Mulan PSL v2 for more details. | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @file ab_micro850.c | ||||
|  * @brief PLC AB MICRO850 app | ||||
|  * @version 3.0 | ||||
|  * @author AIIT XUOS Lab | ||||
|  * @date 2023.10.5 | ||||
|  */ | ||||
| 
 | ||||
| #include <control.h> | ||||
| 
 | ||||
| void ControlAB850Test(void) | ||||
| { | ||||
|     int i, j = 0; | ||||
|     int read_data_length = 0; | ||||
|     uint8_t read_data[128] = {0}; | ||||
|     ControlProtocolType modbus_tcp_protocol = ControlProtocolFind(); | ||||
|     if (NULL == modbus_tcp_protocol) { | ||||
|         printf("%s get modbus tcp protocol %p failed\n", __func__, modbus_tcp_protocol); | ||||
|         return; | ||||
|     } | ||||
|     printf("%s get modbus tcp protocol %p successfull\n", __func__, modbus_tcp_protocol); | ||||
| 
 | ||||
|     if (CONTROL_REGISTERED == modbus_tcp_protocol->protocol_status) { | ||||
|         ControlProtocolOpen(modbus_tcp_protocol); | ||||
|         for (;;) { | ||||
|             read_data_length = ControlProtocolRead(modbus_tcp_protocol, read_data, sizeof(read_data)); | ||||
|             printf("%s read [%d] modbus tcp data %d using receipe file\n", __func__, i, read_data_length); | ||||
|             if (read_data_length) { | ||||
|                 for (j = 0; j < read_data_length; j ++) { | ||||
|                     printf("j %d data 0x%x\n", j, read_data[j]); | ||||
|                 } | ||||
|             } | ||||
|             i++; | ||||
|             memset(read_data, 0, sizeof(read_data));           | ||||
|             PrivTaskDelay(10000); | ||||
|         } | ||||
|         //ControlProtocolClose(modbus_tcp_protocol);
 | ||||
|     } | ||||
| } | ||||
| PRIV_SHELL_CMD_FUNCTION(ControlAB850Test, AB Plc MICRO850 Demo, PRIV_SHELL_CMD_MAIN_ATTR); | ||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.3 MiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.6 MiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 14 KiB | 
|  | @ -0,0 +1,34 @@ | |||
| { | ||||
|     "device_id": "ab_l30", | ||||
|     "device_name": "robot", | ||||
|     "communication_type": 0, | ||||
|     "socket_config": { | ||||
|         "plc_ip": "192.168.250.57", | ||||
|         "local_ip": "192.168.250.123", | ||||
|         "gateway": "192.168.250.1", | ||||
|         "netmask": "255.255.255.0", | ||||
|         "port": 44818 | ||||
|     }, | ||||
|     "protocol_type": 12, | ||||
|     "read_period": 100, | ||||
|     "read_item_list": [ | ||||
|         { | ||||
|             "value_name": "L30_SPEED", | ||||
|             "value_type": 3, | ||||
|             "wordlen": "WORD", | ||||
|             "amount": 1 | ||||
|         }, | ||||
|         { | ||||
|             "value_name": "L30_TORQUE", | ||||
|             "value_type": 9, | ||||
|             "wordlen": "WORD", | ||||
|             "amount": 1 | ||||
|         }, | ||||
|        { | ||||
|             "value_name": "D", | ||||
|             "value_type": 3, | ||||
|             "wordlen": "WORD", | ||||
|             "amount": 1 | ||||
|         } | ||||
|     ] | ||||
| } | ||||
|  | @ -0,0 +1,51 @@ | |||
| { | ||||
|     "device_id": 1, | ||||
|     "device_name": "AB_850", | ||||
| 	"communication_type": 0, | ||||
|     "socket_config": { | ||||
|         "plc_ip": "192.168.250.32", | ||||
| 		"local_ip": "192.168.250.56", | ||||
|         "gateway": "192.168.250.1", | ||||
| 		"netmask": "255.255.255.0", | ||||
|         "port": 502 | ||||
|     }, | ||||
|     "protocol_type": 2, | ||||
|     "read_period": 100, | ||||
|     "read_item_list": [ | ||||
|         { | ||||
|             "value_name": "D106.0", | ||||
|             "value_type": 1, | ||||
|             "function_code": 1, | ||||
|             "start_address": 848, | ||||
|             "quantity": 1 | ||||
|         }, | ||||
|         { | ||||
|             "value_name": "D106.1", | ||||
|             "value_type": 1, | ||||
|             "function_code": 1, | ||||
|             "start_address":849, | ||||
|             "quantity": 1 | ||||
|         }, | ||||
|         { | ||||
|             "value_name": "D100", | ||||
|             "value_type": 3, | ||||
|             "function_code": 3, | ||||
|             "start_address": 100, | ||||
|             "quantity": 1 | ||||
|         }, | ||||
|         { | ||||
|             "value_name": "D102", | ||||
|             "value_type": 3, | ||||
|             "function_code": 3, | ||||
|             "start_address": 102, | ||||
|             "quantity": 1 | ||||
|         }, | ||||
|         { | ||||
|             "value_name": "D103", | ||||
|             "value_type": 3, | ||||
|             "function_code": 3, | ||||
|             "start_address": 103, | ||||
|             "quantity": 1 | ||||
|         } | ||||
|     ] | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| SRC_FILES := abb_pm5630.c | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
|  | @ -0,0 +1,87 @@ | |||
| # ABB通信测试 | ||||
| 
 | ||||
| [TOC] | ||||
| 
 | ||||
| ## 通信接线及参数设置 | ||||
| 
 | ||||
| * 网口 | ||||
| 
 | ||||
|   *Mosbus TCP协议,IP:192.168.250.58,Port:502 | ||||
| 
 | ||||
| ## 存储区 | ||||
| 
 | ||||
| - 存储区MW区 | ||||
| 
 | ||||
| ## JSON配方设计 | ||||
| 
 | ||||
| * 共测试Word和real共2种类型数据,real型数据有2个Word组成,以下为JSON文件解释。 | ||||
| 
 | ||||
|   - ```json | ||||
|     { | ||||
|         "device_id": 1,                     //设备ID默认是1,此参数无效 | ||||
|         "device_name": "ABB_PM5630",        //设备名称,自定义 | ||||
|         "communication_type": 0,            //通讯协议类型 0是以太网,1是串口  | ||||
|         "socket_config": {                  //以太网配置 | ||||
|             "plc_ip": "192.168.250.58",     //PLC的IP地址 | ||||
|             "local_ip": "192.168.250.233",   //矽达通IP地址设定 | ||||
|             "gateway": "192.168.250.1",     //矽达通的网关地址设定 | ||||
|             "netmask": "255.255.255.0",     //矽达通子网掩码设定 | ||||
|             "port":502                      //端口号设定 | ||||
|             }, | ||||
|         "protocol_type": 2,                 //通讯协议,2代表modbus-tcp协议 | ||||
|         "read_period": 100,                 //交互周期ms | ||||
|         "read_item_list": [ | ||||
|             { | ||||
|                 "value_name": "MW0",  //变量名称,自定义 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 3,   //功能码。3是读 | ||||
|             	"start_address": 0,   //起始地址 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "MW1",  //变量名称,自定义 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 3,   //功能码。3是读 | ||||
|             	"start_address": 1,   //起始地址偏移1位 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "MW10",  //变量名称,自定义 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 3,   //功能码。3是读 | ||||
|             	"start_address": 10,   //起始地址偏移10位 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "MD20_1",  //变量名称,自定义 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 3,   //功能码。3是读 | ||||
|             	"start_address": 20,   //起始地址偏移20位 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             }, | ||||
|             { | ||||
|                 "value_name": "MD20_2",  //变量名称,自定义,MD20_1和MD20_2组成real型数据 | ||||
|                 "value_type": 3,      //变量类型,BOOL = 1,INT8 = 2,INT16,INT32,UINT8,UINT16,UINT32,DOUBLE,FLOAT = 9 | ||||
|                 "function_code": 3,   //功能码。3是读 | ||||
|             	"start_address": 21,   //起始地址偏移21位 | ||||
|                 "data_length": 1      //默认是1,代表读取1个数据类型长度 | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
|     ``` | ||||
| 
 | ||||
| ## 通信测试 | ||||
| 
 | ||||
|  (1) 新增1个通信demo,命名为abb_pm5630.c; | ||||
| 
 | ||||
|  (2) 复制modbus_tcp样例代码程序到abb_pm5630.c文件中; | ||||
| 
 | ||||
|  (3) void **ControlABBPM5630Test**(void) 更改函数名; | ||||
| 
 | ||||
|  (4) PRIV_SHELL_CMD_FUNCTION(**ControlABBPM5630Test**, ABB Plc PM5630 Demo**, PRIV_SHELL_CMD_MAIN_ATTR);更改测试指令; | ||||
| 
 | ||||
|  (5) 剪裁配置完成后,用过烧写器下载至矽数通中,重启后完成测试。 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
|  | @ -0,0 +1,52 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022 AIIT XUOS Lab | ||||
|  * XiUOS is licensed under Mulan PSL v2. | ||||
|  * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||||
|  * You may obtain a copy of Mulan PSL v2 at: | ||||
|  *        http://license.coscl.org.cn/MulanPSL2
 | ||||
|  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||||
|  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||||
|  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||||
|  * See the Mulan PSL v2 for more details. | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @file abb_pm5630.c | ||||
|  * @brief PLC ABB pm5630 app | ||||
|  * @version 3.0 | ||||
|  * @author AIIT XUOS Lab | ||||
|  * @date 2023.10.20 | ||||
|  */ | ||||
| 
 | ||||
| #include <control.h> | ||||
| 
 | ||||
| void ControlABBPM5630Test(void) | ||||
| { | ||||
|     int i, j = 0; | ||||
|     int read_data_length = 0; | ||||
|     uint8_t read_data[128] = {0}; | ||||
|     ControlProtocolType modbus_tcp_protocol = ControlProtocolFind(); | ||||
|     if (NULL == modbus_tcp_protocol) { | ||||
|         printf("%s get modbus tcp protocol %p failed\n", __func__, modbus_tcp_protocol); | ||||
|         return; | ||||
|     } | ||||
|     printf("%s get modbus tcp protocol %p successfull\n", __func__, modbus_tcp_protocol); | ||||
| 
 | ||||
|     if (CONTROL_REGISTERED == modbus_tcp_protocol->protocol_status) { | ||||
|         ControlProtocolOpen(modbus_tcp_protocol); | ||||
|         for (;;) { | ||||
|             read_data_length = ControlProtocolRead(modbus_tcp_protocol, read_data, sizeof(read_data)); | ||||
|             printf("%s read [%d] modbus tcp data %d using receipe file\n", __func__, i, read_data_length); | ||||
|             if (read_data_length) { | ||||
|                 for (j = 0; j < read_data_length; j ++) { | ||||
|                     printf("j %d data 0x%x\n", j, read_data[j]); | ||||
|                 } | ||||
|             } | ||||
|             i++; | ||||
|             memset(read_data, 0, sizeof(read_data));           | ||||
|             PrivTaskDelay(10000); | ||||
|         } | ||||
|         //ControlProtocolClose(modbus_tcp_protocol);
 | ||||
|     } | ||||
| } | ||||
| PRIV_SHELL_CMD_FUNCTION(ControlABBPM5630Test, ABB Plc PM5630 Demo, PRIV_SHELL_CMD_MAIN_ATTR); | ||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.7 MiB | 
|  | @ -0,0 +1,51 @@ | |||
| { | ||||
|     "device_id": 1, | ||||
|     "device_name": "ABB_PM5630", | ||||
| 	"communication_type": 0, | ||||
|     "socket_config": { | ||||
|         "plc_ip": "192.168.250.32", | ||||
| 		"local_ip": "192.168.250.58", | ||||
|         "gateway": "192.168.250.1", | ||||
| 		"netmask": "255.255.255.0", | ||||
|         "port": 502 | ||||
|     }, | ||||
|     "protocol_type": 2, | ||||
|     "read_period": 100, | ||||
|     "read_item_list": [ | ||||
|         { | ||||
|             "value_name": "MW0", | ||||
|             "value_type": 3, | ||||
|             "function_code": 3, | ||||
|             "start_address": 0, | ||||
|             "quantity": 1 | ||||
|         }, | ||||
|         { | ||||
|             "value_name": "MW1", | ||||
|             "value_type": 3, | ||||
|             "function_code": 3, | ||||
|             "start_address":1, | ||||
|             "quantity": 1 | ||||
|         }, | ||||
|         { | ||||
|             "value_name": "MW10", | ||||
|             "value_type": 3, | ||||
|             "function_code": 3, | ||||
|             "start_address": 10, | ||||
|             "quantity": 1 | ||||
|         }, | ||||
| 		{ | ||||
|             "value_name": "MD20_1", | ||||
|             "value_type": 3, | ||||
|             "function_code": 3, | ||||
|             "start_address":20, | ||||
|             "quantity": 1 | ||||
|         }, | ||||
| 		{ | ||||
|             "value_name": "MD20_2", | ||||
|             "value_type": 3, | ||||
|             "function_code": 3, | ||||
|             "start_address":21, | ||||
|             "quantity": 1 | ||||
|         } | ||||
|     ] | ||||
| } | ||||
|  | @ -13,3 +13,11 @@ config CONNECTION_ADAPTER_POWERLINK | |||
|     if CONNECTION_ADAPTER_POWERLINK | ||||
|         source "$APP_DIR/Framework/connection/industrial_network/powerlink/Kconfig" | ||||
|     endif | ||||
| 
 | ||||
| config CONNECTION_ADAPTER_FREEMODBUSTCP | ||||
|     bool "Using FREEMODBUSTCP on industrial network adapter device" | ||||
|     default n | ||||
| 
 | ||||
|     if CONNECTION_ADAPTER_FREEMODBUSTCP | ||||
|         source "$APP_DIR/Framework/connection/industrial_network/freemodbus_tcp/Kconfig" | ||||
|     endif | ||||
|  |  | |||
|  | @ -8,4 +8,8 @@ ifeq ($(CONFIG_CONNECTION_ADAPTER_ETHERCAT),y) | |||
| 	SRC_DIR += ethercat | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(CONFIG_CONNECTION_ADAPTER_FREEMODBUSTCP),y) | ||||
| 	SRC_DIR += freemodbus_tcp | ||||
| endif | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| SRC_FILES := $(wildcard ./*.c) | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
|  | @ -0,0 +1,411 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- System includes ----------------------------------*/ | ||||
| #include "stdlib.h" | ||||
| #include "string.h" | ||||
| 
 | ||||
| /* ----------------------- Platform includes --------------------------------*/ | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbconfig.h" | ||||
| #include "mbframe.h" | ||||
| #include "mbproto.h" | ||||
| #include "mbfunc.h" | ||||
| 
 | ||||
| #include "mbport.h" | ||||
| #if MB_RTU_ENABLED == 1 | ||||
| #include "mbrtu.h" | ||||
| #endif | ||||
| #if MB_ASCII_ENABLED == 1 | ||||
| #include "mbascii.h" | ||||
| #endif | ||||
| #if MB_TCP_ENABLED == 1 | ||||
| #include "mbtcp.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifndef MB_PORT_HAS_CLOSE | ||||
| #define MB_PORT_HAS_CLOSE 0 | ||||
| #endif | ||||
| 
 | ||||
| /* ----------------------- Static variables ---------------------------------*/ | ||||
| 
 | ||||
| static UCHAR    ucMBAddress; | ||||
| static eMBMode  eMBCurrentMode; | ||||
| 
 | ||||
| static enum | ||||
| { | ||||
|     STATE_ENABLED, | ||||
|     STATE_DISABLED, | ||||
|     STATE_NOT_INITIALIZED | ||||
| } eMBState = STATE_NOT_INITIALIZED; | ||||
| 
 | ||||
| /* Functions pointer which are initialized in eMBInit( ). Depending on the
 | ||||
|  * mode (RTU or ASCII) the are set to the correct implementations. | ||||
|  */ | ||||
| static peMBFrameSend peMBFrameSendCur; | ||||
| static pvMBFrameStart pvMBFrameStartCur; | ||||
| static pvMBFrameStop pvMBFrameStopCur; | ||||
| static peMBFrameReceive peMBFrameReceiveCur; | ||||
| static pvMBFrameClose pvMBFrameCloseCur; | ||||
| 
 | ||||
| /* Callback functions required by the porting layer. They are called when
 | ||||
|  * an external event has happend which includes a timeout or the reception | ||||
|  * or transmission of a character. | ||||
|  */ | ||||
| BOOL( *pxMBFrameCBByteReceived ) ( void ); | ||||
| BOOL( *pxMBFrameCBTransmitterEmpty ) ( void ); | ||||
| BOOL( *pxMBPortCBTimerExpired ) ( void ); | ||||
| 
 | ||||
| BOOL( *pxMBFrameCBReceiveFSMCur ) ( void ); | ||||
| BOOL( *pxMBFrameCBTransmitFSMCur ) ( void ); | ||||
| 
 | ||||
| /* An array of Modbus functions handlers which associates Modbus function
 | ||||
|  * codes with implementing functions. | ||||
|  */ | ||||
| static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = { | ||||
| #if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0 | ||||
|     {MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID}, | ||||
| #endif | ||||
| #if MB_FUNC_READ_INPUT_ENABLED > 0 | ||||
|     {MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister}, | ||||
| #endif | ||||
| #if MB_FUNC_READ_HOLDING_ENABLED > 0 | ||||
|     {MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister}, | ||||
| #endif | ||||
| #if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0 | ||||
|     {MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBFuncWriteMultipleHoldingRegister}, | ||||
| #endif | ||||
| #if MB_FUNC_WRITE_HOLDING_ENABLED > 0 | ||||
|     {MB_FUNC_WRITE_REGISTER, eMBFuncWriteHoldingRegister}, | ||||
| #endif | ||||
| #if MB_FUNC_READWRITE_HOLDING_ENABLED > 0 | ||||
|     {MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBFuncReadWriteMultipleHoldingRegister}, | ||||
| #endif | ||||
| #if MB_FUNC_READ_COILS_ENABLED > 0 | ||||
|     {MB_FUNC_READ_COILS, eMBFuncReadCoils}, | ||||
| #endif | ||||
| #if MB_FUNC_WRITE_COIL_ENABLED > 0 | ||||
|     {MB_FUNC_WRITE_SINGLE_COIL, eMBFuncWriteCoil}, | ||||
| #endif | ||||
| #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0 | ||||
|     {MB_FUNC_WRITE_MULTIPLE_COILS, eMBFuncWriteMultipleCoils}, | ||||
| #endif | ||||
| #if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0 | ||||
|     {MB_FUNC_READ_DISCRETE_INPUTS, eMBFuncReadDiscreteInputs}, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| eMBErrorCode | ||||
| eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
| 
 | ||||
|     /* check preconditions */ | ||||
|     if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) || | ||||
|         ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) ) | ||||
|     { | ||||
|         eStatus = MB_EINVAL; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         ucMBAddress = ucSlaveAddress; | ||||
| 
 | ||||
|         switch ( eMode ) | ||||
|         { | ||||
| #if MB_RTU_ENABLED > 0 | ||||
|         case MB_RTU: | ||||
|             pvMBFrameStartCur = eMBRTUStart; | ||||
|             pvMBFrameStopCur = eMBRTUStop; | ||||
|             peMBFrameSendCur = eMBRTUSend; | ||||
|             peMBFrameReceiveCur = eMBRTUReceive; | ||||
|             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; | ||||
|             pxMBFrameCBByteReceived = xMBRTUReceiveFSM; | ||||
|             pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM; | ||||
|             pxMBPortCBTimerExpired = xMBRTUTimerT35Expired; | ||||
| 
 | ||||
|             eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity ); | ||||
|             break; | ||||
| #endif | ||||
| #if MB_ASCII_ENABLED > 0 | ||||
|         case MB_ASCII: | ||||
|             pvMBFrameStartCur = eMBASCIIStart; | ||||
|             pvMBFrameStopCur = eMBASCIIStop; | ||||
|             peMBFrameSendCur = eMBASCIISend; | ||||
|             peMBFrameReceiveCur = eMBASCIIReceive; | ||||
|             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; | ||||
|             pxMBFrameCBByteReceived = xMBASCIIReceiveFSM; | ||||
|             pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM; | ||||
|             pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired; | ||||
| 
 | ||||
|             eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity ); | ||||
|             break; | ||||
| #endif | ||||
|         default: | ||||
|             eStatus = MB_EINVAL; | ||||
|         } | ||||
| 
 | ||||
|         if( eStatus == MB_ENOERR ) | ||||
|         { | ||||
|             if( !xMBPortEventInit(  ) ) | ||||
|             { | ||||
|                 /* port dependent event module initalization failed. */ | ||||
|                 eStatus = MB_EPORTERR; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 eMBCurrentMode = eMode; | ||||
|                 eMBState = STATE_DISABLED; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| #if MB_TCP_ENABLED > 0 | ||||
| eMBErrorCode | ||||
| eMBTCPInit( USHORT ucTCPPort ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
| 
 | ||||
|     if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR ) | ||||
|     { | ||||
|         eMBState = STATE_DISABLED; | ||||
|     } | ||||
|     else if( !xMBPortEventInit(  ) ) | ||||
|     { | ||||
|         /* Port dependent event module initalization failed. */ | ||||
|         eStatus = MB_EPORTERR; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         pvMBFrameStartCur = eMBTCPStart; | ||||
|         pvMBFrameStopCur = eMBTCPStop; | ||||
|         peMBFrameReceiveCur = eMBTCPReceive; | ||||
|         peMBFrameSendCur = eMBTCPSend; | ||||
|         pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBTCPPortClose : NULL; | ||||
|         ucMBAddress = MB_TCP_PSEUDO_ADDRESS; | ||||
|         eMBCurrentMode = MB_TCP; | ||||
|         eMBState = STATE_DISABLED; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| eMBErrorCode | ||||
| eMBRegisterCB( UCHAR ucFunctionCode, pxMBFunctionHandler pxHandler ) | ||||
| { | ||||
|     int             i; | ||||
|     eMBErrorCode    eStatus; | ||||
| 
 | ||||
|     if( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= 127 ) ) | ||||
|     { | ||||
|         ENTER_CRITICAL_SECTION(  ); | ||||
|         if( pxHandler != NULL ) | ||||
|         { | ||||
|             for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) | ||||
|             { | ||||
|                 if( ( xFuncHandlers[i].pxHandler == NULL ) || | ||||
|                     ( xFuncHandlers[i].pxHandler == pxHandler ) ) | ||||
|                 { | ||||
|                     xFuncHandlers[i].ucFunctionCode = ucFunctionCode; | ||||
|                     xFuncHandlers[i].pxHandler = pxHandler; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             eStatus = ( i != MB_FUNC_HANDLERS_MAX ) ? MB_ENOERR : MB_ENORES; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) | ||||
|             { | ||||
|                 if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) | ||||
|                 { | ||||
|                     xFuncHandlers[i].ucFunctionCode = 0; | ||||
|                     xFuncHandlers[i].pxHandler = NULL; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             /* Remove can't fail. */ | ||||
|             eStatus = MB_ENOERR; | ||||
|         } | ||||
|         EXIT_CRITICAL_SECTION(  ); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         eStatus = MB_EINVAL; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| eMBErrorCode | ||||
| eMBClose( void ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
| 
 | ||||
|     if( eMBState == STATE_DISABLED ) | ||||
|     { | ||||
|         if( pvMBFrameCloseCur != NULL ) | ||||
|         { | ||||
|             pvMBFrameCloseCur(  ); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         eStatus = MB_EILLSTATE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| eMBErrorCode | ||||
| eMBEnable( void ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
| 
 | ||||
|     if( eMBState == STATE_DISABLED ) | ||||
|     { | ||||
|         /* Activate the protocol stack. */ | ||||
|         pvMBFrameStartCur(  ); | ||||
|         eMBState = STATE_ENABLED; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         eStatus = MB_EILLSTATE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| eMBErrorCode | ||||
| eMBDisable( void ) | ||||
| { | ||||
|     eMBErrorCode    eStatus; | ||||
| 
 | ||||
|     if( eMBState == STATE_ENABLED ) | ||||
|     { | ||||
|         pvMBFrameStopCur(  ); | ||||
|         eMBState = STATE_DISABLED; | ||||
|         eStatus = MB_ENOERR; | ||||
|     } | ||||
|     else if( eMBState == STATE_DISABLED ) | ||||
|     { | ||||
|         eStatus = MB_ENOERR; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         eStatus = MB_EILLSTATE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| eMBErrorCode | ||||
| eMBPoll( void ) | ||||
| { | ||||
|     static UCHAR   *ucMBFrame; | ||||
|     static UCHAR    ucRcvAddress; | ||||
|     static UCHAR    ucFunctionCode; | ||||
|     static USHORT   usLength; | ||||
|     static eMBException eException; | ||||
| 
 | ||||
|     int             i; | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
|     eMBEventType    eEvent; | ||||
| 
 | ||||
|     /* Check if the protocol stack is ready. */ | ||||
|     if( eMBState != STATE_ENABLED ) | ||||
|     { | ||||
|         return MB_EILLSTATE; | ||||
|     } | ||||
| 
 | ||||
|     /* Check if there is a event available. If not return control to caller.
 | ||||
|      * Otherwise we will handle the event. */ | ||||
|     if( xMBPortEventGet( &eEvent ) == TRUE ) | ||||
|     { | ||||
|         switch ( eEvent ) | ||||
|         { | ||||
|         case EV_READY: | ||||
|             break; | ||||
| 
 | ||||
|         case EV_FRAME_RECEIVED: | ||||
|             eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); | ||||
|             if( eStatus == MB_ENOERR ) | ||||
|             { | ||||
|                 /* Check if the frame is for us. If not ignore the frame. */ | ||||
|                 if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) | ||||
|                 { | ||||
|                     ( void )xMBPortEventPost( EV_EXECUTE ); | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
|         case EV_EXECUTE: | ||||
|             ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; | ||||
|             eException = MB_EX_ILLEGAL_FUNCTION; | ||||
|             for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) | ||||
|             { | ||||
|                 /* No more function handlers registered. Abort. */ | ||||
|                 if( xFuncHandlers[i].ucFunctionCode == 0 ) | ||||
|                 { | ||||
|                     break; | ||||
|                 } | ||||
|                 else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) | ||||
|                 { | ||||
|                     eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength ); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             /* If the request was not sent to the broadcast address we
 | ||||
|              * return a reply. */ | ||||
|             if( ucRcvAddress != MB_ADDRESS_BROADCAST ) | ||||
|             { | ||||
|                 if( eException != MB_EX_NONE ) | ||||
|                 { | ||||
|                     /* An exception occured. Build an error frame. */ | ||||
|                     usLength = 0; | ||||
|                     ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR ); | ||||
|                     ucMBFrame[usLength++] = eException; | ||||
|                 } | ||||
|                 if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ) | ||||
|                 { | ||||
|                     vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ); | ||||
|                 }                 | ||||
|                 eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
|         case EV_FRAME_SENT: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     return MB_ENOERR; | ||||
| } | ||||
|  | @ -0,0 +1,416 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _MB_H | ||||
| #define _MB_H | ||||
| 
 | ||||
| #include "port.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_BEGIN_EXTERN_C | ||||
| #endif | ||||
| 
 | ||||
| #include "mbport.h" | ||||
| #include "mbproto.h" | ||||
| 
 | ||||
| /*! \defgroup modbus Modbus
 | ||||
|  * \code #include "mb.h" \endcode | ||||
|  * | ||||
|  * This module defines the interface for the application. It contains | ||||
|  * the basic functions and types required to use the Modbus protocol stack. | ||||
|  * A typical application will want to call eMBInit() first. If the device | ||||
|  * is ready to answer network requests it must then call eMBEnable() to activate | ||||
|  * the protocol stack. In the main loop the function eMBPoll() must be called | ||||
|  * periodically. The time interval between pooling depends on the configured | ||||
|  * Modbus timeout. If an RTOS is available a separate task should be created | ||||
|  * and the task should always call the function eMBPoll(). | ||||
|  * | ||||
|  * \code | ||||
|  * // Initialize protocol stack in RTU mode for a slave with address 10 = 0x0A
 | ||||
|  * eMBInit( MB_RTU, 0x0A, 38400, MB_PAR_EVEN ); | ||||
|  * // Enable the Modbus Protocol Stack.
 | ||||
|  * eMBEnable(  ); | ||||
|  * for( ;; ) | ||||
|  * { | ||||
|  *     // Call the main polling loop of the Modbus protocol stack.
 | ||||
|  *     eMBPoll(  ); | ||||
|  *     ... | ||||
|  * } | ||||
|  * \endcode | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Use the default Modbus TCP port (502) | ||||
|  */ | ||||
| #define MB_TCP_PORT_USE_DEFAULT 0    | ||||
| 
 | ||||
| /* ----------------------- Type definitions ---------------------------------*/ | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Modbus serial transmission modes (RTU/ASCII). | ||||
|  * | ||||
|  * Modbus serial supports two transmission modes. Either ASCII or RTU. RTU | ||||
|  * is faster but has more hardware requirements and requires a network with | ||||
|  * a low jitter. ASCII is slower and more reliable on slower links (E.g. modems) | ||||
|  */ | ||||
|     typedef enum | ||||
| { | ||||
|     MB_RTU,                     /*!< RTU transmission mode. */ | ||||
|     MB_ASCII,                   /*!< ASCII transmission mode. */ | ||||
|     MB_TCP                      /*!< TCP mode. */ | ||||
| } eMBMode; | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief If register should be written or read. | ||||
|  * | ||||
|  * This value is passed to the callback functions which support either | ||||
|  * reading or writing register values. Writing means that the application | ||||
|  * registers should be updated and reading means that the modbus protocol | ||||
|  * stack needs to know the current register values. | ||||
|  * | ||||
|  * \see eMBRegHoldingCB( ), eMBRegCoilsCB( ), eMBRegDiscreteCB( ) and  | ||||
|  *   eMBRegInputCB( ). | ||||
|  */ | ||||
| typedef enum | ||||
| { | ||||
|     MB_REG_READ,                /*!< Read register values and pass to protocol stack. */ | ||||
|     MB_REG_WRITE                /*!< Update register values. */ | ||||
| } eMBRegisterMode; | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Errorcodes used by all function in the protocol stack. | ||||
|  */ | ||||
| typedef enum | ||||
| { | ||||
|     MB_ENOERR,                  /*!< no error. */ | ||||
|     MB_ENOREG,                  /*!< illegal register address. */ | ||||
|     MB_EINVAL,                  /*!< illegal argument. */ | ||||
|     MB_EPORTERR,                /*!< porting layer error. */ | ||||
|     MB_ENORES,                  /*!< insufficient resources. */ | ||||
|     MB_EIO,                     /*!< I/O error. */ | ||||
|     MB_EILLSTATE,               /*!< protocol stack in illegal state. */ | ||||
|     MB_ETIMEDOUT                /*!< timeout error occurred. */ | ||||
| } eMBErrorCode; | ||||
| 
 | ||||
| 
 | ||||
| /* ----------------------- Function prototypes ------------------------------*/ | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Initialize the Modbus protocol stack. | ||||
|  * | ||||
|  * This functions initializes the ASCII or RTU module and calls the | ||||
|  * init functions of the porting layer to prepare the hardware. Please | ||||
|  * note that the receiver is still disabled and no Modbus frames are | ||||
|  * processed until eMBEnable( ) has been called. | ||||
|  * | ||||
|  * \param eMode If ASCII or RTU mode should be used. | ||||
|  * \param ucSlaveAddress The slave address. Only frames sent to this | ||||
|  *   address or to the broadcast address are processed. | ||||
|  * \param ucPort The port to use. E.g. 1 for COM1 on windows. This value | ||||
|  *   is platform dependent and some ports simply choose to ignore it. | ||||
|  * \param ulBaudRate The baudrate. E.g. 19200. Supported baudrates depend | ||||
|  *   on the porting layer. | ||||
|  * \param eParity Parity used for serial transmission. | ||||
|  * | ||||
|  * \return If no error occurs the function returns eMBErrorCode::MB_ENOERR. | ||||
|  *   The protocol is then in the disabled state and ready for activation | ||||
|  *   by calling eMBEnable( ). Otherwise one of the following error codes  | ||||
|  *   is returned: | ||||
|  *    - eMBErrorCode::MB_EINVAL If the slave address was not valid. Valid | ||||
|  *        slave addresses are in the range 1 - 247. | ||||
|  *    - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error. | ||||
|  */ | ||||
| eMBErrorCode    eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, | ||||
|                          UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ); | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Initialize the Modbus protocol stack for Modbus TCP. | ||||
|  * | ||||
|  * This function initializes the Modbus TCP Module. Please note that | ||||
|  * frame processing is still disabled until eMBEnable( ) is called. | ||||
|  * | ||||
|  * \param usTCPPort The TCP port to listen on. | ||||
|  * \return If the protocol stack has been initialized correctly the function | ||||
|  *   returns eMBErrorCode::MB_ENOERR. Otherwise one of the following error | ||||
|  *   codes is returned: | ||||
|  *    - eMBErrorCode::MB_EINVAL If the slave address was not valid. Valid | ||||
|  *        slave addresses are in the range 1 - 247. | ||||
|  *    - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error. | ||||
|  */ | ||||
| eMBErrorCode    eMBTCPInit( USHORT usTCPPort ); | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Release resources used by the protocol stack. | ||||
|  * | ||||
|  * This function disables the Modbus protocol stack and release all | ||||
|  * hardware resources. It must only be called when the protocol stack  | ||||
|  * is disabled.  | ||||
|  * | ||||
|  * \note Note all ports implement this function. A port which wants to  | ||||
|  *   get an callback must define the macro MB_PORT_HAS_CLOSE to 1. | ||||
|  * | ||||
|  * \return If the resources where released it return eMBErrorCode::MB_ENOERR. | ||||
|  *   If the protocol stack is not in the disabled state it returns | ||||
|  *   eMBErrorCode::MB_EILLSTATE. | ||||
|  */ | ||||
| eMBErrorCode    eMBClose( void ); | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Enable the Modbus protocol stack. | ||||
|  * | ||||
|  * This function enables processing of Modbus frames. Enabling the protocol | ||||
|  * stack is only possible if it is in the disabled state. | ||||
|  * | ||||
|  * \return If the protocol stack is now in the state enabled it returns  | ||||
|  *   eMBErrorCode::MB_ENOERR. If it was not in the disabled state it  | ||||
|  *   return eMBErrorCode::MB_EILLSTATE. | ||||
|  */ | ||||
| eMBErrorCode    eMBEnable( void ); | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Disable the Modbus protocol stack. | ||||
|  * | ||||
|  * This function disables processing of Modbus frames. | ||||
|  * | ||||
|  * \return If the protocol stack has been disabled it returns  | ||||
|  *  eMBErrorCode::MB_ENOERR. If it was not in the enabled state it returns | ||||
|  *  eMBErrorCode::MB_EILLSTATE. | ||||
|  */ | ||||
| eMBErrorCode    eMBDisable( void ); | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief The main pooling loop of the Modbus protocol stack. | ||||
|  * | ||||
|  * This function must be called periodically. The timer interval required | ||||
|  * is given by the application dependent Modbus slave timeout. Internally the | ||||
|  * function calls xMBPortEventGet() and waits for an event from the receiver or | ||||
|  * transmitter state machines.  | ||||
|  * | ||||
|  * \return If the protocol stack is not in the enabled state the function | ||||
|  *   returns eMBErrorCode::MB_EILLSTATE. Otherwise it returns  | ||||
|  *   eMBErrorCode::MB_ENOERR. | ||||
|  */ | ||||
| eMBErrorCode    eMBPoll( void ); | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Configure the slave id of the device. | ||||
|  * | ||||
|  * This function should be called when the Modbus function <em>Report Slave ID</em> | ||||
|  * is enabled ( By defining MB_FUNC_OTHER_REP_SLAVEID_ENABLED in mbconfig.h ). | ||||
|  * | ||||
|  * \param ucSlaveID Values is returned in the <em>Slave ID</em> byte of the | ||||
|  *   <em>Report Slave ID</em> response. | ||||
|  * \param xIsRunning If TRUE the <em>Run Indicator Status</em> byte is set to 0xFF. | ||||
|  *   otherwise the <em>Run Indicator Status</em> is 0x00. | ||||
|  * \param pucAdditional Values which should be returned in the <em>Additional</em> | ||||
|  *   bytes of the <em> Report Slave ID</em> response. | ||||
|  * \param usAdditionalLen Length of the buffer <code>pucAdditonal</code>. | ||||
|  * | ||||
|  * \return If the static buffer defined by MB_FUNC_OTHER_REP_SLAVEID_BUF in | ||||
|  *   mbconfig.h is to small it returns eMBErrorCode::MB_ENORES. Otherwise | ||||
|  *   it returns eMBErrorCode::MB_ENOERR. | ||||
|  */ | ||||
| eMBErrorCode    eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning, | ||||
|                                UCHAR const *pucAdditional, | ||||
|                                USHORT usAdditionalLen ); | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Registers a callback handler for a given function code. | ||||
|  * | ||||
|  * This function registers a new callback handler for a given function code. | ||||
|  * The callback handler supplied is responsible for interpreting the Modbus PDU and | ||||
|  * the creation of an appropriate response. In case of an error it should return | ||||
|  * one of the possible Modbus exceptions which results in a Modbus exception frame | ||||
|  * sent by the protocol stack.  | ||||
|  * | ||||
|  * \param ucFunctionCode The Modbus function code for which this handler should | ||||
|  *   be registers. Valid function codes are in the range 1 to 127. | ||||
|  * \param pxHandler The function handler which should be called in case | ||||
|  *   such a frame is received. If \c NULL a previously registered function handler | ||||
|  *   for this function code is removed. | ||||
|  * | ||||
|  * \return eMBErrorCode::MB_ENOERR if the handler has been installed. If no | ||||
|  *   more resources are available it returns eMBErrorCode::MB_ENORES. In this | ||||
|  *   case the values in mbconfig.h should be adjusted. If the argument was not | ||||
|  *   valid it returns eMBErrorCode::MB_EINVAL. | ||||
|  */ | ||||
| eMBErrorCode    eMBRegisterCB( UCHAR ucFunctionCode,  | ||||
|                                pxMBFunctionHandler pxHandler ); | ||||
| 
 | ||||
| /* ----------------------- Callback -----------------------------------------*/ | ||||
| 
 | ||||
| /*! \defgroup modbus_registers Modbus Registers
 | ||||
|  * \code #include "mb.h" \endcode | ||||
|  * The protocol stack does not internally allocate any memory for the | ||||
|  * registers. This makes the protocol stack very small and also usable on | ||||
|  * low end targets. In addition the values don't have to be in the memory | ||||
|  * and could for example be stored in a flash.<br> | ||||
|  * Whenever the protocol stack requires a value it calls one of the callback | ||||
|  * function with the register address and the number of registers to read | ||||
|  * as an argument. The application should then read the actual register values | ||||
|  * (for example the ADC voltage) and should store the result in the supplied | ||||
|  * buffer.<br> | ||||
|  * If the protocol stack wants to update a register value because a write | ||||
|  * register function was received a buffer with the new register values is | ||||
|  * passed to the callback function. The function should then use these values | ||||
|  * to update the application register values. | ||||
|  */ | ||||
| 
 | ||||
| /*! \ingroup modbus_registers
 | ||||
|  * \brief Callback function used if the value of a <em>Input Register</em> | ||||
|  *   is required by the protocol stack. The starting register address is given | ||||
|  *   by \c usAddress and the last register is given by <tt>usAddress + | ||||
|  *   usNRegs - 1</tt>. | ||||
|  * | ||||
|  * \param pucRegBuffer A buffer where the callback function should write | ||||
|  *   the current value of the modbus registers to. | ||||
|  * \param usAddress The starting address of the register. Input registers | ||||
|  *   are in the range 1 - 65535. | ||||
|  * \param usNRegs Number of registers the callback function must supply. | ||||
|  * | ||||
|  * \return The function must return one of the following error codes: | ||||
|  *   - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal | ||||
|  *       Modbus response is sent. | ||||
|  *   - eMBErrorCode::MB_ENOREG If the application can not supply values | ||||
|  *       for registers within this range. In this case a  | ||||
|  *       <b>ILLEGAL DATA ADDRESS</b> exception frame is sent as a response. | ||||
|  *   - eMBErrorCode::MB_ETIMEDOUT If the requested register block is | ||||
|  *       currently not available and the application dependent response | ||||
|  *       timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b> | ||||
|  *       exception is sent as a response. | ||||
|  *   - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case | ||||
|  *       a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response. | ||||
|  */ | ||||
| eMBErrorCode    eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, | ||||
|                                USHORT usNRegs ); | ||||
| 
 | ||||
| /*! \ingroup modbus_registers
 | ||||
|  * \brief Callback function used if a <em>Holding Register</em> value is | ||||
|  *   read or written by the protocol stack. The starting register address | ||||
|  *   is given by \c usAddress and the last register is given by | ||||
|  *   <tt>usAddress + usNRegs - 1</tt>. | ||||
|  * | ||||
|  * \param pucRegBuffer If the application registers values should be updated the | ||||
|  *   buffer points to the new registers values. If the protocol stack needs | ||||
|  *   to now the current values the callback function should write them into | ||||
|  *   this buffer. | ||||
|  * \param usAddress The starting address of the register. | ||||
|  * \param usNRegs Number of registers to read or write. | ||||
|  * \param eMode If eMBRegisterMode::MB_REG_WRITE the application register  | ||||
|  *   values should be updated from the values in the buffer. For example | ||||
|  *   this would be the case when the Modbus master has issued an  | ||||
|  *   <b>WRITE SINGLE REGISTER</b> command. | ||||
|  *   If the value eMBRegisterMode::MB_REG_READ the application should copy  | ||||
|  *   the current values into the buffer \c pucRegBuffer. | ||||
|  * | ||||
|  * \return The function must return one of the following error codes: | ||||
|  *   - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal | ||||
|  *       Modbus response is sent. | ||||
|  *   - eMBErrorCode::MB_ENOREG If the application can not supply values | ||||
|  *       for registers within this range. In this case a  | ||||
|  *       <b>ILLEGAL DATA ADDRESS</b> exception frame is sent as a response. | ||||
|  *   - eMBErrorCode::MB_ETIMEDOUT If the requested register block is | ||||
|  *       currently not available and the application dependent response | ||||
|  *       timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b> | ||||
|  *       exception is sent as a response. | ||||
|  *   - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case | ||||
|  *       a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response. | ||||
|  */ | ||||
| eMBErrorCode    eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, | ||||
|                                  USHORT usNRegs, eMBRegisterMode eMode ); | ||||
| 
 | ||||
| /*! \ingroup modbus_registers
 | ||||
|  * \brief Callback function used if a <em>Coil Register</em> value is | ||||
|  *   read or written by the protocol stack. If you are going to use | ||||
|  *   this function you might use the functions xMBUtilSetBits(  ) and | ||||
|  *   xMBUtilGetBits(  ) for working with bitfields. | ||||
|  * | ||||
|  * \param pucRegBuffer The bits are packed in bytes where the first coil | ||||
|  *   starting at address \c usAddress is stored in the LSB of the | ||||
|  *   first byte in the buffer <code>pucRegBuffer</code>. | ||||
|  *   If the buffer should be written by the callback function unused | ||||
|  *   coil values (I.e. if not a multiple of eight coils is used) should be set | ||||
|  *   to zero. | ||||
|  * \param usAddress The first coil number. | ||||
|  * \param usNCoils Number of coil values requested. | ||||
|  * \param eMode If eMBRegisterMode::MB_REG_WRITE the application values should | ||||
|  *   be updated from the values supplied in the buffer \c pucRegBuffer. | ||||
|  *   If eMBRegisterMode::MB_REG_READ the application should store the current | ||||
|  *   values in the buffer \c pucRegBuffer. | ||||
|  * | ||||
|  * \return The function must return one of the following error codes: | ||||
|  *   - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal | ||||
|  *       Modbus response is sent. | ||||
|  *   - eMBErrorCode::MB_ENOREG If the application does not map an coils | ||||
|  *       within the requested address range. In this case a  | ||||
|  *       <b>ILLEGAL DATA ADDRESS</b> is sent as a response. | ||||
|  *   - eMBErrorCode::MB_ETIMEDOUT If the requested register block is | ||||
|  *       currently not available and the application dependent response | ||||
|  *       timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b> | ||||
|  *       exception is sent as a response. | ||||
|  *   - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case | ||||
|  *       a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response. | ||||
|  */ | ||||
| eMBErrorCode    eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, | ||||
|                                USHORT usNCoils, eMBRegisterMode eMode ); | ||||
| 
 | ||||
| /*! \ingroup modbus_registers
 | ||||
|  * \brief Callback function used if a <em>Input Discrete Register</em> value is | ||||
|  *   read by the protocol stack. | ||||
|  * | ||||
|  * If you are going to use his function you might use the functions | ||||
|  * xMBUtilSetBits(  ) and xMBUtilGetBits(  ) for working with bitfields. | ||||
|  * | ||||
|  * \param pucRegBuffer The buffer should be updated with the current | ||||
|  *   coil values. The first discrete input starting at \c usAddress must be | ||||
|  *   stored at the LSB of the first byte in the buffer. If the requested number | ||||
|  *   is not a multiple of eight the remaining bits should be set to zero. | ||||
|  * \param usAddress The starting address of the first discrete input. | ||||
|  * \param usNDiscrete Number of discrete input values. | ||||
|  * \return The function must return one of the following error codes: | ||||
|  *   - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal | ||||
|  *       Modbus response is sent. | ||||
|  *   - eMBErrorCode::MB_ENOREG If no such discrete inputs exists. | ||||
|  *       In this case a <b>ILLEGAL DATA ADDRESS</b> exception frame is sent  | ||||
|  *       as a response. | ||||
|  *   - eMBErrorCode::MB_ETIMEDOUT If the requested register block is | ||||
|  *       currently not available and the application dependent response | ||||
|  *       timeout would be violated. In this case a <b>SLAVE DEVICE BUSY</b> | ||||
|  *       exception is sent as a response. | ||||
|  *   - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case | ||||
|  *       a <b>SLAVE DEVICE FAILURE</b> exception is sent as a response. | ||||
|  */ | ||||
| eMBErrorCode    eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, | ||||
|                                   USHORT usNDiscrete ); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_END_EXTERN_C | ||||
| #endif | ||||
| #endif | ||||
|  | @ -0,0 +1,131 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _MB_CONFIG_H | ||||
| #define _MB_CONFIG_H | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_BEGIN_EXTERN_C | ||||
| #endif | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| /*! \defgroup modbus_cfg Modbus Configuration
 | ||||
|  * | ||||
|  * Most modules in the protocol stack are completly optional and can be | ||||
|  * excluded. This is specially important if target resources are very small | ||||
|  * and program memory space should be saved.<br> | ||||
|  * | ||||
|  * All of these settings are available in the file <code>mbconfig.h</code> | ||||
|  */ | ||||
| /*! \addtogroup modbus_cfg
 | ||||
|  *  @{ | ||||
|  */ | ||||
| /*! \brief If Modbus ASCII support is enabled. */ | ||||
| #define MB_ASCII_ENABLED                        (  0 ) | ||||
| 
 | ||||
| /*! \brief If Modbus RTU support is enabled. */ | ||||
| #define MB_RTU_ENABLED                          (  0 ) | ||||
| 
 | ||||
| /*! \brief If Modbus TCP support is enabled. */ | ||||
| #define MB_TCP_ENABLED                          (  1 ) | ||||
| 
 | ||||
| /*! \brief The character timeout value for Modbus ASCII.
 | ||||
|  * | ||||
|  * The character timeout value is not fixed for Modbus ASCII and is therefore | ||||
|  * a configuration option. It should be set to the maximum expected delay | ||||
|  * time of the network. | ||||
|  */ | ||||
| #define MB_ASCII_TIMEOUT_SEC                    (  1 ) | ||||
| 
 | ||||
| /*! \brief Timeout to wait in ASCII prior to enabling transmitter.
 | ||||
|  * | ||||
|  * If defined the function calls vMBPortSerialDelay with the argument | ||||
|  * MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS to allow for a delay before | ||||
|  * the serial transmitter is enabled. This is required because some | ||||
|  * targets are so fast that there is no time between receiving and | ||||
|  * transmitting the frame. If the master is to slow with enabling its  | ||||
|  * receiver then he will not receive the response correctly. | ||||
|  */ | ||||
| #ifndef MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS | ||||
| #define MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS    ( 0 ) | ||||
| #endif | ||||
| 
 | ||||
| /*! \brief Maximum number of Modbus functions codes the protocol stack
 | ||||
|  *    should support. | ||||
|  * | ||||
|  * The maximum number of supported Modbus functions must be greater than | ||||
|  * the sum of all enabled functions in this file and custom function | ||||
|  * handlers. If set to small adding more functions will fail. | ||||
|  */ | ||||
| #define MB_FUNC_HANDLERS_MAX                    ( 16 ) | ||||
| 
 | ||||
| /*! \brief Number of bytes which should be allocated for the <em>Report Slave ID
 | ||||
|  *    </em>command. | ||||
|  * | ||||
|  * This number limits the maximum size of the additional segment in the | ||||
|  * report slave id function. See eMBSetSlaveID(  ) for more information on | ||||
|  * how to set this value. It is only used if MB_FUNC_OTHER_REP_SLAVEID_ENABLED | ||||
|  * is set to <code>1</code>. | ||||
|  */ | ||||
| #define MB_FUNC_OTHER_REP_SLAVEID_BUF           ( 32 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Report Slave ID</em> function should be enabled. */ | ||||
| #define MB_FUNC_OTHER_REP_SLAVEID_ENABLED       (  1 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Read Input Registers</em> function should be enabled. */ | ||||
| #define MB_FUNC_READ_INPUT_ENABLED              (  1 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Read Holding Registers</em> function should be enabled. */ | ||||
| #define MB_FUNC_READ_HOLDING_ENABLED            (  1 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Write Single Register</em> function should be enabled. */ | ||||
| #define MB_FUNC_WRITE_HOLDING_ENABLED           (  1 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Write Multiple registers</em> function should be enabled. */ | ||||
| #define MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED  (  1 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Read Coils</em> function should be enabled. */ | ||||
| #define MB_FUNC_READ_COILS_ENABLED              (  1 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Write Coils</em> function should be enabled. */ | ||||
| #define MB_FUNC_WRITE_COIL_ENABLED              (  1 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Write Multiple Coils</em> function should be enabled. */ | ||||
| #define MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED    (  1 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Read Discrete Inputs</em> function should be enabled. */ | ||||
| #define MB_FUNC_READ_DISCRETE_INPUTS_ENABLED    (  1 ) | ||||
| 
 | ||||
| /*! \brief If the <em>Read/Write Multiple Registers</em> function should be enabled. */ | ||||
| #define MB_FUNC_READWRITE_HOLDING_ENABLED       (  1 ) | ||||
| 
 | ||||
| /*! @} */ | ||||
| #ifdef __cplusplus | ||||
|     PR_END_EXTERN_C | ||||
| #endif | ||||
| #endif | ||||
|  | @ -0,0 +1,86 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _MB_FRAME_H | ||||
| #define _MB_FRAME_H | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_BEGIN_EXTERN_C | ||||
| #endif | ||||
| 
 | ||||
| /*!
 | ||||
|  * Constants which defines the format of a modbus frame. The example is | ||||
|  * shown for a Modbus RTU/ASCII frame. Note that the Modbus PDU is not | ||||
|  * dependent on the underlying transport. | ||||
|  * | ||||
|  * <code> | ||||
|  * <------------------------ MODBUS SERIAL LINE PDU (1) -------------------> | ||||
|  *              <----------- MODBUS PDU (1') ----------------> | ||||
|  *  +-----------+---------------+----------------------------+-------------+ | ||||
|  *  | Address   | Function Code | Data                       | CRC/LRC     | | ||||
|  *  +-----------+---------------+----------------------------+-------------+ | ||||
|  *  |           |               |                                   | | ||||
|  * (2)        (3/2')           (3')                                (4) | ||||
|  * | ||||
|  * (1)  ... MB_SER_PDU_SIZE_MAX = 256 | ||||
|  * (2)  ... MB_SER_PDU_ADDR_OFF = 0 | ||||
|  * (3)  ... MB_SER_PDU_PDU_OFF  = 1 | ||||
|  * (4)  ... MB_SER_PDU_SIZE_CRC = 2 | ||||
|  * | ||||
|  * (1') ... MB_PDU_SIZE_MAX     = 253 | ||||
|  * (2') ... MB_PDU_FUNC_OFF     = 0 | ||||
|  * (3') ... MB_PDU_DATA_OFF     = 1 | ||||
|  * </code> | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define MB_PDU_SIZE_MAX     253 /*!< Maximum size of a PDU. */ | ||||
| #define MB_PDU_SIZE_MIN     1   /*!< Function Code */ | ||||
| #define MB_PDU_FUNC_OFF     0   /*!< Offset of function code in PDU. */ | ||||
| #define MB_PDU_DATA_OFF     1   /*!< Offset for response data in PDU. */ | ||||
| 
 | ||||
| /* ----------------------- Prototypes  0-------------------------------------*/ | ||||
| typedef void    ( *pvMBFrameStart ) ( void ); | ||||
| 
 | ||||
| typedef void    ( *pvMBFrameStop ) ( void ); | ||||
| 
 | ||||
| typedef eMBErrorCode( *peMBFrameReceive ) ( UCHAR * pucRcvAddress, | ||||
|                                             UCHAR ** pucFrame, | ||||
|                                             USHORT * pusLength ); | ||||
| 
 | ||||
| typedef eMBErrorCode( *peMBFrameSend ) ( UCHAR slaveAddress, | ||||
|                                          const UCHAR * pucFrame, | ||||
|                                          USHORT usLength ); | ||||
| 
 | ||||
| typedef void( *pvMBFrameClose ) ( void ); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_END_EXTERN_C | ||||
| #endif | ||||
| #endif | ||||
|  | @ -0,0 +1,79 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
|   | ||||
| #ifndef _MB_FUNC_H | ||||
| #define _MB_FUNC_H | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_BEGIN_EXTERN_C | ||||
| #endif | ||||
| #if MB_FUNC_OTHER_REP_SLAVEID_BUF > 0 | ||||
|     eMBException eMBFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_READ_INPUT_ENABLED > 0 | ||||
| eMBException    eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_READ_HOLDING_ENABLED > 0 | ||||
| eMBException    eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_WRITE_HOLDING_ENABLED > 0 | ||||
| eMBException    eMBFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0 | ||||
| eMBException    eMBFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_READ_COILS_ENABLED > 0 | ||||
| eMBException    eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_WRITE_COIL_ENABLED > 0 | ||||
| eMBException    eMBFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0 | ||||
| eMBException    eMBFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0 | ||||
| eMBException    eMBFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_READWRITE_HOLDING_ENABLED > 0 | ||||
| eMBException    eMBFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_END_EXTERN_C | ||||
| #endif | ||||
| #endif | ||||
|  | @ -0,0 +1,269 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- System includes ----------------------------------*/ | ||||
| #include "stdlib.h" | ||||
| #include "string.h" | ||||
| 
 | ||||
| /* ----------------------- Platform includes --------------------------------*/ | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbframe.h" | ||||
| #include "mbproto.h" | ||||
| #include "mbconfig.h" | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define MB_PDU_FUNC_READ_ADDR_OFF           ( MB_PDU_DATA_OFF ) | ||||
| #define MB_PDU_FUNC_READ_COILCNT_OFF        ( MB_PDU_DATA_OFF + 2 ) | ||||
| #define MB_PDU_FUNC_READ_SIZE               ( 4 ) | ||||
| #define MB_PDU_FUNC_READ_COILCNT_MAX        ( 0x07D0 ) | ||||
| 
 | ||||
| #define MB_PDU_FUNC_WRITE_ADDR_OFF          ( MB_PDU_DATA_OFF ) | ||||
| #define MB_PDU_FUNC_WRITE_VALUE_OFF         ( MB_PDU_DATA_OFF + 2 ) | ||||
| #define MB_PDU_FUNC_WRITE_SIZE              ( 4 ) | ||||
| 
 | ||||
| #define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF      ( MB_PDU_DATA_OFF ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF   ( MB_PDU_DATA_OFF + 2 ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF   ( MB_PDU_DATA_OFF + 4 ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_VALUES_OFF    ( MB_PDU_DATA_OFF + 5 ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_SIZE_MIN      ( 5 ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX   ( 0x07B0 ) | ||||
| 
 | ||||
| /* ----------------------- Static functions ---------------------------------*/ | ||||
| eMBException    prveMBError2Exception( eMBErrorCode eErrorCode ); | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| 
 | ||||
| #if MB_FUNC_READ_COILS_ENABLED > 0 | ||||
| 
 | ||||
| eMBException | ||||
| eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     USHORT          usRegAddress; | ||||
|     USHORT          usCoilCount; | ||||
|     UCHAR           ucNBytes; | ||||
|     UCHAR          *pucFrameCur; | ||||
| 
 | ||||
|     eMBException    eStatus = MB_EX_NONE; | ||||
|     eMBErrorCode    eRegStatus; | ||||
| 
 | ||||
|     if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) ) | ||||
|     { | ||||
|         usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 ); | ||||
|         usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] ); | ||||
|         usRegAddress++; | ||||
| 
 | ||||
|         usCoilCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] << 8 ); | ||||
|         usCoilCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF + 1] ); | ||||
| 
 | ||||
|         /* Check if the number of registers to read is valid. If not
 | ||||
|          * return Modbus illegal data value exception.  | ||||
|          */ | ||||
|         if( ( usCoilCount >= 1 ) && | ||||
|             ( usCoilCount < MB_PDU_FUNC_READ_COILCNT_MAX ) ) | ||||
|         { | ||||
|             /* Set the current PDU data pointer to the beginning. */ | ||||
|             pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF]; | ||||
|             *usLen = MB_PDU_FUNC_OFF; | ||||
| 
 | ||||
|             /* First byte contains the function code. */ | ||||
|             *pucFrameCur++ = MB_FUNC_READ_COILS; | ||||
|             *usLen += 1; | ||||
| 
 | ||||
|             /* Test if the quantity of coils is a multiple of 8. If not last
 | ||||
|              * byte is only partially field with unused coils set to zero. */ | ||||
|             if( ( usCoilCount & 0x0007 ) != 0 ) | ||||
|             { | ||||
|                 ucNBytes = ( UCHAR )( usCoilCount / 8 + 1 ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ucNBytes = ( UCHAR )( usCoilCount / 8 ); | ||||
|             } | ||||
|             *pucFrameCur++ = ucNBytes; | ||||
|             *usLen += 1; | ||||
| 
 | ||||
|             eRegStatus = | ||||
|                 eMBRegCoilsCB( pucFrameCur, usRegAddress, usCoilCount, | ||||
|                                MB_REG_READ ); | ||||
| 
 | ||||
|             /* If an error occured convert it into a Modbus exception. */ | ||||
|             if( eRegStatus != MB_ENOERR ) | ||||
|             { | ||||
|                 eStatus = prveMBError2Exception( eRegStatus ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 /* The response contains the function code, the starting address
 | ||||
|                  * and the quantity of registers. We reuse the old values in the  | ||||
|                  * buffer because they are still valid. */ | ||||
|                 *usLen += ucNBytes;; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* Can't be a valid read coil register request because the length
 | ||||
|          * is incorrect. */ | ||||
|         eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| #if MB_FUNC_WRITE_COIL_ENABLED > 0 | ||||
| eMBException | ||||
| eMBFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     USHORT          usRegAddress; | ||||
|     UCHAR           ucBuf[2]; | ||||
| 
 | ||||
|     eMBException    eStatus = MB_EX_NONE; | ||||
|     eMBErrorCode    eRegStatus; | ||||
| 
 | ||||
|     if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) ) | ||||
|     { | ||||
|         usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 ); | ||||
|         usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] ); | ||||
|         usRegAddress++; | ||||
| 
 | ||||
|         if( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF + 1] == 0x00 ) && | ||||
|             ( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) || | ||||
|               ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0x00 ) ) ) | ||||
|         { | ||||
|             ucBuf[1] = 0; | ||||
|             if( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) | ||||
|             { | ||||
|                 ucBuf[0] = 1; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ucBuf[0] = 0; | ||||
|             } | ||||
|             eRegStatus = | ||||
|                 eMBRegCoilsCB( &ucBuf[0], usRegAddress, 1, MB_REG_WRITE ); | ||||
| 
 | ||||
|             /* If an error occured convert it into a Modbus exception. */ | ||||
|             if( eRegStatus != MB_ENOERR ) | ||||
|             { | ||||
|                 eStatus = prveMBError2Exception( eRegStatus ); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* Can't be a valid write coil register request because the length
 | ||||
|          * is incorrect. */ | ||||
|         eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0 | ||||
| eMBException | ||||
| eMBFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     USHORT          usRegAddress; | ||||
|     USHORT          usCoilCnt; | ||||
|     UCHAR           ucByteCount; | ||||
|     UCHAR           ucByteCountVerify; | ||||
| 
 | ||||
|     eMBException    eStatus = MB_EX_NONE; | ||||
|     eMBErrorCode    eRegStatus; | ||||
| 
 | ||||
|     if( *usLen > ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) ) | ||||
|     { | ||||
|         usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 ); | ||||
|         usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] ); | ||||
|         usRegAddress++; | ||||
| 
 | ||||
|         usCoilCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF] << 8 ); | ||||
|         usCoilCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF + 1] ); | ||||
| 
 | ||||
|         ucByteCount = pucFrame[MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF]; | ||||
| 
 | ||||
|         /* Compute the number of expected bytes in the request. */ | ||||
|         if( ( usCoilCnt & 0x0007 ) != 0 ) | ||||
|         { | ||||
|             ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 + 1 ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 ); | ||||
|         } | ||||
| 
 | ||||
|         if( ( usCoilCnt >= 1 ) && | ||||
|             ( usCoilCnt <= MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX ) && | ||||
|             ( ucByteCountVerify == ucByteCount ) ) | ||||
|         { | ||||
|             eRegStatus = | ||||
|                 eMBRegCoilsCB( &pucFrame[MB_PDU_FUNC_WRITE_MUL_VALUES_OFF], | ||||
|                                usRegAddress, usCoilCnt, MB_REG_WRITE ); | ||||
| 
 | ||||
|             /* If an error occured convert it into a Modbus exception. */ | ||||
|             if( eRegStatus != MB_ENOERR ) | ||||
|             { | ||||
|                 eStatus = prveMBError2Exception( eRegStatus ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 /* The response contains the function code, the starting address
 | ||||
|                  * and the quantity of registers. We reuse the old values in the  | ||||
|                  * buffer because they are still valid. */ | ||||
|                 *usLen = MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* Can't be a valid write coil register request because the length
 | ||||
|          * is incorrect. */ | ||||
|         eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,28 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
|  | @ -0,0 +1,133 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- System includes ----------------------------------*/ | ||||
| #include "stdlib.h" | ||||
| #include "string.h" | ||||
| 
 | ||||
| /* ----------------------- Platform includes --------------------------------*/ | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbframe.h" | ||||
| #include "mbproto.h" | ||||
| #include "mbconfig.h" | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define MB_PDU_FUNC_READ_ADDR_OFF           ( MB_PDU_DATA_OFF ) | ||||
| #define MB_PDU_FUNC_READ_DISCCNT_OFF        ( MB_PDU_DATA_OFF + 2 ) | ||||
| #define MB_PDU_FUNC_READ_SIZE               ( 4 ) | ||||
| #define MB_PDU_FUNC_READ_DISCCNT_MAX        ( 0x07D0 ) | ||||
| 
 | ||||
| /* ----------------------- Static functions ---------------------------------*/ | ||||
| eMBException    prveMBError2Exception( eMBErrorCode eErrorCode ); | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| 
 | ||||
| #if MB_FUNC_READ_COILS_ENABLED > 0 | ||||
| 
 | ||||
| eMBException | ||||
| eMBFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     USHORT          usRegAddress; | ||||
|     USHORT          usDiscreteCnt; | ||||
|     UCHAR           ucNBytes; | ||||
|     UCHAR          *pucFrameCur; | ||||
| 
 | ||||
|     eMBException    eStatus = MB_EX_NONE; | ||||
|     eMBErrorCode    eRegStatus; | ||||
| 
 | ||||
|     if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) ) | ||||
|     { | ||||
|         usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 ); | ||||
|         usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] ); | ||||
|         usRegAddress++; | ||||
| 
 | ||||
|         usDiscreteCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF] << 8 ); | ||||
|         usDiscreteCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF + 1] ); | ||||
| 
 | ||||
|         /* Check if the number of registers to read is valid. If not
 | ||||
|          * return Modbus illegal data value exception.  | ||||
|          */ | ||||
|         if( ( usDiscreteCnt >= 1 ) && | ||||
|             ( usDiscreteCnt < MB_PDU_FUNC_READ_DISCCNT_MAX ) ) | ||||
|         { | ||||
|             /* Set the current PDU data pointer to the beginning. */ | ||||
|             pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF]; | ||||
|             *usLen = MB_PDU_FUNC_OFF; | ||||
| 
 | ||||
|             /* First byte contains the function code. */ | ||||
|             *pucFrameCur++ = MB_FUNC_READ_DISCRETE_INPUTS; | ||||
|             *usLen += 1; | ||||
| 
 | ||||
|             /* Test if the quantity of coils is a multiple of 8. If not last
 | ||||
|              * byte is only partially field with unused coils set to zero. */ | ||||
|             if( ( usDiscreteCnt & 0x0007 ) != 0 ) | ||||
|             { | ||||
|                 ucNBytes = ( UCHAR ) ( usDiscreteCnt / 8 + 1 ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ucNBytes = ( UCHAR ) ( usDiscreteCnt / 8 ); | ||||
|             } | ||||
|             *pucFrameCur++ = ucNBytes; | ||||
|             *usLen += 1; | ||||
| 
 | ||||
|             eRegStatus = | ||||
|                 eMBRegDiscreteCB( pucFrameCur, usRegAddress, usDiscreteCnt ); | ||||
| 
 | ||||
|             /* If an error occured convert it into a Modbus exception. */ | ||||
|             if( eRegStatus != MB_ENOERR ) | ||||
|             { | ||||
|                 eStatus = prveMBError2Exception( eRegStatus ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 /* The response contains the function code, the starting address
 | ||||
|                  * and the quantity of registers. We reuse the old values in the  | ||||
|                  * buffer because they are still valid. */ | ||||
|                 *usLen += ucNBytes;; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* Can't be a valid read coil register request because the length
 | ||||
|          * is incorrect. */ | ||||
|         eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,307 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- System includes ----------------------------------*/ | ||||
| #include "stdlib.h" | ||||
| #include "string.h" | ||||
| 
 | ||||
| /* ----------------------- Platform includes --------------------------------*/ | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbframe.h" | ||||
| #include "mbproto.h" | ||||
| #include "mbconfig.h" | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define MB_PDU_FUNC_READ_ADDR_OFF               ( MB_PDU_DATA_OFF + 0) | ||||
| #define MB_PDU_FUNC_READ_REGCNT_OFF             ( MB_PDU_DATA_OFF + 2 ) | ||||
| #define MB_PDU_FUNC_READ_SIZE                   ( 4 ) | ||||
| #define MB_PDU_FUNC_READ_REGCNT_MAX             ( 0x007D ) | ||||
| 
 | ||||
| #define MB_PDU_FUNC_WRITE_ADDR_OFF              ( MB_PDU_DATA_OFF + 0) | ||||
| #define MB_PDU_FUNC_WRITE_VALUE_OFF             ( MB_PDU_DATA_OFF + 2 ) | ||||
| #define MB_PDU_FUNC_WRITE_SIZE                  ( 4 ) | ||||
| 
 | ||||
| #define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF          ( MB_PDU_DATA_OFF + 0 ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF        ( MB_PDU_DATA_OFF + 2 ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF       ( MB_PDU_DATA_OFF + 4 ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_VALUES_OFF        ( MB_PDU_DATA_OFF + 5 ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_SIZE_MIN          ( 5 ) | ||||
| #define MB_PDU_FUNC_WRITE_MUL_REGCNT_MAX        ( 0x0078 ) | ||||
| 
 | ||||
| #define MB_PDU_FUNC_READWRITE_READ_ADDR_OFF     ( MB_PDU_DATA_OFF + 0 ) | ||||
| #define MB_PDU_FUNC_READWRITE_READ_REGCNT_OFF   ( MB_PDU_DATA_OFF + 2 ) | ||||
| #define MB_PDU_FUNC_READWRITE_WRITE_ADDR_OFF    ( MB_PDU_DATA_OFF + 4 ) | ||||
| #define MB_PDU_FUNC_READWRITE_WRITE_REGCNT_OFF  ( MB_PDU_DATA_OFF + 6 ) | ||||
| #define MB_PDU_FUNC_READWRITE_BYTECNT_OFF       ( MB_PDU_DATA_OFF + 8 ) | ||||
| #define MB_PDU_FUNC_READWRITE_WRITE_VALUES_OFF  ( MB_PDU_DATA_OFF + 9 ) | ||||
| #define MB_PDU_FUNC_READWRITE_SIZE_MIN          ( 9 ) | ||||
| 
 | ||||
| /* ----------------------- Static functions ---------------------------------*/ | ||||
| eMBException    prveMBError2Exception( eMBErrorCode eErrorCode ); | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| 
 | ||||
| #if MB_FUNC_WRITE_HOLDING_ENABLED > 0 | ||||
| 
 | ||||
| eMBException | ||||
| eMBFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     USHORT          usRegAddress; | ||||
|     eMBException    eStatus = MB_EX_NONE; | ||||
|     eMBErrorCode    eRegStatus; | ||||
| 
 | ||||
|     if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) ) | ||||
|     { | ||||
|         usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 ); | ||||
|         usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] ); | ||||
|         usRegAddress++; | ||||
| 
 | ||||
|         /* Make callback to update the value. */ | ||||
|         eRegStatus = eMBRegHoldingCB( &pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF], | ||||
|                                       usRegAddress, 1, MB_REG_WRITE ); | ||||
| 
 | ||||
|         /* If an error occured convert it into a Modbus exception. */ | ||||
|         if( eRegStatus != MB_ENOERR ) | ||||
|         { | ||||
|             eStatus = prveMBError2Exception( eRegStatus ); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* Can't be a valid request because the length is incorrect. */ | ||||
|         eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0 | ||||
| eMBException | ||||
| eMBFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     USHORT          usRegAddress; | ||||
|     USHORT          usRegCount; | ||||
|     UCHAR           ucRegByteCount; | ||||
| 
 | ||||
|     eMBException    eStatus = MB_EX_NONE; | ||||
|     eMBErrorCode    eRegStatus; | ||||
| 
 | ||||
|     if( *usLen >= ( MB_PDU_FUNC_WRITE_MUL_SIZE_MIN + MB_PDU_SIZE_MIN ) ) | ||||
|     { | ||||
|         usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 ); | ||||
|         usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] ); | ||||
|         usRegAddress++; | ||||
| 
 | ||||
|         usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF] << 8 ); | ||||
|         usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF + 1] ); | ||||
| 
 | ||||
|         ucRegByteCount = pucFrame[MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF]; | ||||
| 
 | ||||
|         if( ( usRegCount >= 1 ) && | ||||
|             ( usRegCount <= MB_PDU_FUNC_WRITE_MUL_REGCNT_MAX ) && | ||||
|             ( ucRegByteCount == ( UCHAR ) ( 2 * usRegCount ) ) ) | ||||
|         { | ||||
|             /* Make callback to update the register values. */ | ||||
|             eRegStatus = | ||||
|                 eMBRegHoldingCB( &pucFrame[MB_PDU_FUNC_WRITE_MUL_VALUES_OFF], | ||||
|                                  usRegAddress, usRegCount, MB_REG_WRITE ); | ||||
| 
 | ||||
|             /* If an error occured convert it into a Modbus exception. */ | ||||
|             if( eRegStatus != MB_ENOERR ) | ||||
|             { | ||||
|                 eStatus = prveMBError2Exception( eRegStatus ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 /* The response contains the function code, the starting
 | ||||
|                  * address and the quantity of registers. We reuse the | ||||
|                  * old values in the buffer because they are still valid. | ||||
|                  */ | ||||
|                 *usLen = MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* Can't be a valid request because the length is incorrect. */ | ||||
|         eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_READ_HOLDING_ENABLED > 0 | ||||
| 
 | ||||
| eMBException | ||||
| eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     USHORT          usRegAddress; | ||||
|     USHORT          usRegCount; | ||||
|     UCHAR          *pucFrameCur; | ||||
| 
 | ||||
|     eMBException    eStatus = MB_EX_NONE; | ||||
|     eMBErrorCode    eRegStatus; | ||||
| 
 | ||||
|     if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) ) | ||||
|     { | ||||
|         usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 ); | ||||
|         usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] ); | ||||
|         usRegAddress++; | ||||
| 
 | ||||
|         usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 ); | ||||
|         usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] ); | ||||
| 
 | ||||
|         /* Check if the number of registers to read is valid. If not
 | ||||
|          * return Modbus illegal data value exception.  | ||||
|          */ | ||||
|         if( ( usRegCount >= 1 ) && ( usRegCount <= MB_PDU_FUNC_READ_REGCNT_MAX ) ) | ||||
|         { | ||||
|             /* Set the current PDU data pointer to the beginning. */ | ||||
|             pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF]; | ||||
|             *usLen = MB_PDU_FUNC_OFF; | ||||
| 
 | ||||
|             /* First byte contains the function code. */ | ||||
|             *pucFrameCur++ = MB_FUNC_READ_HOLDING_REGISTER; | ||||
|             *usLen += 1; | ||||
| 
 | ||||
|             /* Second byte in the response contain the number of bytes. */ | ||||
|             *pucFrameCur++ = ( UCHAR ) ( usRegCount * 2 ); | ||||
|             *usLen += 1; | ||||
| 
 | ||||
|             /* Make callback to fill the buffer. */ | ||||
|             eRegStatus = eMBRegHoldingCB( pucFrameCur, usRegAddress, usRegCount, MB_REG_READ ); | ||||
|             /* If an error occured convert it into a Modbus exception. */ | ||||
|             if( eRegStatus != MB_ENOERR ) | ||||
|             { | ||||
|                 eStatus = prveMBError2Exception( eRegStatus ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *usLen += usRegCount * 2; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* Can't be a valid request because the length is incorrect. */ | ||||
|         eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #if MB_FUNC_READWRITE_HOLDING_ENABLED > 0 | ||||
| 
 | ||||
| eMBException | ||||
| eMBFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     USHORT          usRegReadAddress; | ||||
|     USHORT          usRegReadCount; | ||||
|     USHORT          usRegWriteAddress; | ||||
|     USHORT          usRegWriteCount; | ||||
|     UCHAR           ucRegWriteByteCount; | ||||
|     UCHAR          *pucFrameCur; | ||||
| 
 | ||||
|     eMBException    eStatus = MB_EX_NONE; | ||||
|     eMBErrorCode    eRegStatus; | ||||
| 
 | ||||
|     if( *usLen >= ( MB_PDU_FUNC_READWRITE_SIZE_MIN + MB_PDU_SIZE_MIN ) ) | ||||
|     { | ||||
|         usRegReadAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_ADDR_OFF] << 8U ); | ||||
|         usRegReadAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_ADDR_OFF + 1] ); | ||||
|         usRegReadAddress++; | ||||
| 
 | ||||
|         usRegReadCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_REGCNT_OFF] << 8U ); | ||||
|         usRegReadCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_READ_REGCNT_OFF + 1] ); | ||||
| 
 | ||||
|         usRegWriteAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_ADDR_OFF] << 8U ); | ||||
|         usRegWriteAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_ADDR_OFF + 1] ); | ||||
|         usRegWriteAddress++; | ||||
| 
 | ||||
|         usRegWriteCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_REGCNT_OFF] << 8U ); | ||||
|         usRegWriteCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READWRITE_WRITE_REGCNT_OFF + 1] ); | ||||
| 
 | ||||
|         ucRegWriteByteCount = pucFrame[MB_PDU_FUNC_READWRITE_BYTECNT_OFF]; | ||||
| 
 | ||||
|         if( ( usRegReadCount >= 1 ) && ( usRegReadCount <= 0x7D ) && | ||||
|             ( usRegWriteCount >= 1 ) && ( usRegWriteCount <= 0x79 ) && | ||||
|             ( ( 2 * usRegWriteCount ) == ucRegWriteByteCount ) ) | ||||
|         { | ||||
|             /* Make callback to update the register values. */ | ||||
|             eRegStatus = eMBRegHoldingCB( &pucFrame[MB_PDU_FUNC_READWRITE_WRITE_VALUES_OFF], | ||||
|                                           usRegWriteAddress, usRegWriteCount, MB_REG_WRITE ); | ||||
| 
 | ||||
|             if( eRegStatus == MB_ENOERR ) | ||||
|             { | ||||
|                 /* Set the current PDU data pointer to the beginning. */ | ||||
|                 pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF]; | ||||
|                 *usLen = MB_PDU_FUNC_OFF; | ||||
| 
 | ||||
|                 /* First byte contains the function code. */ | ||||
|                 *pucFrameCur++ = MB_FUNC_READWRITE_MULTIPLE_REGISTERS; | ||||
|                 *usLen += 1; | ||||
| 
 | ||||
|                 /* Second byte in the response contain the number of bytes. */ | ||||
|                 *pucFrameCur++ = ( UCHAR ) ( usRegReadCount * 2 ); | ||||
|                 *usLen += 1; | ||||
| 
 | ||||
|                 /* Make the read callback. */ | ||||
|                 eRegStatus = | ||||
|                     eMBRegHoldingCB( pucFrameCur, usRegReadAddress, usRegReadCount, MB_REG_READ ); | ||||
|                 if( eRegStatus == MB_ENOERR ) | ||||
|                 { | ||||
|                     *usLen += 2 * usRegReadCount; | ||||
|                 } | ||||
|             } | ||||
|             if( eRegStatus != MB_ENOERR ) | ||||
|             { | ||||
|                 eStatus = prveMBError2Exception( eRegStatus ); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|         } | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,121 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- System includes ----------------------------------*/ | ||||
| #include "stdlib.h" | ||||
| #include "string.h" | ||||
| 
 | ||||
| /* ----------------------- Platform includes --------------------------------*/ | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbframe.h" | ||||
| #include "mbproto.h" | ||||
| #include "mbconfig.h" | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define MB_PDU_FUNC_READ_ADDR_OFF           ( MB_PDU_DATA_OFF ) | ||||
| #define MB_PDU_FUNC_READ_REGCNT_OFF         ( MB_PDU_DATA_OFF + 2 ) | ||||
| #define MB_PDU_FUNC_READ_SIZE               ( 4 ) | ||||
| #define MB_PDU_FUNC_READ_REGCNT_MAX         ( 0x007D ) | ||||
| 
 | ||||
| #define MB_PDU_FUNC_READ_RSP_BYTECNT_OFF    ( MB_PDU_DATA_OFF ) | ||||
| 
 | ||||
| /* ----------------------- Static functions ---------------------------------*/ | ||||
| eMBException    prveMBError2Exception( eMBErrorCode eErrorCode ); | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| #if MB_FUNC_READ_INPUT_ENABLED > 0 | ||||
| 
 | ||||
| eMBException | ||||
| eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     USHORT          usRegAddress; | ||||
|     USHORT          usRegCount; | ||||
|     UCHAR          *pucFrameCur; | ||||
| 
 | ||||
|     eMBException    eStatus = MB_EX_NONE; | ||||
|     eMBErrorCode    eRegStatus; | ||||
| 
 | ||||
|     if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) ) | ||||
|     { | ||||
|         usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 ); | ||||
|         usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] ); | ||||
|         usRegAddress++; | ||||
| 
 | ||||
|         usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 ); | ||||
|         usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] ); | ||||
| 
 | ||||
|         /* Check if the number of registers to read is valid. If not
 | ||||
|          * return Modbus illegal data value exception.  | ||||
|          */ | ||||
|         if( ( usRegCount >= 1 ) | ||||
|             && ( usRegCount < MB_PDU_FUNC_READ_REGCNT_MAX ) ) | ||||
|         { | ||||
|             /* Set the current PDU data pointer to the beginning. */ | ||||
|             pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF]; | ||||
|             *usLen = MB_PDU_FUNC_OFF; | ||||
| 
 | ||||
|             /* First byte contains the function code. */ | ||||
|             *pucFrameCur++ = MB_FUNC_READ_INPUT_REGISTER; | ||||
|             *usLen += 1; | ||||
| 
 | ||||
|             /* Second byte in the response contain the number of bytes. */ | ||||
|             *pucFrameCur++ = ( UCHAR )( usRegCount * 2 ); | ||||
|             *usLen += 1; | ||||
| 
 | ||||
|             eRegStatus = | ||||
|                 eMBRegInputCB( pucFrameCur, usRegAddress, usRegCount ); | ||||
| 
 | ||||
|             /* If an error occured convert it into a Modbus exception. */ | ||||
|             if( eRegStatus != MB_ENOERR ) | ||||
|             { | ||||
|                 eStatus = prveMBError2Exception( eRegStatus ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 *usLen += usRegCount * 2; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* Can't be a valid read input register request because the length
 | ||||
|          * is incorrect. */ | ||||
|         eStatus = MB_EX_ILLEGAL_DATA_VALUE; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,87 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- System includes ----------------------------------*/ | ||||
| #include "stdlib.h" | ||||
| #include "string.h" | ||||
| 
 | ||||
| /* ----------------------- Platform includes --------------------------------*/ | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbframe.h" | ||||
| #include "mbproto.h" | ||||
| #include "mbconfig.h" | ||||
| 
 | ||||
| #if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0 | ||||
| 
 | ||||
| /* ----------------------- Static variables ---------------------------------*/ | ||||
| static UCHAR    ucMBSlaveID[MB_FUNC_OTHER_REP_SLAVEID_BUF]; | ||||
| static USHORT   usMBSlaveIDLen; | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| 
 | ||||
| eMBErrorCode | ||||
| eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning, | ||||
|                UCHAR const *pucAdditional, USHORT usAdditionalLen ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
| 
 | ||||
|     /* the first byte and second byte in the buffer is reserved for
 | ||||
|      * the parameter ucSlaveID and the running flag. The rest of | ||||
|      * the buffer is available for additional data. */ | ||||
|     if( usAdditionalLen + 2 < MB_FUNC_OTHER_REP_SLAVEID_BUF ) | ||||
|     { | ||||
|         usMBSlaveIDLen = 0; | ||||
|         ucMBSlaveID[usMBSlaveIDLen++] = ucSlaveID; | ||||
|         ucMBSlaveID[usMBSlaveIDLen++] = ( UCHAR )( xIsRunning ? 0xFF : 0x00 ); | ||||
|         if( usAdditionalLen > 0 ) | ||||
|         { | ||||
|             memcpy( &ucMBSlaveID[usMBSlaveIDLen], pucAdditional, | ||||
|                     ( size_t )usAdditionalLen ); | ||||
|             usMBSlaveIDLen += usAdditionalLen; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         eStatus = MB_ENORES; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| eMBException | ||||
| eMBFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen ) | ||||
| { | ||||
|     memcpy( &pucFrame[MB_PDU_DATA_OFF], &ucMBSlaveID[0], ( size_t )usMBSlaveIDLen ); | ||||
|     *usLen = ( USHORT )( MB_PDU_DATA_OFF + usMBSlaveIDLen ); | ||||
|     return MB_EX_NONE; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,128 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _MB_PORT_H | ||||
| #define _MB_PORT_H | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_BEGIN_EXTERN_C | ||||
| #endif | ||||
| 
 | ||||
| /* ----------------------- Type definitions ---------------------------------*/ | ||||
| 
 | ||||
| typedef enum | ||||
| { | ||||
|     EV_READY,                   /*!< Startup finished. */ | ||||
|     EV_FRAME_RECEIVED,          /*!< Frame received. */ | ||||
|     EV_EXECUTE,                 /*!< Execute function. */ | ||||
|     EV_FRAME_SENT               /*!< Frame sent. */ | ||||
| } eMBEventType; | ||||
| 
 | ||||
| /*! \ingroup modbus
 | ||||
|  * \brief Parity used for characters in serial mode. | ||||
|  * | ||||
|  * The parity which should be applied to the characters sent over the serial | ||||
|  * link. Please note that this values are actually passed to the porting | ||||
|  * layer and therefore not all parity modes might be available. | ||||
|  */ | ||||
| typedef enum | ||||
| { | ||||
|     MB_PAR_NONE,                /*!< No parity. */ | ||||
|     MB_PAR_ODD,                 /*!< Odd parity. */ | ||||
|     MB_PAR_EVEN                 /*!< Even parity. */ | ||||
| } eMBParity; | ||||
| 
 | ||||
| /* ----------------------- Supporting functions -----------------------------*/ | ||||
| BOOL            xMBPortEventInit( void ); | ||||
| 
 | ||||
| BOOL            xMBPortEventPost( eMBEventType eEvent ); | ||||
| 
 | ||||
| BOOL            xMBPortEventGet(  /*@out@ */ eMBEventType * eEvent ); | ||||
| 
 | ||||
| /* ----------------------- Serial port functions ----------------------------*/ | ||||
| 
 | ||||
| BOOL            xMBPortSerialInit( UCHAR ucPort, ULONG ulBaudRate, | ||||
|                                    UCHAR ucDataBits, eMBParity eParity ); | ||||
| 
 | ||||
| void            vMBPortClose( void ); | ||||
| 
 | ||||
| void            xMBPortSerialClose( void ); | ||||
| 
 | ||||
| void            vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ); | ||||
| 
 | ||||
| BOOL            xMBPortSerialGetByte( CHAR * pucByte ); | ||||
| 
 | ||||
| BOOL            xMBPortSerialPutByte( CHAR ucByte ); | ||||
| 
 | ||||
| /* ----------------------- Timers functions ---------------------------------*/ | ||||
| BOOL            xMBPortTimersInit( USHORT usTimeOut50us ); | ||||
| 
 | ||||
| void            xMBPortTimersClose( void ); | ||||
| 
 | ||||
| void            vMBPortTimersEnable( void ); | ||||
| 
 | ||||
| void            vMBPortTimersDisable( void ); | ||||
| 
 | ||||
| void            vMBPortTimersDelay( USHORT usTimeOutMS ); | ||||
| 
 | ||||
| /* ----------------------- Callback for the protocol stack ------------------*/ | ||||
| 
 | ||||
| /*!
 | ||||
|  * \brief Callback function for the porting layer when a new byte is | ||||
|  *   available. | ||||
|  * | ||||
|  * Depending upon the mode this callback function is used by the RTU or | ||||
|  * ASCII transmission layers. In any case a call to xMBPortSerialGetByte() | ||||
|  * must immediately return a new character. | ||||
|  * | ||||
|  * \return <code>TRUE</code> if a event was posted to the queue because | ||||
|  *   a new byte was received. The port implementation should wake up the | ||||
|  *   tasks which are currently blocked on the eventqueue. | ||||
|  */ | ||||
| extern          BOOL( *pxMBFrameCBByteReceived ) ( void ); | ||||
| 
 | ||||
| extern          BOOL( *pxMBFrameCBTransmitterEmpty ) ( void ); | ||||
| 
 | ||||
| extern          BOOL( *pxMBPortCBTimerExpired ) ( void ); | ||||
| 
 | ||||
| /* ----------------------- TCP port functions -------------------------------*/ | ||||
| BOOL            xMBTCPPortInit( USHORT usTCPPort ); | ||||
| 
 | ||||
| void            vMBTCPPortClose( void ); | ||||
| 
 | ||||
| void            vMBTCPPortDisable( void ); | ||||
| 
 | ||||
| BOOL            xMBTCPPortGetRequest( UCHAR **ppucMBTCPFrame, USHORT * usTCPLength ); | ||||
| 
 | ||||
| BOOL            xMBTCPPortSendResponse( const UCHAR *pucMBTCPFrame, USHORT usTCPLength ); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_END_EXTERN_C | ||||
| #endif | ||||
| #endif | ||||
|  | @ -0,0 +1,82 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _MB_PROTO_H | ||||
| #define _MB_PROTO_H | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_BEGIN_EXTERN_C | ||||
| #endif | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define MB_ADDRESS_BROADCAST    ( 0 )   /*! Modbus broadcast address. */ | ||||
| #define MB_ADDRESS_MIN          ( 1 )   /*! Smallest possible slave address. */ | ||||
| #define MB_ADDRESS_MAX          ( 247 ) /*! Biggest possible slave address. */ | ||||
| #define MB_FUNC_NONE                          (  0 ) | ||||
| #define MB_FUNC_READ_COILS                    (  1 ) | ||||
| #define MB_FUNC_READ_DISCRETE_INPUTS          (  2 ) | ||||
| #define MB_FUNC_WRITE_SINGLE_COIL             (  5 ) | ||||
| #define MB_FUNC_WRITE_MULTIPLE_COILS          ( 15 ) | ||||
| #define MB_FUNC_READ_HOLDING_REGISTER         (  3 ) | ||||
| #define MB_FUNC_READ_INPUT_REGISTER           (  4 ) | ||||
| #define MB_FUNC_WRITE_REGISTER                (  6 ) | ||||
| #define MB_FUNC_WRITE_MULTIPLE_REGISTERS      ( 16 ) | ||||
| #define MB_FUNC_READWRITE_MULTIPLE_REGISTERS  ( 23 ) | ||||
| #define MB_FUNC_DIAG_READ_EXCEPTION           (  7 ) | ||||
| #define MB_FUNC_DIAG_DIAGNOSTIC               (  8 ) | ||||
| #define MB_FUNC_DIAG_GET_COM_EVENT_CNT        ( 11 ) | ||||
| #define MB_FUNC_DIAG_GET_COM_EVENT_LOG        ( 12 ) | ||||
| #define MB_FUNC_OTHER_REPORT_SLAVEID          ( 17 ) | ||||
| #define MB_FUNC_ERROR                         ( 128 ) | ||||
| /* ----------------------- Type definitions ---------------------------------*/ | ||||
|     typedef enum | ||||
| { | ||||
|     MB_EX_NONE = 0x00, | ||||
|     MB_EX_ILLEGAL_FUNCTION = 0x01, | ||||
|     MB_EX_ILLEGAL_DATA_ADDRESS = 0x02, | ||||
|     MB_EX_ILLEGAL_DATA_VALUE = 0x03, | ||||
|     MB_EX_SLAVE_DEVICE_FAILURE = 0x04, | ||||
|     MB_EX_ACKNOWLEDGE = 0x05, | ||||
|     MB_EX_SLAVE_BUSY = 0x06, | ||||
|     MB_EX_MEMORY_PARITY_ERROR = 0x08, | ||||
|     MB_EX_GATEWAY_PATH_FAILED = 0x0A, | ||||
|     MB_EX_GATEWAY_TGT_FAILED = 0x0B | ||||
| } eMBException; | ||||
| 
 | ||||
| typedef         eMBException( *pxMBFunctionHandler ) ( UCHAR * pucFrame, USHORT * pusLength ); | ||||
| 
 | ||||
| typedef struct | ||||
| { | ||||
|     UCHAR           ucFunctionCode; | ||||
|     pxMBFunctionHandler pxHandler; | ||||
| } xMBFunctionHandler; | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_END_EXTERN_C | ||||
| #endif | ||||
| #endif | ||||
|  | @ -0,0 +1,157 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- System includes ----------------------------------*/ | ||||
| #include "stdlib.h" | ||||
| #include "string.h" | ||||
| 
 | ||||
| /* ----------------------- Platform includes --------------------------------*/ | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbconfig.h" | ||||
| #include "mbtcp.h" | ||||
| #include "mbframe.h" | ||||
| #include "mbport.h" | ||||
| 
 | ||||
| #if MB_TCP_ENABLED > 0 | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| 
 | ||||
| /* ----------------------- MBAP Header --------------------------------------*/ | ||||
| /*
 | ||||
|  * | ||||
|  * <------------------------ MODBUS TCP/IP ADU(1) -------------------------> | ||||
|  *              <----------- MODBUS PDU (1') ----------------> | ||||
|  *  +-----------+---------------+------------------------------------------+ | ||||
|  *  | TID | PID | Length | UID  |Code | Data                               | | ||||
|  *  +-----------+---------------+------------------------------------------+ | ||||
|  *  |     |     |        |      |                                            | ||||
|  * (2)   (3)   (4)      (5)    (6)                                           | ||||
|  * | ||||
|  * (2)  ... MB_TCP_TID          = 0 (Transaction Identifier - 2 Byte)  | ||||
|  * (3)  ... MB_TCP_PID          = 2 (Protocol Identifier - 2 Byte) | ||||
|  * (4)  ... MB_TCP_LEN          = 4 (Number of bytes - 2 Byte) | ||||
|  * (5)  ... MB_TCP_UID          = 6 (Unit Identifier - 1 Byte) | ||||
|  * (6)  ... MB_TCP_FUNC         = 7 (Modbus Function Code) | ||||
|  * | ||||
|  * (1)  ... Modbus TCP/IP Application Data Unit | ||||
|  * (1') ... Modbus Protocol Data Unit | ||||
|  */ | ||||
| 
 | ||||
| #define MB_TCP_TID          0 | ||||
| #define MB_TCP_PID          2 | ||||
| #define MB_TCP_LEN          4 | ||||
| #define MB_TCP_UID          6 | ||||
| #define MB_TCP_FUNC         7 | ||||
| 
 | ||||
| #define MB_TCP_PROTOCOL_ID  0   /* 0 = Modbus Protocol */ | ||||
| 
 | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| eMBErrorCode | ||||
| eMBTCPDoInit( USHORT ucTCPPort ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
| 
 | ||||
|     if( xMBTCPPortInit( ucTCPPort ) == FALSE ) | ||||
|     { | ||||
|         eStatus = MB_EPORTERR; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| eMBTCPStart( void ) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void | ||||
| eMBTCPStop( void ) | ||||
| { | ||||
|     /* Make sure that no more clients are connected. */ | ||||
|     vMBTCPPortDisable( ); | ||||
| } | ||||
| 
 | ||||
| eMBErrorCode | ||||
| eMBTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLength ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_EIO; | ||||
|     UCHAR          *pucMBTCPFrame; | ||||
|     USHORT          usLength; | ||||
|     USHORT          usPID; | ||||
| 
 | ||||
|     if( xMBTCPPortGetRequest( &pucMBTCPFrame, &usLength ) != FALSE ) | ||||
|     { | ||||
|         usPID = pucMBTCPFrame[MB_TCP_PID] << 8U; | ||||
|         usPID |= pucMBTCPFrame[MB_TCP_PID + 1]; | ||||
| 
 | ||||
|         if( usPID == MB_TCP_PROTOCOL_ID ) | ||||
|         { | ||||
|             *ppucFrame = &pucMBTCPFrame[MB_TCP_FUNC]; | ||||
|             *pusLength = usLength - MB_TCP_FUNC; | ||||
|             eStatus = MB_ENOERR; | ||||
| 
 | ||||
|             /* Modbus TCP does not use any addresses. Fake the source address such
 | ||||
|              * that the processing part deals with this frame. | ||||
|              */ | ||||
|             *pucRcvAddress = MB_TCP_PSEUDO_ADDRESS; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         eStatus = MB_EIO; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| eMBErrorCode | ||||
| eMBTCPSend( UCHAR _unused, const UCHAR * pucFrame, USHORT usLength ) | ||||
| { | ||||
|     eMBErrorCode    eStatus = MB_ENOERR; | ||||
|     UCHAR          *pucMBTCPFrame = ( UCHAR * ) pucFrame - MB_TCP_FUNC; | ||||
|     USHORT          usTCPLength = usLength + MB_TCP_FUNC; | ||||
| 
 | ||||
|     /* The MBAP header is already initialized because the caller calls this
 | ||||
|      * function with the buffer returned by the previous call. Therefore we  | ||||
|      * only have to update the length in the header. Note that the length  | ||||
|      * header includes the size of the Modbus PDU and the UID Byte. Therefore  | ||||
|      * the length is usLength plus one. | ||||
|      */ | ||||
|     pucMBTCPFrame[MB_TCP_LEN] = ( usLength + 1 ) >> 8U; | ||||
|     pucMBTCPFrame[MB_TCP_LEN + 1] = ( usLength + 1 ) & 0xFF; | ||||
|     if( xMBTCPPortSendResponse( pucMBTCPFrame, usTCPLength ) == FALSE ) | ||||
|     { | ||||
|         eStatus = MB_EIO; | ||||
|     } | ||||
|     return eStatus; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,52 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _MB_TCP_H | ||||
| #define _MB_TCP_H | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_BEGIN_EXTERN_C | ||||
| #endif | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define MB_TCP_PSEUDO_ADDRESS   255 | ||||
| 
 | ||||
| /* ----------------------- Function prototypes ------------------------------*/ | ||||
|     eMBErrorCode eMBTCPDoInit( USHORT ucTCPPort ); | ||||
| void            eMBTCPStart( void ); | ||||
| void            eMBTCPStop( void ); | ||||
| eMBErrorCode    eMBTCPReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, | ||||
|                                USHORT * pusLength ); | ||||
| eMBErrorCode    eMBTCPSend( UCHAR _unused, const UCHAR * pucFrame, | ||||
|                             USHORT usLength ); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_END_EXTERN_C | ||||
| #endif | ||||
| #endif | ||||
|  | @ -0,0 +1,140 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /* ----------------------- System includes ----------------------------------*/ | ||||
| #include "stdlib.h" | ||||
| #include "string.h" | ||||
| 
 | ||||
| /* ----------------------- Platform includes --------------------------------*/ | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbproto.h" | ||||
| 
 | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define BITS_UCHAR      8U | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| void | ||||
| xMBUtilSetBits( UCHAR * ucByteBuf, USHORT usBitOffset, UCHAR ucNBits, | ||||
|                 UCHAR ucValue ) | ||||
| { | ||||
|     USHORT          usWordBuf; | ||||
|     USHORT          usMask; | ||||
|     USHORT          usByteOffset; | ||||
|     USHORT          usNPreBits; | ||||
|     USHORT          usValue = ucValue; | ||||
| 
 | ||||
|     assert( ucNBits <= 8 ); | ||||
|     assert( ( size_t )BITS_UCHAR == sizeof( UCHAR ) * 8 ); | ||||
| 
 | ||||
|     /* Calculate byte offset for first byte containing the bit values starting
 | ||||
|      * at usBitOffset. */ | ||||
|     usByteOffset = ( USHORT )( ( usBitOffset ) / BITS_UCHAR ); | ||||
| 
 | ||||
|     /* How many bits precede our bits to set. */ | ||||
|     usNPreBits = ( USHORT )( usBitOffset - usByteOffset * BITS_UCHAR ); | ||||
| 
 | ||||
|     /* Move bit field into position over bits to set */ | ||||
|     usValue <<= usNPreBits; | ||||
| 
 | ||||
|     /* Prepare a mask for setting the new bits. */ | ||||
|     usMask = ( USHORT )( ( 1 << ( USHORT ) ucNBits ) - 1 ); | ||||
|     usMask <<= usBitOffset - usByteOffset * BITS_UCHAR; | ||||
| 
 | ||||
|     /* copy bits into temporary storage. */ | ||||
|     usWordBuf = ucByteBuf[usByteOffset]; | ||||
|     usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UCHAR; | ||||
| 
 | ||||
|     /* Zero out bit field bits and then or value bits into them. */ | ||||
|     usWordBuf = ( USHORT )( ( usWordBuf & ( ~usMask ) ) | usValue ); | ||||
| 
 | ||||
|     /* move bits back into storage */ | ||||
|     ucByteBuf[usByteOffset] = ( UCHAR )( usWordBuf & 0xFF ); | ||||
|     ucByteBuf[usByteOffset + 1] = ( UCHAR )( usWordBuf >> BITS_UCHAR ); | ||||
| } | ||||
| 
 | ||||
| UCHAR | ||||
| xMBUtilGetBits( UCHAR * ucByteBuf, USHORT usBitOffset, UCHAR ucNBits ) | ||||
| { | ||||
|     USHORT          usWordBuf; | ||||
|     USHORT          usMask; | ||||
|     USHORT          usByteOffset; | ||||
|     USHORT          usNPreBits; | ||||
| 
 | ||||
|     /* Calculate byte offset for first byte containing the bit values starting
 | ||||
|      * at usBitOffset. */ | ||||
|     usByteOffset = ( USHORT )( ( usBitOffset ) / BITS_UCHAR ); | ||||
| 
 | ||||
|     /* How many bits precede our bits to set. */ | ||||
|     usNPreBits = ( USHORT )( usBitOffset - usByteOffset * BITS_UCHAR ); | ||||
| 
 | ||||
|     /* Prepare a mask for setting the new bits. */ | ||||
|     usMask = ( USHORT )( ( 1 << ( USHORT ) ucNBits ) - 1 ); | ||||
| 
 | ||||
|     /* copy bits into temporary storage. */ | ||||
|     usWordBuf = ucByteBuf[usByteOffset]; | ||||
|     usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UCHAR; | ||||
| 
 | ||||
|     /* throw away unneeded bits. */ | ||||
|     usWordBuf >>= usNPreBits; | ||||
| 
 | ||||
|     /* mask away bits above the requested bitfield. */ | ||||
|     usWordBuf &= usMask; | ||||
| 
 | ||||
|     return ( UCHAR ) usWordBuf; | ||||
| } | ||||
| 
 | ||||
| eMBException | ||||
| prveMBError2Exception( eMBErrorCode eErrorCode ) | ||||
| { | ||||
|     eMBException    eStatus; | ||||
| 
 | ||||
|     switch ( eErrorCode ) | ||||
|     { | ||||
|         case MB_ENOERR: | ||||
|             eStatus = MB_EX_NONE; | ||||
|             break; | ||||
| 
 | ||||
|         case MB_ENOREG: | ||||
|             eStatus = MB_EX_ILLEGAL_DATA_ADDRESS; | ||||
|             break; | ||||
| 
 | ||||
|         case MB_ETIMEDOUT: | ||||
|             eStatus = MB_EX_SLAVE_BUSY; | ||||
|             break; | ||||
| 
 | ||||
|         default: | ||||
|             eStatus = MB_EX_SLAVE_DEVICE_FAILURE; | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     return eStatus; | ||||
| } | ||||
|  | @ -0,0 +1,107 @@ | |||
| /* 
 | ||||
|  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. | ||||
|  * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at> | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions | ||||
|  * are met: | ||||
|  * 1. Redistributions of source code must retain the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer. | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright | ||||
|  *    notice, this list of conditions and the following disclaimer in the | ||||
|  *    documentation and/or other materials provided with the distribution. | ||||
|  * 3. The name of the author may not be used to endorse or promote products | ||||
|  *    derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||||
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||||
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _MB_UTILS_H | ||||
| #define _MB_UTILS_H | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_BEGIN_EXTERN_C | ||||
| #endif | ||||
| /*! \defgroup modbus_utils Utilities
 | ||||
|  * | ||||
|  * This module contains some utility functions which can be used by | ||||
|  * the application. It includes some special functions for working with | ||||
|  * bitfields backed by a character array buffer. | ||||
|  * | ||||
|  */ | ||||
| /*! \addtogroup modbus_utils
 | ||||
|  *  @{ | ||||
|  */ | ||||
| /*! \brief Function to set bits in a byte buffer.
 | ||||
|  * | ||||
|  * This function allows the efficient use of an array to implement bitfields. | ||||
|  * The array used for storing the bits must always be a multiple of two | ||||
|  * bytes. Up to eight bits can be set or cleared in one operation. | ||||
|  * | ||||
|  * \param ucByteBuf A buffer where the bit values are stored. Must be a | ||||
|  *   multiple of 2 bytes. No length checking is performed and if | ||||
|  *   usBitOffset / 8 is greater than the size of the buffer memory contents | ||||
|  *   is overwritten. | ||||
|  * \param usBitOffset The starting address of the bits to set. The first | ||||
|  *   bit has the offset 0. | ||||
|  * \param ucNBits Number of bits to modify. The value must always be smaller | ||||
|  *   than 8. | ||||
|  * \param ucValues Thew new values for the bits. The value for the first bit | ||||
|  *   starting at <code>usBitOffset</code> is the LSB of the value | ||||
|  *   <code>ucValues</code> | ||||
|  * | ||||
|  * \code | ||||
|  * ucBits[2] = {0, 0}; | ||||
|  * | ||||
|  * // Set bit 4 to 1 (read: set 1 bit starting at bit offset 4 to value 1)
 | ||||
|  * xMBUtilSetBits( ucBits, 4, 1, 1 ); | ||||
|  * | ||||
|  * // Set bit 7 to 1 and bit 8 to 0.
 | ||||
|  * xMBUtilSetBits( ucBits, 7, 2, 0x01 ); | ||||
|  * | ||||
|  * // Set bits 8 - 11 to 0x05 and bits 12 - 15 to 0x0A;
 | ||||
|  * xMBUtilSetBits( ucBits, 8, 8, 0x5A); | ||||
|  * \endcode | ||||
|  */ | ||||
| void            xMBUtilSetBits( UCHAR * ucByteBuf, USHORT usBitOffset, | ||||
|                                 UCHAR ucNBits, UCHAR ucValues ); | ||||
| 
 | ||||
| /*! \brief Function to read bits in a byte buffer.
 | ||||
|  * | ||||
|  * This function is used to extract up bit values from an array. Up to eight | ||||
|  * bit values can be extracted in one step. | ||||
|  * | ||||
|  * \param ucByteBuf A buffer where the bit values are stored. | ||||
|  * \param usBitOffset The starting address of the bits to set. The first | ||||
|  *   bit has the offset 0. | ||||
|  * \param ucNBits Number of bits to modify. The value must always be smaller | ||||
|  *   than 8. | ||||
|  * | ||||
|  * \code | ||||
|  * UCHAR ucBits[2] = {0, 0}; | ||||
|  * UCHAR ucResult; | ||||
|  * | ||||
|  * // Extract the bits 3 - 10.
 | ||||
|  * ucResult = xMBUtilGetBits( ucBits, 3, 8 ); | ||||
|  * \endcode | ||||
|  */ | ||||
| UCHAR           xMBUtilGetBits( UCHAR * ucByteBuf, USHORT usBitOffset, | ||||
|                                 UCHAR ucNBits ); | ||||
| 
 | ||||
| /*! @} */ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_END_EXTERN_C | ||||
| #endif | ||||
| #endif | ||||
|  | @ -0,0 +1,83 @@ | |||
| /*
 | ||||
|  * FreeModbus Libary: BSD Socket Library Port | ||||
|  * Copyright (C) 2006 Christian Walter <wolti@sil.at> | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  * | ||||
|  * File: $Id$ | ||||
|  */ | ||||
| 
 | ||||
|  /**********************************************************
 | ||||
|  *	Linux TCP support. | ||||
|  *	Based on Walter's project.  | ||||
|  *	Modified by Steven Guo <gotop167@163.com> | ||||
|  ***********************************************************/ | ||||
| 
 | ||||
| #ifndef _PORT_H | ||||
| #define _PORT_H | ||||
| 
 | ||||
| 
 | ||||
| #include <assert.h> | ||||
| 
 | ||||
| #define	INLINE | ||||
| #define PR_BEGIN_EXTERN_C			extern "C" { | ||||
| #define	PR_END_EXTERN_C				} | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_BEGIN_EXTERN_C | ||||
| #endif | ||||
| /* ----------------------- Defines ------------------------------------------*/ | ||||
| #define ENTER_CRITICAL_SECTION( ) | ||||
| #define EXIT_CRITICAL_SECTION( ) | ||||
| #define MB_PORT_HAS_CLOSE	1 | ||||
| #ifndef TRUE | ||||
| #define TRUE            1 | ||||
| #endif | ||||
| #ifndef FALSE | ||||
| #define FALSE           0 | ||||
| #endif | ||||
| /* ----------------------- Type definitions ---------------------------------*/ | ||||
| typedef int     SOCKET; | ||||
| 
 | ||||
| #define SOCKET_ERROR (-1) | ||||
| #define INVALID_SOCKET (~0) | ||||
| 
 | ||||
| 
 | ||||
| typedef char    BOOL; | ||||
| typedef unsigned char UCHAR; | ||||
| typedef unsigned char BYTE; | ||||
| typedef char    CHAR; | ||||
| typedef unsigned short USHORT; | ||||
| typedef short   SHORT; | ||||
| 
 | ||||
| typedef unsigned long ULONG; | ||||
| typedef long    LONG; | ||||
| typedef enum | ||||
| { | ||||
|     MB_LOG_DEBUG, | ||||
|     MB_LOG_INFO, | ||||
|     MB_LOG_WARN, | ||||
|     MB_LOG_ERROR | ||||
| } eMBPortLogLevel; | ||||
| 
 | ||||
| /* ----------------------- Function prototypes ------------------------------*/ | ||||
| 
 | ||||
| void            TcpvMBPortLog( eMBPortLogLevel eLevel, const CHAR * szModule, const CHAR * szFmt, | ||||
|                                ... ); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| PR_END_EXTERN_C | ||||
| #endif | ||||
| #endif | ||||
|  | @ -0,0 +1,72 @@ | |||
| /*
 | ||||
|  * FreeModbus Libary: Win32 Port | ||||
|  * Copyright (C) 2006 Christian Walter <wolti@sil.at> | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  * | ||||
|  * File: $Id$ | ||||
|  */ | ||||
| 
 | ||||
|  /**********************************************************
 | ||||
|  *	Linux TCP support. | ||||
|  *	Based on Walter's project.  | ||||
|  *	Modified by Steven Guo <gotop167@163.com> | ||||
|  ***********************************************************/ | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbport.h" | ||||
| 
 | ||||
| /* ----------------------- Variables ----------------------------------------*/ | ||||
| static eMBEventType eQueuedEvent; | ||||
| static BOOL     xEventInQueue; | ||||
| 
 | ||||
| /* ----------------------- Function prototypes ------------------------------*/ | ||||
| BOOL            xMBPortTCPPool( void ); | ||||
| 
 | ||||
| /* ----------------------- Start implementation -----------------------------*/ | ||||
| BOOL | ||||
| xMBPortEventInit( void ) | ||||
| { | ||||
|     xEventInQueue = FALSE; | ||||
|     return TRUE; | ||||
| } | ||||
| 
 | ||||
| BOOL | ||||
| xMBPortEventPost( eMBEventType eEvent ) | ||||
| { | ||||
|     xEventInQueue = TRUE; | ||||
|     eQueuedEvent = eEvent; | ||||
|     return TRUE; | ||||
| } | ||||
| 
 | ||||
| BOOL | ||||
| xMBPortEventGet( eMBEventType * eEvent ) | ||||
| { | ||||
|     BOOL            xEventHappened = FALSE; | ||||
| 
 | ||||
|     if( xEventInQueue ) | ||||
|     { | ||||
|         *eEvent = eQueuedEvent; | ||||
|         xEventInQueue = FALSE; | ||||
|         xEventHappened = TRUE; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         /* We can't do anything with errors from the pooling module. */ | ||||
|         ( void )xMBPortTCPPool(  ); | ||||
|     } | ||||
|     return xEventHappened; | ||||
| } | ||||
|  | @ -0,0 +1,69 @@ | |||
| /*
 | ||||
|  * FreeModbus Libary: Win32 Port | ||||
|  * Copyright (C) 2006 Christian Walter <wolti@sil.at> | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  * | ||||
|  * File: $Id$ | ||||
|  */ | ||||
| 
 | ||||
|  /**********************************************************
 | ||||
|  *	Linux TCP support. | ||||
|  *	Based on Walter's project.  | ||||
|  *	Modified by Steven Guo <gotop167@163.com> | ||||
|  ***********************************************************/ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdarg.h> | ||||
| 
 | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbport.h" | ||||
| #include "mbconfig.h" | ||||
| 
 | ||||
| 
 | ||||
| BOOL | ||||
| prvMBTCPPortAddressToString( SOCKET xSocket, CHAR * szAddr, USHORT usBufSize ) | ||||
| { | ||||
|     return TRUE; | ||||
| } | ||||
| 
 | ||||
| CHAR           * | ||||
| prvMBTCPPortFrameToString( UCHAR * pucFrame, USHORT usFrameLen ) | ||||
| { | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| CHAR           * | ||||
| WsaError2String( int iError ) | ||||
| { | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| vMBPortLog( eMBPortLogLevel eLevel, const CHAR * szModule, const CHAR * szFmt, ... ) | ||||
| { | ||||
|     va_list         args; | ||||
|     static const CHAR *arszLevel2Str[] = { "DEBUG", "INFO", "WARN", "ERROR" }; | ||||
| 
 | ||||
|     fprintf( stderr, "%s: %s: ", arszLevel2Str[eLevel], szModule ); | ||||
| 
 | ||||
|     va_start( args, szFmt ); | ||||
|     fprintf( stderr, szFmt, args ); | ||||
|     va_end( args ); | ||||
| } | ||||
|  | @ -0,0 +1,367 @@ | |||
| /*
 | ||||
|  * FreeModbus Libary: Win32 Port | ||||
|  * Copyright (C) 2006 Christian Walter <wolti@sil.at> | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  * | ||||
|  * File: $Id$ | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Design Notes: | ||||
|  * | ||||
|  * The xMBPortTCPInit function allocates a socket and binds the socket to | ||||
|  * all available interfaces ( bind with INADDR_ANY ). In addition it | ||||
|  * creates an array of event objects which is used to check the state of | ||||
|  * the clients. On event object is used to handle new connections or | ||||
|  * closed ones. The other objects are used on a per client basis for | ||||
|  * processing. | ||||
|  */ | ||||
| 
 | ||||
|  /**********************************************************
 | ||||
|  *	Linux TCP support. | ||||
|  *	Based on Walter's project.  | ||||
|  *	Modified by Steven Guo <gotop167@163.com> | ||||
|  ***********************************************************/ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <sys/types.h> | ||||
| #include <sockets.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #include <errno.h> | ||||
| #include <time.h> | ||||
| #include "port.h" | ||||
| 
 | ||||
| /* ----------------------- Modbus includes ----------------------------------*/ | ||||
| #include "mb.h" | ||||
| #include "mbport.h" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* ----------------------- MBAP Header --------------------------------------*/ | ||||
| #define MB_TCP_UID          6 | ||||
| #define MB_TCP_LEN          4 | ||||
| #define MB_TCP_FUNC         7 | ||||
| 
 | ||||
| /* ----------------------- Defines  -----------------------------------------*/ | ||||
| #define MB_TCP_DEFAULT_PORT 502 /* TCP listening port. */ | ||||
| #define MB_TCP_POOL_TIMEOUT 50  /* pool timeout for event waiting. */ | ||||
| #define MB_TCP_READ_TIMEOUT 1000        /* Maximum timeout to wait for packets. */ | ||||
| #define MB_TCP_READ_CYCLE   100 /* Time between checking for new data. */ | ||||
| 
 | ||||
| #define MB_TCP_DEBUG        1   /* Set to 1 for additional debug output. */ | ||||
| 
 | ||||
| #define MB_TCP_BUF_SIZE     ( 256 + 7 ) /* Must hold a complete Modbus TCP frame. */ | ||||
| 
 | ||||
| #define EV_CONNECTION       0 | ||||
| #define EV_CLIENT           1 | ||||
| #define EV_NEVENTS          EV_CLIENT + 1 | ||||
| 
 | ||||
| /* ----------------------- Static variables ---------------------------------*/ | ||||
| SOCKET          xListenSocket; | ||||
| SOCKET          xClientSocket = INVALID_SOCKET; | ||||
| static fd_set   allset; | ||||
| 
 | ||||
| static UCHAR    aucTCPBuf[MB_TCP_BUF_SIZE]; | ||||
| static USHORT   usTCPBufPos; | ||||
| static USHORT   usTCPFrameBytesLeft; | ||||
| 
 | ||||
| /* ----------------------- External functions -------------------------------*/ | ||||
| CHAR           *WsaError2String( int dwError ); | ||||
| 
 | ||||
| /* ----------------------- Static functions ---------------------------------*/ | ||||
| BOOL            prvMBTCPPortAddressToString( SOCKET xSocket, CHAR * szAddr, USHORT usBufSize ); | ||||
| CHAR           *prvMBTCPPortFrameToString( UCHAR * pucFrame, USHORT usFrameLen ); | ||||
| static BOOL     prvbMBPortAcceptClient( void ); | ||||
| static void     prvvMBPortReleaseClient( void ); | ||||
| 
 | ||||
| 
 | ||||
| /* ----------------------- Begin implementation -----------------------------*/ | ||||
| 
 | ||||
| BOOL | ||||
| xMBTCPPortInit( USHORT usTCPPort ) | ||||
| { | ||||
|     USHORT          usPort; | ||||
|     struct sockaddr_in serveraddr; | ||||
| 
 | ||||
|     if( usTCPPort == 0 ) | ||||
|     { | ||||
|         usPort = MB_TCP_DEFAULT_PORT; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         usPort = ( USHORT ) usTCPPort; | ||||
|     } | ||||
|     memset( &serveraddr, 0, sizeof( serveraddr ) ); | ||||
|     serveraddr.sin_family = AF_INET; | ||||
|     serveraddr.sin_addr.s_addr = htonl( INADDR_ANY ); | ||||
|     serveraddr.sin_port = htons( usPort ); | ||||
|     if( ( xListenSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) == -1 ) | ||||
|     { | ||||
|         fprintf( stderr, "Create socket failed.\r\n" ); | ||||
|         return FALSE; | ||||
|     } | ||||
|     else if( bind( xListenSocket, ( struct sockaddr * )&serveraddr, sizeof( serveraddr ) ) == -1 ) | ||||
|     { | ||||
|         perror("bind"); | ||||
|         fprintf( stderr, "Bind socket failed.\r\n" ); | ||||
|         return FALSE; | ||||
|     } | ||||
|     else if( listen( xListenSocket, 5 ) == -1 ) | ||||
|     { | ||||
|         fprintf( stderr, "Listen socket failed.\r\n" ); | ||||
|         return FALSE; | ||||
|     } | ||||
|     FD_ZERO( &allset ); | ||||
|     FD_SET( xListenSocket, &allset ); | ||||
|     return TRUE; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| vMBTCPPortClose(  ) | ||||
| { | ||||
|     // Close all client sockets. 
 | ||||
|     if( xClientSocket != SOCKET_ERROR ) | ||||
|     { | ||||
|         prvvMBPortReleaseClient(  ); | ||||
|     } | ||||
|     // Close the listener socket.
 | ||||
|     if( xListenSocket != SOCKET_ERROR ) | ||||
|     { | ||||
|         close( xListenSocket ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void | ||||
| vMBTCPPortDisable( void ) | ||||
| { | ||||
|     /* Close all client sockets. */ | ||||
|     if( xClientSocket != SOCKET_ERROR ) | ||||
|     { | ||||
|         prvvMBPortReleaseClient(  ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /*! \ingroup port_win32tcp
 | ||||
|  * | ||||
|  * \brief Pool the listening socket and currently connected Modbus TCP clients | ||||
|  *   for new events. | ||||
|  * \internal | ||||
|  * | ||||
|  * This function checks if new clients want to connect or if already connected  | ||||
|  * clients are sending requests. If a new client is connected and there are  | ||||
|  * still client slots left (The current implementation supports only one) | ||||
|  * then the connection is accepted and an event object for the new client | ||||
|  * socket is activated (See prvbMBPortAcceptClient() ). | ||||
|  * Events for already existing clients in \c FD_READ and \c FD_CLOSE. In case of | ||||
|  * an \c FD_CLOSE the client connection is released (See prvvMBPortReleaseClient() ). | ||||
|  * In case of an \c FD_READ command the existing data is read from the client | ||||
|  * and if a complete frame has been received the Modbus Stack is notified. | ||||
|  * | ||||
|  * \return FALSE in case of an internal I/O error. For example if the internal | ||||
|  *   event objects are in an invalid state. Note that this does not include any  | ||||
|  *   client errors. In all other cases returns TRUE. | ||||
|  */ | ||||
| BOOL | ||||
| xMBPortTCPPool( void ) | ||||
| { | ||||
|     int             n; | ||||
|     fd_set          fread; | ||||
|     struct timeval  tval; | ||||
| 
 | ||||
|     tval.tv_sec = 0; | ||||
|     tval.tv_usec = 5000; | ||||
|     int             ret; | ||||
|     USHORT          usLength; | ||||
| 
 | ||||
|     if( xClientSocket == INVALID_SOCKET ) | ||||
|     { | ||||
|         /* Accept to client */ | ||||
|         if( ( n = select( xListenSocket + 1, &allset, NULL, NULL, NULL ) ) < 0 ) | ||||
|         { | ||||
|             if( errno == EINTR ) | ||||
|             { | ||||
|                 ; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ; | ||||
|             } | ||||
|         } | ||||
|         if( FD_ISSET( xListenSocket, &allset ) ) | ||||
|         { | ||||
|             ( void )prvbMBPortAcceptClient(  ); | ||||
|         } | ||||
|     } | ||||
|     while( TRUE ) | ||||
|     { | ||||
|         FD_ZERO( &fread ); | ||||
|         FD_SET( xClientSocket, &fread ); | ||||
|         if( ( ( ret = select( xClientSocket + 1, &fread, NULL, NULL, &tval ) ) == SOCKET_ERROR ) | ||||
|             || !ret ) | ||||
|         { | ||||
|             continue; | ||||
|         } | ||||
|         if( ret > 0 ) | ||||
|         { | ||||
|             if( FD_ISSET( xClientSocket, &fread ) ) | ||||
|             { | ||||
|                 if( ( ( ret = | ||||
|                         recv( xClientSocket, &aucTCPBuf[usTCPBufPos], usTCPFrameBytesLeft, | ||||
|                               0 ) ) == SOCKET_ERROR ) || ( !ret ) ) | ||||
|                 { | ||||
|                     close( xClientSocket ); | ||||
|                     xClientSocket = INVALID_SOCKET; | ||||
|                     return TRUE; | ||||
|                 } | ||||
|                 usTCPBufPos += ret; | ||||
|                 usTCPFrameBytesLeft -= ret; | ||||
|                 if( usTCPBufPos >= MB_TCP_FUNC ) | ||||
|                 { | ||||
|                     /* Length is a byte count of Modbus PDU (function code + data) and the
 | ||||
|                      * unit identifier. */ | ||||
|                     usLength = aucTCPBuf[MB_TCP_LEN] << 8U; | ||||
|                     usLength |= aucTCPBuf[MB_TCP_LEN + 1]; | ||||
| 
 | ||||
|                     /* Is the frame already complete. */ | ||||
|                     if( usTCPBufPos < ( MB_TCP_UID + usLength ) ) | ||||
|                     { | ||||
|                         usTCPFrameBytesLeft = usLength + MB_TCP_UID - usTCPBufPos; | ||||
|                     } | ||||
|                     /* The frame is complete. */ | ||||
|                     else if( usTCPBufPos == ( MB_TCP_UID + usLength ) ) | ||||
|                     { | ||||
|                         ( void )xMBPortEventPost( EV_FRAME_RECEIVED ); | ||||
|                         return TRUE; | ||||
|                     } | ||||
|                     /* This can not happend because we always calculate the number of bytes
 | ||||
|                      * to receive. */ | ||||
|                     else | ||||
|                     { | ||||
|                         assert( usTCPBufPos <= ( MB_TCP_UID + usLength ) ); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return TRUE; | ||||
| } | ||||
| 
 | ||||
| /*!
 | ||||
|  * \ingroup port_win32tcp | ||||
|  * \brief Receives parts of a Modbus TCP frame and if complete notifies | ||||
|  *    the protocol stack. | ||||
|  * \internal  | ||||
|  * | ||||
|  * This function reads a complete Modbus TCP frame from the protocol stack. | ||||
|  * It starts by reading the header with an initial request size for | ||||
|  * usTCPFrameBytesLeft = MB_TCP_FUNC. If the header is complete the  | ||||
|  * number of bytes left can be calculated from it (See Length in MBAP header). | ||||
|  * Further read calls are issued until the frame is complete. | ||||
|  * | ||||
|  * \return \c TRUE if part of a Modbus TCP frame could be processed. In case | ||||
|  *   of a communication error the function returns \c FALSE. | ||||
|  */ | ||||
| 
 | ||||
| BOOL | ||||
| xMBTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength ) | ||||
| { | ||||
|     *ppucMBTCPFrame = &aucTCPBuf[0]; | ||||
|     *usTCPLength = usTCPBufPos; | ||||
| 
 | ||||
|     /* Reset the buffer. */ | ||||
|     usTCPBufPos = 0; | ||||
|     usTCPFrameBytesLeft = MB_TCP_FUNC; | ||||
|     return TRUE; | ||||
| } | ||||
| 
 | ||||
| BOOL | ||||
| xMBTCPPortSendResponse( const UCHAR * pucMBTCPFrame, USHORT usTCPLength ) | ||||
| { | ||||
|     BOOL            bFrameSent = FALSE; | ||||
|     BOOL            bAbort = FALSE; | ||||
|     int             res; | ||||
|     int             iBytesSent = 0; | ||||
|     int             iTimeOut = MB_TCP_READ_TIMEOUT; | ||||
| 
 | ||||
|     do | ||||
|     { | ||||
|         res = send( xClientSocket, &pucMBTCPFrame[iBytesSent], usTCPLength - iBytesSent, 0 ); | ||||
|         switch ( res ) | ||||
|         { | ||||
|         case -1: | ||||
|             if( iTimeOut > 0 ) | ||||
|             { | ||||
|                 iTimeOut -= MB_TCP_READ_CYCLE; | ||||
|                 // usleep( MB_TCP_READ_CYCLE );
 | ||||
|                 // PrivTaskDelay( MB_TCP_READ_CYCLE );
 | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 bAbort = TRUE; | ||||
|             } | ||||
|             break; | ||||
|         case 0: | ||||
|             prvvMBPortReleaseClient(  ); | ||||
|             bAbort = TRUE; | ||||
|             break; | ||||
|         default: | ||||
|             iBytesSent += res; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     while( ( iBytesSent != usTCPLength ) && !bAbort ); | ||||
| 
 | ||||
|     bFrameSent = iBytesSent == usTCPLength ? TRUE : FALSE; | ||||
| 
 | ||||
|     return bFrameSent; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| prvvMBPortReleaseClient(  ) | ||||
| { | ||||
|     ( void )recv( xClientSocket, &aucTCPBuf[0], MB_TCP_BUF_SIZE, 0 ); | ||||
| 
 | ||||
|     ( void )close( xClientSocket ); | ||||
|     xClientSocket = INVALID_SOCKET; | ||||
| } | ||||
| 
 | ||||
| BOOL | ||||
| prvbMBPortAcceptClient(  ) | ||||
| { | ||||
|     SOCKET          xNewSocket; | ||||
|     BOOL            bOkay; | ||||
| 
 | ||||
|     /* Check if we can handle a new connection. */ | ||||
| 
 | ||||
|     if( xClientSocket != INVALID_SOCKET ) | ||||
|     { | ||||
|         fprintf( stderr, "can't accept new client. all connections in use.\n" ); | ||||
|         bOkay = FALSE; | ||||
|     } | ||||
|     else if( ( xNewSocket = accept( xListenSocket, NULL, NULL ) ) == INVALID_SOCKET ) | ||||
|     { | ||||
|         bOkay = FALSE; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         xClientSocket = xNewSocket; | ||||
|         usTCPBufPos = 0; | ||||
|         usTCPFrameBytesLeft = MB_TCP_FUNC; | ||||
|         bOkay = TRUE; | ||||
|     } | ||||
|     return bOkay; | ||||
| } | ||||
|  | @ -13,3 +13,5 @@ config CONTROL_PROTOCOL_MODBUS_UART | |||
| if CONTROL_PROTOCOL_MODBUS_UART | ||||
|         source "$APP_DIR/Framework/control/ipc_protocol/modbus_uart/Kconfig" | ||||
| endif | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,5 +6,9 @@ ifeq ($(CONFIG_CONTROL_PROTOCOL_MODBUS_UART), y) | |||
| 	SRC_DIR := modbus_uart | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(CONFIG_CONTROL_PROTOCOL_ETHERCAT), y) | ||||
| 	SRC_DIR := ethercat | ||||
| endif | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
| 
 | ||||
|  |  | |||
|  | @ -447,7 +447,7 @@ int ModbusTcpProtocolFormatCmd(struct ControlRecipe *p_recipe, ProtocolFormatInf | |||
|         p_read_item_data); | ||||
| 
 | ||||
|     ControlPrintfList("CMD", modbustcp_read_item->data_info.base_data_info.p_command, modbustcp_read_item->data_info.base_data_info.command_length); | ||||
|     protocol_format_info->last_item_size = GetValueTypeMemorySize(modbustcp_read_item->value_type); | ||||
|     protocol_format_info->last_item_size = GetValueTypeMemorySize(modbustcp_read_item->value_type,1); | ||||
| 
 | ||||
|     last_item_size += protocol_format_info->last_item_size; | ||||
| 
 | ||||
|  |  | |||
|  | @ -398,7 +398,7 @@ int ModbusUartProtocolFormatCmd(struct ControlRecipe *p_recipe, ProtocolFormatIn | |||
|         p_read_item_data); | ||||
| 
 | ||||
|     ControlPrintfList("CMD", modbusuart_read_item->data_info.base_data_info.p_command, modbusuart_read_item->data_info.base_data_info.command_length); | ||||
|     protocol_format_info->last_item_size = GetValueTypeMemorySize(modbusuart_read_item->value_type); | ||||
|     protocol_format_info->last_item_size = GetValueTypeMemorySize(modbusuart_read_item->value_type,1); | ||||
| 
 | ||||
|     last_item_size += protocol_format_info->last_item_size; | ||||
| 
 | ||||
|  |  | |||
|  | @ -27,3 +27,12 @@ config CONTROL_PROTOCOL_S7 | |||
| if CONTROL_PROTOCOL_S7 | ||||
|         source "$APP_DIR/Framework/control/plc_protocol/s7/Kconfig" | ||||
| endif | ||||
| 
 | ||||
| config CONTROL_PROTOCOL_CIP | ||||
|         bool "Using cip control protocol" | ||||
|         default n | ||||
|         select CONTROL_USING_SOCKET | ||||
| if CONTROL_PROTOCOL_CIP | ||||
|         source "$APP_DIR/Framework/control/plc_protocol/ethernet_ip_cip/Kconfig" | ||||
| endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,4 +14,8 @@ ifeq ($(CONFIG_CONTROL_PROTOCOL_S7), y) | |||
| 	SRC_DIR := s7 | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(CONFIG_CONTROL_PROTOCOL_CIP), y) | ||||
| 	SRC_DIR := ethernet_ip_cip | ||||
| endif | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| SRC_FILES := $(wildcard ./*.c) | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
|  | @ -0,0 +1,394 @@ | |||
| #include "ab_cip_helper.h" | ||||
| #include "ab_cip.h" | ||||
| #include "ab_cip_private.h" | ||||
| 
 | ||||
| // #include "socket.h"
 | ||||
| #include "cip_socket.h" | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <math.h> | ||||
| #ifdef _WIN32 | ||||
| #include <winsock2.h> | ||||
| #include <ws2tcpip.h> | ||||
| #include <windows.h> | ||||
| #pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ | ||||
| #pragma warning(disable : 4996) | ||||
| #else | ||||
| #include <sys/types.h> | ||||
| // #include <sys/socket.h>
 | ||||
| // #include <arpa/inet.h>
 | ||||
| #include <sockets.h> | ||||
| #include "lwip/inet.h" | ||||
| #endif | ||||
| 
 | ||||
| int port = 102; | ||||
| char ip_address[64] = {0}; | ||||
| bool ab_cip_connect(char *ip_addr, int port, int slot, int *fd) | ||||
| { | ||||
| 	bool ret = false; | ||||
| 	int temp_fd = -1; | ||||
| 	g_plc_slot = slot; | ||||
| 
 | ||||
| 	temp_fd = socket_open_tcp_client_socket(ip_addr, port); | ||||
| 	*fd = temp_fd; | ||||
| 
 | ||||
| 	printf("%d %s  %d\n",temp_fd,__func__,__LINE__); | ||||
| 	 | ||||
| 	if (temp_fd >= 0) | ||||
| 		ret = initialization_on_connect(temp_fd); | ||||
| 
 | ||||
| 	printf("%d %s  %d\n",ret,__func__,__LINE__); | ||||
| 
 | ||||
| 	if (!ret && temp_fd > 0) | ||||
| 	{ | ||||
| 		socket_close_tcp_socket(temp_fd); | ||||
| 		*fd = -1; | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| bool ab_cip_disconnect(int fd) | ||||
| { | ||||
| 	socket_close_tcp_socket(fd); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////
 | ||||
| cip_error_code_e ab_cip_read_bool(int fd, const char *address, bool *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	byte_array_info read_data; | ||||
| 	memset(&read_data, 0, sizeof(read_data)); | ||||
| 	ret = read_value(fd, address, 1, &read_data); | ||||
| 	if (ret == CIP_ERROR_CODE_OK && read_data.length > 0) | ||||
| 	{ | ||||
| 		*val = (bool)read_data.data[0]; | ||||
| 		RELEASE_DATA(read_data.data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_read_short(int fd, const char *address, short *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	byte_array_info read_data; | ||||
| 	memset(&read_data, 0, sizeof(read_data)); | ||||
| 	ret = read_value(fd, address, 1, &read_data); | ||||
| 	if (ret == CIP_ERROR_CODE_OK && read_data.length >= 2) | ||||
| 	{ | ||||
| 		*val = bytes2short(read_data.data);		 | ||||
| 	} | ||||
| 	RELEASE_DATA(read_data.data); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_read_ushort(int fd, const char *address, ushort *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	byte_array_info read_data; | ||||
| 	memset(&read_data, 0, sizeof(read_data)); | ||||
| 	ret = read_value(fd, address, 1, &read_data); | ||||
| 	if (ret == CIP_ERROR_CODE_OK && read_data.length >= 2) | ||||
| 	{ | ||||
| 		*val = bytes2ushort(read_data.data); | ||||
| 		RELEASE_DATA(read_data.data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_read_int32(int fd, const char *address, int32 *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	byte_array_info read_data; | ||||
| 	memset(&read_data, 0, sizeof(read_data)); | ||||
| 	ret = read_value(fd, address, 4, &read_data); | ||||
| 	if (ret == CIP_ERROR_CODE_OK && read_data.length >= 4) | ||||
| 	{ | ||||
| 		*val = bytes2int32(read_data.data); | ||||
| 		RELEASE_DATA(read_data.data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_read_uint32(int fd, const char *address, uint32 *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	byte_array_info read_data; | ||||
| 	memset(&read_data, 0, sizeof(read_data)); | ||||
| 	ret = read_value(fd, address, 4, &read_data); | ||||
| 	if (ret == CIP_ERROR_CODE_OK && read_data.length >= 2) | ||||
| 	{ | ||||
| 		*val = bytes2uint32(read_data.data); | ||||
| 		RELEASE_DATA(read_data.data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_read_int64(int fd, const char *address, int64 *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	byte_array_info read_data; | ||||
| 	memset(&read_data, 0, sizeof(read_data)); | ||||
| 	ret = read_value(fd, address, 8, &read_data); | ||||
| 	if (ret == CIP_ERROR_CODE_OK && read_data.length >= 8) | ||||
| 	{ | ||||
| 		*val = bytes2bigInt(read_data.data); | ||||
| 		RELEASE_DATA(read_data.data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_read_uint64(int fd, const char *address, uint64 *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	byte_array_info read_data; | ||||
| 	memset(&read_data, 0, sizeof(read_data)); | ||||
| 	ret = read_value(fd, address, 8, &read_data); | ||||
| 	if (ret == CIP_ERROR_CODE_OK && read_data.length >= 8) | ||||
| 	{ | ||||
| 		*val = bytes2ubigInt(read_data.data); | ||||
| 		RELEASE_DATA(read_data.data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_read_float(int fd, const char *address, float *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	byte_array_info read_data; | ||||
| 	memset(&read_data, 0, sizeof(read_data)); | ||||
| 	ret = read_value(fd, address, 1, &read_data); | ||||
| 	if (ret == CIP_ERROR_CODE_OK && read_data.length >= 4) | ||||
| 	{ | ||||
| 		*val = bytes2float(read_data.data);	 | ||||
| 	} | ||||
| 	RELEASE_DATA(read_data.data); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_read_double(int fd, const char *address, double *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	byte_array_info read_data; | ||||
| 	memset(&read_data, 0, sizeof(read_data)); | ||||
| 	ret = read_value(fd, address, 8, &read_data); | ||||
| 	if (ret == CIP_ERROR_CODE_OK && read_data.length >= 8) | ||||
| 	{ | ||||
| 		*val = bytes2double(read_data.data); | ||||
| 		RELEASE_DATA(read_data.data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_read_string(int fd, const char *address, int *length, char **val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (length != NULL && *length > 0) | ||||
| 	{ | ||||
| 		byte_array_info read_data; | ||||
| 		memset(&read_data, 0, sizeof(read_data)); | ||||
| 		ret = read_value(fd, address, *length, &read_data); | ||||
| 		if (ret == CIP_ERROR_CODE_OK && read_data.length > 6) | ||||
| 		{ | ||||
| 			*length = 0; | ||||
| 			uint32 str_length = bytes2uint32(read_data.data + 2); | ||||
| 			if (str_length > 0) | ||||
| 			{ | ||||
| 				*length = str_length; | ||||
| 				char *ret_str = (char *)malloc(str_length + 1); | ||||
| 				memset(ret_str, 0, str_length + 1); | ||||
| 				memcpy(ret_str, read_data.data + 6, str_length); | ||||
| 				*val = ret_str; | ||||
| 			} | ||||
| 			RELEASE_DATA(read_data.data); | ||||
| 		} | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_bool(int fd, const char *address, bool val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL) | ||||
| 	{ | ||||
| 		int write_len = 2; | ||||
| 		byte_array_info write_data; | ||||
| 		memset(&write_data, 0, sizeof(write_data)); | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		if (val) | ||||
| 		{ | ||||
| 			write_data.data[0] = 0xFF; | ||||
| 			write_data.data[1] = 0xFF; | ||||
| 		} | ||||
| 		ret = write_value(fd, address, 1, 0xC1, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_short(int fd, const char *address, short val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL) | ||||
| 	{ | ||||
| 		int write_len = 2; | ||||
| 		byte_array_info write_data; | ||||
| 		memset(&write_data, 0, sizeof(write_data)); | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		short2bytes(val, write_data.data); | ||||
| 		ret = write_value(fd, address, 1, 0xC3, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_ushort(int fd, const char *address, ushort val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL) | ||||
| 	{ | ||||
| 		int write_len = 2; | ||||
| 		byte_array_info write_data; | ||||
| 		memset(&write_data, 0, sizeof(write_data)); | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		ushort2bytes(val, write_data.data); | ||||
| 		ret = write_value(fd, address, 1, 0xC3, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_int32(int fd, const char *address, int32 val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL) | ||||
| 	{ | ||||
| 		int write_len = 4; | ||||
| 		byte_array_info write_data; | ||||
| 		memset(&write_data, 0, sizeof(write_data)); | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		int2bytes(val, write_data.data); | ||||
| 		ret = write_value(fd, address, 1, 0xC4, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_uint32(int fd, const char *address, uint32 val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL) | ||||
| 	{ | ||||
| 		int write_len = 4; | ||||
| 		byte_array_info write_data; | ||||
| 		memset(&write_data, 0, sizeof(write_data)); | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		uint2bytes(val, write_data.data); | ||||
| 		ret = write_value(fd, address, 1, 0xC4, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_int64(int fd, const char *address, int64 val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL) | ||||
| 	{ | ||||
| 		int write_len = 8; | ||||
| 		byte_array_info write_data; | ||||
| 		memset(&write_data, 0, sizeof(write_data)); | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		bigInt2bytes(val, write_data.data); | ||||
| 		ret = write_value(fd, address, 1, 0xC5, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_uint64(int fd, const char *address, uint64 val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL) | ||||
| 	{ | ||||
| 		int write_len = 8; | ||||
| 		byte_array_info write_data; | ||||
| 		memset(&write_data, 0, sizeof(write_data)); | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		ubigInt2bytes(val, write_data.data); | ||||
| 		ret = write_value(fd, address, 1, 0xC5, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_float(int fd, const char *address, float val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL) | ||||
| 	{ | ||||
| 		int write_len = 4; | ||||
| 		byte_array_info write_data; | ||||
| 		memset(&write_data, 0, sizeof(write_data)); | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		float2bytes(val, write_data.data); | ||||
| 		ret = write_value(fd, address, 1, 0xCA, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_double(int fd, const char *address, double val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL) | ||||
| 	{ | ||||
| 		int write_len = 8; | ||||
| 		byte_array_info write_data; | ||||
| 		memset(&write_data, 0, sizeof(write_data)); | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		double2bytes(val, write_data.data); | ||||
| 		ret = write_value(fd, address, 1, 0xCB, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e ab_cip_write_string(int fd, const char *address, int length, const char *val) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
| 	if (fd > 0 && address != NULL && val != NULL) | ||||
| 	{ | ||||
| 		byte write_len = length + 1; | ||||
| 		byte_array_info write_data = {0}; | ||||
| 		write_data.data = (byte *)malloc(write_len); | ||||
| 		memset(write_data.data, 0, write_len); | ||||
| 		memcpy(write_data.data, &write_len, 1); | ||||
| 		memcpy(write_data.data + 1, val, length); | ||||
| 		write_data.length = write_len; | ||||
| 
 | ||||
| 		char temp_addr[100] = {0}; | ||||
| 		sprintf(temp_addr, "%s", address); | ||||
| 		ret = write_value(fd, temp_addr, 1, 0xD0, write_data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| byte get_plc_slot() | ||||
| { | ||||
| 	return g_plc_slot; | ||||
| } | ||||
| 
 | ||||
| void set_plc_slot(byte slot) | ||||
| { | ||||
| 	g_plc_slot = slot; | ||||
| } | ||||
|  | @ -0,0 +1,40 @@ | |||
| #ifndef __H_AB_CIP_H__ | ||||
| #define __H_AB_CIP_H__ | ||||
| 
 | ||||
| #include "typedef.h" | ||||
| 
 | ||||
| /////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| byte get_plc_slot(); | ||||
| void set_plc_slot(byte slot); | ||||
| 
 | ||||
| /////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| bool ab_cip_connect(char *ip_addr, int port, int slot, int *fd); | ||||
| bool ab_cip_disconnect(int fd); | ||||
| 
 | ||||
| // read
 | ||||
| cip_error_code_e ab_cip_read_bool(int fd, const char *address, bool *val); | ||||
| cip_error_code_e ab_cip_read_short(int fd, const char *address, short *val); | ||||
| cip_error_code_e ab_cip_read_ushort(int fd, const char *address, ushort *val); | ||||
| cip_error_code_e ab_cip_read_int32(int fd, const char *address, int32 *val); | ||||
| cip_error_code_e ab_cip_read_uint32(int fd, const char *address, uint32 *val); | ||||
| cip_error_code_e ab_cip_read_int64(int fd, const char *address, int64 *val); | ||||
| cip_error_code_e ab_cip_read_uint64(int fd, const char *address, uint64 *val); | ||||
| cip_error_code_e ab_cip_read_float(int fd, const char *address, float *val); | ||||
| cip_error_code_e ab_cip_read_double(int fd, const char *address, double *val); | ||||
| cip_error_code_e ab_cip_read_string(int fd, const char *address, int *length, char **val); // need free val
 | ||||
| 
 | ||||
| // write
 | ||||
| cip_error_code_e ab_cip_write_bool(int fd, const char *address, bool val); | ||||
| cip_error_code_e ab_cip_write_short(int fd, const char *address, short val); | ||||
| cip_error_code_e ab_cip_write_ushort(int fd, const char *address, ushort val); | ||||
| cip_error_code_e ab_cip_write_int32(int fd, const char *address, int32 val); | ||||
| cip_error_code_e ab_cip_write_uint32(int fd, const char *address, uint32 val); | ||||
| cip_error_code_e ab_cip_write_int64(int fd, const char *address, int64 val); | ||||
| cip_error_code_e ab_cip_write_uint64(int fd, const char *address, uint64 val); | ||||
| cip_error_code_e ab_cip_write_float(int fd, const char *address, float val); | ||||
| cip_error_code_e ab_cip_write_double(int fd, const char *address, double val); | ||||
| cip_error_code_e ab_cip_write_string(int fd, const char *address, int length, const char *val); | ||||
| 
 | ||||
| #endif //__H_AB_CIP_H__
 | ||||
|  | @ -0,0 +1,333 @@ | |||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include "ab_cip_helper.h" | ||||
| #include "cip_socket.h" | ||||
| 
 | ||||
| extern uint32 g_session; | ||||
| extern byte g_plc_slot; | ||||
| extern byte g_registered_command[28]; | ||||
| 
 | ||||
| // 从地址构造核心报文
 | ||||
| byte_array_info build_read_core_command(const char *address, int length) | ||||
| { | ||||
| 	size_t addr_length = strlen(address); | ||||
| 	size_t addr_adjust_length = addr_length; | ||||
| 	if (addr_adjust_length % 2 == 1) | ||||
| 		addr_adjust_length += 1; | ||||
| 
 | ||||
| 	char *temp_address = (char *)malloc(addr_adjust_length); | ||||
| 	memset(temp_address, 0, addr_adjust_length); | ||||
| 	memcpy(temp_address, address, strlen(address)); | ||||
| 
 | ||||
| 	const ushort command_len = 9 + 26 + (ushort)addr_adjust_length + 1 + 24; | ||||
| 	byte *command = (byte *)malloc(command_len); | ||||
| 	memset(command, 0, command_len); | ||||
| 
 | ||||
| 	command[0] = 0x6F; // 命令
 | ||||
| 	command[2] = (byte)((command_len - 24) % 256); | ||||
| 	command[3] = (byte)((command_len - 24) / 256); // 长度
 | ||||
| 
 | ||||
| 	char temp_session[4] = {0}; | ||||
| 	uint2bytes(g_session, temp_session); | ||||
| 	command[4] = temp_session[0]; | ||||
| 	command[5] = temp_session[1]; | ||||
| 	command[6] = temp_session[2]; | ||||
| 	command[7] = temp_session[3]; // 会话句柄
 | ||||
| 
 | ||||
| 	command[0 + 24] = 0x00; | ||||
| 	command[1 + 24] = 0x00; | ||||
| 	command[2 + 24] = 0x00; | ||||
| 	command[3 + 24] = 0x00; // 接口句柄,默认为0x00000000(CIP)
 | ||||
| 	command[4 + 24] = 0x01; | ||||
| 	command[5 + 24] = 0x0A; // 超时(0x000A)
 | ||||
| 	command[6 + 24] = 0x02; | ||||
| 	command[7 + 24] = 0x00; // 项数(0x0002)
 | ||||
| 	command[8 + 24] = 0x00; | ||||
| 	command[9 + 24] = 0x00; // 空地址项(0x0000)
 | ||||
| 	command[10 + 24] = 0x00; | ||||
| 	command[11 + 24] = 0x00; // 长度(0x0000)
 | ||||
| 	command[12 + 24] = 0xB2; | ||||
| 	command[13 + 24] = 0x00;								  // 未连接数据项(0x00b2)
 | ||||
| 	command[14 + 24] = (byte)((command_len - 16 - 24) % 256); // 后面数据包的长度,等全部生成后在赋值
 | ||||
| 	command[15 + 24] = (byte)((command_len - 16 - 24) / 256); | ||||
| 	command[16 + 24] = 0x52; // 服务类型(0x03请求服务列表,0x52请求标签数据)
 | ||||
| 	command[17 + 24] = 0x02; // 请求路径大小
 | ||||
| 	command[18 + 24] = 0x20; | ||||
| 	command[19 + 24] = 0x06; // 请求路径(0x0620)
 | ||||
| 	command[20 + 24] = 0x24; | ||||
| 	command[21 + 24] = 0x01; // 请求路径(0x0124)
 | ||||
| 	command[22 + 24] = 0x0A; | ||||
| 	command[23 + 24] = 0xF0; | ||||
| 	command[24 + 24] = (byte)((6 + addr_adjust_length) % 256); // CIP指令长度
 | ||||
| 	command[25 + 24] = (byte)((6 + addr_adjust_length) / 256); | ||||
| 
 | ||||
| 	command[0 + 24 + 26] = 0x4C; // 读取数据
 | ||||
| 	command[1 + 24 + 26] = (byte)((addr_adjust_length + 2) / 2); | ||||
| 	command[2 + 24 + 26] = 0x91; | ||||
| 	command[3 + 24 + 26] = (byte)addr_length; | ||||
| 	memcpy(command + 4 + 24 + 26, temp_address, addr_adjust_length); | ||||
| 	command[4 + 24 + 26 + addr_adjust_length] = (byte)((length) % 256); | ||||
| 	command[5 + 24 + 26 + addr_adjust_length] = (byte)((length) / 256); | ||||
| 
 | ||||
| 	command[6 + 24 + 26 + addr_adjust_length] = 0x01; | ||||
| 	command[7 + 24 + 26 + addr_adjust_length] = 0x00; | ||||
| 	command[8 + 24 + 26 + addr_adjust_length] = 0x01; | ||||
| 	command[9 + 24 + 26 + addr_adjust_length] = g_plc_slot; | ||||
| 
 | ||||
| 	byte_array_info ret = {0}; | ||||
| 	ret.data = command; | ||||
| 	ret.length = command_len; | ||||
| 	free(temp_address); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| byte_array_info build_write_core_command(const char *address, ushort typeCode, int length, byte_array_info value) | ||||
| { | ||||
| 	int val_len = 0; | ||||
| 	if (value.data != NULL) | ||||
| 		val_len = value.length; | ||||
| 
 | ||||
| 	size_t addr_length = strlen(address); | ||||
| 	size_t addr_adjust_length = addr_length; | ||||
| 	if (addr_adjust_length % 2 == 1) | ||||
| 		addr_adjust_length += 1; | ||||
| 
 | ||||
| 	char *temp_address = (char *)malloc(addr_adjust_length); | ||||
| 	memset(temp_address, 0, addr_adjust_length); | ||||
| 	memcpy(temp_address, address, strlen(address)); | ||||
| 
 | ||||
| 	const ushort command_len = 8 + 26 + (ushort)addr_adjust_length + val_len + 4 + 24; | ||||
| 	byte *command = (byte *)malloc(command_len); | ||||
| 	memset(command, 0, command_len); | ||||
| 
 | ||||
| 	command[0] = 0x6F; // 命令
 | ||||
| 	command[2] = (byte)((command_len - 24) % 256); | ||||
| 	command[3] = (byte)((command_len - 24) / 256); // 长度
 | ||||
| 
 | ||||
| 	char temp_session[4] = {0}; | ||||
| 	uint2bytes(g_session, temp_session); | ||||
| 	command[4] = temp_session[0]; | ||||
| 	command[5] = temp_session[1]; | ||||
| 	command[6] = temp_session[2]; | ||||
| 	command[7] = temp_session[3]; // 会话句柄
 | ||||
| 
 | ||||
| 	command[0 + 24] = 0x00; | ||||
| 	command[1 + 24] = 0x00; | ||||
| 	command[2 + 24] = 0x00; | ||||
| 	command[3 + 24] = 0x00; // 接口句柄,默认为0x00000000(CIP)
 | ||||
| 	command[4 + 24] = 0x01; | ||||
| 	command[5 + 24] = 0x0A; // 超时(0x0001)
 | ||||
| 	command[6 + 24] = 0x02; | ||||
| 	command[7 + 24] = 0x00; // 项数(0x0002)
 | ||||
| 	command[8 + 24] = 0x00; | ||||
| 	command[9 + 24] = 0x00; | ||||
| 	command[10 + 24] = 0x00; | ||||
| 	command[11 + 24] = 0x00; // 空地址项(0x0000)
 | ||||
| 	command[12 + 24] = 0xB2; | ||||
| 	command[13 + 24] = 0x00; // 未连接数据项(0x00b2)
 | ||||
| 	command[14 + 24] = (byte)((command_len - 16 - 24) % 256); | ||||
| 	; // 后面数据包的长度,等全部生成后在赋值
 | ||||
| 	command[15 + 24] = (byte)((command_len - 16 - 24) / 256); | ||||
| 	; | ||||
| 	command[16 + 24] = 0x52; // 服务类型(0x03请求服务列表,0x52请求标签数据)
 | ||||
| 	command[17 + 24] = 0x02; // 请求路径大小
 | ||||
| 	command[18 + 24] = 0x20; | ||||
| 	command[19 + 24] = 0x06; // 请求路径(0x0620)
 | ||||
| 	command[20 + 24] = 0x24; | ||||
| 	command[21 + 24] = 0x01; // 请求路径(0x0124)
 | ||||
| 	command[22 + 24] = 0x0A; | ||||
| 	command[23 + 24] = 0xF0; | ||||
| 	command[24 + 24] = (byte)((8 + val_len + addr_adjust_length) % 256); // CIP指令长度
 | ||||
| 	command[25 + 24] = (byte)((8 + val_len + addr_adjust_length) / 256); | ||||
| 
 | ||||
| 	command[0 + 26 + 24] = 0x4D; // 写数据
 | ||||
| 	command[1 + 26 + 24] = (byte)((addr_adjust_length + 2) / 2); | ||||
| 	command[2 + 26 + 24] = 0x91; | ||||
| 	command[3 + 26 + 24] = (byte)addr_length; | ||||
| 	memcpy(command + 4 + 26 + 24, temp_address, addr_adjust_length); | ||||
| 	command[4 + 26 + 24 + addr_adjust_length] = (byte)(typeCode % 256); | ||||
| 	command[5 + 26 + 24 + addr_adjust_length] = (byte)(typeCode) / 256; | ||||
| 	command[6 + 26 + 24 + addr_adjust_length] = (byte)(length % 256); // TODO length ??
 | ||||
| 	command[7 + 26 + 24 + addr_adjust_length] = (byte)(length / 256); | ||||
| 	memcpy(command + 8 + 26 + 24 + addr_adjust_length, value.data, value.length); | ||||
| 
 | ||||
| 	command[8 + 26 + 24 + addr_adjust_length + val_len] = 0x01; | ||||
| 	command[9 + 26 + 24 + addr_adjust_length + val_len] = 0x00; | ||||
| 	command[10 + 26 + 24 + addr_adjust_length + val_len] = 0x01; | ||||
| 	command[11 + 26 + 24 + addr_adjust_length + val_len] = g_plc_slot; | ||||
| 
 | ||||
| 	if (value.data != NULL) | ||||
| 		free(value.data); | ||||
| 
 | ||||
| 	byte_array_info ret = {0}; | ||||
| 	ret.data = command; | ||||
| 	ret.length = command_len; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e cip_analysis_read_byte(byte_array_info response, byte_array_info *ret) | ||||
| { | ||||
| 	cip_error_code_e ret_code = CIP_ERROR_CODE_OK; | ||||
| 	if (response.length == 0) | ||||
| 		return CIP_ERROR_CODE_FAILED; | ||||
| 
 | ||||
| 	int temp_length = 0; | ||||
| 	int data_length = 0; | ||||
| 	if (response.length >= 40) // index 38 is count[ushort]
 | ||||
| 	{ | ||||
| 		data_length = bytes2ushort(response.data + 38); | ||||
| 		if (data_length > 6) | ||||
| 		{ | ||||
| 			temp_length = data_length - 6; | ||||
| 			ret->data = (byte *)malloc(temp_length); | ||||
| 			memset(ret->data, 0, temp_length); | ||||
| 			memcpy(ret->data, response.data + 46, temp_length); | ||||
| 			ret->length = temp_length; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		ret_code = CIP_ERROR_CODE_UNKOWN; | ||||
| 	} | ||||
| 	return ret_code; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e cip_analysis_write_byte(byte_array_info response) | ||||
| { | ||||
| 	cip_error_code_e ret_code = CIP_ERROR_CODE_OK; | ||||
| 	if (response.length == 0) | ||||
| 		return CIP_ERROR_CODE_FAILED; | ||||
| 
 | ||||
| 	return ret_code; | ||||
| } | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////
 | ||||
| cip_error_code_e read_value(int fd, const char *address, int length, byte_array_info *out_bytes) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_UNKOWN; | ||||
| 	byte_array_info core_cmd = build_read_core_command(address, length); | ||||
| 	if (core_cmd.data != NULL) | ||||
| 	{ | ||||
| 		int need_send = core_cmd.length; | ||||
| 		int real_sends = socket_send_data(fd, core_cmd.data, need_send); | ||||
| 		if (real_sends == need_send) | ||||
| 		{ | ||||
| 			byte temp[BUFFER_SIZE] = {0}; | ||||
| 			memset(temp, 0, BUFFER_SIZE); | ||||
| 			byte_array_info response = {0}; | ||||
| 			response.data = temp; | ||||
| 			response.length = BUFFER_SIZE; | ||||
| 			if (cip_read_response(fd, &response)) | ||||
| 				ret = cip_analysis_read_byte(response, out_bytes); | ||||
| 			free(response.data); | ||||
| 		} | ||||
| 		free(core_cmd.data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| cip_error_code_e write_value(int fd, const char *address, int length, ushort type_code, byte_array_info in_bytes) | ||||
| { | ||||
| 	cip_error_code_e ret = CIP_ERROR_CODE_UNKOWN; | ||||
| 	byte_array_info core_cmd = build_write_core_command(address, type_code, length, in_bytes); | ||||
| 	if (core_cmd.data != NULL) | ||||
| 	{ | ||||
| 		int need_send = core_cmd.length; | ||||
| 		int real_sends = socket_send_data(fd, core_cmd.data, need_send); | ||||
| 		if (real_sends == need_send) | ||||
| 		{ | ||||
| 			byte temp[BUFFER_SIZE] = {0}; | ||||
| 			memset(temp, 0, BUFFER_SIZE); | ||||
| 			byte_array_info response = {0}; | ||||
| 			response.data = temp; | ||||
| 			response.length = BUFFER_SIZE; | ||||
| 
 | ||||
| 			if (cip_read_response(fd, &response)) | ||||
| 				ret = cip_analysis_write_byte(response); | ||||
| 		} | ||||
| 		free(core_cmd.data); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| bool initialization_on_connect(int fd) | ||||
| { | ||||
| 	bool is_ok = false; | ||||
| 	g_session = 0; | ||||
| 
 | ||||
| 	// First handshake -> send regiseter command
 | ||||
| 	byte_array_info temp = {0}; | ||||
| 	int command_len = sizeof(g_registered_command); | ||||
| 	temp.data = (byte *)malloc(command_len); | ||||
| 	memcpy(temp.data, g_registered_command, command_len); | ||||
| 	temp.length = command_len; | ||||
| 	is_ok = read_data_from_server(fd, temp, &g_session); | ||||
| 
 | ||||
| 	// 返回成功的信号 -> Return a successful signal
 | ||||
| 	return is_ok; | ||||
| 	free(temp.data); | ||||
| } | ||||
| 
 | ||||
| bool cip_read_response(int fd, byte_array_info *response) | ||||
| { | ||||
| 	bool is_ok = false; | ||||
| 	int nread = 0; | ||||
| 	int content_size = 0; | ||||
| 
 | ||||
| 	if (fd < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	byte *content = NULL; | ||||
| 	byte head[HEAD_SIZE]; | ||||
| 	memset(head, 0, HEAD_SIZE); | ||||
| 	int recv_size = socket_recv_data_one_loop(fd, head, HEAD_SIZE); | ||||
| 	if (recv_size >= HEAD_SIZE) // header size
 | ||||
| 	{ | ||||
| 		content_size = bytes2ushort(head + 2); | ||||
| 		if (content_size > 0) | ||||
| 		{ | ||||
| 			content = (byte *)malloc(content_size); | ||||
| 			memset(content, 0, content_size); | ||||
| 		} | ||||
| 		recv_size = socket_recv_data(fd, content, content_size); | ||||
| 		if (recv_size == content_size) | ||||
| 		{ | ||||
| 			response->length = HEAD_SIZE + content_size; | ||||
| 			response->data = (byte *)malloc(response->length); | ||||
| 			memset(response->data, 0, response->length); | ||||
| 			memcpy(response->data, head, HEAD_SIZE); | ||||
| 			memcpy(response->data + HEAD_SIZE, content, content_size); | ||||
| 
 | ||||
| 			is_ok = true; | ||||
| 		} | ||||
| 
 | ||||
| 		free(content); | ||||
| 	} | ||||
| 	return is_ok; | ||||
| } | ||||
| 
 | ||||
| bool read_data_from_server(int fd, byte_array_info send, int *session) | ||||
| { | ||||
| 	bool is_ok = false; | ||||
| 	int need_send = send.length; | ||||
| 	int real_sends = socket_send_data(fd, send.data, need_send); | ||||
| 	if (real_sends == need_send) | ||||
| 	{ | ||||
| 		byte_array_info response = {0}; | ||||
| 		is_ok = cip_read_response(fd, &response); | ||||
| 		if (is_ok) | ||||
| 		{ | ||||
| 			is_ok = false; | ||||
| 			if (response.length > 8) | ||||
| 			{ | ||||
| 				*session = bytes2uint32(response.data + 4); | ||||
| 				is_ok = true; | ||||
| 			} | ||||
| 
 | ||||
| 			if (response.data != NULL) | ||||
| 				free(response.data); | ||||
| 		} | ||||
| 	} | ||||
| 	return is_ok; | ||||
| } | ||||
|  | @ -0,0 +1,32 @@ | |||
| #ifndef __H_AB_CIP_HELPER_H__ | ||||
| #define __H_AB_CIP_HELPER_H__ | ||||
| #include "utill.h" | ||||
| 
 | ||||
| #define RELEASE_DATA(addr) \ | ||||
|     {                      \ | ||||
|         if (addr != NULL)  \ | ||||
|         {                  \ | ||||
|             free(addr);    \ | ||||
|         }                  \ | ||||
|     } | ||||
| #define BUFFER_SIZE 1024 | ||||
| #define HEAD_SIZE 24 | ||||
| 
 | ||||
| byte_array_info build_read_core_command(const char *address, int length); | ||||
| byte_array_info build_write_core_command(const char *address, ushort typeCode, int length, byte_array_info value); | ||||
| 
 | ||||
| cip_error_code_e cip_analysis_read_byte(byte_array_info response, byte_array_info *ret); | ||||
| cip_error_code_e cip_analysis_write_byte(byte_array_info response); | ||||
| 
 | ||||
| bool read_data_from_server(int fd, byte_array_info send, int *session); | ||||
| bool cip_read_response(int fd, byte_array_info *response); | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////
 | ||||
| cip_error_code_e read_value(int fd, const char *address, int length, byte_array_info *out_bytes); | ||||
| cip_error_code_e write_value(int fd, const char *address, int length, ushort type_code, byte_array_info in_bytes); | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| bool initialization_on_connect(int fd); | ||||
| 
 | ||||
| #endif //__H_AB_CIP_HELPER_H__
 | ||||
|  | @ -0,0 +1,27 @@ | |||
| #ifndef __H_AB_CIP_PRIVATE_H__ | ||||
| #define __H_AB_CIP_PRIVATE_H__ | ||||
| #include "typedef.h" | ||||
| 
 | ||||
| /// <summary>
 | ||||
| /// 注册命令
 | ||||
| /// </summary>
 | ||||
| const byte g_registered_command[] = | ||||
| 	{ | ||||
| 		0x65, 0x00,										// 注册请求
 | ||||
| 		0x04, 0x00,										// 命令数据长度(单位字节)
 | ||||
| 		0x00, 0x00, 0x00, 0x00,							// 会话句柄,初始值为0x00000000
 | ||||
| 		0x00, 0x00, 0x00, 0x00,							// 状态,初始值为0x00000000(状态好)
 | ||||
| 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 请求通信一方的说明
 | ||||
| 		0x00, 0x00, 0x00, 0x00,							// 选项,默认为0x00000000
 | ||||
| 		0x01, 0x00,										// 协议版本(0x0001)
 | ||||
| 		0x00, 0x00										// 选项标记(0x0000
 | ||||
| }; | ||||
| 
 | ||||
| ///////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| // 会话句柄(由AB PLC生成)
 | ||||
| uint32 g_session; | ||||
| 
 | ||||
| byte g_plc_slot; | ||||
| 
 | ||||
| #endif //__H_AB_CIP_PRIVATE_H__
 | ||||
|  | @ -0,0 +1,318 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022 AIIT XUOS Lab | ||||
|  * XiUOS is licensed under Mulan PSL v2. | ||||
|  * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||||
|  * You may obtain a copy of Mulan PSL v2 at: | ||||
|  *        http://license.coscl.org.cn/MulanPSL2
 | ||||
|  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||||
|  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||||
|  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||||
|  * See the Mulan PSL v2 for more details. | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @file CIP.c | ||||
|  * @brief plc protocol CIP Ethernet/IP | ||||
|  * @version 3.0 | ||||
|  * @author AIIT XUOS Lab | ||||
|  * @date 2023-4-14 | ||||
|  */ | ||||
| #pragma warning(disable : 4996) | ||||
| #include <cip.h> | ||||
| #include "ab_cip.h" | ||||
| 
 | ||||
| 
 | ||||
| CIPReadItem CIP_read_item[1024] = {0}; | ||||
| 
 | ||||
| /**
 | ||||
|  * @description: S7 Receive Plc Data Task | ||||
|  * @param parameter - parameter pointer | ||||
|  * @return | ||||
|  */ | ||||
| void *ReceivePlcDataTask(void *parameter) | ||||
| { | ||||
|     struct ControlProtocol *control_protocol = (struct ControlProtocol *)parameter;  | ||||
|     ReadPlcDataByRecipe(control_protocol->recipe); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @description: S7 Protocol Open | ||||
|  * @param control_protocol - control protocol pointer | ||||
|  * @return success : 0 error | ||||
|  */ | ||||
| int CIPOpen(struct ControlProtocol *control_protocol) | ||||
| { | ||||
|     ControlProtocolOpenDef(control_protocol); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @description: S7 Protocol Close | ||||
|  * @param control_protocol - control protocol pointer | ||||
|  * @return success : 0 error | ||||
|  */ | ||||
| int CIPClose(struct ControlProtocol *control_protocol) | ||||
| {     | ||||
|     ControlProtocolCloseDef(); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @description: S7 Protocol Read Data | ||||
|  * @param control_protocol - control protocol pointer | ||||
|  * @param buf - read data buffer pointer | ||||
|  * @param len - read data length | ||||
|  * @return success : data length error : 0 | ||||
|  */ | ||||
| int CIPRead(struct ControlProtocol *control_protocol, void *buf, size_t len) | ||||
| { | ||||
|     struct CircularAreaApp *circular_area = (struct CircularAreaApp *)control_protocol->args; | ||||
|     return CircularAreaAppRead(circular_area, buf, len); | ||||
| } | ||||
| 
 | ||||
| static struct ControlDone CIP_protocol_done =  | ||||
| { | ||||
|     ._open = CIPOpen, | ||||
|     ._close = CIPClose, | ||||
|     ._read = CIPRead, | ||||
|     ._write = NULL, | ||||
|     ._ioctl = NULL, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * @description: Push Data Onto a Stack One By One | ||||
|  * @param datastack - data stack pointer | ||||
|  * @param args - data pointer | ||||
|  * @param length - data length | ||||
|  * @return  | ||||
|  */ | ||||
| void PushDataIntoStack(uint8_t *datastack,uint8_t* args,uint16_t length) | ||||
| { | ||||
|     static int index = 8;     | ||||
|     for(int i =0; i < length; i ++) { | ||||
|         datastack[index] = args[i]; | ||||
|         index++; | ||||
|         if(index >= control_protocol->recipe->protocol_data.data_length){ | ||||
|             index = 8; | ||||
|         }      | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #define GET_RESULT(ret)    \ | ||||
| 	{                      \ | ||||
| 		if (ret != 0)      \ | ||||
| 			faild_count++; \ | ||||
| 	} | ||||
| 
 | ||||
| /**
 | ||||
|  * @description: Read PLC Data By Recipe | ||||
|  * @param p_recipe - recipe pointer | ||||
|  * @return success : 0 error : -1 | ||||
|  */ | ||||
| int8_t ReadPlcDataByRecipe(struct ControlRecipe *p_recipe) | ||||
| {    | ||||
|     static BasicSocketPlc plc_socket = {0}; | ||||
|     uint16_t data_length = control_protocol->recipe->protocol_data.data_length; | ||||
|     uint8_t *CIP_data = control_protocol->recipe->protocol_data.data; | ||||
|     struct CircularAreaApp *circular_area = (struct CircularAreaApp *)control_protocol->args; | ||||
|     memset(&plc_socket, 0, sizeof(BasicSocketPlc)); | ||||
|     char plc_ip_string[15] = {0}; | ||||
|     sprintf(plc_ip_string, "%u.%u.%u.%u", | ||||
|         p_recipe->socket_config.plc_ip[0], | ||||
|         p_recipe->socket_config.plc_ip[1], | ||||
|         p_recipe->socket_config.plc_ip[2], | ||||
|         p_recipe->socket_config.plc_ip[3]); | ||||
|     char *plc_ip = plc_ip_string; | ||||
|     plc_socket.port = control_protocol->recipe->socket_config.port; | ||||
|     int fd = -1; | ||||
| 	int slot = 0; | ||||
| 	bool ret_con = ab_cip_connect(plc_ip, plc_socket.port, 0, &fd); | ||||
|     cip_error_code_e ret = CIP_ERROR_CODE_FAILED; | ||||
|     int faild_count = 0; | ||||
|     char address[50] = {0}; | ||||
|     int i = 0; | ||||
|     uint8_t val[8] = {0} ; | ||||
|     bool all_success = false; | ||||
|     bool b_val = true; | ||||
|     short s_val = 0; | ||||
|     ushort us_val = 0; | ||||
|     int i_val = 0; | ||||
|     uint32 ui_val = 0; | ||||
|     float f_val = 0; | ||||
|     double d_val = 0; | ||||
|     while (1){ | ||||
|         if (ret_con || fd > 0) | ||||
|         { | ||||
|             faild_count = 0; | ||||
|             for (i = 0; i < p_recipe->read_item_count; i++) | ||||
|             {   | ||||
|                 printf("==============Test count: %d==============\n", i + 1); | ||||
|                 strcpy(address, CIP_read_item[i].value_name); | ||||
|                 switch (CIP_read_item[i].value_type) | ||||
|                 { | ||||
|                 case 1: | ||||
|                     ////////////////////////////////////////////////////////////////////////// 
 | ||||
|                     // strcpy(address, CIP_read_item[i].value_name);//
 | ||||
|                     // ret = ab_cip_write_bool(fd, address, val);
 | ||||
|                     // printf("Write\t %s \tbool:\t %d, \tret: %d\n", address, val, ret);
 | ||||
|                     // GET_RESULT(ret);
 | ||||
| 
 | ||||
|                     b_val = false; | ||||
|                     ret = ab_cip_read_bool(fd, address, &b_val); | ||||
|                     // printf("Read\t %s \tbool:\t %d\n", address, b_val);
 | ||||
|                     GET_RESULT(ret); | ||||
|                     memcpy(val,&b_val,sizeof(b_val)); | ||||
|                     PushDataIntoStack(CIP_data,val,1); | ||||
|                     break;   | ||||
| 
 | ||||
|                 case 3: | ||||
|                     //////////////////////////////////////////////////////////////////////////
 | ||||
|                     // short w_s_val = 23;
 | ||||
|                     // strcpy(address, CIP_read_item[i].value_name);
 | ||||
|                     // ret = ab_cip_write_short(fd, address, w_s_val);
 | ||||
|                     // printf("Write\t %s \tshort:\t %d, \tret: %d\n", address, w_s_val, ret);
 | ||||
|                     // GET_RESULT(ret);
 | ||||
| 
 | ||||
|                     s_val = 0; | ||||
|                     ret = ab_cip_read_short(fd, address, &s_val); | ||||
|                     printf("Read\t %s \tshort:\t %d\n", address, s_val); | ||||
|                     GET_RESULT(ret);  | ||||
|                     memcpy(val,&s_val,sizeof(s_val)); | ||||
|                     PushDataIntoStack(CIP_data,val,2); | ||||
|                     break; | ||||
| 
 | ||||
|                 case 4: | ||||
|                     //////////////////////////////////////////////////////////////////////////
 | ||||
|                     // int32 w_i_val = 12345;
 | ||||
|                     strcpy(address, CIP_read_item[i].value_name); | ||||
|                     // ret = ab_cip_write_int32(fd, address, w_i_val);
 | ||||
|                     // printf("Write\t %s \tint32:\t %d, \tret: %d\n", address, w_i_val, ret);
 | ||||
|                     // GET_RESULT(ret);
 | ||||
| 
 | ||||
|                     i_val = 0; | ||||
|                     ret = ab_cip_read_int32(fd, address, &i_val); | ||||
|                     // printf("Read\t %s \tint32:\t %d\n", address, i_val);
 | ||||
|                     GET_RESULT(ret); | ||||
|                     memcpy(val,&i_val,sizeof(s_val)); | ||||
|                     PushDataIntoStack(CIP_data,val,2); | ||||
|                     break; | ||||
|                  | ||||
|                 case 6: | ||||
|                     //////////////////////////////////////////////////////////////////////////
 | ||||
|                     // ushort w_us_val = 22;
 | ||||
|                     strcpy(address, CIP_read_item[i].value_name); | ||||
|                     // ret = ab_cip_write_ushort(fd, address, w_us_val);
 | ||||
|                     // printf("Write\t %s \tushort:\t %d, \tret: %d\n", address, w_us_val, ret);
 | ||||
|                     // GET_RESULT(ret);
 | ||||
| 
 | ||||
|                     us_val = 0; | ||||
|                     ret = ab_cip_read_ushort(fd, address, &us_val); | ||||
|                     // printf("Read\t %s \tushort:\t %d\n", address, us_val);
 | ||||
|                     GET_RESULT(ret); | ||||
|                     memcpy(val,&us_val,sizeof(us_val)); | ||||
|                     PushDataIntoStack(CIP_data,val,1); | ||||
|                     break; | ||||
| 
 | ||||
|                 case 7: | ||||
|                     // //////////////////////////////////////////////////////////////////////////
 | ||||
|                     // uint32 w_ui_val = 22345;
 | ||||
|                     strcpy(address, CIP_read_item[i].value_name); | ||||
|                     // ret = ab_cip_write_uint32(fd, address, w_ui_val);
 | ||||
|                     // printf("Write\t %s \tuint32:\t %d, \tret: %d\n", address, w_ui_val, ret);
 | ||||
|                     // GET_RESULT(ret);
 | ||||
|      | ||||
|                     ui_val = 0; | ||||
|                     ret = ab_cip_read_uint32(fd, address, &ui_val); | ||||
|                     // printf("Read\t %s \tuint32:\t %d\n", address, ui_val);
 | ||||
|                     GET_RESULT(ret); | ||||
|                     memcpy(val,&ui_val,sizeof(us_val)); | ||||
|                     PushDataIntoStack(CIP_data,val,1); | ||||
|                     break; | ||||
| 
 | ||||
|                 case 8: | ||||
|                     ////////////////////////////////////////////////////////////////////////
 | ||||
|                     // double w_d_val = 12345.6789;
 | ||||
|                     strcpy(address, CIP_read_item[i].value_name); | ||||
|                     // ret = ab_cip_write_double(fd, address, w_d_val);
 | ||||
|                     // printf("Write\t %s \tdouble:\t %lf, \tret: %d\n", address, w_d_val, ret);
 | ||||
|                     // GET_RESULT(ret);
 | ||||
| 
 | ||||
|                     d_val = 0.0; | ||||
|                     ret = ab_cip_read_double(fd, address, &d_val); | ||||
|                     // printf("Read\t %s \tdouble:\t %lf\n", address, d_val);
 | ||||
|                     GET_RESULT(ret); | ||||
|                     memcpy(val,&d_val,sizeof(us_val)); | ||||
|                     PushDataIntoStack(CIP_data,val,1); | ||||
|                     break; | ||||
| 
 | ||||
|                 case 9: | ||||
|                     //////////////////////////////////////////////////////////////////////////
 | ||||
|                     // float w_f_val = 32.454f;
 | ||||
|                     // strcpy(address, CIP_read_item[i].value_name);
 | ||||
|                     // ret = ab_cip_write_float(fd, address, w_f_val);
 | ||||
|                     // printf("Write\t %s \tfloat:\t %f, \tret: %d\n", address, w_f_val, ret);
 | ||||
|                     // GET_RESULT(ret);
 | ||||
| 
 | ||||
|                     f_val = 0.0; | ||||
|                     ret = ab_cip_read_float(fd, address, &f_val); | ||||
|                     printf("Read\t %s \tfloat:\t %f\n", address, f_val); | ||||
|                     GET_RESULT(ret); | ||||
|                     memcpy(val,&f_val,sizeof(us_val)); | ||||
|                     PushDataIntoStack(CIP_data,val,4);                | ||||
|                     break; | ||||
| 
 | ||||
|                 default: | ||||
|                     printf("value type no found!\n"); | ||||
|                     break; | ||||
|                 } | ||||
|                 // ControlPrintfList("CIP RECV", CIP_data, data_length);
 | ||||
|             }       | ||||
|             printf("All Failed count: %d\n", faild_count); | ||||
|             if(faild_count != 0) | ||||
|             {          | ||||
|                 ab_cip_disconnect(fd); | ||||
|                 printf("get value failed!\n"); | ||||
|                 break; | ||||
|             } | ||||
|             /*read all variable item data, put them into circular_area*/ | ||||
|             printf("%s get %d item %d length\n", __func__, i, data_length); | ||||
|             CircularAreaAppWrite(circular_area, CIP_data, data_length, 0); | ||||
|             PrivTaskDelay(100); | ||||
|         } | ||||
|     }     | ||||
|     return ret;  | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @description: CIP Protocol Cmd Generate | ||||
|  * @param p_recipe - recipe pointer | ||||
|  * @param protocol_format_info - protocol format info pointer | ||||
|  * @return success : 0 error : -1 | ||||
|  */ | ||||
| int CIPProtocolFormatCmd(struct ControlRecipe *p_recipe, ProtocolFormatInfo *protocol_format_info) | ||||
| { | ||||
|     int ret = 0; | ||||
|     int i = protocol_format_info->read_item_index; | ||||
|     // CIPReadItem *CIP_read_item = (CIPReadItem *)p_recipe->read_item; 
 | ||||
|     CIP_read_item[i].value_type = cJSON_GetObjectItem(protocol_format_info->read_single_item_json, "value_type")->valueint; | ||||
|     strncpy(CIP_read_item[i].value_name, cJSON_GetObjectItem(protocol_format_info->read_single_item_json, "value_name")->valuestring, 20);     | ||||
|      | ||||
|     return ret; | ||||
| } | ||||
| /**
 | ||||
|  * @description: CIP Protocol Init | ||||
|  * @param p_recipe - recipe pointer | ||||
|  * @return success : 0 error : -1 | ||||
|  */ | ||||
| int CipProtocolInit(struct ControlRecipe *p_recipe) | ||||
| {    | ||||
|     p_recipe->read_item = PrivMalloc(sizeof(CIPReadItem) * p_recipe->read_item_count); | ||||
|     if (NULL == p_recipe->read_item) { | ||||
|         PrivFree(p_recipe->read_item); | ||||
|         return -1; | ||||
|     } | ||||
|     memset(p_recipe->read_item, 0, sizeof(CIPReadItem)); | ||||
|     p_recipe->ControlProtocolFormatCmd = CIPProtocolFormatCmd; | ||||
|     p_recipe->done = &CIP_protocol_done; | ||||
|     return 0; | ||||
| } | ||||
|  | @ -0,0 +1,174 @@ | |||
| #include "cip_socket.h" | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <winsock2.h> | ||||
| #include <ws2tcpip.h> | ||||
| #include <windows.h> | ||||
| #pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ | ||||
| #else | ||||
| #include <errno.h> | ||||
| #include <sys/types.h> | ||||
| // #include <sys/socket.h>
 | ||||
| #include <sockets.h> | ||||
| // #include <arpa/inet.h>
 | ||||
| #include "lwip/inet.h" | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
| int socket_send_data(int fd, void *buf, int nbytes) | ||||
| { | ||||
| 	int nleft, nwritten; | ||||
| 	char *ptr = (char *)buf; | ||||
| 
 | ||||
| 	if (fd < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	nleft = nbytes; | ||||
| 	while (nleft > 0) | ||||
| 	{ | ||||
| 		nwritten = send(fd, ptr, nleft, 0); | ||||
| 		if (nwritten <= 0) | ||||
| 		{ | ||||
| 			if (errno == EINTR) | ||||
| 				continue; | ||||
| 			else | ||||
| 				return -1; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			nleft -= nwritten; | ||||
| 			ptr += nwritten; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return (nbytes - nleft); | ||||
| } | ||||
| 
 | ||||
| int socket_recv_data(int fd, void *buf, int nbytes) | ||||
| { | ||||
| 	int nleft, nread; | ||||
| 	char *ptr = (char *)buf; | ||||
| 
 | ||||
| 	if (fd < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	nleft = nbytes; | ||||
| 	while (nleft > 0) | ||||
| 	{ | ||||
| 		nread = recv(fd, ptr, nleft, 0); | ||||
| 		if (nread == 0) | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 		else if (nread < 0) | ||||
| 		{ | ||||
| 			if (errno == EINTR) | ||||
| 				continue; | ||||
| 			else | ||||
| 				return -1; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			nleft -= nread; | ||||
| 			ptr += nread; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return (nbytes - nleft); | ||||
| } | ||||
| 
 | ||||
| int socket_recv_data_one_loop(int fd, void *buf, int nbytes) | ||||
| { | ||||
| 	int nleft, nread; | ||||
| 	char *ptr = (char *)buf; | ||||
| 
 | ||||
| 	if (fd < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	nleft = nbytes; | ||||
| 	while (nleft > 0) | ||||
| 	{ | ||||
| 		nread = recv(fd, ptr, nleft, 0); | ||||
| 		if (nread == 0) | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 		else if (nread < 0) | ||||
| 		{ | ||||
| 			if (errno == EINTR) | ||||
| 				continue; | ||||
| 			else | ||||
| 				return -1; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			nleft -= nread; | ||||
| 			ptr += nread; | ||||
| 
 | ||||
| 			// 目前只接收一次
 | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return (nbytes - nleft); | ||||
| } | ||||
| 
 | ||||
| int socket_open_tcp_client_socket(char *destIp, short destPort) | ||||
| { | ||||
| 	int sockFd = 0; | ||||
| 	struct sockaddr_in serverAddr; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	sockFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);  //IPPROTO_TCP
 | ||||
| 
 | ||||
| 	printf("%d %s  %d\n",sockFd ,__func__,__LINE__); | ||||
| 	 | ||||
| 	if (sockFd < 0) | ||||
| 	{ | ||||
| 		return -1; | ||||
| #pragma warning(disable : 4996) | ||||
| 	} | ||||
| 
 | ||||
| 	memset((char *)&serverAddr, 0, sizeof(serverAddr)); | ||||
| 	serverAddr.sin_family = AF_INET; | ||||
| 	serverAddr.sin_addr.s_addr = inet_addr(destIp); | ||||
| 	serverAddr.sin_port = (uint16_t)htons((uint16_t)destPort); | ||||
| 
 | ||||
| 	ret = connect(sockFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); | ||||
| 	if (ret != 0) | ||||
| 	{ | ||||
| 		socket_close_tcp_socket(sockFd); | ||||
| 		sockFd = -1; | ||||
| 	} | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| 	int timeout = 5000; // 5s
 | ||||
| 	ret = setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout)); | ||||
| 	ret = setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout)); | ||||
| #else | ||||
| 	struct timeval timeout = {5, 0}; // 3s
 | ||||
| 	ret = setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout)); | ||||
| 	ret = setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout)); | ||||
| #endif | ||||
| 
 | ||||
| 	return sockFd; | ||||
| } | ||||
| 
 | ||||
| void socket_close_tcp_socket(int sockFd) | ||||
| { | ||||
| 	if (sockFd > 0) | ||||
| 	{ | ||||
| #ifdef _WIN32 | ||||
| 		closesocket(sockFd); | ||||
| #else | ||||
| 		close(sockFd); | ||||
| #endif | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void tinet_ntoa(char *ipstr, unsigned int ip) | ||||
| { | ||||
| 	sprintf(ipstr, "%d.%d.%d.%d", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, ip >> 24); | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| #ifndef __SOCKET_H_ | ||||
| #define __SOCKET_H_ | ||||
| 
 | ||||
| #include "utill.h" | ||||
| 
 | ||||
| int socket_send_data(int fd, void *ptr, int nbytes); | ||||
| int socket_recv_data(int fd, void *ptr, int nbytes); | ||||
| int socket_recv_data_one_loop(int fd, void *ptr, int nbytes); | ||||
| int socket_open_tcp_client_socket(char *ip, short port); | ||||
| void socket_close_tcp_socket(int sockFd); | ||||
| 
 | ||||
| #endif //__SOCKET_H_
 | ||||
|  | @ -0,0 +1,21 @@ | |||
| #ifndef __H_TYPEDEF_H__ | ||||
| #define __H_TYPEDEF_H__ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| typedef unsigned char byte; | ||||
| typedef unsigned short ushort; | ||||
| typedef signed int int32; | ||||
| typedef unsigned int uint32; | ||||
| typedef long long int64; | ||||
| typedef unsigned long long uint64; | ||||
| 
 | ||||
| typedef enum _tag_cip_error_code | ||||
| { | ||||
| 	CIP_ERROR_CODE_OK = 0,		// 成功
 | ||||
| 	CIP_ERROR_CODE_FAILED = 1,	// 错误
 | ||||
| 	CIP_ERROR_CODE_UNKOWN = 99, // 未知错误
 | ||||
| } cip_error_code_e; | ||||
| 
 | ||||
| #endif // !__H_TYPEDEF_H__
 | ||||
|  | @ -0,0 +1,383 @@ | |||
| #include "utill.h" | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <Windows.h> | ||||
| #else | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
| #define _WS2_32_WINSOCK_SWAP_LONG(l) \ | ||||
| 	((((l) >> 24) & 0x000000FFL) |   \ | ||||
| 	 (((l) >> 8) & 0x0000FF00L) |    \ | ||||
| 	 (((l) << 8) & 0x00FF0000L) |    \ | ||||
| 	 (((l) << 24) & 0xFF000000L)) | ||||
| 
 | ||||
| #define _WS2_32_WINSOCK_SWAP_LONGLONG(l)    \ | ||||
| 	((((l) >> 56) & 0x00000000000000FFLL) | \ | ||||
| 	 (((l) >> 40) & 0x000000000000FF00LL) | \ | ||||
| 	 (((l) >> 24) & 0x0000000000FF0000LL) | \ | ||||
| 	 (((l) >> 8) & 0x00000000FF000000LL) |  \ | ||||
| 	 (((l) << 8) & 0x000000FF00000000LL) |  \ | ||||
| 	 (((l) << 24) & 0x0000FF0000000000LL) | \ | ||||
| 	 (((l) << 40) & 0x00FF000000000000LL) | \ | ||||
| 	 (((l) << 56) & 0xFF00000000000000LL)) | ||||
| 
 | ||||
| void short2bytes(short i, byte *bytes) | ||||
| { | ||||
| 	int size = 2; | ||||
| 	memset(bytes, 0, sizeof(byte) * size); | ||||
| 	bytes[0] = (byte)(0xff & i); | ||||
| 	bytes[1] = (byte)((0xff00 & i) >> 8); | ||||
| } | ||||
| 
 | ||||
| short bytes2short(byte *bytes) | ||||
| { | ||||
| 	short iRetVal = bytes[0] & 0xFF; | ||||
| 	iRetVal |= (((short)bytes[1] << 8) & 0xFF00); | ||||
| 	return iRetVal; | ||||
| } | ||||
| 
 | ||||
| void ushort2bytes(ushort i, byte *bytes) | ||||
| { | ||||
| 	int size = 2; | ||||
| 	memset(bytes, 0, sizeof(byte) * size); | ||||
| 	bytes[0] = (byte)(0xff & i); | ||||
| 	bytes[1] = (byte)((0xff00 & i) >> 8); | ||||
| } | ||||
| 
 | ||||
| ushort bytes2ushort(byte *bytes) | ||||
| { | ||||
| 	ushort iRetVal = bytes[0] & 0xFF; | ||||
| 	iRetVal |= (((ushort)bytes[1] << 8) & 0xFF00); | ||||
| 	return iRetVal; | ||||
| } | ||||
| 
 | ||||
| void int2bytes(int32 i, byte *bytes) | ||||
| { | ||||
| 	int size = 4; | ||||
| 	memset(bytes, 0, sizeof(byte) * size); | ||||
| 	bytes[0] = (byte)(0xff & i); | ||||
| 	bytes[1] = (byte)((0xff00 & i) >> 8); | ||||
| 	bytes[2] = (byte)((0xff0000 & i) >> 16); | ||||
| 	bytes[3] = (byte)((0xff000000 & i) >> 24); | ||||
| } | ||||
| 
 | ||||
| int32 bytes2int32(byte *bytes) | ||||
| { | ||||
| 	int32 iRetVal = bytes[0] & 0xFF; | ||||
| 	iRetVal |= (((int32)bytes[1] << 8) & 0xFF00); | ||||
| 	iRetVal |= (((int32)bytes[2] << 16) & 0xFF0000); | ||||
| 	iRetVal |= (((int32)bytes[3] << 24) & 0xFF000000); | ||||
| 	return iRetVal; | ||||
| } | ||||
| 
 | ||||
| void uint2bytes(uint32 i, byte *bytes) | ||||
| { | ||||
| 	int size = 4; | ||||
| 	memset(bytes, 0, sizeof(byte) * size); | ||||
| 	bytes[0] = (byte)(0xff & i); | ||||
| 	bytes[1] = (byte)((0xff00 & i) >> 8); | ||||
| 	bytes[2] = (byte)((0xff0000 & i) >> 16); | ||||
| 	bytes[3] = (byte)((0xff000000 & i) >> 24); | ||||
| } | ||||
| 
 | ||||
| uint32 bytes2uint32(byte *bytes) | ||||
| { | ||||
| 	uint32 iRetVal = bytes[0] & 0xFF; | ||||
| 	iRetVal |= (((uint32)bytes[1] << 8) & 0xFF00); | ||||
| 	iRetVal |= (((uint32)bytes[2] << 16) & 0xFF0000); | ||||
| 	iRetVal |= (((uint32)bytes[3] << 24) & 0xFF000000); | ||||
| 	return iRetVal; | ||||
| } | ||||
| 
 | ||||
| void bigInt2bytes(int64 i, byte *bytes) | ||||
| { | ||||
| 	int size = 8; | ||||
| 	memset(bytes, 0, sizeof(byte) * size); | ||||
| 	bytes[0] = (byte)(0xff & i); | ||||
| 	bytes[1] = (byte)(0xff & (i >> 8)); | ||||
| 	bytes[2] = (byte)(0xff & (i >> 16)); | ||||
| 	bytes[3] = (byte)(0xff & (i >> 24)); | ||||
| 	bytes[4] = (byte)(0xff & (i >> 32)); | ||||
| 	bytes[5] = (byte)(0xff & (i >> 40)); | ||||
| 	bytes[6] = (byte)(0xff & (i >> 48)); | ||||
| 	bytes[7] = (byte)(0xff & (i >> 56)); | ||||
| } | ||||
| 
 | ||||
| int64 bytes2bigInt(byte *bytes) | ||||
| { | ||||
| 	int64 iRetVal = bytes[0] & 0xFF; | ||||
| 	iRetVal |= (((int64)bytes[1] << 8) & 0xFF00); | ||||
| 	iRetVal |= (((int64)bytes[2] << 16) & 0xFF0000); | ||||
| 	iRetVal |= (((int64)bytes[3] << 24) & 0xFF000000); | ||||
| 	iRetVal |= (((int64)bytes[4] << 32) & 0xFF00000000); | ||||
| 	iRetVal |= (((int64)bytes[5] << 40) & 0xFF0000000000); | ||||
| 	iRetVal |= (((int64)bytes[6] << 48) & 0xFF000000000000); | ||||
| 	iRetVal |= (((int64)bytes[7] << 56) & 0xFF00000000000000); | ||||
| 	return iRetVal; | ||||
| } | ||||
| 
 | ||||
| void ubigInt2bytes(uint64 i, byte *bytes) | ||||
| { | ||||
| 	int size = 8; | ||||
| 	memset(bytes, 0, sizeof(byte) * size); | ||||
| 	bytes[0] = (byte)(0xff & i); | ||||
| 	bytes[1] = (byte)(0xff & (i >> 8)); | ||||
| 	bytes[2] = (byte)(0xff & (i >> 16)); | ||||
| 	bytes[3] = (byte)(0xff & (i >> 24)); | ||||
| 	bytes[4] = (byte)(0xff & (i >> 32)); | ||||
| 	bytes[5] = (byte)(0xff & (i >> 40)); | ||||
| 	bytes[6] = (byte)(0xff & (i >> 48)); | ||||
| 	bytes[7] = (byte)(0xff & (i >> 56)); | ||||
| } | ||||
| 
 | ||||
| uint64 bytes2ubigInt(byte *bytes) | ||||
| { | ||||
| 	uint64 iRetVal = bytes[0] & 0xFF; | ||||
| 	iRetVal |= (((uint64)bytes[1] << 8) & 0xFF00); | ||||
| 	iRetVal |= (((uint64)bytes[2] << 16) & 0xFF0000); | ||||
| 	iRetVal |= (((uint64)bytes[3] << 24) & 0xFF000000); | ||||
| 	iRetVal |= (((uint64)bytes[4] << 32) & 0xFF00000000); | ||||
| 	iRetVal |= (((uint64)bytes[5] << 40) & 0xFF0000000000); | ||||
| 	iRetVal |= (((uint64)bytes[6] << 48) & 0xFF000000000000); | ||||
| 	iRetVal |= (((uint64)bytes[7] << 56) & 0xFF00000000000000); | ||||
| 	return iRetVal; | ||||
| } | ||||
| 
 | ||||
| void float2bytes(float i, byte *bytes) | ||||
| { | ||||
| 	int size = 4; | ||||
| 	int temp = *(int *)&i; | ||||
| 	int2bytes(temp, bytes); | ||||
| } | ||||
| 
 | ||||
| float bytes2float(byte *bytes) | ||||
| { | ||||
| 	int temp = bytes2int32(bytes); | ||||
| 	return *(float *)&temp; | ||||
| } | ||||
| 
 | ||||
| void double2bytes(double i, byte *bytes) | ||||
| { | ||||
| 	int64 temp = *(int64 *)&i; | ||||
| 	bigInt2bytes(temp, bytes); | ||||
| } | ||||
| 
 | ||||
| double bytes2double(byte *bytes) | ||||
| { | ||||
| 	int64 temp = bytes2bigInt(bytes); | ||||
| 	return *(double *)&temp; | ||||
| } | ||||
| 
 | ||||
| int str_to_int(const char *address) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	ret = (int)strtol(address, NULL, 10); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| // void str_toupper(char *input)
 | ||||
| // {
 | ||||
| // 	if (input == NULL)
 | ||||
| // 		return;
 | ||||
| 
 | ||||
| // 	int32 len = strlen(input), i = 0;
 | ||||
| // 	for (; i < len; i++)
 | ||||
| // 		input[i] = toupper(input[i]);
 | ||||
| // }
 | ||||
| 
 | ||||
| // void str_tolower(char *input)
 | ||||
| // {
 | ||||
| // 	if (input == NULL)
 | ||||
| // 		return;
 | ||||
| 
 | ||||
| // 	int32 len = strlen(input), i = 0;
 | ||||
| // 	for (; i < len; i++)
 | ||||
| // 		input[i] = tolower(input[i]);
 | ||||
| // }
 | ||||
| 
 | ||||
| /**
 | ||||
|  * ×Ö·û´®originÒÔ×Ö·û´®prefix¿ªÍ·£¬·µ»Ø0£»·ñÔò·µ»Ø1£»Òì³£·µ»Ø-1 | ||||
|  */ | ||||
| int str_start_with(const char *origin, char *prefix) | ||||
| { | ||||
| 	if (origin == NULL || | ||||
| 		prefix == NULL || | ||||
| 		strlen(prefix) > strlen(origin)) | ||||
| 	{ | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	int n = strlen(prefix), i; | ||||
| 	for (i = 0; i < n; i++) | ||||
| 	{ | ||||
| 		if (origin[i] != prefix[i]) | ||||
| 		{ | ||||
| 			return 1; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ×Ö·û´®originÒÔ×Ö·û´®end½á⣬·µ»Ø0£»·ñÔò·µ»Ø1£»Òì³£·µ»Ø-1 | ||||
|  */ | ||||
| int str_end_with(const char *origin, char *end) | ||||
| { | ||||
| 	if (origin == NULL || | ||||
| 		end == NULL || | ||||
| 		strlen(end) > strlen(origin)) | ||||
| 	{ | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	int n = strlen(end); | ||||
| 	int m = strlen(origin); | ||||
| 	int i; | ||||
| 	for (i = 0; i < n; i++) | ||||
| 	{ | ||||
| 		if (origin[m - i - 1] != end[n - i - 1]) | ||||
| 			return 1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| uint32 htonf_(float value) | ||||
| { | ||||
| 	uint32 Tempval; | ||||
| 	uint32 Retval; | ||||
| 	Tempval = *(uint32 *)(&value); | ||||
| 	Retval = _WS2_32_WINSOCK_SWAP_LONG(Tempval); | ||||
| 	return Retval; | ||||
| } | ||||
| 
 | ||||
| float ntohf_(uint32 value) | ||||
| { | ||||
| 	const uint32 Tempval = _WS2_32_WINSOCK_SWAP_LONG(value); | ||||
| 	float Retval; | ||||
| 	*((uint32 *)&Retval) = Tempval; | ||||
| 	return Retval; | ||||
| } | ||||
| 
 | ||||
| uint64 htond_(double value) | ||||
| { | ||||
| 	uint64 Tempval; | ||||
| 	uint64 Retval; | ||||
| 	Tempval = *(uint64 *)(&value); | ||||
| 	Retval = _WS2_32_WINSOCK_SWAP_LONGLONG(Tempval); | ||||
| 	return Retval; | ||||
| } | ||||
| 
 | ||||
| double ntohd_(uint64 value) | ||||
| { | ||||
| 	const uint64 Tempval = _WS2_32_WINSOCK_SWAP_LONGLONG(value); | ||||
| 	double Retval; | ||||
| 	*((uint64 *)&Retval) = Tempval; | ||||
| 	return Retval; | ||||
| } | ||||
| 
 | ||||
| uint64 htonll_(uint64 Value) | ||||
| { | ||||
| 	const uint64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG(Value); | ||||
| 	return Retval; | ||||
| } | ||||
| 
 | ||||
| uint64 ntohll_(uint64 Value) | ||||
| { | ||||
| 	const uint64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG(Value); | ||||
| 	return Retval; | ||||
| } | ||||
| 
 | ||||
| #ifndef _WIN32 | ||||
| /*
 | ||||
| ============= | ||||
| itoa | ||||
| 
 | ||||
| Convert integer to string | ||||
| 
 | ||||
| PARAMS: | ||||
| - value     A 64-bit number to convert | ||||
| - str       Destination buffer; should be 66 characters long for radix2, 24 - radix8, 22 - radix10, 18 - radix16. | ||||
| - radix     Radix must be in range -36 .. 36. Negative values used for signed numbers. | ||||
| ============= | ||||
| */ | ||||
| 
 | ||||
| char *cip_itoa(unsigned long long value, char str[], int radix) | ||||
| { | ||||
| 	char buf[66]; | ||||
| 	char *dest = buf + sizeof(buf); | ||||
| 	bool sign = false; | ||||
| 
 | ||||
| 	if (value == 0) | ||||
| 	{ | ||||
| 		memcpy(str, "0", 2); | ||||
| 		return str; | ||||
| 	} | ||||
| 
 | ||||
| 	if (radix < 0) | ||||
| 	{ | ||||
| 		radix = -radix; | ||||
| 		if ((long long)value < 0) | ||||
| 		{ | ||||
| 			value = -value; | ||||
| 			sign = true; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	*--dest = '\0'; | ||||
| 
 | ||||
| 	switch (radix) | ||||
| 	{ | ||||
| 	case 16: | ||||
| 		while (value) | ||||
| 		{ | ||||
| 			*--dest = '0' + (value & 0xF); | ||||
| 			if (*dest > '9') | ||||
| 				*dest += 'A' - '9' - 1; | ||||
| 			value >>= 4; | ||||
| 		} | ||||
| 		break; | ||||
| 	case 10: | ||||
| 		while (value) | ||||
| 		{ | ||||
| 			*--dest = '0' + (value % 10); | ||||
| 			value /= 10; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case 8: | ||||
| 		while (value) | ||||
| 		{ | ||||
| 			*--dest = '0' + (value & 7); | ||||
| 			value >>= 3; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case 2: | ||||
| 		while (value) | ||||
| 		{ | ||||
| 			*--dest = '0' + (value & 1); | ||||
| 			value >>= 1; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	default: // The slow version, but universal
 | ||||
| 		while (value) | ||||
| 		{ | ||||
| 			*--dest = '0' + (value % radix); | ||||
| 			if (*dest > '9') | ||||
| 				*dest += 'A' - '9' - 1; | ||||
| 			value /= radix; | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (sign) | ||||
| 		*--dest = '-'; | ||||
| 
 | ||||
| 	memcpy(str, dest, buf + sizeof(buf) - dest); | ||||
| 	return str; | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,58 @@ | |||
| #ifndef __UTILL_H__ | ||||
| #define __UTILL_H__ | ||||
| 
 | ||||
| #include "typedef.h" | ||||
| 
 | ||||
| typedef struct _tag_byte_array_info | ||||
| { | ||||
| 	byte *data; // 内容
 | ||||
| 	int length; // 长度
 | ||||
| } byte_array_info; | ||||
| 
 | ||||
| typedef struct _tag_bool_array_info | ||||
| { | ||||
| 	bool *data; // 内容
 | ||||
| 	int length; // 长度
 | ||||
| } bool_array_info; | ||||
| 
 | ||||
| void short2bytes(short i, byte *bytes); | ||||
| short bytes2short(byte *bytes); | ||||
| 
 | ||||
| void ushort2bytes(ushort i, byte *bytes); | ||||
| ushort bytes2ushort(byte *bytes); | ||||
| 
 | ||||
| void int2bytes(int32 i, byte *bytes); | ||||
| int32 bytes2int32(byte *bytes); | ||||
| 
 | ||||
| void uint2bytes(uint32 i, byte *bytes); | ||||
| uint32 bytes2uint32(byte *bytes); | ||||
| 
 | ||||
| void bigInt2bytes(int64 i, byte *bytes); | ||||
| int64 bytes2bigInt(byte *bytes); | ||||
| 
 | ||||
| void ubigInt2bytes(uint64 i, byte *bytes); | ||||
| uint64 bytes2ubigInt(byte *bytes); | ||||
| 
 | ||||
| void float2bytes(float i, byte *bytes); | ||||
| float bytes2float(byte *bytes); | ||||
| 
 | ||||
| void double2bytes(double i, byte *bytes); | ||||
| double bytes2double(byte *bytes); | ||||
| 
 | ||||
| int str_to_int(const char *address); | ||||
| void str_toupper(char *input); | ||||
| void str_tolower(char *input); | ||||
| int str_start_with(const char *origin, char *prefix); | ||||
| 
 | ||||
| uint32 htonf_(float value); | ||||
| float ntohf_(uint32 value); | ||||
| uint64 htond_(double value); | ||||
| double ntohd_(uint64 value); | ||||
| uint64 htonll_(uint64 Value); | ||||
| uint64 ntohll_(uint64 Value); | ||||
| 
 | ||||
| #ifndef _WIN32 | ||||
| char *cip_itoa(unsigned long long value, char str[], int radix); | ||||
| #endif // !_WIN32
 | ||||
| 
 | ||||
| #endif | ||||
|  | @ -420,7 +420,7 @@ int FinsProtocolFormatCmd(struct ControlRecipe *p_recipe, ProtocolFormatInfo *pr | |||
|         p_read_item_data); | ||||
| 
 | ||||
|     ControlPrintfList("CMD", fins_read_item->data_info.base_data_info.p_command, fins_read_item->data_info.base_data_info.command_length); | ||||
|     protocol_format_info->last_item_size = GetValueTypeMemorySize(fins_read_item->value_type); | ||||
|     protocol_format_info->last_item_size = GetValueTypeMemorySize(fins_read_item->value_type,1); | ||||
| 
 | ||||
|     last_item_size += protocol_format_info->last_item_size; | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,40 @@ | |||
| /*
 | ||||
| * Copyright (c) 2022 AIIT XUOS Lab | ||||
| * XiUOS is licensed under Mulan PSL v2. | ||||
| * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||||
| * You may obtain a copy of Mulan PSL v2 at: | ||||
| *        http://license.coscl.org.cn/MulanPSL2
 | ||||
| * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||||
| * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||||
| * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||||
| * See the Mulan PSL v2 for more details. | ||||
| */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @file cip.h | ||||
|  * @brief plc protocol cip | ||||
|  * @version 3.0 | ||||
|  * @author AIIT XUOS Lab | ||||
|  * @date 2022-10-08 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef Cip_H | ||||
| #define Cip_H | ||||
| 
 | ||||
| #include <control_def.h> | ||||
| 
 | ||||
| #define BASE_PLC_RECV_BUFF_SIZE 1024 | ||||
| 
 | ||||
| ControlProtocolType control_protocol; | ||||
| 
 | ||||
| int8_t ReadPlcDataByRecipe(struct ControlRecipe *p_recipe); | ||||
| void voidpush(uint8_t *datastack,uint8_t* args,uint16_t length); | ||||
| static uint8_t GetUniformValueTypeMemorySize(UniformValueType uniform_value_type); | ||||
| 
 | ||||
| typedef struct | ||||
| { | ||||
|     UniformValueType value_type; | ||||
|     char value_name[20]; | ||||
| }CIPReadItem; | ||||
| 
 | ||||
| #endif | ||||
|  | @ -810,7 +810,7 @@ int MelsecProtocolFormatCmd(struct ControlRecipe *p_recipe, ProtocolFormatInfo * | |||
|     ret = MelsecInitialDataInfo(melsec_read_item, p_read_item_data); | ||||
| 
 | ||||
|     ControlPrintfList("CMD", melsec_read_item->data_info.base_data_info.p_command, melsec_read_item->data_info.base_data_info.command_length); | ||||
|     protocol_format_info->last_item_size = GetValueTypeMemorySize(melsec_read_item->value_type); | ||||
|     protocol_format_info->last_item_size = GetValueTypeMemorySize(melsec_read_item->value_type,1); | ||||
| 
 | ||||
|     last_item_size += protocol_format_info->last_item_size; | ||||
| 
 | ||||
|  |  | |||
|  | @ -133,7 +133,7 @@ int8_t ReadPlcDataByRecipe(struct ControlRecipe *p_recipe) | |||
|                 } | ||||
|                 TS7DataItem data_info = ((S7ReadItem*)p_read_item + i)->data_info; | ||||
|                 Cli_ReadMultiVars(s7_plc, &data_info, 1); | ||||
|                 uint16_t Size = GetValueTypeMemorySize(((S7ReadItem*)p_read_item + i)->value_type); | ||||
|                 uint16_t Size = GetValueTypeMemorySize(((S7ReadItem*)p_read_item + i)->value_type,data_info.Amount); | ||||
|                 ControlPrintfList("S7 RECV", data_info.pdata,Size);           | ||||
|                 PushDataIntoStack(s7_data,data_info.pdata,Size); | ||||
|                 PrivTaskDelay(100); | ||||
|  | @ -196,7 +196,7 @@ static uint8_t InitialS7ReadItem(S7ReadItem* p_read_item, cJSON* read_item_json, | |||
|     printf("value_type is %d, amount is %d, start is %04d, db_number is %d, area is 0x%03x, wordlen is %d.\n", | ||||
|         p_read_item->value_type, p_data_info->Amount, p_data_info->Start, p_data_info->DBNumber, | ||||
|         p_data_info->Area, p_data_info->WordLen); | ||||
|     return GetValueTypeMemorySize(p_read_item->value_type); | ||||
|     return GetValueTypeMemorySize(p_read_item->value_type, p_data_info->Amount); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -55,6 +55,9 @@ typedef enum | |||
|     PROTOCOL_MELSEC_3E_IQ_R, | ||||
|     PROTOCOL_MELSEC_1C, | ||||
|     PROTOCOL_MELSEC_3C, | ||||
|     PROTOCOL_FREEMODBUS_TCP_SERVER, | ||||
|     PROTOCOL_CIP, | ||||
|     PROTOCOL_ETHERCAT, | ||||
|     PROTOCOL_END | ||||
| }ProtocolType; | ||||
| 
 | ||||
|  | @ -114,6 +117,7 @@ int ControlProtocolIoctl(struct ControlProtocol *control_protocol, int cmd, void | |||
| /*Control Framework new certain Protocol*/ | ||||
| ControlProtocolType control_protocol; | ||||
| 
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  |  | |||
|  | @ -49,6 +49,18 @@ extern int ModbusUartProtocolInit(struct ControlRecipe *p_recipe); | |||
| extern int S7ProtocolInit(struct ControlRecipe *p_recipe); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONTROL_PROTOCOL_FREEMODBUS_TCP_SERVER | ||||
| extern int FreeModbusTcpServerInit(struct ControlRecipe *p_recipe); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONTROL_PROTOCOL_CIP | ||||
| extern int CipProtocolInit(struct ControlRecipe *p_recipe); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONTROL_PROTOCOL_ETHERCAT | ||||
| extern int EthercatProtocolInit(struct ControlRecipe *p_recipe); | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
| CONTROL FRAMEWORK READ DATA FORMAT: | ||||
| |  HEAD |device_id|read data length|read item count|         data         | | ||||
|  | @ -88,6 +100,18 @@ static struct ControlProtocolInitParam protocol_init[] = | |||
|     { PROTOCOL_S7, S7ProtocolInit }, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONTROL_PROTOCOL_FREEMODBUS_TCP_SERVER | ||||
|     { PROTOCOL_FREEMODBUS_TCP_SERVER, FreeModbusTcpServerInit }, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONTROL_PROTOCOL_CIP | ||||
|     { PROTOCOL_CIP, CipProtocolInit }, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONTROL_PROTOCOL_ETHERCAT | ||||
|     { PROTOCOL_ETHERCAT, EthercatProtocolInit }, | ||||
| #endif | ||||
| 
 | ||||
| 	{ PROTOCOL_END, NULL }, | ||||
| }; | ||||
| 
 | ||||
|  | @ -143,7 +167,8 @@ static uint16_t GetRecipeTotalDataLength(cJSON* read_item_list_json) | |||
|     for (uint16_t read_item_index = 0; read_item_index < read_item_count; read_item_index++) { | ||||
|         cJSON* read_item_json = cJSON_GetArrayItem(read_item_list_json, read_item_index); | ||||
|         UniformValueType value_type = cJSON_GetObjectItem(read_item_json, "value_type")->valueint; | ||||
|         total_data_length += GetValueTypeMemorySize(value_type); | ||||
|         int value_num = cJSON_GetObjectItem(read_item_json, "amount")->valueint; | ||||
|         total_data_length += GetValueTypeMemorySize(value_type, value_num); | ||||
|     } | ||||
|     return total_data_length; | ||||
| } | ||||
|  | @ -320,7 +345,7 @@ int ControlProtocolOpenDef(struct ControlProtocol *control_protocol) | |||
| 
 | ||||
|     pthread_attr_t attr; | ||||
|     attr.schedparam.sched_priority = 19; | ||||
|     attr.stacksize = 2048; | ||||
|     attr.stacksize = 4096; | ||||
| 
 | ||||
|     char task_name[] = "control_recv_data"; | ||||
|     pthread_args_t args; | ||||
|  | @ -350,26 +375,26 @@ int ControlProtocolCloseDef(void) | |||
|  * @param uniform_value_type - uniform value type | ||||
|  * @return success : size error : 0 | ||||
|  */ | ||||
| uint8_t GetValueTypeMemorySize(UniformValueType uniform_value_type) | ||||
| uint8_t GetValueTypeMemorySize(UniformValueType uniform_value_type,int value_num) | ||||
| { | ||||
|     switch (uniform_value_type) | ||||
|     { | ||||
|     case UNIFORM_BOOL: | ||||
|     case UNIFORM_INT8: | ||||
|     case UNIFORM_UINT8: | ||||
|         return 1; | ||||
|         return 1* value_num; | ||||
|         break; | ||||
|     case UNIFORM_INT16: | ||||
|     case UNIFORM_UINT16: | ||||
|         return 2; | ||||
|         return 2* value_num; | ||||
|         break; | ||||
|     case UNIFORM_INT32: | ||||
|     case UNIFORM_UINT32: | ||||
|     case UNIFORM_FLOAT: | ||||
|         return 4; | ||||
|         return 4* value_num; | ||||
|         break; | ||||
|     case UNIFORM_DOUBLE: | ||||
|         return 8; | ||||
|         return 8* value_num; | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|  |  | |||
|  | @ -130,7 +130,7 @@ struct ControlRecipe | |||
| }; | ||||
| 
 | ||||
| /*Get Value Type Memory Size*/ | ||||
| uint8_t GetValueTypeMemorySize(UniformValueType uniform_value_type); | ||||
| uint8_t GetValueTypeMemorySize(UniformValueType uniform_value_type,int value_num); | ||||
| 
 | ||||
| /*Get basic information from recipe file*/ | ||||
| int RecipeBasicInformation(struct ControlRecipe *p_recipe, cJSON *p_recipe_file_json); | ||||
|  |  | |||
|  | @ -30,4 +30,8 @@ ifeq ($(CONFIG_LIB_USING_SQLITE),y) | |||
|   SRC_DIR += SQLite | ||||
| endif | ||||
| 
 | ||||
| # ifeq ($(CONFIG_LIB_USING_FREEMODBUS),y)
 | ||||
| # 	SRC_DIR += freemodbus
 | ||||
| # endif
 | ||||
| 
 | ||||
| include $(KERNEL_ROOT)/compiler.mk | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ endif | |||
| 
 | ||||
| ifeq ($(CONFIG_SUPPORT_CONNECTION_FRAMEWORK), y) | ||||
| APPPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/connection # | ||||
| APPPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/connection/industrial_network/freemodbus_tcp # | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(CONFIG_SUPPORT_KNOWING_FRAMEWORK), y) | ||||
|  | @ -35,6 +36,7 @@ APPPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control # | |||
| APPPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc/interoperability/opcua # | ||||
| endif | ||||
| 
 | ||||
| 
 | ||||
| ifeq ($(CONFIG_CRYPTO), y) | ||||
| APPPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/security/crypto/include # | ||||
| endif | ||||
|  |  | |||
|  | @ -555,6 +555,7 @@ endif | |||
| ifeq ($(CONFIG_SUPPORT_CONNECTION_FRAMEWORK), y) | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/connection # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/connection/zigbee # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/connection/industrial_network/freemodbus_tcp # | ||||
| 
 | ||||
| ifeq ($(CONFIG_CONNECTION_MODBUS), y) | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/connection/industrial_fieldbus/modbus/freemodbus-latest/modbus/include # | ||||
|  | @ -609,18 +610,27 @@ KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/ipc_protoc | |||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/ipc_protocol/include # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/ipc_protocol/modbus_tcp # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/ipc_protocol/modbus_uart # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/ipc_protocol/ethercat # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/ipc_protocol/ethercat/comm # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/ipc_protocol/ethercat/soem # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc_protocol # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc_protocol/include # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc_protocol/fins # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc_protocol/melsec # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc_protocol/opcua # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc_protocol/s7 # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/ipc_protocol/freemodbustcpserver # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc_protocol/cip # | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(CONFIG_LIB_USING_CJSON), y) | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/lib/cJSON | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(CONFIG_LIB_USING_FREEMODBUS), y) | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/lib/freemodbus | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(CONFIG_LIB_USING_SQLITE), y) | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/lib/SQLite # | ||||
| KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/lib/SQLite/xizi_port # | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue