no message
This commit is contained in:
parent
af649dec3f
commit
4be1252744
@ -34,4 +34,5 @@
|
|||||||
#include "win32/scoped_win_handle.h"
|
#include "win32/scoped_win_handle.h"
|
||||||
#include "win32/security_util.h"
|
#include "win32/security_util.h"
|
||||||
#include "win32/shared_memory.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
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "proto_debuger", "proto_debuger\proto_debuger.vcxproj", "{6D78D8B7-1617-4ED4-B155-94369FC3E73C}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "proto_debuger", "proto_debuger\proto_debuger.vcxproj", "{6D78D8B7-1617-4ED4-B155-94369FC3E73C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmodbus", "libmodbus\libmodbus.vcxproj", "{6FB16778-49D1-4BFD-B135-4D1BA6B5D68A}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Win32 = Debug|Win32
|
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|Win32.Build.0 = Release|Win32
|
||||||
{6D78D8B7-1617-4ED4-B155-94369FC3E73C}.Release|x64.ActiveCfg = Release|x64
|
{6D78D8B7-1617-4ED4-B155-94369FC3E73C}.Release|x64.ActiveCfg = Release|x64
|
||||||
{6D78D8B7-1617-4ED4-B155-94369FC3E73C}.Release|x64.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
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);
|
mMonitor->GetRootNode()->GetChildNode(7)->AddChildNode(node);
|
||||||
|
|
||||||
if (mModbusMasterForm.find(p->name) == mModbusMasterForm.end()) {
|
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);
|
p->data_bits, p->stop_bits, p->verify, p->flow_control);
|
||||||
form->SetChildLayoutXML(L"basic/uart_form.xml");
|
form->SetChildLayoutXML(L"basic/uart_form.xml");
|
||||||
form->SetName(p->name);
|
form->SetName(p->name);
|
||||||
@ -568,11 +568,12 @@ LRESULT BasicForm::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||||||
if (!mRightSide->Add(form))
|
if (!mRightSide->Add(form))
|
||||||
printf("erroer 1");
|
printf("erroer 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
node->AttachAllEvents([this](ui::EventArgs* ev) {
|
node->AttachAllEvents([this](ui::EventArgs* ev) {
|
||||||
if (ui::EventType::kEventSelect == ev->Type) {
|
if (ui::EventType::kEventSelect == ev->Type) {
|
||||||
wprintf(L"%s\r\n", dynamic_cast<ui::TreeNode*> (ev->pSender)->GetText().c_str());
|
wprintf(L"%s\r\n", dynamic_cast<ui::TreeNode*> (ev->pSender)->GetText().c_str());
|
||||||
printf("GetCurSel %d\r\n", mRightSide->GetCurSel());
|
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) {
|
if (p != nullptr) {
|
||||||
p->SetAutoDestroy(true);
|
p->SetAutoDestroy(true);
|
||||||
if (mRightShow != nullptr) {
|
if (mRightShow != nullptr) {
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "websocket_client_form.h"
|
#include "websocket_client_form.h"
|
||||||
#include "websocket_client.h"
|
#include "websocket_client.h"
|
||||||
#include "websocket_server_form.h"
|
#include "websocket_server_form.h"
|
||||||
|
#include "modbus_form.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
@ -66,7 +67,9 @@ private:
|
|||||||
std::map<std::wstring, UdpGroupForm*> mUdpGroupForm;
|
std::map<std::wstring, UdpGroupForm*> mUdpGroupForm;
|
||||||
std::map<std::wstring, WebsocketClientForm*> mWebsocketClientForm;
|
std::map<std::wstring, WebsocketClientForm*> mWebsocketClientForm;
|
||||||
std::map<std::wstring, WebsocketServerForm*> mWebsocketServerForm;
|
std::map<std::wstring, WebsocketServerForm*> mWebsocketServerForm;
|
||||||
std::map<std::wstring, UartForm*> mModbusMasterForm;
|
std::map<std::wstring, ModbusMasterForm*> mModbusMasterForm;
|
||||||
|
|
||||||
ui::Control* mRightShow;
|
ui::Control* mRightShow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
#ifndef LUA_WRAPER_H
|
#ifndef LUA_WRAPER_H
|
||||||
#define LUA_WRAPER_H
|
#define LUA_WRAPER_H
|
||||||
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include<typeinfo>
|
#include<typeinfo>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
#include "lauxlib.h"
|
#include "lauxlib.h"
|
||||||
#include "lualib.h"
|
#include "lualib.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LuaDelegate {
|
class LuaDelegate {
|
||||||
public:
|
public:
|
||||||
LuaDelegate();
|
LuaDelegate();
|
||||||
|
@ -38,3 +38,4 @@ private:
|
|||||||
*/
|
*/
|
||||||
virtual void Cleanup() override;
|
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 (WM_USER + 16)
|
||||||
#define WM_ADD_WEBSOCKET_CLIENT_CLOSE (WM_USER + 17)
|
#define WM_ADD_WEBSOCKET_CLIENT_CLOSE (WM_USER + 17)
|
||||||
#define WM_ADD_UART_MODBUS_MASTER (WM_USER + 18)
|
#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;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
<ClCompile Include="loger.cpp" />
|
<ClCompile Include="loger.cpp" />
|
||||||
<ClCompile Include="lua_wraper.cpp" />
|
<ClCompile Include="lua_wraper.cpp" />
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="modbus_form.cpp" />
|
||||||
<ClCompile Include="new_monitor_form.cpp" />
|
<ClCompile Include="new_monitor_form.cpp" />
|
||||||
<ClCompile Include="serial_port.cpp" />
|
<ClCompile Include="serial_port.cpp" />
|
||||||
<ClCompile Include="tcp_server_form.cpp" />
|
<ClCompile Include="tcp_server_form.cpp" />
|
||||||
@ -192,6 +193,7 @@
|
|||||||
<ClInclude Include="lua_bind.h" />
|
<ClInclude Include="lua_bind.h" />
|
||||||
<ClInclude Include="lua_wraper.h" />
|
<ClInclude Include="lua_wraper.h" />
|
||||||
<ClInclude Include="main.h" />
|
<ClInclude Include="main.h" />
|
||||||
|
<ClInclude Include="modbus_form.h" />
|
||||||
<ClInclude Include="msgdef.h" />
|
<ClInclude Include="msgdef.h" />
|
||||||
<ClInclude Include="new_monitor_form.h" />
|
<ClInclude Include="new_monitor_form.h" />
|
||||||
<ClInclude Include="resource1.h" />
|
<ClInclude Include="resource1.h" />
|
||||||
|
@ -78,6 +78,9 @@
|
|||||||
<ClCompile Include="websocket_server_form.cpp">
|
<ClCompile Include="websocket_server_form.cpp">
|
||||||
<Filter>源文件</Filter>
|
<Filter>源文件</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="modbus_form.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="main.h">
|
<ClInclude Include="main.h">
|
||||||
@ -140,6 +143,9 @@
|
|||||||
<ClInclude Include="websocket_server_form.h">
|
<ClInclude Include="websocket_server_form.h">
|
||||||
<Filter>头文件</Filter>
|
<Filter>头文件</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="modbus_form.h">
|
||||||
|
<Filter>头文件</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Xml Include="..\Debug\resources\themes\default\basic\basic.xml">
|
<Xml Include="..\Debug\resources\themes\default\basic\basic.xml">
|
||||||
|
@ -8,21 +8,21 @@
|
|||||||
map<int, HANDLE> SerialPort::ComMap;
|
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"); //串口注册表路径
|
LPCTSTR Reg_Path = _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"); //串口注册表路径
|
||||||
//TCHAR pnum[8];
|
//TCHAR pnum[8];
|
||||||
HKEY H_Key;
|
HKEY H_Key;
|
||||||
static UINT portlist[255]; //一般情况下255个数组够用了。
|
static UINT portlist[255]; //一般情况下255个数组够用了。
|
||||||
long Status;
|
long Status;
|
||||||
@ -39,23 +39,19 @@ UINT SerialPort::GetPortNum(UINT** PortList)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true)
|
while (true) {
|
||||||
{
|
|
||||||
Com_Length = 256; //存储两个长度,每次都要赋一个大一点的值,不然下次可能会失败,Com_length值翻倍
|
Com_Length = 256; //存储两个长度,每次都要赋一个大一点的值,不然下次可能会失败,Com_length值翻倍
|
||||||
Type_Length = 256;
|
Type_Length = 256;
|
||||||
PortNum = 0;
|
PortNum = 0;
|
||||||
Status = RegEnumValue(H_Key, count++, Type_Name, &Type_Length
|
Status = RegEnumValue(H_Key, count++, Type_Name, &Type_Length
|
||||||
, 0, &Type, PUCHAR(Com_Name), &Com_Length);
|
, 0, &Type, PUCHAR(Com_Name), &Com_Length);
|
||||||
if (Status) //查询失败说明遍历结束,跳出循环。
|
if (Status) { //查询失败说明遍历结束,跳出循环。
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 3; Com_Name[i]; i++) //转换为数字
|
for (int i = 3; Com_Name[i]; i++) { //转换为数字
|
||||||
{
|
|
||||||
PortNum = PortNum * 10 + (Com_Name[i] - '0');
|
PortNum = PortNum * 10 + (Com_Name[i] - '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
portlist[count - 1] = PortNum;
|
portlist[count - 1] = PortNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,8 +63,6 @@ UINT SerialPort::GetPortNum(UINT** PortList)
|
|||||||
//打开端口
|
//打开端口
|
||||||
HANDLE SerialPort::OpenPort(UINT PortNo) //打开端口
|
HANDLE SerialPort::OpenPort(UINT PortNo) //打开端口
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
HANDLE H_Com; //串口句柄
|
HANDLE H_Com; //串口句柄
|
||||||
//PortNum = PortNo; //串口号
|
//PortNum = PortNo; //串口号
|
||||||
|
|
||||||
@ -83,16 +77,14 @@ HANDLE SerialPort::OpenPort(UINT PortNo) //
|
|||||||
OPEN_EXISTING, //已经存在的
|
OPEN_EXISTING, //已经存在的
|
||||||
0,
|
0,
|
||||||
0); //打开串口,同步方式
|
0); //打开串口,同步方式
|
||||||
if (H_Com == INVALID_HANDLE_VALUE)//如果串口打开失败
|
if (H_Com == INVALID_HANDLE_VALUE) {//如果串口打开失败
|
||||||
{
|
|
||||||
return INVALID_HANDLE_VALUE;
|
return INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetupComm(H_Com, 1024, 1024); //设置读写缓冲区为1024;
|
SetupComm(H_Com, 1024, 1024); //设置读写缓冲区为1024;
|
||||||
|
|
||||||
ComMap[PortNo] = H_Com; // 打开成功则,存入map
|
ComMap[PortNo] = H_Com; // 打开成功则,存入map
|
||||||
return H_Com;
|
return H_Com;
|
||||||
}
|
}
|
||||||
|
|
||||||
//关闭端口
|
//关闭端口
|
||||||
void SerialPort::ClosePort(UINT PortNo) //关闭串口
|
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(); )
|
for (map<int, HANDLE>::iterator iter = ComMap.begin(); iter != ComMap.end(); )
|
||||||
{
|
{
|
||||||
CloseHandle(iter->second); //关闭串口
|
CloseHandle(iter->second); //关闭串口
|
||||||
iter = ComMap.erase(iter); //必须这样写 否则迭代器失效会引发异常。
|
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)) //如果端口已经存在
|
if (PortState(PortNo)) //如果端口已经存在
|
||||||
{
|
{
|
||||||
ClosePort(PortNo); //关掉端口
|
ClosePort(PortNo); //关掉端口
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Parity < 0 || Parity >4) //parity 0~4
|
if (Parity < 0 || Parity >4) //parity 0~4
|
||||||
return 0;
|
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);
|
//_stprintf_s(DcbPara, _T("baud=%d parity=%c data=%d stop=%d") , Baud, Parity, Data, Stop);
|
||||||
H_Com = OpenPort(PortNo);
|
H_Com = OpenPort(PortNo);
|
||||||
|
|
||||||
if (H_Com == INVALID_HANDLE_VALUE)
|
if (H_Com == INVALID_HANDLE_VALUE) {
|
||||||
{
|
|
||||||
return 0;
|
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 };
|
COMMTIMEOUTS ComTimeouts = { 0, 0, 10, 0, 10 };
|
||||||
|
|
||||||
DCB S_dcb;
|
DCB S_dcb;
|
||||||
if (!SetCommTimeouts(H_Com, &ComTimeouts))//设置超时
|
if (!SetCommTimeouts(H_Com, &ComTimeouts)) {//设置超时
|
||||||
{
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!GetCommState(H_Com, &S_dcb)) //获取现有DCB结构
|
if (!GetCommState(H_Com, &S_dcb)) { //获取现有DCB结构
|
||||||
{
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
S_dcb.BaudRate = Baud;
|
S_dcb.BaudRate = Baud;
|
||||||
S_dcb.ByteSize = Data;
|
S_dcb.ByteSize = Data;
|
||||||
S_dcb.Parity = Parity;
|
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信号
|
S_dcb.fRtsControl = RTS_CONTROL_DISABLE; //允许Rts信号
|
||||||
if (!SetCommState(H_Com, &S_dcb)) //设置DCB
|
if (!SetCommState(H_Com, &S_dcb)) { //设置DCB
|
||||||
{
|
|
||||||
int i = GetLastError();
|
int i = GetLastError();
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PurgeComm(H_Com, PURGE_RXCLEAR | PURGE_TXCLEAR |
|
PurgeComm(H_Com, PURGE_RXCLEAR | PURGE_TXCLEAR |
|
||||||
PURGE_RXABORT | PURGE_TXABORT); //清空缓冲区
|
PURGE_RXABORT | PURGE_TXABORT); //清空缓冲区
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SerialPort::PortState(UINT PortNo)
|
int SerialPort::PortState(UINT PortNo)
|
||||||
{
|
{
|
||||||
map<int, HANDLE>::iterator iter;
|
map<int, HANDLE>::iterator iter;
|
||||||
|
|
||||||
iter = ComMap.find(PortNo);
|
iter = ComMap.find(PortNo);
|
||||||
|
|
||||||
if (iter != ComMap.end()) //说明查找到了端口
|
if (iter != ComMap.end()) //说明查找到了端口
|
||||||
return 1;
|
return 1;
|
||||||
else
|
else
|
||||||
@ -197,7 +172,6 @@ int SerialPort::PortState(UINT PortNo)
|
|||||||
//读串口
|
//读串口
|
||||||
int SerialPort::ReadPort(UINT PortNo, char* P_recved, int length) //重载函数 一次读取指定长度的数据
|
int SerialPort::ReadPort(UINT PortNo, char* P_recved, int length) //重载函数 一次读取指定长度的数据
|
||||||
{
|
{
|
||||||
|
|
||||||
if (PortState(PortNo) == 0) //串口未打开
|
if (PortState(PortNo) == 0) //串口未打开
|
||||||
return 0;
|
return 0;
|
||||||
HANDLE H_Com = ComMap[PortNo];
|
HANDLE H_Com = ComMap[PortNo];
|
||||||
@ -231,33 +205,27 @@ int SerialPort::CheckRTS(UINT PortNo)
|
|||||||
//写串口
|
//写串口
|
||||||
int SerialPort::WritePort(UINT PortNo, const char* pData, int length)
|
int SerialPort::WritePort(UINT PortNo, const char* pData, int length)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (PortState(PortNo) == 0) //串口未打开
|
if (PortState(PortNo) == 0) //串口未打开
|
||||||
return 0;
|
return 0;
|
||||||
HANDLE H_Com = ComMap[PortNo];
|
HANDLE H_Com = ComMap[PortNo];
|
||||||
|
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
length = strlen(pData);
|
length = strlen(pData);
|
||||||
|
|
||||||
DWORD Recn;
|
DWORD Recn;
|
||||||
BOOL Result = TRUE;
|
BOOL Result = TRUE;
|
||||||
|
|
||||||
if (H_Com == INVALID_HANDLE_VALUE)
|
if (H_Com == INVALID_HANDLE_VALUE)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
Result = WriteFile(H_Com, pData, length, &Recn, NULL);
|
Result = WriteFile(H_Com, pData, length, &Recn, NULL);
|
||||||
if (Result == 0)
|
if (Result == 0)
|
||||||
{
|
{
|
||||||
GetLastError();
|
GetLastError();
|
||||||
PurgeComm(H_Com, PURGE_RXCLEAR | PURGE_RXABORT);
|
PurgeComm(H_Com, PURGE_RXCLEAR | PURGE_RXABORT);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return Recn;
|
return Recn;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//获取要读取的字节数
|
//获取要读取的字节数
|
||||||
UINT SerialPort::GetByte(UINT PortNo) //该函数获取缓冲区字节长度。
|
UINT SerialPort::GetByte(UINT PortNo) //该函数获取缓冲区字节长度。
|
||||||
{
|
{
|
||||||
|
@ -11,8 +11,8 @@ TcpClientForm::~TcpClientForm() {
|
|||||||
|
|
||||||
TcpClientForm::TcpClientForm(ui::Window* hwnd,std::string url, uint32_t port, TcpClientLibevent* p):
|
TcpClientForm::TcpClientForm(ui::Window* hwnd,std::string url, uint32_t port, TcpClientLibevent* p):
|
||||||
m_connected(false),
|
m_connected(false),
|
||||||
mLua(nullptr)
|
mLua(nullptr) {
|
||||||
{
|
|
||||||
mClient = p;
|
mClient = p;
|
||||||
mClient->SetObserver(this);
|
mClient->SetObserver(this);
|
||||||
if (nullptr != hwnd) {
|
if (nullptr != hwnd) {
|
||||||
@ -45,8 +45,7 @@ void TcpClientForm::ShowDataInEdit(const char*p) {
|
|||||||
m_rich_edit_1->AppendText(string2wstring(p), false);
|
m_rich_edit_1->AppendText(string2wstring(p), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpClientForm::Init()
|
void TcpClientForm::Init() {
|
||||||
{
|
|
||||||
ui::ChildBox::Init();
|
ui::ChildBox::Init();
|
||||||
m_label_1 = dynamic_cast<ui::Label*>(FindSubControl(L"uart_info_label"));
|
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"));
|
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;
|
return mClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDelegate* TcpClientForm::LuaVM()
|
LuaDelegate* TcpClientForm::LuaVM() {
|
||||||
{
|
|
||||||
return mLua;
|
return mLua;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpClientForm::OnConnected()
|
void TcpClientForm::OnConnected() {
|
||||||
{
|
|
||||||
std::cout << "服务器连接成功\r\n"<<__FILE__<<" " << __LINE__ << std::endl;
|
std::cout << "服务器连接成功\r\n"<<__FILE__<<" " << __LINE__ << std::endl;
|
||||||
wchar_t p[100] = { 0 };
|
wchar_t p[100] = { 0 };
|
||||||
wsprintf(p, L"地址%s,端口号%d 连接成功", string2wstring(m_url).c_str(), m_port);
|
wsprintf(p, L"地址%s,端口号%d 连接成功", string2wstring(m_url).c_str(), m_port);
|
||||||
m_label_1->SetText(std::wstring(p));
|
m_label_1->SetText(std::wstring(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpClientForm::OnDisConnected(std::string reason)
|
void TcpClientForm::OnDisConnected(std::string reason) {
|
||||||
{
|
|
||||||
wchar_t p[100] = { 0 };
|
wchar_t p[100] = { 0 };
|
||||||
wsprintf(p, L"地址%s,端口号%d 未连接 " , string2wstring(m_url).c_str(), m_port);
|
wsprintf(p, L"地址%s,端口号%d 未连接 " , string2wstring(m_url).c_str(), m_port);
|
||||||
m_label_1->SetText(std::wstring(p) + L" " + string2wstring(reason));
|
m_label_1->SetText(std::wstring(p) + L" " + string2wstring(reason));
|
||||||
m_connected = false;
|
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;
|
//std::cout << (char*)dat << "len is " << len << std::endl;
|
||||||
//m_rich_edit_1->AppendText(string2wstring(std::string((char*)dat)), false);
|
//m_rich_edit_1->AppendText(string2wstring(std::string((char*)dat)), false);
|
||||||
int ret = this->mLua->CallFuntion <std::string>
|
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);
|
MessageBox(0, L"lua脚本错误 OnClientRecvData", L"", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpClientForm::OnClose()
|
void TcpClientForm::OnClose() {
|
||||||
{
|
|
||||||
m_rich_edit_1->AppendText(string2wstring("连接已经断开\r\n"), false);
|
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;
|
std::string mLuaScript;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,3 +55,5 @@ std::string GenerateGuid()
|
|||||||
guid.Data4[7]);
|
guid.Data4[7]);
|
||||||
return std::string(cBuffer);
|
return std::string(cBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,8 +4,7 @@
|
|||||||
#define WEBSOCKET_SERVER_LUA "websocket_server.lua"
|
#define WEBSOCKET_SERVER_LUA "websocket_server.lua"
|
||||||
|
|
||||||
WebsocketServerForm::WebsocketServerForm(ui::Window* hwnd, string url,
|
WebsocketServerForm::WebsocketServerForm(ui::Window* hwnd, string url,
|
||||||
WebsocketServer* server)
|
WebsocketServer* server) {
|
||||||
{
|
|
||||||
if (nullptr != hwnd) {
|
if (nullptr != hwnd) {
|
||||||
this->SetWindow(hwnd, nullptr, false);
|
this->SetWindow(hwnd, nullptr, false);
|
||||||
}
|
}
|
||||||
@ -26,15 +25,13 @@ WebsocketServerForm::WebsocketServerForm(ui::Window* hwnd, string url,
|
|||||||
this->mLua->BindFunction("showdata", LuaShowData);
|
this->mLua->BindFunction("showdata", LuaShowData);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebsocketServerForm::~WebsocketServerForm()
|
WebsocketServerForm::~WebsocketServerForm() {
|
||||||
{
|
|
||||||
std::cout << "~WebsocketServerForm\r\n";
|
std::cout << "~WebsocketServerForm\r\n";
|
||||||
std::cout << "~WebsocketServerForm\r\n";
|
std::cout << "~WebsocketServerForm\r\n";
|
||||||
delete m_server;
|
delete m_server;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebsocketServerForm::Init()
|
void WebsocketServerForm::Init() {
|
||||||
{
|
|
||||||
ui::ChildBox::Init();
|
ui::ChildBox::Init();
|
||||||
|
|
||||||
m_label_1 = dynamic_cast<ui::Label*>(FindSubControl(L"server_info"));
|
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;
|
return mLua;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebsocketServerForm::ShowDataInEdit(const char*src)
|
void WebsocketServerForm::ShowDataInEdit(const char*src) {
|
||||||
{
|
|
||||||
m_rich_edit_1->AppendText(string2wstring(std::string(src)), false);
|
m_rich_edit_1->AppendText(string2wstring(std::string(src)), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user