no message
This commit is contained in:
parent
af649dec3f
commit
4be1252744
@ -34,4 +34,5 @@
|
||||
#include "win32/scoped_win_handle.h"
|
||||
#include "win32/security_util.h"
|
||||
#include "win32/shared_memory.h"
|
||||
#include "win32/win_util.h"
|
||||
#include "win32/win_util.h"
|
||||
|
||||
|
@ -31,6 +31,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ui_components", "..\ui_comp
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "proto_debuger", "proto_debuger\proto_debuger.vcxproj", "{6D78D8B7-1617-4ED4-B155-94369FC3E73C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmodbus", "libmodbus\libmodbus.vcxproj", "{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
@ -133,6 +135,14 @@ Global
|
||||
{6D78D8B7-1617-4ED4-B155-94369FC3E73C}.Release|Win32.Build.0 = Release|Win32
|
||||
{6D78D8B7-1617-4ED4-B155-94369FC3E73C}.Release|x64.ActiveCfg = Release|x64
|
||||
{6D78D8B7-1617-4ED4-B155-94369FC3E73C}.Release|x64.Build.0 = Release|x64
|
||||
{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}.Debug|x64.Build.0 = Debug|x64
|
||||
{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}.Release|Win32.Build.0 = Release|Win32
|
||||
{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}.Release|x64.ActiveCfg = Release|x64
|
||||
{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
164
examples/libmodbus/config.h
Normal file
164
examples/libmodbus/config.h
Normal file
@ -0,0 +1,164 @@
|
||||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||
/* #undef HAVE_ARPA_INET_H */
|
||||
|
||||
/* Define to 1 if you have the declaration of `TIOCSRS485', and to 0 if you
|
||||
don't. */
|
||||
/* #undef HAVE_DECL_TIOCSRS485 */
|
||||
|
||||
/* Define to 1 if you have the declaration of `__CYGWIN__', and to 0 if you
|
||||
don't. */
|
||||
/* #undef HAVE_DECL___CYGWIN__ */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
/* #undef HAVE_DLFCN_H */
|
||||
|
||||
/* Define to 1 if you have the <errno.h> header file. */
|
||||
#define HAVE_ERRNO_H 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if you have the `fork' function. */
|
||||
/* #undef HAVE_FORK */
|
||||
|
||||
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||
/* #undef HAVE_GETADDRINFO */
|
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */
|
||||
/* #undef HAVE_GETTIMEOFDAY */
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <limits.h> header file. */
|
||||
#define HAVE_LIMITS_H 1
|
||||
|
||||
/* Define to 1 if you have the <linux/serial.h> header file. */
|
||||
/* #undef HAVE_LINUX_SERIAL_H */
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the `memset' function. */
|
||||
#define HAVE_MEMSET 1
|
||||
|
||||
/* Define to 1 if you have the <netdb.h> header file. */
|
||||
/* #undef HAVE_NETDB_H */
|
||||
|
||||
/* Define to 1 if you have the <netinet/in.h> header file. */
|
||||
/* #undef HAVE_NETINET_IN_H */
|
||||
|
||||
/* Define to 1 if you have the <netinet/tcp.h> header file. */
|
||||
/* #undef HAVE_NETINET_TCP_H */
|
||||
|
||||
/* Define to 1 if you have the `select' function. */
|
||||
/* #undef HAVE_SELECT */
|
||||
|
||||
/* Define to 1 if you have the `socket' function. */
|
||||
/* #undef HAVE_SOCKET */
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the `strerror' function. */
|
||||
#define HAVE_STRERROR 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
/* #undef HAVE_STRINGS_H */
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strlcpy' function. */
|
||||
/* #undef HAVE_STRLCPY */
|
||||
|
||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||
/* #undef HAVE_SYS_IOCTL_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
/* #undef HAVE_SYS_SOCKET_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
/* #undef HAVE_SYS_TIME_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <termios.h> header file. */
|
||||
/* #undef HAVE_TERMIOS_H */
|
||||
|
||||
/* Define to 1 if you have the <time.h> header file. */
|
||||
#define HAVE_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
/* #undef HAVE_UNISTD_H */
|
||||
|
||||
/* Define to 1 if you have the `vfork' function. */
|
||||
/* #undef HAVE_VFORK */
|
||||
|
||||
/* Define to 1 if you have the <vfork.h> header file. */
|
||||
/* #undef HAVE_VFORK_H */
|
||||
|
||||
/* Define to 1 if you have the <winsock2.h> header file. */
|
||||
#define HAVE_WINSOCK2_H 1
|
||||
|
||||
/* Define to 1 if `fork' works. */
|
||||
/* #undef HAVE_WORKING_FORK */
|
||||
|
||||
/* Define to 1 if `vfork' works. */
|
||||
/* #undef HAVE_WORKING_VFORK */
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
/* #undef LT_OBJDIR */
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "libmodbus"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "https://github.com/stephane/libmodbus/issues"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "libmodbus"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "libmodbus @LIBMODBUS_VERSION@"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "libmodbus"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "@LIBMODBUS_VERSION@"
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
/* #undef TIME_WITH_SYS_TIME */
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "@LIBMODBUS_VERSION@"
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
/* #undef pid_t */
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
/* #undef size_t */
|
||||
|
||||
/* Define as `fork' if `vfork' does not work. */
|
||||
#define vfork fork
|
BIN
examples/libmodbus/libmodbus-master.zip
Normal file
BIN
examples/libmodbus/libmodbus-master.zip
Normal file
Binary file not shown.
161
examples/libmodbus/libmodbus.vcxproj
Normal file
161
examples/libmodbus/libmodbus.vcxproj
Normal file
@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{6fb16778-49d1-4bfd-b135-4d1ba6b5d68a}</ProjectGuid>
|
||||
<RootNamespace>libmodbus</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>D:\project\c++\nim_duilib\examples\libmodbus\;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS ;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\modbus-data.c" />
|
||||
<ClCompile Include="src\modbus-rtu.c" />
|
||||
<ClCompile Include="src\modbus-tcp.c" />
|
||||
<ClCompile Include="src\modbus.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="config.h" />
|
||||
<ClInclude Include="src\modbus-private.h" />
|
||||
<ClInclude Include="src\modbus-rtu-private.h" />
|
||||
<ClInclude Include="src\modbus-rtu.h" />
|
||||
<ClInclude Include="src\modbus-tcp-private.h" />
|
||||
<ClInclude Include="src\modbus-tcp.h" />
|
||||
<ClInclude Include="src\modbus-version.h" />
|
||||
<ClInclude Include="src\modbus.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
57
examples/libmodbus/libmodbus.vcxproj.filters
Normal file
57
examples/libmodbus/libmodbus.vcxproj.filters
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\modbus.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\modbus-data.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\modbus-rtu.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\modbus-tcp.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\modbus-version.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\modbus-tcp-private.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\modbus-tcp.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\modbus-rtu-private.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\modbus-rtu.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\modbus-private.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\modbus.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="config.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
96
examples/libmodbus/src/modbus-data.c
Normal file
96
examples/libmodbus/src/modbus-data.c
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright © 2010-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <stdint.h>
|
||||
#else
|
||||
#include "stdint.h"
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Sets many bits from a single byte value (all 8 bits of the byte value are
|
||||
set) */
|
||||
void modbus_set_bits_from_byte(uint8_t *dest, int index, const uint8_t value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<8; i++) {
|
||||
dest[index+i] = (value & (1 << i)) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets many bits from a table of bytes (only the bits between index and
|
||||
index + nb_bits are set) */
|
||||
void modbus_set_bits_from_bytes(uint8_t *dest, int index, unsigned int nb_bits,
|
||||
const uint8_t *tab_byte)
|
||||
{
|
||||
unsigned int i;
|
||||
int shift = 0;
|
||||
|
||||
for (i = index; i < index + nb_bits; i++) {
|
||||
dest[i] = tab_byte[(i - index) / 8] & (1 << shift) ? 1 : 0;
|
||||
/* gcc doesn't like: shift = (++shift) % 8; */
|
||||
shift++;
|
||||
shift %= 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* Gets the byte value from many bits.
|
||||
To obtain a full byte, set nb_bits to 8. */
|
||||
uint8_t modbus_get_byte_from_bits(const uint8_t *src, int index,
|
||||
unsigned int nb_bits)
|
||||
{
|
||||
unsigned int i;
|
||||
uint8_t value = 0;
|
||||
|
||||
if (nb_bits > 8) {
|
||||
/* Assert is ignored if NDEBUG is set */
|
||||
assert(nb_bits < 8);
|
||||
nb_bits = 8;
|
||||
}
|
||||
|
||||
for (i=0; i < nb_bits; i++) {
|
||||
value |= (src[index+i] << i);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Get a float from 4 bytes in Modbus format */
|
||||
float modbus_get_float(const uint16_t *src)
|
||||
{
|
||||
float f = 0.0f;
|
||||
uint32_t i;
|
||||
|
||||
i = (((uint32_t)src[1]) << 16) + src[0];
|
||||
memcpy(&f, &i, sizeof(float));
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/* Set a float to 4 bytes in Modbus format */
|
||||
void modbus_set_float(float f, uint16_t *dest)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
|
||||
memcpy(&i, &f, sizeof(uint32_t));
|
||||
dest[0] = (uint16_t)i;
|
||||
dest[1] = (uint16_t)(i >> 16);
|
||||
}
|
126
examples/libmodbus/src/modbus-private.h
Normal file
126
examples/libmodbus/src/modbus-private.h
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright © 2010-2012 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _MODBUS_PRIVATE_H_
|
||||
#define _MODBUS_PRIVATE_H_
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# include <stdint.h>
|
||||
# include <sys/time.h>
|
||||
#else
|
||||
# include "stdint.h"
|
||||
# include <time.h>
|
||||
typedef int ssize_t;
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <config.h>
|
||||
|
||||
#include "modbus.h"
|
||||
|
||||
MODBUS_BEGIN_DECLS
|
||||
|
||||
/* It's not really the minimal length (the real one is report slave ID
|
||||
* in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP
|
||||
* communications to read many values or write a single one.
|
||||
* Maximum between :
|
||||
* - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2)
|
||||
* - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2)
|
||||
*/
|
||||
#define _MIN_REQ_LENGTH 12
|
||||
|
||||
#define _REPORT_SLAVE_ID 180
|
||||
|
||||
#define _MODBUS_EXCEPTION_RSP_LENGTH 5
|
||||
|
||||
/* Timeouts in microsecond (0.5 s) */
|
||||
#define _RESPONSE_TIMEOUT 500000
|
||||
#define _BYTE_TIMEOUT 500000
|
||||
|
||||
/* Function codes */
|
||||
#define _FC_READ_COILS 0x01
|
||||
#define _FC_READ_DISCRETE_INPUTS 0x02
|
||||
#define _FC_READ_HOLDING_REGISTERS 0x03
|
||||
#define _FC_READ_INPUT_REGISTERS 0x04
|
||||
#define _FC_WRITE_SINGLE_COIL 0x05
|
||||
#define _FC_WRITE_SINGLE_REGISTER 0x06
|
||||
#define _FC_READ_EXCEPTION_STATUS 0x07
|
||||
#define _FC_WRITE_MULTIPLE_COILS 0x0F
|
||||
#define _FC_WRITE_MULTIPLE_REGISTERS 0x10
|
||||
#define _FC_REPORT_SLAVE_ID 0x11
|
||||
#define _FC_WRITE_AND_READ_REGISTERS 0x17
|
||||
|
||||
typedef enum {
|
||||
_MODBUS_BACKEND_TYPE_RTU=0,
|
||||
_MODBUS_BACKEND_TYPE_TCP
|
||||
} modbus_bakend_type_t;
|
||||
|
||||
/* This structure reduces the number of params in functions and so
|
||||
* optimizes the speed of execution (~ 37%). */
|
||||
typedef struct _sft {
|
||||
int slave;
|
||||
int function;
|
||||
int t_id;
|
||||
} sft_t;
|
||||
|
||||
typedef struct _modbus_backend {
|
||||
unsigned int backend_type;
|
||||
unsigned int header_length;
|
||||
unsigned int checksum_length;
|
||||
unsigned int max_adu_length;
|
||||
int (*set_slave) (modbus_t *ctx, int slave);
|
||||
int (*build_request_basis) (modbus_t *ctx, int function, int addr,
|
||||
int nb, uint8_t *req);
|
||||
int (*build_response_basis) (sft_t *sft, uint8_t *rsp);
|
||||
int (*prepare_response_tid) (const uint8_t *req, int *req_length);
|
||||
int (*send_msg_pre) (uint8_t *req, int req_length);
|
||||
ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length);
|
||||
ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length);
|
||||
int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
|
||||
const int msg_length);
|
||||
int (*pre_check_confirmation) (modbus_t *ctx, const uint8_t *req,
|
||||
const uint8_t *rsp, int rsp_length);
|
||||
int (*connect) (modbus_t *ctx);
|
||||
void (*close) (modbus_t *ctx);
|
||||
int (*flush) (modbus_t *ctx);
|
||||
int (*select) (modbus_t *ctx, fd_set *rfds, struct timeval *tv, int msg_length);
|
||||
int (*filter_request) (modbus_t *ctx, int slave);
|
||||
} modbus_backend_t;
|
||||
|
||||
struct _modbus {
|
||||
/* Slave address */
|
||||
int slave;
|
||||
/* Socket or file descriptor */
|
||||
int s;
|
||||
int debug;
|
||||
int error_recovery;
|
||||
struct timeval response_timeout;
|
||||
struct timeval byte_timeout;
|
||||
const modbus_backend_t *backend;
|
||||
void *backend_data;
|
||||
};
|
||||
|
||||
void _modbus_init_common(modbus_t *ctx);
|
||||
void _error_print(modbus_t *ctx, const char *context);
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char *dest, const char *src, size_t dest_size);
|
||||
#endif
|
||||
|
||||
MODBUS_END_DECLS
|
||||
|
||||
#endif /* _MODBUS_PRIVATE_H_ */
|
86
examples/libmodbus/src/modbus-rtu-private.h
Normal file
86
examples/libmodbus/src/modbus-rtu-private.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _MODBUS_RTU_PRIVATE_H_
|
||||
#define _MODBUS_RTU_PRIVATE_H_
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <stdint.h>
|
||||
#else
|
||||
#include "stdint.h"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
#define _MODBUS_RTU_HEADER_LENGTH 1
|
||||
#define _MODBUS_RTU_PRESET_REQ_LENGTH 6
|
||||
#define _MODBUS_RTU_PRESET_RSP_LENGTH 2
|
||||
|
||||
#define _MODBUS_RTU_CHECKSUM_LENGTH 2
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define ENOTSUP WSAEOPNOTSUPP
|
||||
|
||||
/* WIN32: struct containing serial handle and a receive buffer */
|
||||
#define PY_BUF_SIZE 512
|
||||
struct win32_ser {
|
||||
/* File handle */
|
||||
HANDLE fd;
|
||||
/* Receive buffer */
|
||||
uint8_t buf[PY_BUF_SIZE];
|
||||
/* Received chars */
|
||||
DWORD n_bytes;
|
||||
};
|
||||
#endif /* _WIN32 */
|
||||
|
||||
typedef struct _modbus_rtu {
|
||||
/* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X for
|
||||
KeySpan USB<->Serial adapters this string had to be made bigger on OS X
|
||||
as the directory+file name was bigger than 19 bytes. Making it 67 bytes
|
||||
for now, but OS X does support 256 byte file names. May become a problem
|
||||
in the future. */
|
||||
#if defined(__APPLE_CC__)
|
||||
char device[64];
|
||||
#else
|
||||
char device[16];
|
||||
#endif
|
||||
/* Bauds: 9600, 19200, 57600, 115200, etc */
|
||||
int baud;
|
||||
/* Data bit */
|
||||
uint8_t data_bit;
|
||||
/* Stop bit */
|
||||
uint8_t stop_bit;
|
||||
/* Parity: 'N', 'O', 'E' */
|
||||
char parity;
|
||||
#if defined(_WIN32)
|
||||
struct win32_ser w_ser;
|
||||
DCB old_dcb;
|
||||
#else
|
||||
/* Save old termios settings */
|
||||
struct termios old_tios;
|
||||
#endif
|
||||
#if HAVE_DECL_TIOCSRS485
|
||||
int serial_mode;
|
||||
#endif
|
||||
} modbus_rtu_t;
|
||||
|
||||
#endif /* _MODBUS_RTU_PRIVATE_H_ */
|
925
examples/libmodbus/src/modbus-rtu.c
Normal file
925
examples/libmodbus/src/modbus-rtu.c
Normal file
@ -0,0 +1,925 @@
|
||||
/*
|
||||
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#include "modbus-private.h"
|
||||
|
||||
#include "modbus-rtu.h"
|
||||
#include "modbus-rtu-private.h"
|
||||
|
||||
#if HAVE_DECL_TIOCSRS485
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/serial.h>
|
||||
#endif
|
||||
|
||||
/* Table of CRC values for high-order byte */
|
||||
static const uint8_t table_crc_hi[] = {
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
|
||||
};
|
||||
|
||||
/* Table of CRC values for low-order byte */
|
||||
static const uint8_t table_crc_lo[] = {
|
||||
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
|
||||
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
|
||||
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
|
||||
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
|
||||
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
|
||||
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
|
||||
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
|
||||
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
|
||||
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
|
||||
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
|
||||
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
|
||||
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
|
||||
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
|
||||
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
|
||||
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
|
||||
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
|
||||
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
|
||||
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
|
||||
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
|
||||
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
|
||||
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
|
||||
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
|
||||
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
|
||||
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
|
||||
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
|
||||
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
|
||||
};
|
||||
|
||||
/* Define the slave ID of the remote device to talk in master mode or set the
|
||||
* internal slave ID in slave mode */
|
||||
static int _modbus_set_slave(modbus_t *ctx, int slave)
|
||||
{
|
||||
/* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
|
||||
if (slave >= 0 && slave <= 247) {
|
||||
ctx->slave = slave;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Builds a RTU request header */
|
||||
static int _modbus_rtu_build_request_basis(modbus_t *ctx, int function,
|
||||
int addr, int nb,
|
||||
uint8_t *req)
|
||||
{
|
||||
assert(ctx->slave != -1);
|
||||
req[0] = ctx->slave;
|
||||
req[1] = function;
|
||||
req[2] = addr >> 8;
|
||||
req[3] = addr & 0x00ff;
|
||||
req[4] = nb >> 8;
|
||||
req[5] = nb & 0x00ff;
|
||||
|
||||
return _MODBUS_RTU_PRESET_REQ_LENGTH;
|
||||
}
|
||||
|
||||
/* Builds a RTU response header */
|
||||
static int _modbus_rtu_build_response_basis(sft_t *sft, uint8_t *rsp)
|
||||
{
|
||||
/* In this case, the slave is certainly valid because a check is already
|
||||
* done in _modbus_rtu_listen */
|
||||
rsp[0] = sft->slave;
|
||||
rsp[1] = sft->function;
|
||||
|
||||
return _MODBUS_RTU_PRESET_RSP_LENGTH;
|
||||
}
|
||||
|
||||
static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length)
|
||||
{
|
||||
uint8_t crc_hi = 0xFF; /* high CRC byte initialized */
|
||||
uint8_t crc_lo = 0xFF; /* low CRC byte initialized */
|
||||
unsigned int i; /* will index into CRC lookup */
|
||||
|
||||
/* pass through message buffer */
|
||||
while (buffer_length--) {
|
||||
i = crc_hi ^ *buffer++; /* calculate the CRC */
|
||||
crc_hi = crc_lo ^ table_crc_hi[i];
|
||||
crc_lo = table_crc_lo[i];
|
||||
}
|
||||
|
||||
return (crc_hi << 8 | crc_lo);
|
||||
}
|
||||
|
||||
int _modbus_rtu_prepare_response_tid(const uint8_t *req, int *req_length)
|
||||
{
|
||||
(*req_length) -= _MODBUS_RTU_CHECKSUM_LENGTH;
|
||||
/* No TID */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _modbus_rtu_send_msg_pre(uint8_t *req, int req_length)
|
||||
{
|
||||
uint16_t crc = crc16(req, req_length);
|
||||
req[req_length++] = crc >> 8;
|
||||
req[req_length++] = crc & 0x00FF;
|
||||
|
||||
return req_length;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
/* This simple implementation is sort of a substitute of the select() call,
|
||||
* working this way: the win32_ser_select() call tries to read some data from
|
||||
* the serial port, setting the timeout as the select() call would. Data read is
|
||||
* stored into the receive buffer, that is then consumed by the win32_ser_read()
|
||||
* call. So win32_ser_select() does both the event waiting and the reading,
|
||||
* while win32_ser_read() only consumes the receive buffer.
|
||||
*/
|
||||
|
||||
static void win32_ser_init(struct win32_ser *ws) {
|
||||
/* Clear everything */
|
||||
memset(ws, 0x00, sizeof(struct win32_ser));
|
||||
|
||||
/* Set file handle to invalid */
|
||||
ws->fd = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
/* FIXME Try to remove length_to_read -> max_len argument, only used by win32 */
|
||||
static int win32_ser_select(struct win32_ser *ws, int max_len,
|
||||
struct timeval *tv) {
|
||||
COMMTIMEOUTS comm_to;
|
||||
unsigned int msec = 0;
|
||||
|
||||
/* Check if some data still in the buffer to be consumed */
|
||||
if (ws->n_bytes > 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Setup timeouts like select() would do.
|
||||
FIXME Please someone on Windows can look at this?
|
||||
Does it possible to use WaitCommEvent?
|
||||
When tv is NULL, MAXDWORD isn't infinite!
|
||||
*/
|
||||
if (tv == NULL) {
|
||||
msec = MAXDWORD;
|
||||
} else {
|
||||
msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
|
||||
if (msec < 1)
|
||||
msec = 1;
|
||||
}
|
||||
|
||||
comm_to.ReadIntervalTimeout = msec;
|
||||
comm_to.ReadTotalTimeoutMultiplier = 0;
|
||||
comm_to.ReadTotalTimeoutConstant = msec;
|
||||
comm_to.WriteTotalTimeoutMultiplier = 0;
|
||||
comm_to.WriteTotalTimeoutConstant = 1000;
|
||||
SetCommTimeouts(ws->fd, &comm_to);
|
||||
|
||||
/* Read some bytes */
|
||||
if ((max_len > PY_BUF_SIZE) || (max_len < 0)) {
|
||||
max_len = PY_BUF_SIZE;
|
||||
}
|
||||
|
||||
if (ReadFile(ws->fd, &ws->buf, max_len, &ws->n_bytes, NULL)) {
|
||||
/* Check if some bytes available */
|
||||
if (ws->n_bytes > 0) {
|
||||
/* Some bytes read */
|
||||
return 1;
|
||||
} else {
|
||||
/* Just timed out */
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* Some kind of error */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int win32_ser_read(struct win32_ser *ws, uint8_t *p_msg,
|
||||
unsigned int max_len) {
|
||||
unsigned int n = ws->n_bytes;
|
||||
|
||||
if (max_len < n) {
|
||||
n = max_len;
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
memcpy(p_msg, ws->buf, n);
|
||||
}
|
||||
|
||||
ws->n_bytes -= n;
|
||||
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
modbus_rtu_t *ctx_rtu = ctx->backend_data;
|
||||
DWORD n_bytes = 0;
|
||||
return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) ? n_bytes : -1;
|
||||
#else
|
||||
return write(ctx->s, req, req_length);
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return win32_ser_read(&((modbus_rtu_t *)ctx->backend_data)->w_ser, rsp, rsp_length);
|
||||
#else
|
||||
return read(ctx->s, rsp, rsp_length);
|
||||
#endif
|
||||
}
|
||||
|
||||
int _modbus_rtu_flush(modbus_t *);
|
||||
|
||||
/* The check_crc16 function shall return the message length if the CRC is
|
||||
valid. Otherwise it shall return -1 and set errno to EMBADCRC. */
|
||||
int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
|
||||
const int msg_length)
|
||||
{
|
||||
uint16_t crc_calculated;
|
||||
uint16_t crc_received;
|
||||
|
||||
crc_calculated = crc16(msg, msg_length - 2);
|
||||
crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1];
|
||||
|
||||
/* Check CRC of msg */
|
||||
if (crc_calculated == crc_received) {
|
||||
return msg_length;
|
||||
} else {
|
||||
if (ctx->debug) {
|
||||
fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n",
|
||||
crc_received, crc_calculated);
|
||||
}
|
||||
if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
|
||||
_modbus_rtu_flush(ctx);
|
||||
}
|
||||
errno = EMBBADCRC;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets up a serial port for RTU communications */
|
||||
static int _modbus_rtu_connect(modbus_t *ctx)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
DCB dcb;
|
||||
#else
|
||||
struct termios tios;
|
||||
speed_t speed;
|
||||
#endif
|
||||
modbus_rtu_t *ctx_rtu = ctx->backend_data;
|
||||
|
||||
if (ctx->debug) {
|
||||
printf("Opening %s at %d bauds (%c, %d, %d)\n",
|
||||
ctx_rtu->device, ctx_rtu->baud, ctx_rtu->parity,
|
||||
ctx_rtu->data_bit, ctx_rtu->stop_bit);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
/* Some references here:
|
||||
* http://msdn.microsoft.com/en-us/library/aa450602.aspx
|
||||
*/
|
||||
win32_ser_init(&ctx_rtu->w_ser);
|
||||
|
||||
/* ctx_rtu->device should contain a string like "COMxx:" xx being a decimal
|
||||
* number */
|
||||
ctx_rtu->w_ser.fd = CreateFileA(ctx_rtu->device,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
/* Error checking */
|
||||
if (ctx_rtu->w_ser.fd == INVALID_HANDLE_VALUE) {
|
||||
fprintf(stderr, "ERROR Can't open the device %s (%s)\n",
|
||||
ctx_rtu->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Save params */
|
||||
ctx_rtu->old_dcb.DCBlength = sizeof(DCB);
|
||||
if (!GetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb)) {
|
||||
fprintf(stderr, "ERROR Error getting configuration (LastError %d)\n",
|
||||
(int)GetLastError());
|
||||
CloseHandle(ctx_rtu->w_ser.fd);
|
||||
ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Build new configuration (starting from current settings) */
|
||||
dcb = ctx_rtu->old_dcb;
|
||||
|
||||
/* Speed setting */
|
||||
switch (ctx_rtu->baud) {
|
||||
case 110:
|
||||
dcb.BaudRate = CBR_110;
|
||||
break;
|
||||
case 300:
|
||||
dcb.BaudRate = CBR_300;
|
||||
break;
|
||||
case 600:
|
||||
dcb.BaudRate = CBR_600;
|
||||
break;
|
||||
case 1200:
|
||||
dcb.BaudRate = CBR_1200;
|
||||
break;
|
||||
case 2400:
|
||||
dcb.BaudRate = CBR_2400;
|
||||
break;
|
||||
case 4800:
|
||||
dcb.BaudRate = CBR_4800;
|
||||
break;
|
||||
case 9600:
|
||||
dcb.BaudRate = CBR_9600;
|
||||
break;
|
||||
case 19200:
|
||||
dcb.BaudRate = CBR_19200;
|
||||
break;
|
||||
case 38400:
|
||||
dcb.BaudRate = CBR_38400;
|
||||
break;
|
||||
case 57600:
|
||||
dcb.BaudRate = CBR_57600;
|
||||
break;
|
||||
case 115200:
|
||||
dcb.BaudRate = CBR_115200;
|
||||
break;
|
||||
default:
|
||||
dcb.BaudRate = CBR_9600;
|
||||
printf("WARNING Unknown baud rate %d for %s (B9600 used)\n",
|
||||
ctx_rtu->baud, ctx_rtu->device);
|
||||
}
|
||||
|
||||
/* Data bits */
|
||||
switch (ctx_rtu->data_bit) {
|
||||
case 5:
|
||||
dcb.ByteSize = 5;
|
||||
break;
|
||||
case 6:
|
||||
dcb.ByteSize = 6;
|
||||
break;
|
||||
case 7:
|
||||
dcb.ByteSize = 7;
|
||||
break;
|
||||
case 8:
|
||||
default:
|
||||
dcb.ByteSize = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stop bits */
|
||||
if (ctx_rtu->stop_bit == 1)
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
else /* 2 */
|
||||
dcb.StopBits = TWOSTOPBITS;
|
||||
|
||||
/* Parity */
|
||||
if (ctx_rtu->parity == 'N') {
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.fParity = FALSE;
|
||||
} else if (ctx_rtu->parity == 'E') {
|
||||
dcb.Parity = EVENPARITY;
|
||||
dcb.fParity = TRUE;
|
||||
} else {
|
||||
/* odd */
|
||||
dcb.Parity = ODDPARITY;
|
||||
dcb.fParity = TRUE;
|
||||
}
|
||||
|
||||
/* Hardware handshaking left as default settings retrieved */
|
||||
|
||||
/* No software handshaking */
|
||||
dcb.fTXContinueOnXoff = TRUE;
|
||||
dcb.fOutX = FALSE;
|
||||
dcb.fInX = FALSE;
|
||||
|
||||
/* Binary mode (it's the only supported on Windows anyway) */
|
||||
dcb.fBinary = TRUE;
|
||||
|
||||
/* Don't want errors to be blocking */
|
||||
dcb.fAbortOnError = FALSE;
|
||||
|
||||
/* TODO: any other flags!? */
|
||||
|
||||
/* Setup port */
|
||||
if (!SetCommState(ctx_rtu->w_ser.fd, &dcb)) {
|
||||
fprintf(stderr, "ERROR Error setting new configuration (LastError %d)\n",
|
||||
(int)GetLastError());
|
||||
CloseHandle(ctx_rtu->w_ser.fd);
|
||||
ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
/* The O_NOCTTY flag tells UNIX that this program doesn't want
|
||||
to be the "controlling terminal" for that port. If you
|
||||
don't specify this then any input (such as keyboard abort
|
||||
signals and so forth) will affect your process
|
||||
|
||||
Timeouts are ignored in canonical input mode or when the
|
||||
NDELAY option is set on the file via open or fcntl */
|
||||
ctx->s = open(ctx_rtu->device, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);
|
||||
if (ctx->s == -1) {
|
||||
fprintf(stderr, "ERROR Can't open the device %s (%s)\n",
|
||||
ctx_rtu->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Save */
|
||||
tcgetattr(ctx->s, &(ctx_rtu->old_tios));
|
||||
|
||||
memset(&tios, 0, sizeof(struct termios));
|
||||
|
||||
/* C_ISPEED Input baud (new interface)
|
||||
C_OSPEED Output baud (new interface)
|
||||
*/
|
||||
switch (ctx_rtu->baud) {
|
||||
case 110:
|
||||
speed = B110;
|
||||
break;
|
||||
case 300:
|
||||
speed = B300;
|
||||
break;
|
||||
case 600:
|
||||
speed = B600;
|
||||
break;
|
||||
case 1200:
|
||||
speed = B1200;
|
||||
break;
|
||||
case 2400:
|
||||
speed = B2400;
|
||||
break;
|
||||
case 4800:
|
||||
speed = B4800;
|
||||
break;
|
||||
case 9600:
|
||||
speed = B9600;
|
||||
break;
|
||||
case 19200:
|
||||
speed = B19200;
|
||||
break;
|
||||
case 38400:
|
||||
speed = B38400;
|
||||
break;
|
||||
case 57600:
|
||||
speed = B57600;
|
||||
break;
|
||||
case 115200:
|
||||
speed = B115200;
|
||||
break;
|
||||
default:
|
||||
speed = B9600;
|
||||
if (ctx->debug) {
|
||||
fprintf(stderr,
|
||||
"WARNING Unknown baud rate %d for %s (B9600 used)\n",
|
||||
ctx_rtu->baud, ctx_rtu->device);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the baud rate */
|
||||
if ((cfsetispeed(&tios, speed) < 0) ||
|
||||
(cfsetospeed(&tios, speed) < 0)) {
|
||||
close(ctx->s);
|
||||
ctx->s = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* C_CFLAG Control options
|
||||
CLOCAL Local line - do not change "owner" of port
|
||||
CREAD Enable receiver
|
||||
*/
|
||||
tios.c_cflag |= (CREAD | CLOCAL);
|
||||
/* CSIZE, HUPCL, CRTSCTS (hardware flow control) */
|
||||
|
||||
/* Set data bits (5, 6, 7, 8 bits)
|
||||
CSIZE Bit mask for data bits
|
||||
*/
|
||||
tios.c_cflag &= ~CSIZE;
|
||||
switch (ctx_rtu->data_bit) {
|
||||
case 5:
|
||||
tios.c_cflag |= CS5;
|
||||
break;
|
||||
case 6:
|
||||
tios.c_cflag |= CS6;
|
||||
break;
|
||||
case 7:
|
||||
tios.c_cflag |= CS7;
|
||||
break;
|
||||
case 8:
|
||||
default:
|
||||
tios.c_cflag |= CS8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stop bit (1 or 2) */
|
||||
if (ctx_rtu->stop_bit == 1)
|
||||
tios.c_cflag &=~ CSTOPB;
|
||||
else /* 2 */
|
||||
tios.c_cflag |= CSTOPB;
|
||||
|
||||
/* PARENB Enable parity bit
|
||||
PARODD Use odd parity instead of even */
|
||||
if (ctx_rtu->parity == 'N') {
|
||||
/* None */
|
||||
tios.c_cflag &=~ PARENB;
|
||||
} else if (ctx_rtu->parity == 'E') {
|
||||
/* Even */
|
||||
tios.c_cflag |= PARENB;
|
||||
tios.c_cflag &=~ PARODD;
|
||||
} else {
|
||||
/* Odd */
|
||||
tios.c_cflag |= PARENB;
|
||||
tios.c_cflag |= PARODD;
|
||||
}
|
||||
|
||||
/* Read the man page of termios if you need more information. */
|
||||
|
||||
/* This field isn't used on POSIX systems
|
||||
tios.c_line = 0;
|
||||
*/
|
||||
|
||||
/* C_LFLAG Line options
|
||||
|
||||
ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
|
||||
ICANON Enable canonical input (else raw)
|
||||
XCASE Map uppercase \lowercase (obsolete)
|
||||
ECHO Enable echoing of input characters
|
||||
ECHOE Echo erase character as BS-SP-BS
|
||||
ECHOK Echo NL after kill character
|
||||
ECHONL Echo NL
|
||||
NOFLSH Disable flushing of input buffers after
|
||||
interrupt or quit characters
|
||||
IEXTEN Enable extended functions
|
||||
ECHOCTL Echo control characters as ^char and delete as ~?
|
||||
ECHOPRT Echo erased character as character erased
|
||||
ECHOKE BS-SP-BS entire line on line kill
|
||||
FLUSHO Output being flushed
|
||||
PENDIN Retype pending input at next read or input char
|
||||
TOSTOP Send SIGTTOU for background output
|
||||
|
||||
Canonical input is line-oriented. Input characters are put
|
||||
into a buffer which can be edited interactively by the user
|
||||
until a CR (carriage return) or LF (line feed) character is
|
||||
received.
|
||||
|
||||
Raw input is unprocessed. Input characters are passed
|
||||
through exactly as they are received, when they are
|
||||
received. Generally you'll deselect the ICANON, ECHO,
|
||||
ECHOE, and ISIG options when using raw input
|
||||
*/
|
||||
|
||||
/* Raw input */
|
||||
tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||
|
||||
/* C_IFLAG Input options
|
||||
|
||||
Constant Description
|
||||
INPCK Enable parity check
|
||||
IGNPAR Ignore parity errors
|
||||
PARMRK Mark parity errors
|
||||
ISTRIP Strip parity bits
|
||||
IXON Enable software flow control (outgoing)
|
||||
IXOFF Enable software flow control (incoming)
|
||||
IXANY Allow any character to start flow again
|
||||
IGNBRK Ignore break condition
|
||||
BRKINT Send a SIGINT when a break condition is detected
|
||||
INLCR Map NL to CR
|
||||
IGNCR Ignore CR
|
||||
ICRNL Map CR to NL
|
||||
IUCLC Map uppercase to lowercase
|
||||
IMAXBEL Echo BEL on input line too long
|
||||
*/
|
||||
if (ctx_rtu->parity == 'N') {
|
||||
/* None */
|
||||
tios.c_iflag &= ~INPCK;
|
||||
} else {
|
||||
tios.c_iflag |= INPCK;
|
||||
}
|
||||
|
||||
/* Software flow control is disabled */
|
||||
tios.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||
|
||||
/* C_OFLAG Output options
|
||||
OPOST Postprocess output (not set = raw output)
|
||||
ONLCR Map NL to CR-NL
|
||||
|
||||
ONCLR ant others needs OPOST to be enabled
|
||||
*/
|
||||
|
||||
/* Raw ouput */
|
||||
tios.c_oflag &=~ OPOST;
|
||||
|
||||
/* C_CC Control characters
|
||||
VMIN Minimum number of characters to read
|
||||
VTIME Time to wait for data (tenths of seconds)
|
||||
|
||||
UNIX serial interface drivers provide the ability to
|
||||
specify character and packet timeouts. Two elements of the
|
||||
c_cc array are used for timeouts: VMIN and VTIME. Timeouts
|
||||
are ignored in canonical input mode or when the NDELAY
|
||||
option is set on the file via open or fcntl.
|
||||
|
||||
VMIN specifies the minimum number of characters to read. If
|
||||
it is set to 0, then the VTIME value specifies the time to
|
||||
wait for every character read. Note that this does not mean
|
||||
that a read call for N bytes will wait for N characters to
|
||||
come in. Rather, the timeout will apply to the first
|
||||
character and the read call will return the number of
|
||||
characters immediately available (up to the number you
|
||||
request).
|
||||
|
||||
If VMIN is non-zero, VTIME specifies the time to wait for
|
||||
the first character read. If a character is read within the
|
||||
time given, any read will block (wait) until all VMIN
|
||||
characters are read. That is, once the first character is
|
||||
read, the serial interface driver expects to receive an
|
||||
entire packet of characters (VMIN bytes total). If no
|
||||
character is read within the time allowed, then the call to
|
||||
read returns 0. This method allows you to tell the serial
|
||||
driver you need exactly N bytes and any read call will
|
||||
return 0 or N bytes. However, the timeout only applies to
|
||||
the first character read, so if for some reason the driver
|
||||
misses one character inside the N byte packet then the read
|
||||
call could block forever waiting for additional input
|
||||
characters.
|
||||
|
||||
VTIME specifies the amount of time to wait for incoming
|
||||
characters in tenths of seconds. If VTIME is set to 0 (the
|
||||
default), reads will block (wait) indefinitely unless the
|
||||
NDELAY option is set on the port with open or fcntl.
|
||||
*/
|
||||
/* Unused because we use open with the NDELAY option */
|
||||
tios.c_cc[VMIN] = 0;
|
||||
tios.c_cc[VTIME] = 0;
|
||||
|
||||
if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) {
|
||||
close(ctx->s);
|
||||
ctx->s = -1;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_DECL_TIOCSRS485
|
||||
/* The RS232 mode has been set by default */
|
||||
ctx_rtu->serial_mode = MODBUS_RTU_RS232;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode)
|
||||
{
|
||||
if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
|
||||
#if HAVE_DECL_TIOCSRS485
|
||||
modbus_rtu_t *ctx_rtu = ctx->backend_data;
|
||||
struct serial_rs485 rs485conf;
|
||||
memset(&rs485conf, 0x0, sizeof(struct serial_rs485));
|
||||
|
||||
if (mode == MODBUS_RTU_RS485) {
|
||||
rs485conf.flags = SER_RS485_ENABLED;
|
||||
if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx_rtu->serial_mode |= MODBUS_RTU_RS485;
|
||||
return 0;
|
||||
} else if (mode == MODBUS_RTU_RS232) {
|
||||
if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx_rtu->serial_mode = MODBUS_RTU_RS232;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
if (ctx->debug) {
|
||||
fprintf(stderr, "This function isn't supported on your platform\n");
|
||||
}
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Wrong backend and invalid mode specified */
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int modbus_rtu_get_serial_mode(modbus_t *ctx) {
|
||||
if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
|
||||
#if HAVE_DECL_TIOCSRS485
|
||||
modbus_rtu_t *ctx_rtu = ctx->backend_data;
|
||||
return ctx_rtu->serial_mode;
|
||||
#else
|
||||
if (ctx->debug) {
|
||||
fprintf(stderr, "This function isn't supported on your platform\n");
|
||||
}
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
#endif
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void _modbus_rtu_close(modbus_t *ctx)
|
||||
{
|
||||
/* Closes the file descriptor in RTU mode */
|
||||
modbus_rtu_t *ctx_rtu = ctx->backend_data;
|
||||
|
||||
#if defined(_WIN32)
|
||||
/* Revert settings */
|
||||
if (!SetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb))
|
||||
fprintf(stderr, "ERROR Couldn't revert to configuration (LastError %d)\n",
|
||||
(int)GetLastError());
|
||||
|
||||
if (!CloseHandle(ctx_rtu->w_ser.fd))
|
||||
fprintf(stderr, "ERROR Error while closing handle (LastError %d)\n",
|
||||
(int)GetLastError());
|
||||
#else
|
||||
tcsetattr(ctx->s, TCSANOW, &(ctx_rtu->old_tios));
|
||||
close(ctx->s);
|
||||
#endif
|
||||
}
|
||||
|
||||
int _modbus_rtu_flush(modbus_t *ctx)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
modbus_rtu_t *ctx_rtu = ctx->backend_data;
|
||||
ctx_rtu->w_ser.n_bytes = 0;
|
||||
return (FlushFileBuffers(ctx_rtu->w_ser.fd) == FALSE);
|
||||
#else
|
||||
return tcflush(ctx->s, TCIOFLUSH);
|
||||
#endif
|
||||
}
|
||||
|
||||
int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds,
|
||||
struct timeval *tv, int length_to_read)
|
||||
{
|
||||
int s_rc;
|
||||
#if defined(_WIN32)
|
||||
s_rc = win32_ser_select(&(((modbus_rtu_t*)ctx->backend_data)->w_ser),
|
||||
length_to_read, tv);
|
||||
if (s_rc == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s_rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
while ((s_rc = select(ctx->s+1, rfds, NULL, NULL, tv)) == -1) {
|
||||
if (errno == EINTR) {
|
||||
if (ctx->debug) {
|
||||
fprintf(stderr, "A non blocked signal was caught\n");
|
||||
}
|
||||
/* Necessary after an error */
|
||||
FD_ZERO(rfds);
|
||||
FD_SET(ctx->s, rfds);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_rc == 0) {
|
||||
/* Timeout */
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return s_rc;
|
||||
}
|
||||
|
||||
int _modbus_rtu_filter_request(modbus_t *ctx, int slave)
|
||||
{
|
||||
/* Filter on the Modbus unit identifier (slave) in RTU mode */
|
||||
if (slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) {
|
||||
/* Ignores the request (not for me) */
|
||||
if (ctx->debug) {
|
||||
printf("Request for slave %d ignored (not %d)\n",
|
||||
slave, ctx->slave);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const modbus_backend_t _modbus_rtu_backend = {
|
||||
_MODBUS_BACKEND_TYPE_RTU,
|
||||
_MODBUS_RTU_HEADER_LENGTH,
|
||||
_MODBUS_RTU_CHECKSUM_LENGTH,
|
||||
MODBUS_RTU_MAX_ADU_LENGTH,
|
||||
_modbus_set_slave,
|
||||
_modbus_rtu_build_request_basis,
|
||||
_modbus_rtu_build_response_basis,
|
||||
_modbus_rtu_prepare_response_tid,
|
||||
_modbus_rtu_send_msg_pre,
|
||||
_modbus_rtu_send,
|
||||
_modbus_rtu_recv,
|
||||
_modbus_rtu_check_integrity,
|
||||
NULL,
|
||||
_modbus_rtu_connect,
|
||||
_modbus_rtu_close,
|
||||
_modbus_rtu_flush,
|
||||
_modbus_rtu_select,
|
||||
_modbus_rtu_filter_request
|
||||
};
|
||||
|
||||
modbus_t* modbus_new_rtu(const char *device,
|
||||
int baud, char parity, int data_bit,
|
||||
int stop_bit)
|
||||
{
|
||||
modbus_t *ctx;
|
||||
modbus_rtu_t *ctx_rtu;
|
||||
size_t dest_size;
|
||||
size_t ret_size;
|
||||
|
||||
ctx = (modbus_t *) malloc(sizeof(modbus_t));
|
||||
_modbus_init_common(ctx);
|
||||
|
||||
ctx->backend = &_modbus_rtu_backend;
|
||||
ctx->backend_data = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t));
|
||||
ctx_rtu = (modbus_rtu_t *)ctx->backend_data;
|
||||
|
||||
dest_size = sizeof(ctx_rtu->device);
|
||||
ret_size = strlcpy(ctx_rtu->device, device, dest_size);
|
||||
if (ret_size == 0) {
|
||||
fprintf(stderr, "The device string is empty\n");
|
||||
modbus_free(ctx);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ret_size >= dest_size) {
|
||||
fprintf(stderr, "The device string has been truncated\n");
|
||||
modbus_free(ctx);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx_rtu->baud = baud;
|
||||
if (parity == 'N' || parity == 'E' || parity == 'O') {
|
||||
ctx_rtu->parity = parity;
|
||||
} else {
|
||||
modbus_free(ctx);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
ctx_rtu->data_bit = data_bit;
|
||||
ctx_rtu->stop_bit = stop_bit;
|
||||
|
||||
return ctx;
|
||||
}
|
42
examples/libmodbus/src/modbus-rtu.h
Normal file
42
examples/libmodbus/src/modbus-rtu.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _MODBUS_RTU_H_
|
||||
#define _MODBUS_RTU_H_
|
||||
|
||||
#include "modbus.h"
|
||||
|
||||
MODBUS_BEGIN_DECLS
|
||||
|
||||
/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
|
||||
* RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes
|
||||
*/
|
||||
#define MODBUS_RTU_MAX_ADU_LENGTH 256
|
||||
|
||||
modbus_t* modbus_new_rtu(const char *device, int baud, char parity,
|
||||
int data_bit, int stop_bit);
|
||||
|
||||
#define MODBUS_RTU_RS232 0
|
||||
#define MODBUS_RTU_RS485 1
|
||||
|
||||
int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode);
|
||||
int modbus_rtu_get_serial_mode(modbus_t *ctx);
|
||||
|
||||
MODBUS_END_DECLS
|
||||
|
||||
#endif /* _MODBUS_RTU_H_ */
|
47
examples/libmodbus/src/modbus-tcp-private.h
Normal file
47
examples/libmodbus/src/modbus-tcp-private.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _MODBUS_TCP_PRIVATE_H_
|
||||
#define _MODBUS_TCP_PRIVATE_H_
|
||||
|
||||
#define _MODBUS_TCP_HEADER_LENGTH 7
|
||||
#define _MODBUS_TCP_PRESET_REQ_LENGTH 12
|
||||
#define _MODBUS_TCP_PRESET_RSP_LENGTH 8
|
||||
|
||||
#define _MODBUS_TCP_CHECKSUM_LENGTH 0
|
||||
|
||||
typedef struct _modbus_tcp {
|
||||
/* TCP port */
|
||||
int port;
|
||||
/* IP address */
|
||||
char ip[16];
|
||||
} modbus_tcp_t;
|
||||
|
||||
#define _MODBUS_TCP_PI_NODE_LENGTH 1025
|
||||
#define _MODBUS_TCP_PI_SERVICE_LENGTH 32
|
||||
|
||||
typedef struct _modbus_tcp_pi {
|
||||
/* TCP port */
|
||||
int port;
|
||||
/* Node */
|
||||
char node[_MODBUS_TCP_PI_NODE_LENGTH];
|
||||
/* Service */
|
||||
char service[_MODBUS_TCP_PI_SERVICE_LENGTH];
|
||||
} modbus_tcp_pi_t;
|
||||
|
||||
#endif /* _MODBUS_TCP_PRIVATE_H_ */
|
743
examples/libmodbus/src/modbus-tcp.c
Normal file
743
examples/libmodbus/src/modbus-tcp.c
Normal file
@ -0,0 +1,743 @@
|
||||
/*
|
||||
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define OS_WIN32
|
||||
/* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
|
||||
* minwg32 headers check WINVER before allowing the use of these */
|
||||
# ifndef WINVER
|
||||
# define WINVER 0x0501
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <ws2tcpip.h>
|
||||
# define SHUT_RDWR 2
|
||||
# define close closesocket
|
||||
#else
|
||||
# include <sys/socket.h>
|
||||
# include <sys/ioctl.h>
|
||||
|
||||
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
|
||||
# define OS_BSD
|
||||
# include <netinet/in_systm.h>
|
||||
#endif
|
||||
|
||||
# include <netinet/in.h>
|
||||
# include <netinet/ip.h>
|
||||
# include <netinet/tcp.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <poll.h>
|
||||
# include <netdb.h>
|
||||
#endif
|
||||
|
||||
#if !defined(MSG_NOSIGNAL)
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
#include "modbus-private.h"
|
||||
|
||||
#include "modbus-tcp.h"
|
||||
#include "modbus-tcp-private.h"
|
||||
|
||||
#ifdef OS_WIN32
|
||||
static int _modbus_tcp_init_win32(void)
|
||||
{
|
||||
/* Initialise Windows Socket API */
|
||||
WSADATA wsaData;
|
||||
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
fprintf(stderr, "WSAStartup() returned error code %d\n",
|
||||
(unsigned int)GetLastError());
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _modbus_set_slave(modbus_t *ctx, int slave)
|
||||
{
|
||||
/* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
|
||||
if (slave >= 0 && slave <= 247) {
|
||||
ctx->slave = slave;
|
||||
} else if (slave == MODBUS_TCP_SLAVE) {
|
||||
/* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
|
||||
* restore the default value. */
|
||||
ctx->slave = slave;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Builds a TCP request header */
|
||||
int _modbus_tcp_build_request_basis(modbus_t *ctx, int function,
|
||||
int addr, int nb,
|
||||
uint8_t *req)
|
||||
{
|
||||
|
||||
/* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b
|
||||
(page 23/46):
|
||||
The transaction identifier is used to associate the future response
|
||||
with the request. So, at a time, on a TCP connection, this identifier
|
||||
must be unique. */
|
||||
static uint16_t t_id = 0;
|
||||
|
||||
/* Transaction ID */
|
||||
if (t_id < UINT16_MAX)
|
||||
t_id++;
|
||||
else
|
||||
t_id = 0;
|
||||
req[0] = t_id >> 8;
|
||||
req[1] = t_id & 0x00ff;
|
||||
|
||||
/* Protocol Modbus */
|
||||
req[2] = 0;
|
||||
req[3] = 0;
|
||||
|
||||
/* Length will be defined later by set_req_length_tcp at offsets 4
|
||||
and 5 */
|
||||
|
||||
req[6] = ctx->slave;
|
||||
req[7] = function;
|
||||
req[8] = addr >> 8;
|
||||
req[9] = addr & 0x00ff;
|
||||
req[10] = nb >> 8;
|
||||
req[11] = nb & 0x00ff;
|
||||
|
||||
return _MODBUS_TCP_PRESET_REQ_LENGTH;
|
||||
}
|
||||
|
||||
/* Builds a TCP response header */
|
||||
int _modbus_tcp_build_response_basis(sft_t *sft, uint8_t *rsp)
|
||||
{
|
||||
/* Extract from MODBUS Messaging on TCP/IP Implementation
|
||||
Guide V1.0b (page 23/46):
|
||||
The transaction identifier is used to associate the future
|
||||
response with the request. */
|
||||
rsp[0] = sft->t_id >> 8;
|
||||
rsp[1] = sft->t_id & 0x00ff;
|
||||
|
||||
/* Protocol Modbus */
|
||||
rsp[2] = 0;
|
||||
rsp[3] = 0;
|
||||
|
||||
/* Length will be set later by send_msg (4 and 5) */
|
||||
|
||||
/* The slave ID is copied from the indication */
|
||||
rsp[6] = sft->slave;
|
||||
rsp[7] = sft->function;
|
||||
|
||||
return _MODBUS_TCP_PRESET_RSP_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
int _modbus_tcp_prepare_response_tid(const uint8_t *req, int *req_length)
|
||||
{
|
||||
return (req[0] << 8) + req[1];
|
||||
}
|
||||
|
||||
int _modbus_tcp_send_msg_pre(uint8_t *req, int req_length)
|
||||
{
|
||||
/* Substract the header length to the message length */
|
||||
int mbap_length = req_length - 6;
|
||||
|
||||
req[4] = mbap_length >> 8;
|
||||
req[5] = mbap_length & 0x00FF;
|
||||
|
||||
return req_length;
|
||||
}
|
||||
|
||||
ssize_t _modbus_tcp_send(modbus_t *ctx, const uint8_t *req, int req_length)
|
||||
{
|
||||
/* MSG_NOSIGNAL
|
||||
Requests not to send SIGPIPE on errors on stream oriented
|
||||
sockets when the other end breaks the connection. The EPIPE
|
||||
error is still returned. */
|
||||
return send(ctx->s, (const char*)req, req_length, MSG_NOSIGNAL);
|
||||
}
|
||||
|
||||
ssize_t _modbus_tcp_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) {
|
||||
return recv(ctx->s, (char *)rsp, rsp_length, 0);
|
||||
}
|
||||
|
||||
int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length)
|
||||
{
|
||||
return msg_length;
|
||||
}
|
||||
|
||||
int _modbus_tcp_pre_check_confirmation(modbus_t *ctx, const uint8_t *req,
|
||||
const uint8_t *rsp, int rsp_length)
|
||||
{
|
||||
/* Check TID */
|
||||
if (req[0] != rsp[0] || req[1] != rsp[1]) {
|
||||
if (ctx->debug) {
|
||||
fprintf(stderr, "Invalid TID received 0x%X (not 0x%X)\n",
|
||||
(rsp[0] << 8) + rsp[1], (req[0] << 8) + req[1]);
|
||||
}
|
||||
errno = EMBBADDATA;
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int _modbus_tcp_set_ipv4_options(int s)
|
||||
{
|
||||
int rc;
|
||||
int option;
|
||||
|
||||
/* Set the TCP no delay flag */
|
||||
/* SOL_TCP = IPPROTO_TCP */
|
||||
option = 1;
|
||||
rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
|
||||
(const void *)&option, sizeof(int));
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef OS_WIN32
|
||||
/**
|
||||
* Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
|
||||
* necessary to workaround that problem.
|
||||
**/
|
||||
/* Set the IP low delay option */
|
||||
option = IPTOS_LOWDELAY;
|
||||
rc = setsockopt(s, IPPROTO_IP, IP_TOS,
|
||||
(const void *)&option, sizeof(int));
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Establishes a modbus TCP connection with a Modbus server. */
|
||||
static int _modbus_tcp_connect(modbus_t *ctx)
|
||||
{
|
||||
int rc;
|
||||
struct sockaddr_in addr;
|
||||
modbus_tcp_t *ctx_tcp = ctx->backend_data;
|
||||
|
||||
#ifdef OS_WIN32
|
||||
if (_modbus_tcp_init_win32() == -1) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx->s = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (ctx->s == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = _modbus_tcp_set_ipv4_options(ctx->s);
|
||||
if (rc == -1) {
|
||||
close(ctx->s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->debug) {
|
||||
printf("Connecting to %s\n", ctx_tcp->ip);
|
||||
}
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(ctx_tcp->port);
|
||||
addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip);
|
||||
rc = connect(ctx->s, (struct sockaddr *)&addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
if (rc == -1) {
|
||||
close(ctx->s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Establishes a modbus TCP PI connection with a Modbus server. */
|
||||
static int _modbus_tcp_pi_connect(modbus_t *ctx)
|
||||
{
|
||||
int rc;
|
||||
struct addrinfo *ai_list;
|
||||
struct addrinfo *ai_ptr;
|
||||
struct addrinfo ai_hints;
|
||||
modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data;
|
||||
|
||||
#ifdef OS_WIN32
|
||||
if (_modbus_tcp_init_win32() == -1) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(&ai_hints, 0, sizeof(ai_hints));
|
||||
#ifdef AI_ADDRCONFIG
|
||||
ai_hints.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif
|
||||
ai_hints.ai_family = AF_UNSPEC;
|
||||
ai_hints.ai_socktype = SOCK_STREAM;
|
||||
ai_hints.ai_addr = NULL;
|
||||
ai_hints.ai_canonname = NULL;
|
||||
ai_hints.ai_next = NULL;
|
||||
|
||||
ai_list = NULL;
|
||||
rc = getaddrinfo(ctx_tcp_pi->node, ctx_tcp_pi->service,
|
||||
&ai_hints, &ai_list);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
|
||||
int s;
|
||||
|
||||
s = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
|
||||
if (s < 0)
|
||||
continue;
|
||||
|
||||
if (ai_ptr->ai_family == AF_INET)
|
||||
_modbus_tcp_set_ipv4_options(s);
|
||||
|
||||
rc = connect(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
|
||||
if (rc != 0) {
|
||||
close(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx->s = s;
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(ai_list);
|
||||
|
||||
if (ctx->s < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Closes the network connection and socket in TCP mode */
|
||||
void _modbus_tcp_close(modbus_t *ctx)
|
||||
{
|
||||
shutdown(ctx->s, SHUT_RDWR);
|
||||
close(ctx->s);
|
||||
}
|
||||
|
||||
int _modbus_tcp_flush(modbus_t *ctx)
|
||||
{
|
||||
int rc;
|
||||
int rc_sum = 0;
|
||||
|
||||
do {
|
||||
/* Extract the garbage from the socket */
|
||||
char devnull[MODBUS_TCP_MAX_ADU_LENGTH];
|
||||
#ifndef OS_WIN32
|
||||
rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, MSG_DONTWAIT);
|
||||
#else
|
||||
/* On Win32, it's a bit more complicated to not wait */
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(ctx->s, &rfds);
|
||||
rc = select(ctx->s+1, &rfds, NULL, NULL, &tv);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
/* There is data to flush */
|
||||
rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, 0);
|
||||
}
|
||||
#endif
|
||||
if (rc > 0) {
|
||||
rc_sum += rc;
|
||||
}
|
||||
} while (rc == MODBUS_TCP_MAX_ADU_LENGTH);
|
||||
|
||||
return rc_sum;
|
||||
}
|
||||
|
||||
/* Listens for any request from one or many modbus masters in TCP */
|
||||
int modbus_tcp_listen(modbus_t *ctx, int nb_connection)
|
||||
{
|
||||
int new_socket;
|
||||
int yes;
|
||||
struct sockaddr_in addr;
|
||||
modbus_tcp_t *ctx_tcp = ctx->backend_data;
|
||||
|
||||
#ifdef OS_WIN32
|
||||
if (_modbus_tcp_init_win32() == -1) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
new_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (new_socket == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
yes = 1;
|
||||
if (setsockopt(new_socket, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &yes, sizeof(yes)) == -1) {
|
||||
close(new_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
/* If the modbus port is < to 1024, we need the setuid root. */
|
||||
addr.sin_port = htons(ctx_tcp->port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
if (bind(new_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
close(new_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(new_socket, nb_connection) == -1) {
|
||||
close(new_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return new_socket;
|
||||
}
|
||||
|
||||
int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection)
|
||||
{
|
||||
int rc;
|
||||
struct addrinfo *ai_list;
|
||||
struct addrinfo *ai_ptr;
|
||||
struct addrinfo ai_hints;
|
||||
const char *node;
|
||||
const char *service;
|
||||
int new_socket;
|
||||
modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data;
|
||||
|
||||
if (ctx_tcp_pi->node[0] == 0)
|
||||
node = NULL; /* == any */
|
||||
else
|
||||
node = ctx_tcp_pi->node;
|
||||
|
||||
if (ctx_tcp_pi->service[0] == 0)
|
||||
service = "502";
|
||||
else
|
||||
service = ctx_tcp_pi->service;
|
||||
|
||||
memset(&ai_hints, 0, sizeof (ai_hints));
|
||||
ai_hints.ai_flags |= AI_PASSIVE;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
ai_hints.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif
|
||||
ai_hints.ai_family = AF_UNSPEC;
|
||||
ai_hints.ai_socktype = SOCK_STREAM;
|
||||
ai_hints.ai_addr = NULL;
|
||||
ai_hints.ai_canonname = NULL;
|
||||
ai_hints.ai_next = NULL;
|
||||
|
||||
ai_list = NULL;
|
||||
rc = getaddrinfo(node, service, &ai_hints, &ai_list);
|
||||
if (rc != 0)
|
||||
return -1;
|
||||
|
||||
new_socket = -1;
|
||||
for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
|
||||
int s;
|
||||
|
||||
s = socket(ai_ptr->ai_family, ai_ptr->ai_socktype,
|
||||
ai_ptr->ai_protocol);
|
||||
if (s < 0) {
|
||||
if (ctx->debug) {
|
||||
perror("socket");
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
int yes = 1;
|
||||
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
|
||||
(void *) &yes, sizeof (yes));
|
||||
if (rc != 0) {
|
||||
close(s);
|
||||
if (ctx->debug) {
|
||||
perror("setsockopt");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
rc = bind(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
|
||||
if (rc != 0) {
|
||||
close(s);
|
||||
if (ctx->debug) {
|
||||
perror("bind");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = listen(s, nb_connection);
|
||||
if (rc != 0) {
|
||||
close(s);
|
||||
if (ctx->debug) {
|
||||
perror("listen");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
new_socket = s;
|
||||
break;
|
||||
}
|
||||
freeaddrinfo(ai_list);
|
||||
|
||||
if (new_socket < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return new_socket;
|
||||
}
|
||||
|
||||
/* On success, the function return a non-negative integer that is a descriptor
|
||||
for the accepted socket. On error, -1 is returned, and errno is set
|
||||
appropriately. */
|
||||
int modbus_tcp_accept(modbus_t *ctx, int *socket)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen;
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
ctx->s = accept(*socket, (struct sockaddr *)&addr, &addrlen);
|
||||
if (ctx->s == -1) {
|
||||
close(*socket);
|
||||
*socket = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->debug) {
|
||||
printf("The client connection from %s is accepted\n",
|
||||
inet_ntoa(addr.sin_addr));
|
||||
}
|
||||
|
||||
return ctx->s;
|
||||
}
|
||||
|
||||
int modbus_tcp_pi_accept(modbus_t *ctx, int *socket)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
ctx->s = accept(*socket, (void *)&addr, &addrlen);
|
||||
if (ctx->s == -1) {
|
||||
close(*socket);
|
||||
*socket = 0;
|
||||
}
|
||||
|
||||
if (ctx->debug) {
|
||||
printf("The client connection is accepted.\n");
|
||||
}
|
||||
|
||||
return ctx->s;
|
||||
}
|
||||
|
||||
int _modbus_tcp_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int length_to_read)
|
||||
{
|
||||
int s_rc;
|
||||
while ((s_rc = select(ctx->s+1, rfds, NULL, NULL, tv)) == -1) {
|
||||
if (errno == EINTR) {
|
||||
if (ctx->debug) {
|
||||
fprintf(stderr, "A non blocked signal was caught\n");
|
||||
}
|
||||
/* Necessary after an error */
|
||||
FD_ZERO(rfds);
|
||||
FD_SET(ctx->s, rfds);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_rc == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s_rc;
|
||||
}
|
||||
|
||||
int _modbus_tcp_filter_request(modbus_t *ctx, int slave)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const modbus_backend_t _modbus_tcp_backend = {
|
||||
_MODBUS_BACKEND_TYPE_TCP,
|
||||
_MODBUS_TCP_HEADER_LENGTH,
|
||||
_MODBUS_TCP_CHECKSUM_LENGTH,
|
||||
MODBUS_TCP_MAX_ADU_LENGTH,
|
||||
_modbus_set_slave,
|
||||
_modbus_tcp_build_request_basis,
|
||||
_modbus_tcp_build_response_basis,
|
||||
_modbus_tcp_prepare_response_tid,
|
||||
_modbus_tcp_send_msg_pre,
|
||||
_modbus_tcp_send,
|
||||
_modbus_tcp_recv,
|
||||
_modbus_tcp_check_integrity,
|
||||
_modbus_tcp_pre_check_confirmation,
|
||||
_modbus_tcp_connect,
|
||||
_modbus_tcp_close,
|
||||
_modbus_tcp_flush,
|
||||
_modbus_tcp_select,
|
||||
_modbus_tcp_filter_request
|
||||
};
|
||||
|
||||
|
||||
const modbus_backend_t _modbus_tcp_pi_backend = {
|
||||
_MODBUS_BACKEND_TYPE_TCP,
|
||||
_MODBUS_TCP_HEADER_LENGTH,
|
||||
_MODBUS_TCP_CHECKSUM_LENGTH,
|
||||
MODBUS_TCP_MAX_ADU_LENGTH,
|
||||
_modbus_set_slave,
|
||||
_modbus_tcp_build_request_basis,
|
||||
_modbus_tcp_build_response_basis,
|
||||
_modbus_tcp_prepare_response_tid,
|
||||
_modbus_tcp_send_msg_pre,
|
||||
_modbus_tcp_send,
|
||||
_modbus_tcp_recv,
|
||||
_modbus_tcp_check_integrity,
|
||||
_modbus_tcp_pre_check_confirmation,
|
||||
_modbus_tcp_pi_connect,
|
||||
_modbus_tcp_close,
|
||||
_modbus_tcp_flush,
|
||||
_modbus_tcp_select,
|
||||
_modbus_tcp_filter_request
|
||||
};
|
||||
|
||||
modbus_t* modbus_new_tcp(const char *ip, int port)
|
||||
{
|
||||
modbus_t *ctx;
|
||||
modbus_tcp_t *ctx_tcp;
|
||||
size_t dest_size;
|
||||
size_t ret_size;
|
||||
|
||||
#if defined(OS_BSD)
|
||||
/* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore
|
||||
handler for SIGPIPE. */
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
if (sigaction(SIGPIPE, &sa, NULL) < 0) {
|
||||
/* The debug flag can't be set here... */
|
||||
fprintf(stderr, "Coud not install SIGPIPE handler.\n");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx = (modbus_t *) malloc(sizeof(modbus_t));
|
||||
_modbus_init_common(ctx);
|
||||
|
||||
/* Could be changed after to reach a remote serial Modbus device */
|
||||
ctx->slave = MODBUS_TCP_SLAVE;
|
||||
|
||||
ctx->backend = &(_modbus_tcp_backend);
|
||||
|
||||
ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t));
|
||||
ctx_tcp = (modbus_tcp_t *)ctx->backend_data;
|
||||
|
||||
dest_size = sizeof(char) * 16;
|
||||
ret_size = strlcpy(ctx_tcp->ip, ip, dest_size);
|
||||
if (ret_size == 0) {
|
||||
fprintf(stderr, "The IP string is empty\n");
|
||||
modbus_free(ctx);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ret_size >= dest_size) {
|
||||
fprintf(stderr, "The IP string has been truncated\n");
|
||||
modbus_free(ctx);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx_tcp->port = port;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
modbus_t* modbus_new_tcp_pi(const char *node, const char *service)
|
||||
{
|
||||
modbus_t *ctx;
|
||||
modbus_tcp_pi_t *ctx_tcp_pi;
|
||||
size_t dest_size;
|
||||
size_t ret_size;
|
||||
|
||||
ctx = (modbus_t *) malloc(sizeof(modbus_t));
|
||||
_modbus_init_common(ctx);
|
||||
|
||||
/* Could be changed after to reach a remote serial Modbus device */
|
||||
ctx->slave = MODBUS_TCP_SLAVE;
|
||||
|
||||
ctx->backend = &(_modbus_tcp_pi_backend);
|
||||
|
||||
ctx->backend_data = (modbus_tcp_pi_t *) malloc(sizeof(modbus_tcp_pi_t));
|
||||
ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data;
|
||||
|
||||
dest_size = sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH;
|
||||
ret_size = strlcpy(ctx_tcp_pi->node, node, dest_size);
|
||||
if (ret_size == 0) {
|
||||
fprintf(stderr, "The node string is empty\n");
|
||||
modbus_free(ctx);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ret_size >= dest_size) {
|
||||
fprintf(stderr, "The node string has been truncated\n");
|
||||
modbus_free(ctx);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dest_size = sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH;
|
||||
ret_size = strlcpy(ctx_tcp_pi->service, service, dest_size);
|
||||
if (ret_size == 0) {
|
||||
fprintf(stderr, "The service string is empty\n");
|
||||
modbus_free(ctx);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ret_size >= dest_size) {
|
||||
fprintf(stderr, "The service string has been truncated\n");
|
||||
modbus_free(ctx);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
53
examples/libmodbus/src/modbus-tcp.h
Normal file
53
examples/libmodbus/src/modbus-tcp.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _MODBUS_TCP_H_
|
||||
#define _MODBUS_TCP_H_
|
||||
|
||||
#include "modbus.h"
|
||||
|
||||
MODBUS_BEGIN_DECLS
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
/* Win32 with MinGW, supplement to <errno.h> */
|
||||
#include <winsock2.h>
|
||||
#define ECONNRESET WSAECONNRESET
|
||||
#define ECONNREFUSED WSAECONNREFUSED
|
||||
#define ETIMEDOUT WSAETIMEDOUT
|
||||
#define ENOPROTOOPT WSAENOPROTOOPT
|
||||
#endif
|
||||
|
||||
#define MODBUS_TCP_DEFAULT_PORT 502
|
||||
#define MODBUS_TCP_SLAVE 0xFF
|
||||
|
||||
/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
|
||||
* TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes
|
||||
*/
|
||||
#define MODBUS_TCP_MAX_ADU_LENGTH 260
|
||||
|
||||
modbus_t* modbus_new_tcp(const char *ip_address, int port);
|
||||
int modbus_tcp_listen(modbus_t *ctx, int nb_connection);
|
||||
int modbus_tcp_accept(modbus_t *ctx, int *socket);
|
||||
|
||||
modbus_t* modbus_new_tcp_pi(const char *node, const char *service);
|
||||
int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection);
|
||||
int modbus_tcp_pi_accept(modbus_t *ctx, int *socket);
|
||||
|
||||
MODBUS_END_DECLS
|
||||
|
||||
#endif /* _MODBUS_TCP_H_ */
|
52
examples/libmodbus/src/modbus-version.h
Normal file
52
examples/libmodbus/src/modbus-version.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright © 2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _MODBUS_VERSION_H_
|
||||
#define _MODBUS_VERSION_H_
|
||||
|
||||
/* The major version, (1, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||
#define LIBMODBUS_VERSION_MAJOR (3)
|
||||
|
||||
/* The minor version (2, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||
#define LIBMODBUS_VERSION_MINOR (0)
|
||||
|
||||
/* The micro version (3, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||
#define LIBMODBUS_VERSION_MICRO (8)
|
||||
|
||||
/* The full version, like 1.2.3 */
|
||||
#define LIBMODBUS_VERSION 3.0.8
|
||||
|
||||
/* The full version, in string form (suited for string concatenation)
|
||||
*/
|
||||
#define LIBMODBUS_VERSION_STRING "3.0.8"
|
||||
|
||||
/* Numerically encoded version, like 0x010203 */
|
||||
#define LIBMODBUS_VERSION_HEX ((LIBMODBUS_MAJOR_VERSION << 24) | \
|
||||
(LIBMODBUS_MINOR_VERSION << 16) | \
|
||||
(LIBMODBUS_MICRO_VERSION << 8))
|
||||
|
||||
/* Evaluates to True if the version is greater than @major, @minor and @micro
|
||||
*/
|
||||
#define LIBMODBUS_VERSION_CHECK(major,minor,micro) \
|
||||
(LIBMODBUS_VERSION_MAJOR > (major) || \
|
||||
(LIBMODBUS_VERSION_MAJOR == (major) && \
|
||||
LIBMODBUS_VERSION_MINOR > (minor)) || \
|
||||
(LIBMODBUS_VERSION_MAJOR == (major) && \
|
||||
LIBMODBUS_VERSION_MINOR == (minor) && \
|
||||
LIBMODBUS_VERSION_MICRO >= (micro)))
|
||||
|
||||
#endif /* _MODBUS_VERSION_H_ */
|
52
examples/libmodbus/src/modbus-version.h.in
Normal file
52
examples/libmodbus/src/modbus-version.h.in
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright © 2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _MODBUS_VERSION_H_
|
||||
#define _MODBUS_VERSION_H_
|
||||
|
||||
/* The major version, (1, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||
#define LIBMODBUS_VERSION_MAJOR (@LIBMODBUS_VERSION_MAJOR@)
|
||||
|
||||
/* The minor version (2, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||
#define LIBMODBUS_VERSION_MINOR (@LIBMODBUS_VERSION_MINOR@)
|
||||
|
||||
/* The micro version (3, if %LIBMODBUS_VERSION is 1.2.3) */
|
||||
#define LIBMODBUS_VERSION_MICRO (@LIBMODBUS_VERSION_MICRO@)
|
||||
|
||||
/* The full version, like 1.2.3 */
|
||||
#define LIBMODBUS_VERSION @LIBMODBUS_VERSION@
|
||||
|
||||
/* The full version, in string form (suited for string concatenation)
|
||||
*/
|
||||
#define LIBMODBUS_VERSION_STRING "@LIBMODBUS_VERSION@"
|
||||
|
||||
/* Numerically encoded version, like 0x010203 */
|
||||
#define LIBMODBUS_VERSION_HEX ((LIBMODBUS_MAJOR_VERSION << 24) | \
|
||||
(LIBMODBUS_MINOR_VERSION << 16) | \
|
||||
(LIBMODBUS_MICRO_VERSION << 8))
|
||||
|
||||
/* Evaluates to True if the version is greater than @major, @minor and @micro
|
||||
*/
|
||||
#define LIBMODBUS_VERSION_CHECK(major,minor,micro) \
|
||||
(LIBMODBUS_VERSION_MAJOR > (major) || \
|
||||
(LIBMODBUS_VERSION_MAJOR == (major) && \
|
||||
LIBMODBUS_VERSION_MINOR > (minor)) || \
|
||||
(LIBMODBUS_VERSION_MAJOR == (major) && \
|
||||
LIBMODBUS_VERSION_MINOR == (minor) && \
|
||||
LIBMODBUS_VERSION_MICRO >= (micro)))
|
||||
|
||||
#endif /* _MODBUS_VERSION_H_ */
|
1677
examples/libmodbus/src/modbus.c
Normal file
1677
examples/libmodbus/src/modbus.c
Normal file
File diff suppressed because it is too large
Load Diff
223
examples/libmodbus/src/modbus.h
Normal file
223
examples/libmodbus/src/modbus.h
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _MODBUS_H_
|
||||
#define _MODBUS_H_
|
||||
|
||||
/* Add this for macros that defined unix flavor */
|
||||
#if (defined(__unix__) || defined(unix)) && !defined(USG)
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include "stdint.h"
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#include "modbus-version.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define MODBUS_BEGIN_DECLS extern "C" {
|
||||
# define MODBUS_END_DECLS }
|
||||
#else
|
||||
# define MODBUS_BEGIN_DECLS
|
||||
# define MODBUS_END_DECLS
|
||||
#endif
|
||||
|
||||
MODBUS_BEGIN_DECLS
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef OFF
|
||||
#define OFF 0
|
||||
#endif
|
||||
|
||||
#ifndef ON
|
||||
#define ON 1
|
||||
#endif
|
||||
|
||||
#define MODBUS_BROADCAST_ADDRESS 0
|
||||
|
||||
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12)
|
||||
* Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0)
|
||||
* (chapter 6 section 11 page 29)
|
||||
* Quantity of Coils to write (2 bytes): 1 to 1968 (0x7B0)
|
||||
*/
|
||||
#define MODBUS_MAX_READ_BITS 2000
|
||||
#define MODBUS_MAX_WRITE_BITS 1968
|
||||
|
||||
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15)
|
||||
* Quantity of Registers to read (2 bytes): 1 to 125 (0x7D)
|
||||
* (chapter 6 section 12 page 31)
|
||||
* Quantity of Registers to write (2 bytes) 1 to 123 (0x7B)
|
||||
* (chapter 6 section 17 page 38)
|
||||
* Quantity of Registers to write in R/W registers (2 bytes) 1 to 121 (0x79)
|
||||
*/
|
||||
#define MODBUS_MAX_READ_REGISTERS 125
|
||||
#define MODBUS_MAX_WRITE_REGISTERS 123
|
||||
#define MODBUS_MAX_RW_WRITE_REGISTERS 121
|
||||
|
||||
/* Random number to avoid errno conflicts */
|
||||
#define MODBUS_ENOBASE 112345678
|
||||
|
||||
/* Protocol exceptions */
|
||||
enum {
|
||||
MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 0x01,
|
||||
MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS,
|
||||
MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,
|
||||
MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE,
|
||||
MODBUS_EXCEPTION_ACKNOWLEDGE,
|
||||
MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY,
|
||||
MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE,
|
||||
MODBUS_EXCEPTION_MEMORY_PARITY,
|
||||
MODBUS_EXCEPTION_NOT_DEFINED,
|
||||
MODBUS_EXCEPTION_GATEWAY_PATH,
|
||||
MODBUS_EXCEPTION_GATEWAY_TARGET,
|
||||
MODBUS_EXCEPTION_MAX
|
||||
};
|
||||
|
||||
#define EMBXILFUN (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION)
|
||||
#define EMBXILADD (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS)
|
||||
#define EMBXILVAL (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE)
|
||||
#define EMBXSFAIL (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE)
|
||||
#define EMBXACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE)
|
||||
#define EMBXSBUSY (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY)
|
||||
#define EMBXNACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE)
|
||||
#define EMBXMEMPAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY)
|
||||
#define EMBXGPATH (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH)
|
||||
#define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET)
|
||||
|
||||
/* Native libmodbus error codes */
|
||||
#define EMBBADCRC (EMBXGTAR + 1)
|
||||
#define EMBBADDATA (EMBXGTAR + 2)
|
||||
#define EMBBADEXC (EMBXGTAR + 3)
|
||||
#define EMBUNKEXC (EMBXGTAR + 4)
|
||||
#define EMBMDATA (EMBXGTAR + 5)
|
||||
|
||||
extern const unsigned int libmodbus_version_major;
|
||||
extern const unsigned int libmodbus_version_minor;
|
||||
extern const unsigned int libmodbus_version_micro;
|
||||
|
||||
typedef struct _modbus modbus_t;
|
||||
|
||||
typedef struct {
|
||||
int nb_bits;
|
||||
int nb_input_bits;
|
||||
int nb_input_registers;
|
||||
int nb_registers;
|
||||
uint8_t *tab_bits;
|
||||
uint8_t *tab_input_bits;
|
||||
uint16_t *tab_input_registers;
|
||||
uint16_t *tab_registers;
|
||||
} modbus_mapping_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MODBUS_ERROR_RECOVERY_NONE = 0,
|
||||
MODBUS_ERROR_RECOVERY_LINK = (1<<1),
|
||||
MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2),
|
||||
} modbus_error_recovery_mode;
|
||||
|
||||
int modbus_set_slave(modbus_t* ctx, int slave);
|
||||
int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery);
|
||||
void modbus_set_socket(modbus_t *ctx, int socket);
|
||||
int modbus_get_socket(modbus_t *ctx);
|
||||
|
||||
void modbus_get_response_timeout(modbus_t *ctx, struct timeval *timeout);
|
||||
void modbus_set_response_timeout(modbus_t *ctx, const struct timeval *timeout);
|
||||
|
||||
void modbus_get_byte_timeout(modbus_t *ctx, struct timeval *timeout);
|
||||
void modbus_set_byte_timeout(modbus_t *ctx, const struct timeval *timeout);
|
||||
|
||||
int modbus_get_header_length(modbus_t *ctx);
|
||||
|
||||
int modbus_connect(modbus_t *ctx);
|
||||
void modbus_close(modbus_t *ctx);
|
||||
|
||||
void modbus_free(modbus_t *ctx);
|
||||
|
||||
int modbus_flush(modbus_t *ctx);
|
||||
void modbus_set_debug(modbus_t *ctx, int boolean);
|
||||
|
||||
const char *modbus_strerror(int errnum);
|
||||
|
||||
int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
|
||||
int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
|
||||
int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
|
||||
int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
|
||||
int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
|
||||
int modbus_write_register(modbus_t *ctx, int reg_addr, int value);
|
||||
int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
|
||||
int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
|
||||
int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb,
|
||||
const uint16_t *src, int read_addr, int read_nb,
|
||||
uint16_t *dest);
|
||||
int modbus_report_slave_id(modbus_t *ctx, uint8_t *dest);
|
||||
|
||||
modbus_mapping_t* modbus_mapping_new(int nb_coil_status, int nb_input_status,
|
||||
int nb_holding_registers, int nb_input_registers);
|
||||
void modbus_mapping_free(modbus_mapping_t *mb_mapping);
|
||||
|
||||
int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length);
|
||||
|
||||
int modbus_receive(modbus_t *ctx, uint8_t *req);
|
||||
int modbus_receive_from(modbus_t *ctx, int sockfd, uint8_t *req);
|
||||
|
||||
int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
|
||||
|
||||
int modbus_reply(modbus_t *ctx, const uint8_t *req,
|
||||
int req_length, modbus_mapping_t *mb_mapping);
|
||||
int modbus_reply_exception(modbus_t *ctx, const uint8_t *req,
|
||||
unsigned int exception_code);
|
||||
|
||||
/**
|
||||
* UTILS FUNCTIONS
|
||||
**/
|
||||
|
||||
#define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF)
|
||||
#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF)
|
||||
#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) ((tab_int16[(index)] << 16) + tab_int16[(index) + 1])
|
||||
#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) ((tab_int8[(index)] << 8) + tab_int8[(index) + 1])
|
||||
#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \
|
||||
do { \
|
||||
tab_int8[(index)] = (value) >> 8; \
|
||||
tab_int8[(index) + 1] = (value) & 0xFF; \
|
||||
} while (0)
|
||||
|
||||
void modbus_set_bits_from_byte(uint8_t *dest, int index, const uint8_t value);
|
||||
void modbus_set_bits_from_bytes(uint8_t *dest, int index, unsigned int nb_bits,
|
||||
const uint8_t *tab_byte);
|
||||
uint8_t modbus_get_byte_from_bits(const uint8_t *src, int index, unsigned int nb_bits);
|
||||
float modbus_get_float(const uint16_t *src);
|
||||
void modbus_set_float(float f, uint16_t *dest);
|
||||
|
||||
#include "modbus-tcp.h"
|
||||
#include "modbus-rtu.h"
|
||||
|
||||
MODBUS_END_DECLS
|
||||
|
||||
#endif /* _MODBUS_H_ */
|
@ -559,7 +559,7 @@ LRESULT BasicForm::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
mMonitor->GetRootNode()->GetChildNode(7)->AddChildNode(node);
|
||||
|
||||
if (mModbusMasterForm.find(p->name) == mModbusMasterForm.end()) {
|
||||
auto form = new UartForm(this, p->name, p->baurate, p->port_num,
|
||||
auto form = new ModbusMasterForm(this, p->name, p->baurate, p->port_num,
|
||||
p->data_bits, p->stop_bits, p->verify, p->flow_control);
|
||||
form->SetChildLayoutXML(L"basic/uart_form.xml");
|
||||
form->SetName(p->name);
|
||||
@ -568,11 +568,12 @@ LRESULT BasicForm::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
if (!mRightSide->Add(form))
|
||||
printf("erroer 1");
|
||||
}
|
||||
|
||||
node->AttachAllEvents([this](ui::EventArgs* ev) {
|
||||
if (ui::EventType::kEventSelect == ev->Type) {
|
||||
wprintf(L"%s\r\n", dynamic_cast<ui::TreeNode*> (ev->pSender)->GetText().c_str());
|
||||
printf("GetCurSel %d\r\n", mRightSide->GetCurSel());
|
||||
UartForm* p = mModbusMasterForm[dynamic_cast<ui::TreeNode*> (ev->pSender)->GetText()];
|
||||
ModbusMasterForm* p = mModbusMasterForm[dynamic_cast<ui::TreeNode*> (ev->pSender)->GetText()];
|
||||
if (p != nullptr) {
|
||||
p->SetAutoDestroy(true);
|
||||
if (mRightShow != nullptr) {
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "websocket_client_form.h"
|
||||
#include "websocket_client.h"
|
||||
#include "websocket_server_form.h"
|
||||
#include "modbus_form.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
@ -66,7 +67,9 @@ private:
|
||||
std::map<std::wstring, UdpGroupForm*> mUdpGroupForm;
|
||||
std::map<std::wstring, WebsocketClientForm*> mWebsocketClientForm;
|
||||
std::map<std::wstring, WebsocketServerForm*> mWebsocketServerForm;
|
||||
std::map<std::wstring, UartForm*> mModbusMasterForm;
|
||||
std::map<std::wstring, ModbusMasterForm*> mModbusMasterForm;
|
||||
|
||||
ui::Control* mRightShow;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1,21 +1,18 @@
|
||||
#ifndef LUA_WRAPER_H
|
||||
#define LUA_WRAPER_H
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include<typeinfo>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
}
|
||||
|
||||
|
||||
class LuaDelegate {
|
||||
public:
|
||||
LuaDelegate();
|
||||
|
@ -38,3 +38,4 @@ private:
|
||||
*/
|
||||
virtual void Cleanup() override;
|
||||
};
|
||||
|
||||
|
160
examples/proto_debuger/modbus_form.cpp
Normal file
160
examples/proto_debuger/modbus_form.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
#include "modbus_form.h"
|
||||
#include "lua_bind.h"
|
||||
#include "msgdef.h"
|
||||
|
||||
ModbusMasterForm::ModbusMasterForm(ui::Window* hwnd, std::wstring name,
|
||||
uint32_t baurate, UINT portnum,
|
||||
uint8_t data_bits, uint8_t stop_bits,
|
||||
uint8_t verify, uint8_t flow_control) {
|
||||
|
||||
m_portnum = portnum;
|
||||
m_name = name;
|
||||
m_baurate = baurate;
|
||||
m_data_bits = data_bits;
|
||||
m_stop_bits = stop_bits;
|
||||
m_verify = verify;
|
||||
m_flow_contro = flow_control;
|
||||
|
||||
m_runing = true;
|
||||
mLua = new LuaDelegate;
|
||||
|
||||
mLuaFile.open(MODBUS_LUA_SCRIPT);
|
||||
std::string lua_script;
|
||||
if (mLuaFile.is_open()) {
|
||||
std::string s;
|
||||
while (getline(mLuaFile, s)) {
|
||||
lua_script += s + "\r\n";
|
||||
}
|
||||
mLuaFile.close();
|
||||
}
|
||||
mLuaScript = lua_script;
|
||||
mLua->DoString(lua_script);
|
||||
std::cout << "lua script is " << lua_script << std::endl;
|
||||
|
||||
m_thread_recv = new std::thread([this]() {
|
||||
UINT PortNum = 0;
|
||||
for (int i = 3; m_name[i] != '\0'; i++)
|
||||
{
|
||||
PortNum = PortNum * 10 + (m_name[i] - '0');
|
||||
}
|
||||
char recv[1024] = { 0 };
|
||||
while (this->m_runing) {
|
||||
if (0 < SerialPort::ReadPort(PortNum, recv, 1024)) {
|
||||
printf("recv data: %s", recv);
|
||||
this->m_show_recv += string2wstring(recv);
|
||||
this->mLua->CallFuntion<std::string>("OnUartData", std::string(recv));
|
||||
if (this->mEditRecv != nullptr) {
|
||||
this->mEditRecv->AppendText(string2wstring(recv), false);
|
||||
::PostMessage(this->GetWindow()->GetHWND(), WM_ADD_UART_RECVDATA, 0, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Sleep(100);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (nullptr != hwnd) {
|
||||
// set window so we get getwindow by GetWindow funtion
|
||||
this->SetWindow(hwnd, nullptr, false);
|
||||
}
|
||||
this->mLua->BindFunction("showdata", LuaShowData);
|
||||
|
||||
}
|
||||
|
||||
ModbusMasterForm::~ModbusMasterForm() {
|
||||
|
||||
}
|
||||
|
||||
void ModbusMasterForm::Init() {
|
||||
ui::ChildBox::Init();
|
||||
/*
|
||||
auto mRightSide = dynamic_cast<ui::Label*> (FindSubControl(L"uart_info_label"));
|
||||
wchar_t p[100] = { 0 };
|
||||
wsprintf(p, L"串口号: %s 波特率%d 数据位: %d 停止位: %d ",
|
||||
m_name.c_str(), m_baurate,
|
||||
m_data_bits, m_stop_bits);
|
||||
mRightSide->SetText(std::wstring(p));
|
||||
|
||||
mEditSend = dynamic_cast<ui::RichEdit*>(FindSubControl(L"uart_send_edit"));
|
||||
mEditRecv = dynamic_cast<ui::RichEdit*>(FindSubControl(L"uart_recv_eidt"));
|
||||
mEditLua = dynamic_cast<ui::RichEdit*>(FindSubControl(L"lua_script"));
|
||||
mEditLua->SetRich(true);
|
||||
mEditLua->SetReturnMsgWantCtrl(true);
|
||||
|
||||
mEditLua->SetText(string2wstring(mLuaScript));
|
||||
mEditRecv->SetReadOnly(true);
|
||||
mEditRecv->SetRich(false);
|
||||
mEditRecv->SetAttribute(L"autovscroll", L"true");
|
||||
mEditRecv->SetAttribute(L"multiline", L"true");
|
||||
mEditRecv->SetReturnMsgWantCtrl(true);
|
||||
mEditRecv->SetNeedReturnMsg(true);
|
||||
mEditRecv->SetWordWrap(true);
|
||||
|
||||
auto mBtnSend = static_cast<ui::Button*>(FindSubControl(L"btn_send_data"));
|
||||
if (mBtnSend != nullptr) {
|
||||
mBtnSend->AttachClick([this](ui::EventArgs*) {
|
||||
UINT PortNum = 0;
|
||||
for (int i = 3; m_name[i] != '\0'; i++) //转换为数字
|
||||
{
|
||||
PortNum = PortNum * 10 + (m_name[i] - '0');
|
||||
}
|
||||
|
||||
auto x = mEditSend->GetText();
|
||||
auto tmp = wstring2string(x);
|
||||
wprintf(L"%s\r\n", x.c_str());
|
||||
SerialPort::WritePort(PortNum, tmp.c_str(), tmp.size());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
auto mBtnSaveLua = static_cast<ui::Button*>(FindSubControl(L"btn_save_lua"));
|
||||
if (nullptr != mBtnSaveLua) {
|
||||
mBtnSaveLua->AttachClick([this](ui::EventArgs*) {
|
||||
std::cout << "保存lua脚本\r\n";
|
||||
std::string lua = wstring2string(mEditLua->GetText());
|
||||
if (0 == this->mLua->UpdateScript(lua)) {
|
||||
this->mLuaScript = lua;
|
||||
mLuaFileEdit = std::ofstream(UART_LUA_SCRIPT, std::ios::out | std::ios::trunc);
|
||||
mLuaFileEdit.write(lua.c_str(), lua.size());
|
||||
mLuaFileEdit.flush();
|
||||
mLuaFileEdit.close();
|
||||
}
|
||||
else {
|
||||
MessageBox(0, L"lua脚本错误", L"", 0);
|
||||
return true;
|
||||
}
|
||||
std::cout << lua.c_str() << "\r\n";
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
auto mBtnClose = static_cast<ui::Button*>(FindSubControl(L"btn_close_uart"));
|
||||
if (mBtnClose != nullptr) {
|
||||
mBtnClose->AttachClick([this](ui::EventArgs*) {
|
||||
wstring* name = new wstring(this->GetName());
|
||||
::PostMessage(this->GetWindow()->GetHWND(),
|
||||
WM_ADD_UART_CLOSE, (WPARAM)name, 0);
|
||||
return true;
|
||||
});
|
||||
}*/
|
||||
}
|
||||
|
||||
void ModbusMasterForm::UpdateRecvEdit()
|
||||
{
|
||||
}
|
||||
|
||||
LuaDelegate* ModbusMasterForm::LuaVM() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ModbusMasterForm::ShowDataInEdit(const char*) {
|
||||
|
||||
}
|
||||
|
||||
void ModbusMasterForm::OnUpdateUart()
|
||||
{
|
||||
}
|
||||
|
||||
void ModbusMasterForm::HandleMessage(ui::EventArgs& msg) {
|
||||
|
||||
}
|
||||
|
64
examples/proto_debuger/modbus_form.h
Normal file
64
examples/proto_debuger/modbus_form.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
// C runtime header
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#include <tchar.h>
|
||||
#include <fstream>
|
||||
|
||||
// base header
|
||||
#include "base/base.h"
|
||||
#include "serial_port.h"
|
||||
// duilib
|
||||
#include "duilib/UIlib.h"
|
||||
#include "lua_wraper.h"
|
||||
#include "global.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define MODBUS_LUA_SCRIPT "modbus_script.lua"
|
||||
|
||||
|
||||
class ModbusMasterForm :
|
||||
public ui::ChildBox,
|
||||
LuaBindInterface
|
||||
{
|
||||
public:
|
||||
ModbusMasterForm(ui::Window* window, std::wstring name,
|
||||
uint32_t baurate, UINT portnum,
|
||||
uint8_t data_bits,
|
||||
uint8_t stop_bits,
|
||||
uint8_t verify,
|
||||
uint8_t flow_control);
|
||||
~ModbusMasterForm();
|
||||
|
||||
void ShowDataInEdit(const char*) override;
|
||||
void OnUpdateUart();
|
||||
LuaDelegate* LuaVM();
|
||||
/// 重写父类方法,提供个性化功能,请参考父类声明
|
||||
virtual void Init() override;
|
||||
void UpdateRecvEdit();
|
||||
UINT m_portnum;
|
||||
std::wstring m_name;
|
||||
uint32_t m_baurate;
|
||||
uint8_t m_data_bits;
|
||||
uint8_t m_stop_bits;
|
||||
uint8_t m_verify;
|
||||
uint8_t m_flow_contro;
|
||||
bool m_runing;
|
||||
std::thread* m_thread_recv;
|
||||
wstring m_show_recv;
|
||||
|
||||
ui::RichEdit* mEditSend;
|
||||
ui::RichEdit* mEditRecv;
|
||||
ui::RichEdit* mEditLua;
|
||||
|
||||
HWND m_hwnd;
|
||||
LuaDelegate* mLua;
|
||||
std::string mLuaScript;
|
||||
std::ifstream mLuaFile;
|
||||
std::ofstream mLuaFileEdit;
|
||||
|
||||
private:
|
||||
virtual void HandleMessage(ui::EventArgs& msg);
|
||||
};
|
||||
|
@ -19,3 +19,4 @@
|
||||
#define WM_ADD_WEBSOCKET_CLIENT (WM_USER + 16)
|
||||
#define WM_ADD_WEBSOCKET_CLIENT_CLOSE (WM_USER + 17)
|
||||
#define WM_ADD_UART_MODBUS_MASTER (WM_USER + 18)
|
||||
#define WM_ADD_UART_MODBUS_SLAVE (WM_USER + 19)
|
||||
|
@ -282,6 +282,35 @@ void NewMonitorForm::InitWindow()
|
||||
|
||||
}
|
||||
}
|
||||
if (m_combo_type->GetText() == L"modbus salve") {
|
||||
UINT PortNum = 0;
|
||||
for (int i = 3; m_uart_select_combo->GetText()[i] != '\0'; i++) //转换为数字
|
||||
{
|
||||
PortNum = PortNum * 10 + (m_uart_select_combo->GetText()[i] - '0');
|
||||
}
|
||||
if (1 == SerialPort::InitPort(PortNum, 115200, 0, 8, 0)) {
|
||||
// 打开成功
|
||||
MessageBox(0, L"打开成功", L"", 0);
|
||||
UartInfo* p = new UartInfo;
|
||||
p->port_num = PortNum;
|
||||
p->name = m_uart_select_combo->GetText();
|
||||
p->baurate = 115200;
|
||||
p->data_bits = 8;
|
||||
p->flow_control = 0;
|
||||
p->verify = 0;
|
||||
p->stop_bits = 1;
|
||||
|
||||
auto succ = ::PostMessage(m_parent->GetHWND(), WM_ADD_UART_MODBUS_SLAVE, (WPARAM)p, 0);
|
||||
if (!succ) {
|
||||
printf("postmessage error :%d\r\n", GetLastError());
|
||||
}
|
||||
this->Close();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
@ -168,6 +168,7 @@
|
||||
<ClCompile Include="loger.cpp" />
|
||||
<ClCompile Include="lua_wraper.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="modbus_form.cpp" />
|
||||
<ClCompile Include="new_monitor_form.cpp" />
|
||||
<ClCompile Include="serial_port.cpp" />
|
||||
<ClCompile Include="tcp_server_form.cpp" />
|
||||
@ -192,6 +193,7 @@
|
||||
<ClInclude Include="lua_bind.h" />
|
||||
<ClInclude Include="lua_wraper.h" />
|
||||
<ClInclude Include="main.h" />
|
||||
<ClInclude Include="modbus_form.h" />
|
||||
<ClInclude Include="msgdef.h" />
|
||||
<ClInclude Include="new_monitor_form.h" />
|
||||
<ClInclude Include="resource1.h" />
|
||||
|
@ -78,6 +78,9 @@
|
||||
<ClCompile Include="websocket_server_form.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="modbus_form.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="main.h">
|
||||
@ -140,6 +143,9 @@
|
||||
<ClInclude Include="websocket_server_form.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="modbus_form.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Xml Include="..\Debug\resources\themes\default\basic\basic.xml">
|
||||
|
@ -8,21 +8,21 @@
|
||||
map<int, HANDLE> SerialPort::ComMap;
|
||||
|
||||
//构造函数
|
||||
SerialPort::SerialPort()
|
||||
{
|
||||
SerialPort::SerialPort() {
|
||||
|
||||
} //构造函数
|
||||
}
|
||||
|
||||
//构造函数
|
||||
//析构函数
|
||||
SerialPort::~SerialPort()
|
||||
{
|
||||
|
||||
SerialPort::~SerialPort() {
|
||||
|
||||
}
|
||||
|
||||
//获取串口列表
|
||||
UINT SerialPort::GetPortNum(UINT** PortList)
|
||||
{
|
||||
UINT SerialPort::GetPortNum(UINT** PortList) {
|
||||
LPCTSTR Reg_Path = _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"); //串口注册表路径
|
||||
//TCHAR pnum[8];
|
||||
//TCHAR pnum[8];
|
||||
HKEY H_Key;
|
||||
static UINT portlist[255]; //一般情况下255个数组够用了。
|
||||
long Status;
|
||||
@ -39,23 +39,19 @@ UINT SerialPort::GetPortNum(UINT** PortList)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (true) {
|
||||
Com_Length = 256; //存储两个长度,每次都要赋一个大一点的值,不然下次可能会失败,Com_length值翻倍
|
||||
Type_Length = 256;
|
||||
PortNum = 0;
|
||||
Status = RegEnumValue(H_Key, count++, Type_Name, &Type_Length
|
||||
, 0, &Type, PUCHAR(Com_Name), &Com_Length);
|
||||
if (Status) //查询失败说明遍历结束,跳出循环。
|
||||
{
|
||||
if (Status) { //查询失败说明遍历结束,跳出循环。
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 3; Com_Name[i]; i++) //转换为数字
|
||||
{
|
||||
for (int i = 3; Com_Name[i]; i++) { //转换为数字
|
||||
PortNum = PortNum * 10 + (Com_Name[i] - '0');
|
||||
}
|
||||
|
||||
portlist[count - 1] = PortNum;
|
||||
}
|
||||
|
||||
@ -67,8 +63,6 @@ UINT SerialPort::GetPortNum(UINT** PortList)
|
||||
//打开端口
|
||||
HANDLE SerialPort::OpenPort(UINT PortNo) //打开端口
|
||||
{
|
||||
|
||||
|
||||
HANDLE H_Com; //串口句柄
|
||||
//PortNum = PortNo; //串口号
|
||||
|
||||
@ -83,16 +77,14 @@ HANDLE SerialPort::OpenPort(UINT PortNo) //
|
||||
OPEN_EXISTING, //已经存在的
|
||||
0,
|
||||
0); //打开串口,同步方式
|
||||
if (H_Com == INVALID_HANDLE_VALUE)//如果串口打开失败
|
||||
{
|
||||
if (H_Com == INVALID_HANDLE_VALUE) {//如果串口打开失败
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
SetupComm(H_Com, 1024, 1024); //设置读写缓冲区为1024;
|
||||
|
||||
ComMap[PortNo] = H_Com; // 打开成功则,存入map
|
||||
return H_Com;
|
||||
}
|
||||
|
||||
//关闭端口
|
||||
void SerialPort::ClosePort(UINT PortNo) //关闭串口
|
||||
{
|
||||
@ -107,25 +99,20 @@ void SerialPort::ClosePort(UINT PortNo) //
|
||||
}
|
||||
|
||||
//关闭所有端口
|
||||
void SerialPort::ClearAllPort() //关闭串口
|
||||
{
|
||||
void SerialPort::ClearAllPort() { //关闭串口
|
||||
for (map<int, HANDLE>::iterator iter = ComMap.begin(); iter != ComMap.end(); )
|
||||
{
|
||||
CloseHandle(iter->second); //关闭串口
|
||||
iter = ComMap.erase(iter); //必须这样写 否则迭代器失效会引发异常。
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//初始化串口
|
||||
int SerialPort::InitPort(UINT PortNo, UINT Baud, byte Parity, byte Data, byte Stop)
|
||||
{
|
||||
|
||||
int SerialPort::InitPort(UINT PortNo, UINT Baud, byte Parity, byte Data, byte Stop) {
|
||||
if (PortState(PortNo)) //如果端口已经存在
|
||||
{
|
||||
ClosePort(PortNo); //关掉端口
|
||||
}
|
||||
|
||||
if (Parity < 0 || Parity >4) //parity 0~4
|
||||
return 0;
|
||||
|
||||
@ -137,8 +124,7 @@ int SerialPort::InitPort(UINT PortNo, UINT Baud, byte Parity, byte Data, byte St
|
||||
//_stprintf_s(DcbPara, _T("baud=%d parity=%c data=%d stop=%d") , Baud, Parity, Data, Stop);
|
||||
H_Com = OpenPort(PortNo);
|
||||
|
||||
if (H_Com == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (H_Com == INVALID_HANDLE_VALUE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -147,17 +133,12 @@ int SerialPort::InitPort(UINT PortNo, UINT Baud, byte Parity, byte Data, byte St
|
||||
COMMTIMEOUTS ComTimeouts = { 0, 0, 10, 0, 10 };
|
||||
|
||||
DCB S_dcb;
|
||||
if (!SetCommTimeouts(H_Com, &ComTimeouts))//设置超时
|
||||
{
|
||||
|
||||
if (!SetCommTimeouts(H_Com, &ComTimeouts)) {//设置超时
|
||||
return 0;
|
||||
}
|
||||
if (!GetCommState(H_Com, &S_dcb)) //获取现有DCB结构
|
||||
{
|
||||
|
||||
if (!GetCommState(H_Com, &S_dcb)) { //获取现有DCB结构
|
||||
return 0;
|
||||
}
|
||||
|
||||
S_dcb.BaudRate = Baud;
|
||||
S_dcb.ByteSize = Data;
|
||||
S_dcb.Parity = Parity;
|
||||
@ -169,26 +150,20 @@ int SerialPort::InitPort(UINT PortNo, UINT Baud, byte Parity, byte Data, byte St
|
||||
//}
|
||||
|
||||
S_dcb.fRtsControl = RTS_CONTROL_DISABLE; //允许Rts信号
|
||||
if (!SetCommState(H_Com, &S_dcb)) //设置DCB
|
||||
{
|
||||
if (!SetCommState(H_Com, &S_dcb)) { //设置DCB
|
||||
int i = GetLastError();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PurgeComm(H_Com, PURGE_RXCLEAR | PURGE_TXCLEAR |
|
||||
PURGE_RXABORT | PURGE_TXABORT); //清空缓冲区
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SerialPort::PortState(UINT PortNo)
|
||||
{
|
||||
map<int, HANDLE>::iterator iter;
|
||||
|
||||
iter = ComMap.find(PortNo);
|
||||
|
||||
if (iter != ComMap.end()) //说明查找到了端口
|
||||
return 1;
|
||||
else
|
||||
@ -197,7 +172,6 @@ int SerialPort::PortState(UINT PortNo)
|
||||
//读串口
|
||||
int SerialPort::ReadPort(UINT PortNo, char* P_recved, int length) //重载函数 一次读取指定长度的数据
|
||||
{
|
||||
|
||||
if (PortState(PortNo) == 0) //串口未打开
|
||||
return 0;
|
||||
HANDLE H_Com = ComMap[PortNo];
|
||||
@ -231,33 +205,27 @@ int SerialPort::CheckRTS(UINT PortNo)
|
||||
//写串口
|
||||
int SerialPort::WritePort(UINT PortNo, const char* pData, int length)
|
||||
{
|
||||
|
||||
if (PortState(PortNo) == 0) //串口未打开
|
||||
return 0;
|
||||
HANDLE H_Com = ComMap[PortNo];
|
||||
|
||||
if (length == 0)
|
||||
length = strlen(pData);
|
||||
|
||||
DWORD Recn;
|
||||
BOOL Result = TRUE;
|
||||
|
||||
if (H_Com == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
Result = WriteFile(H_Com, pData, length, &Recn, NULL);
|
||||
if (Result == 0)
|
||||
{
|
||||
GetLastError();
|
||||
PurgeComm(H_Com, PURGE_RXCLEAR | PURGE_RXABORT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return Recn;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//获取要读取的字节数
|
||||
UINT SerialPort::GetByte(UINT PortNo) //该函数获取缓冲区字节长度。
|
||||
{
|
||||
|
@ -11,8 +11,8 @@ TcpClientForm::~TcpClientForm() {
|
||||
|
||||
TcpClientForm::TcpClientForm(ui::Window* hwnd,std::string url, uint32_t port, TcpClientLibevent* p):
|
||||
m_connected(false),
|
||||
mLua(nullptr)
|
||||
{
|
||||
mLua(nullptr) {
|
||||
|
||||
mClient = p;
|
||||
mClient->SetObserver(this);
|
||||
if (nullptr != hwnd) {
|
||||
@ -45,8 +45,7 @@ void TcpClientForm::ShowDataInEdit(const char*p) {
|
||||
m_rich_edit_1->AppendText(string2wstring(p), false);
|
||||
}
|
||||
|
||||
void TcpClientForm::Init()
|
||||
{
|
||||
void TcpClientForm::Init() {
|
||||
ui::ChildBox::Init();
|
||||
m_label_1 = dynamic_cast<ui::Label*>(FindSubControl(L"uart_info_label"));
|
||||
m_rich_edit_1 = dynamic_cast<ui::RichEdit*>(FindSubControl(L"uart_recv_eidt"));
|
||||
@ -107,34 +106,29 @@ void TcpClientForm::Init()
|
||||
}
|
||||
}
|
||||
|
||||
TcpClientLibevent* TcpClientForm::ClientEvent()
|
||||
{
|
||||
TcpClientLibevent* TcpClientForm::ClientEvent() {
|
||||
return mClient;
|
||||
}
|
||||
|
||||
LuaDelegate* TcpClientForm::LuaVM()
|
||||
{
|
||||
LuaDelegate* TcpClientForm::LuaVM() {
|
||||
return mLua;
|
||||
}
|
||||
|
||||
void TcpClientForm::OnConnected()
|
||||
{
|
||||
void TcpClientForm::OnConnected() {
|
||||
std::cout << "服务器连接成功\r\n"<<__FILE__<<" " << __LINE__ << std::endl;
|
||||
wchar_t p[100] = { 0 };
|
||||
wsprintf(p, L"地址%s,端口号%d 连接成功", string2wstring(m_url).c_str(), m_port);
|
||||
m_label_1->SetText(std::wstring(p));
|
||||
}
|
||||
|
||||
void TcpClientForm::OnDisConnected(std::string reason)
|
||||
{
|
||||
void TcpClientForm::OnDisConnected(std::string reason) {
|
||||
wchar_t p[100] = { 0 };
|
||||
wsprintf(p, L"地址%s,端口号%d 未连接 " , string2wstring(m_url).c_str(), m_port);
|
||||
m_label_1->SetText(std::wstring(p) + L" " + string2wstring(reason));
|
||||
m_connected = false;
|
||||
}
|
||||
|
||||
void TcpClientForm::OnData(uint8_t* dat, uint64_t len)
|
||||
{
|
||||
void TcpClientForm::OnData(uint8_t* dat, uint64_t len) {
|
||||
//std::cout << (char*)dat << "len is " << len << std::endl;
|
||||
//m_rich_edit_1->AppendText(string2wstring(std::string((char*)dat)), false);
|
||||
int ret = this->mLua->CallFuntion <std::string>
|
||||
@ -143,13 +137,11 @@ void TcpClientForm::OnData(uint8_t* dat, uint64_t len)
|
||||
MessageBox(0, L"lua脚本错误 OnClientRecvData", L"", 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TcpClientForm::OnClose()
|
||||
{
|
||||
|
||||
void TcpClientForm::OnClose() {
|
||||
m_rich_edit_1->AppendText(string2wstring("连接已经断开\r\n"), false);
|
||||
}
|
||||
|
||||
|
||||
void TcpClientForm::HandleMessage(ui::EventArgs& msg)
|
||||
{
|
||||
void TcpClientForm::HandleMessage(ui::EventArgs& msg) {
|
||||
}
|
||||
|
@ -55,3 +55,5 @@ private:
|
||||
std::string mLuaScript;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -55,3 +55,5 @@ std::string GenerateGuid()
|
||||
guid.Data4[7]);
|
||||
return std::string(cBuffer);
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,8 +4,7 @@
|
||||
#define WEBSOCKET_SERVER_LUA "websocket_server.lua"
|
||||
|
||||
WebsocketServerForm::WebsocketServerForm(ui::Window* hwnd, string url,
|
||||
WebsocketServer* server)
|
||||
{
|
||||
WebsocketServer* server) {
|
||||
if (nullptr != hwnd) {
|
||||
this->SetWindow(hwnd, nullptr, false);
|
||||
}
|
||||
@ -26,15 +25,13 @@ WebsocketServerForm::WebsocketServerForm(ui::Window* hwnd, string url,
|
||||
this->mLua->BindFunction("showdata", LuaShowData);
|
||||
}
|
||||
|
||||
WebsocketServerForm::~WebsocketServerForm()
|
||||
{
|
||||
WebsocketServerForm::~WebsocketServerForm() {
|
||||
std::cout << "~WebsocketServerForm\r\n";
|
||||
std::cout << "~WebsocketServerForm\r\n";
|
||||
delete m_server;
|
||||
}
|
||||
|
||||
void WebsocketServerForm::Init()
|
||||
{
|
||||
void WebsocketServerForm::Init() {
|
||||
ui::ChildBox::Init();
|
||||
|
||||
m_label_1 = dynamic_cast<ui::Label*>(FindSubControl(L"server_info"));
|
||||
@ -116,12 +113,11 @@ void WebsocketServerForm::Init()
|
||||
);
|
||||
}
|
||||
|
||||
LuaDelegate* WebsocketServerForm::LuaVM()
|
||||
{
|
||||
LuaDelegate* WebsocketServerForm::LuaVM() {
|
||||
return mLua;
|
||||
}
|
||||
|
||||
void WebsocketServerForm::ShowDataInEdit(const char*src)
|
||||
{
|
||||
void WebsocketServerForm::ShowDataInEdit(const char*src) {
|
||||
m_rich_edit_1->AppendText(string2wstring(std::string(src)), false);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user