diff --git a/APP_Framework/Applications/app_test/test_can.c b/APP_Framework/Applications/app_test/test_can.c index c6463bc27..d0f3453e0 100644 --- a/APP_Framework/Applications/app_test/test_can.c +++ b/APP_Framework/Applications/app_test/test_can.c @@ -18,6 +18,7 @@ * @date: 2023/2/17 */ #include +#include #include #include #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 diff --git a/APP_Framework/Applications/connection_app/Makefile b/APP_Framework/Applications/connection_app/Makefile index 8aa357308..11c22f202 100755 --- a/APP_Framework/Applications/connection_app/Makefile +++ b/APP_Framework/Applications/connection_app/Makefile @@ -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 diff --git a/APP_Framework/Applications/connection_app/freemodbus_tcp_slave/Makefile b/APP_Framework/Applications/connection_app/freemodbus_tcp_slave/Makefile new file mode 100644 index 000000000..849075937 --- /dev/null +++ b/APP_Framework/Applications/connection_app/freemodbus_tcp_slave/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := tcpserver_sample.c + +include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Applications/connection_app/freemodbus_tcp_slave/slave_sample.c b/APP_Framework/Applications/connection_app/freemodbus_tcp_slave/slave_sample.c new file mode 100644 index 000000000..47e122e99 --- /dev/null +++ b/APP_Framework/Applications/connection_app/freemodbus_tcp_slave/slave_sample.c @@ -0,0 +1,259 @@ +/* + * FreeModbus Libary: Win32 Demo Application + * Copyright (C) 2006 Christian Walter + * + * 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 + ***********************************************************/ + +/* ----------------------- Standard C Libs includes --------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#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= 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; +} diff --git a/APP_Framework/Applications/control_app/plc_demo/Makefile b/APP_Framework/Applications/control_app/plc_demo/Makefile index 747b6d28b..3fba029fa 100755 --- a/APP_Framework/Applications/control_app/plc_demo/Makefile +++ b/APP_Framework/Applications/control_app/plc_demo/Makefile @@ -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 diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/Makefile b/APP_Framework/Applications/control_app/plc_demo/ab/Makefile new file mode 100755 index 000000000..6d8383b89 --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/ab/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := ab_l30erm.c ab_micro850.c + +include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/README_ab_850.md b/APP_Framework/Applications/control_app/plc_demo/ab/README_ab_850.md new file mode 100755 index 000000000..329ca65ae --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/ab/README_ab_850.md @@ -0,0 +1,91 @@ +# AB_850通信测试 + +[TOC] + +## 通信接线及参数设置 + +* 网口 + + *Mosbus TCP协议,IP:192.168.250.56,Port:502 + +## 存储区 + +- 存储区D区 + +## JSON配方设计 + +* AB_850类型PLC需要配置控制器映射 + + ![](./image/modbus映射.png) + +* 共测试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) 剪裁配置完成后,用过烧写器下载至矽数通中,重启后完成测试。 + + + + diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/README_ab_l300erm.md b/APP_Framework/Applications/control_app/plc_demo/ab/README_ab_l300erm.md new file mode 100755 index 000000000..7f814d573 --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/ab/README_ab_l300erm.md @@ -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) 剪裁配置完成后,用过烧写器下载至矽数通中,重启后完成测试。 + + + + diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/ab_l30erm.c b/APP_Framework/Applications/control_app/plc_demo/ab/ab_l30erm.c new file mode 100644 index 000000000..65c56d780 --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/ab/ab_l30erm.c @@ -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 + +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); diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/ab_micro850.c b/APP_Framework/Applications/control_app/plc_demo/ab/ab_micro850.c new file mode 100644 index 000000000..5ea00ac0e --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/ab/ab_micro850.c @@ -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 + +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); \ No newline at end of file diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/image/L30ERM.jpg b/APP_Framework/Applications/control_app/plc_demo/ab/image/L30ERM.jpg new file mode 100644 index 000000000..db7e41e18 Binary files /dev/null and b/APP_Framework/Applications/control_app/plc_demo/ab/image/L30ERM.jpg differ diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/image/MICRO850.jpg b/APP_Framework/Applications/control_app/plc_demo/ab/image/MICRO850.jpg new file mode 100644 index 000000000..baebe73e6 Binary files /dev/null and b/APP_Framework/Applications/control_app/plc_demo/ab/image/MICRO850.jpg differ diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/image/modbus映射.png b/APP_Framework/Applications/control_app/plc_demo/ab/image/modbus映射.png new file mode 100644 index 000000000..7cbf50b67 Binary files /dev/null and b/APP_Framework/Applications/control_app/plc_demo/ab/image/modbus映射.png differ diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/json/test_recipe_L30.json b/APP_Framework/Applications/control_app/plc_demo/ab/json/test_recipe_L30.json new file mode 100644 index 000000000..852788b5d --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/ab/json/test_recipe_L30.json @@ -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 + } + ] +} \ No newline at end of file diff --git a/APP_Framework/Applications/control_app/plc_demo/ab/json/test_recipe_ab_850.json b/APP_Framework/Applications/control_app/plc_demo/ab/json/test_recipe_ab_850.json new file mode 100644 index 000000000..c051d2321 --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/ab/json/test_recipe_ab_850.json @@ -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 + } + ] +} \ No newline at end of file diff --git a/APP_Framework/Applications/control_app/plc_demo/abb/Makefile b/APP_Framework/Applications/control_app/plc_demo/abb/Makefile new file mode 100755 index 000000000..3ef527c15 --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/abb/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := abb_pm5630.c + +include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Applications/control_app/plc_demo/abb/README.md b/APP_Framework/Applications/control_app/plc_demo/abb/README.md new file mode 100644 index 000000000..21b3bbff2 --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/abb/README.md @@ -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) 剪裁配置完成后,用过烧写器下载至矽数通中,重启后完成测试。 + + + + diff --git a/APP_Framework/Applications/control_app/plc_demo/abb/abb_pm5630.c b/APP_Framework/Applications/control_app/plc_demo/abb/abb_pm5630.c new file mode 100644 index 000000000..b7c50e152 --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/abb/abb_pm5630.c @@ -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 + +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); \ No newline at end of file diff --git a/APP_Framework/Applications/control_app/plc_demo/abb/image/ABB_PM5630.jpg b/APP_Framework/Applications/control_app/plc_demo/abb/image/ABB_PM5630.jpg new file mode 100644 index 000000000..1b7ffcea6 Binary files /dev/null and b/APP_Framework/Applications/control_app/plc_demo/abb/image/ABB_PM5630.jpg differ diff --git a/APP_Framework/Applications/control_app/plc_demo/abb/json/test_recipe_abb_PM5630.json b/APP_Framework/Applications/control_app/plc_demo/abb/json/test_recipe_abb_PM5630.json new file mode 100644 index 000000000..f41e5cfac --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/abb/json/test_recipe_abb_PM5630.json @@ -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 + } + ] +} \ No newline at end of file diff --git a/APP_Framework/Framework/connection/industrial_network/Kconfig b/APP_Framework/Framework/connection/industrial_network/Kconfig index 6a1486e30..23d69660a 100644 --- a/APP_Framework/Framework/connection/industrial_network/Kconfig +++ b/APP_Framework/Framework/connection/industrial_network/Kconfig @@ -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 diff --git a/APP_Framework/Framework/connection/industrial_network/Makefile b/APP_Framework/Framework/connection/industrial_network/Makefile index 89b0289c9..2196c6baa 100644 --- a/APP_Framework/Framework/connection/industrial_network/Makefile +++ b/APP_Framework/Framework/connection/industrial_network/Makefile @@ -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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/Kconfig b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/Kconfig new file mode 100755 index 000000000..e69de29bb diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/Makefile b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/Makefile new file mode 100755 index 000000000..a72b1a41e --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := $(wildcard ./*.c) + +include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mb.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mb.c new file mode 100644 index 000000000..5d75b9218 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mb.c @@ -0,0 +1,411 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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; +} diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mb.h b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mb.h new file mode 100644 index 000000000..c375803e0 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mb.h @@ -0,0 +1,416 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 Report Slave ID + * is enabled ( By defining MB_FUNC_OTHER_REP_SLAVEID_ENABLED in mbconfig.h ). + * + * \param ucSlaveID Values is returned in the Slave ID byte of the + * Report Slave ID response. + * \param xIsRunning If TRUE the Run Indicator Status byte is set to 0xFF. + * otherwise the Run Indicator Status is 0x00. + * \param pucAdditional Values which should be returned in the Additional + * bytes of the Report Slave ID response. + * \param usAdditionalLen Length of the buffer pucAdditonal. + * + * \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.
+ * 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.
+ * 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 Input Register + * is required by the protocol stack. The starting register address is given + * by \c usAddress and the last register is given by usAddress + + * usNRegs - 1. + * + * \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 + * ILLEGAL DATA ADDRESS 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 SLAVE DEVICE BUSY + * exception is sent as a response. + * - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case + * a SLAVE DEVICE FAILURE exception is sent as a response. + */ +eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, + USHORT usNRegs ); + +/*! \ingroup modbus_registers + * \brief Callback function used if a Holding Register 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 + * usAddress + usNRegs - 1. + * + * \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 + * WRITE SINGLE REGISTER 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 + * ILLEGAL DATA ADDRESS 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 SLAVE DEVICE BUSY + * exception is sent as a response. + * - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case + * a SLAVE DEVICE FAILURE 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 Coil Register 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 pucRegBuffer. + * 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 + * ILLEGAL DATA ADDRESS 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 SLAVE DEVICE BUSY + * exception is sent as a response. + * - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case + * a SLAVE DEVICE FAILURE 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 Input Discrete Register 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 ILLEGAL DATA ADDRESS 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 SLAVE DEVICE BUSY + * exception is sent as a response. + * - eMBErrorCode::MB_EIO If an unrecoverable error occurred. In this case + * a SLAVE DEVICE FAILURE exception is sent as a response. + */ +eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, + USHORT usNDiscrete ); + +#ifdef __cplusplus +PR_END_EXTERN_C +#endif +#endif diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbconfig.h b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbconfig.h new file mode 100644 index 000000000..17d4871fb --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbconfig.h @@ -0,0 +1,131 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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.
+ * + * All of these settings are available in the file mbconfig.h + */ +/*! \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 Report Slave ID + * 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 1. + */ +#define MB_FUNC_OTHER_REP_SLAVEID_BUF ( 32 ) + +/*! \brief If the Report Slave ID function should be enabled. */ +#define MB_FUNC_OTHER_REP_SLAVEID_ENABLED ( 1 ) + +/*! \brief If the Read Input Registers function should be enabled. */ +#define MB_FUNC_READ_INPUT_ENABLED ( 1 ) + +/*! \brief If the Read Holding Registers function should be enabled. */ +#define MB_FUNC_READ_HOLDING_ENABLED ( 1 ) + +/*! \brief If the Write Single Register function should be enabled. */ +#define MB_FUNC_WRITE_HOLDING_ENABLED ( 1 ) + +/*! \brief If the Write Multiple registers function should be enabled. */ +#define MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED ( 1 ) + +/*! \brief If the Read Coils function should be enabled. */ +#define MB_FUNC_READ_COILS_ENABLED ( 1 ) + +/*! \brief If the Write Coils function should be enabled. */ +#define MB_FUNC_WRITE_COIL_ENABLED ( 1 ) + +/*! \brief If the Write Multiple Coils function should be enabled. */ +#define MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED ( 1 ) + +/*! \brief If the Read Discrete Inputs function should be enabled. */ +#define MB_FUNC_READ_DISCRETE_INPUTS_ENABLED ( 1 ) + +/*! \brief If the Read/Write Multiple Registers function should be enabled. */ +#define MB_FUNC_READWRITE_HOLDING_ENABLED ( 1 ) + +/*! @} */ +#ifdef __cplusplus + PR_END_EXTERN_C +#endif +#endif diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbframe.h b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbframe.h new file mode 100644 index 000000000..189ece5f3 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbframe.h @@ -0,0 +1,86 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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. + * + * + * <------------------------ 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 + * + */ + +/* ----------------------- 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfunc.h b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfunc.h new file mode 100644 index 000000000..716bf95d7 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfunc.h @@ -0,0 +1,79 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfunccoils.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfunccoils.c new file mode 100644 index 000000000..b19ad7cbc --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfunccoils.c @@ -0,0 +1,269 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncdiag.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncdiag.c new file mode 100644 index 000000000..50d6d2ee8 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncdiag.c @@ -0,0 +1,28 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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. + * + */ diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncdisc.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncdisc.c new file mode 100644 index 000000000..0a68309eb --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncdisc.c @@ -0,0 +1,133 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncholding.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncholding.c new file mode 100644 index 000000000..1625ce4da --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncholding.c @@ -0,0 +1,307 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncinput.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncinput.c new file mode 100644 index 000000000..284c7b085 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncinput.c @@ -0,0 +1,121 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncother.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncother.c new file mode 100644 index 000000000..47162130f --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbfuncother.c @@ -0,0 +1,87 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbport.h b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbport.h new file mode 100644 index 000000000..050ec2c59 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbport.h @@ -0,0 +1,128 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 TRUE 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbproto.h b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbproto.h new file mode 100644 index 000000000..3695a1b9f --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbproto.h @@ -0,0 +1,82 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbtcp.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbtcp.c new file mode 100644 index 000000000..ea0704427 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbtcp.c @@ -0,0 +1,157 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbtcp.h b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbtcp.h new file mode 100644 index 000000000..4e5cfcdb8 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbtcp.h @@ -0,0 +1,52 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbutils.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbutils.c new file mode 100644 index 000000000..e43568742 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbutils.c @@ -0,0 +1,140 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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; +} diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbutils.h b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbutils.h new file mode 100644 index 000000000..71043d706 --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/mbutils.h @@ -0,0 +1,107 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006-2018 Christian Walter + * 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 usBitOffset is the LSB of the value + * ucValues + * + * \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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/port.h b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/port.h new file mode 100644 index 000000000..a25a7592f --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/port.h @@ -0,0 +1,83 @@ +/* + * FreeModbus Libary: BSD Socket Library Port + * Copyright (C) 2006 Christian Walter + * + * 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 + ***********************************************************/ + +#ifndef _PORT_H +#define _PORT_H + + +#include + +#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 diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/portevent.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/portevent.c new file mode 100644 index 000000000..b56fa4f0e --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/portevent.c @@ -0,0 +1,72 @@ +/* + * FreeModbus Libary: Win32 Port + * Copyright (C) 2006 Christian Walter + * + * 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 + ***********************************************************/ + +/* ----------------------- 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; +} diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/portother.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/portother.c new file mode 100644 index 000000000..70ec43bbf --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/portother.c @@ -0,0 +1,69 @@ +/* + * FreeModbus Libary: Win32 Port + * Copyright (C) 2006 Christian Walter + * + * 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 + ***********************************************************/ + +#include +#include +#include + +#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 ); +} diff --git a/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/porttcp.c b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/porttcp.c new file mode 100644 index 000000000..d32950d2b --- /dev/null +++ b/APP_Framework/Framework/connection/industrial_network/freemodbus_tcp/porttcp.c @@ -0,0 +1,367 @@ +/* + * FreeModbus Libary: Win32 Port + * Copyright (C) 2006 Christian Walter + * + * 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 + ***********************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/APP_Framework/Framework/control/ipc_protocol/Kconfig b/APP_Framework/Framework/control/ipc_protocol/Kconfig old mode 100755 new mode 100644 index 55c7718f1..79880ef8a --- a/APP_Framework/Framework/control/ipc_protocol/Kconfig +++ b/APP_Framework/Framework/control/ipc_protocol/Kconfig @@ -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 + + diff --git a/APP_Framework/Framework/control/ipc_protocol/Makefile b/APP_Framework/Framework/control/ipc_protocol/Makefile index a22240e9a..d0459bceb 100755 --- a/APP_Framework/Framework/control/ipc_protocol/Makefile +++ b/APP_Framework/Framework/control/ipc_protocol/Makefile @@ -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 diff --git a/APP_Framework/Framework/control/ipc_protocol/modbus_tcp/modbus_tcp.c b/APP_Framework/Framework/control/ipc_protocol/modbus_tcp/modbus_tcp.c index 6babdf9a5..e3c2077cb 100755 --- a/APP_Framework/Framework/control/ipc_protocol/modbus_tcp/modbus_tcp.c +++ b/APP_Framework/Framework/control/ipc_protocol/modbus_tcp/modbus_tcp.c @@ -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; diff --git a/APP_Framework/Framework/control/ipc_protocol/modbus_uart/modbus_uart.c b/APP_Framework/Framework/control/ipc_protocol/modbus_uart/modbus_uart.c index f8e41c3b0..892f7e62d 100644 --- a/APP_Framework/Framework/control/ipc_protocol/modbus_uart/modbus_uart.c +++ b/APP_Framework/Framework/control/ipc_protocol/modbus_uart/modbus_uart.c @@ -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; diff --git a/APP_Framework/Framework/control/plc_protocol/Kconfig b/APP_Framework/Framework/control/plc_protocol/Kconfig index 315d9600f..0e7c20377 100755 --- a/APP_Framework/Framework/control/plc_protocol/Kconfig +++ b/APP_Framework/Framework/control/plc_protocol/Kconfig @@ -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 + diff --git a/APP_Framework/Framework/control/plc_protocol/Makefile b/APP_Framework/Framework/control/plc_protocol/Makefile index 66e467295..250a715e9 100755 --- a/APP_Framework/Framework/control/plc_protocol/Makefile +++ b/APP_Framework/Framework/control/plc_protocol/Makefile @@ -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 diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/Kconfig b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/Kconfig new file mode 100644 index 000000000..e69de29bb diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/Makefile b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/Makefile new file mode 100644 index 000000000..a72b1a41e --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := $(wildcard ./*.c) + +include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip.c b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip.c new file mode 100644 index 000000000..a22f61c1b --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip.c @@ -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 +#include +#include +#include +#ifdef _WIN32 +#include +#include +#include +#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ +#pragma warning(disable : 4996) +#else +#include +// #include +// #include +#include +#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; +} \ No newline at end of file diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip.h b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip.h new file mode 100644 index 000000000..28f773b9b --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip.h @@ -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__ \ No newline at end of file diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip_helper.c b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip_helper.c new file mode 100644 index 000000000..bbe9b20f5 --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip_helper.c @@ -0,0 +1,333 @@ +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip_helper.h b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip_helper.h new file mode 100644 index 000000000..0daa2c38e --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip_helper.h @@ -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__ \ No newline at end of file diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip_private.h b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip_private.h new file mode 100644 index 000000000..605cf7be2 --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/ab_cip_private.h @@ -0,0 +1,27 @@ +#ifndef __H_AB_CIP_PRIVATE_H__ +#define __H_AB_CIP_PRIVATE_H__ +#include "typedef.h" + +/// +/// 注册命令 +/// +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__ diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/cip.c b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/cip.c new file mode 100644 index 000000000..6162bdad8 --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/cip.c @@ -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 +#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; +} diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/cip_socket.c b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/cip_socket.c new file mode 100644 index 000000000..9b4b7d08b --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/cip_socket.c @@ -0,0 +1,174 @@ +#include "cip_socket.h" +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ +#else +#include +#include +// #include +#include +// #include +#include "lwip/inet.h" +#include +#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); +} \ No newline at end of file diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/cip_socket.h b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/cip_socket.h new file mode 100644 index 000000000..9f2e70f69 --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/cip_socket.h @@ -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_ diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/typedef.h b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/typedef.h new file mode 100644 index 000000000..f8f72d30b --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/typedef.h @@ -0,0 +1,21 @@ +#ifndef __H_TYPEDEF_H__ +#define __H_TYPEDEF_H__ + +#include +#include + +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__ \ No newline at end of file diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/utill.c b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/utill.c new file mode 100644 index 000000000..bce6d6367 --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/utill.c @@ -0,0 +1,383 @@ +#include "utill.h" +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#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 \ No newline at end of file diff --git a/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/utill.h b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/utill.h new file mode 100644 index 000000000..b3d5cef63 --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/ethernet_ip_cip/utill.h @@ -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 diff --git a/APP_Framework/Framework/control/plc_protocol/fins/fins.c b/APP_Framework/Framework/control/plc_protocol/fins/fins.c index aef282046..dddef725b 100644 --- a/APP_Framework/Framework/control/plc_protocol/fins/fins.c +++ b/APP_Framework/Framework/control/plc_protocol/fins/fins.c @@ -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; diff --git a/APP_Framework/Framework/control/plc_protocol/include/cip.h b/APP_Framework/Framework/control/plc_protocol/include/cip.h new file mode 100644 index 000000000..e56eb5109 --- /dev/null +++ b/APP_Framework/Framework/control/plc_protocol/include/cip.h @@ -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 + +#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 \ No newline at end of file diff --git a/APP_Framework/Framework/control/plc_protocol/melsec/melsec.c b/APP_Framework/Framework/control/plc_protocol/melsec/melsec.c index 01a25420f..f5a0f5ac9 100644 --- a/APP_Framework/Framework/control/plc_protocol/melsec/melsec.c +++ b/APP_Framework/Framework/control/plc_protocol/melsec/melsec.c @@ -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; diff --git a/APP_Framework/Framework/control/plc_protocol/s7/s7.c b/APP_Framework/Framework/control/plc_protocol/s7/s7.c index 900fd379b..3549e6f78 100644 --- a/APP_Framework/Framework/control/plc_protocol/s7/s7.c +++ b/APP_Framework/Framework/control/plc_protocol/s7/s7.c @@ -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); } /** diff --git a/APP_Framework/Framework/control/shared/control.h b/APP_Framework/Framework/control/shared/control.h index de9beac60..7cbba18f9 100644 --- a/APP_Framework/Framework/control/shared/control.h +++ b/APP_Framework/Framework/control/shared/control.h @@ -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 diff --git a/APP_Framework/Framework/control/shared/control_def.c b/APP_Framework/Framework/control/shared/control_def.c index 79fb251fc..d3f9e9055 100644 --- a/APP_Framework/Framework/control/shared/control_def.c +++ b/APP_Framework/Framework/control/shared/control_def.c @@ -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; diff --git a/APP_Framework/Framework/control/shared/control_def.h b/APP_Framework/Framework/control/shared/control_def.h index dec7a189a..d3e067c9c 100644 --- a/APP_Framework/Framework/control/shared/control_def.h +++ b/APP_Framework/Framework/control/shared/control_def.h @@ -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); diff --git a/APP_Framework/lib/Makefile b/APP_Framework/lib/Makefile index 6703cb6f1..22037bf20 100644 --- a/APP_Framework/lib/Makefile +++ b/APP_Framework/lib/Makefile @@ -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 diff --git a/Ubiquitous/XiZi_IIoT/path_app.mk b/Ubiquitous/XiZi_IIoT/path_app.mk index d54aea191..3a7b9d9d5 100644 --- a/Ubiquitous/XiZi_IIoT/path_app.mk +++ b/Ubiquitous/XiZi_IIoT/path_app.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 diff --git a/Ubiquitous/XiZi_IIoT/path_kernel.mk b/Ubiquitous/XiZi_IIoT/path_kernel.mk index da5ec9324..5eb4be7b8 100755 --- a/Ubiquitous/XiZi_IIoT/path_kernel.mk +++ b/Ubiquitous/XiZi_IIoT/path_kernel.mk @@ -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 #