介绍
倍福官方把ADS通信组件做成了跨平台(Windows、Linux、BSD),而且还开源,这下在Unix开发工控机上位机软件就问题不大了,这是官方的编译器配置要求:
Currently (2024-12-13) tested with:
host os |
host |
target |
compiler |
Alpine 3.21 |
amd64 |
amd64 |
gcc 14.2.0 |
Arch Linux |
amd64 |
amd64 |
clang 18.1.8 |
Arch Linux |
amd64 |
amd64 |
gcc 14.2.1 |
Debian 12 |
amd64 |
amd64 |
clang 14.0.6 |
Debian 12 |
amd64 |
amd64 |
gcc 12.2.0-14 |
Debian 12 |
amd64 |
i686 |
gcc 12.2.0-14 |
Debian 12 |
amd64 |
mips |
gcc 12.2.0-14 |
Debian 12 |
amd64 |
win32 |
gcc 10.2.1-6 |
Debian 12 |
amd64 |
riscv64 |
gcc 12.2.0-13 |
Debian 12 |
arm64 |
arm64 |
gcc 12.2.0-14 |
TC/BSD 14 |
amd64 |
amd64 |
clang 18.1.5 |
Windows 10 |
amd64 |
win64 |
msvc 19.36.33134 |
编译
开源工程是用cmake构建的,在Windows下可以直接用QtCreator打开

配置编译的类型、构建路径、安装目录

用MSVC2017编译需要在顶层cmake加入一行代码

构建工程的过程中会遇到Link错误,明显这个是个静态库lib

在AdsLib里边改一下库的配置类型,你也可以把前面那个BUILD_SHARED_LIBS
的选项去掉

然后点击重新构建就好

测试
ADS组件里边自带的example例程也很简单,跟以太网通信那一套类似,都是对指定IP地址和端口发起连接,TC3的端口为851
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static void runExample(std::ostream& out) { static const AmsNetId remoteNetId { 192, 168, 0, 231, 1, 1 }; static const char remoteIpV4[] = "ads-server";
AdsDevice route {remoteIpV4, remoteNetId, AMSPORT_R0_PLC_TC3}; notificationExample(out, route); notificationByNameExample(out, route); readExample(out, route); readByNameExample(out, route); readWriteExample(out, route); readWriteArrayExample(out, route); readStateExample(out, route); }
|
AdsDevice类型,需要配置AdsDevice
的地址、端口等信息后读取指定地址或名称的寄存器值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| struct AdsDevice { AdsDevice(const std::string& ipV4, AmsNetId netId, uint16_t port);
DeviceInfo GetDeviceInfo() const;
AdsHandle GetHandle(uint32_t offset) const;
AdsHandle GetHandle(const std::string& symbolName) const;
AdsHandle GetHandle(uint32_t indexGroup, uint32_t indexOffset, const AdsNotificationAttrib& notificationAttributes, PAdsNotificationFuncEx callback, uint32_t hUser) const;
AdsHandle OpenFile(const std::string& filename, uint32_t flags) const;
long GetLocalPort() const;
AdsDeviceState GetState() const; void SetState(const ADSSTATE AdsState, const ADSSTATE DeviceState) const;
uint32_t GetTimeout() const; void SetTimeout(const uint32_t timeout) const;
long ReadReqEx2(uint32_t group, uint32_t offset, size_t length, void* buffer, uint32_t* bytesRead) const; long ReadWriteReqEx2(uint32_t indexGroup, uint32_t indexOffset, size_t readLength, void* readData, size_t writeLength, const void* writeData, uint32_t* bytesRead) const; long WriteReqEx(uint32_t group, uint32_t offset, size_t length, const void* buffer) const;
AdsResource<const AmsNetId> m_NetId; const AmsAddr m_Addr; private: AdsResource<const long> m_LocalPort; long CloseFile(uint32_t handle) const; long DeleteNotificationHandle(uint32_t handle) const; long DeleteSymbolHandle(uint32_t handle) const; };
|
AdsVariable
是一个模板类型,封装了各种各样的寄存器类型的数据访问(IndexGroup、group、offset或者symbolName)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| template<typename T> struct AdsVariable { AdsVariable(const AdsDevice& route, const std::string& symbolName) : m_Route(route), m_IndexGroup(ADSIGRP_SYM_VALBYHND), m_Handle(route.GetHandle(symbolName)) {}
AdsVariable(const AdsDevice& route, const uint32_t group, const uint32_t offset) : m_Route(route), m_IndexGroup(group), m_Handle(route.GetHandle(offset)) {}
operator T() const { T buffer; Read(sizeof(buffer), &buffer); return buffer; }
void operator=(const T& value) const { Write(sizeof(T), &value); }
template<typename U, size_t N> operator std::array<U, N>() const { std::array<U, N> buffer; Read(sizeof(U) * N, buffer.data()); return buffer; }
template<typename U, size_t N> void operator=(const std::array<U, N>& value) const { Write(sizeof(U) * N, value.data()); }
void Read(const size_t size, void* data) const { uint32_t bytesRead = 0; auto error = m_Route.ReadReqEx2(m_IndexGroup, *m_Handle, size, data, &bytesRead);
if (error || (size != bytesRead)) { throw AdsException(error); } }
void Write(const size_t size, const void* data) const { auto error = m_Route.WriteReqEx(m_IndexGroup, *m_Handle, size, data); if (error) { throw AdsException(error); } } private: const AdsDevice& m_Route; const uint32_t m_IndexGroup; const AdsHandle m_Handle; };
|
根据group和offset读取寄存器值
1 2 3 4 5 6 7 8 9
| static void readExample(std::ostream& out, const AdsDevice& route) { AdsVariable<uint8_t> readVar {route, 0x4020, 0};
out << __FUNCTION__ << "():\n"; for (size_t i = 0; i < 8; ++i) { out << "ADS read " << std::hex << (uint32_t)readVar << '\n'; } }
|
根据name读取寄存器值
1 2 3 4 5 6 7 8 9
| static void readByNameExample(std::ostream& out, const AdsDevice& route) { AdsVariable<uint8_t> readVar {route, "MAIN.byByte[4]"};
out << __FUNCTION__ << "():\n"; for (size_t i = 0; i < 8; ++i) { out << "ADS read " << std::hex << (uint32_t)readVar << '\n'; } }
|
读写示例
1 2 3 4 5 6 7 8 9 10 11
| static void readWriteExample(std::ostream& out, const AdsDevice& route) { AdsVariable<uint8_t> simpleVar {route, "MAIN.byByte[0]"}; AdsVariable<uint8_t> validation {route, "MAIN.byByte[0]"};
out << __FUNCTION__ << "():\n"; simpleVar = 0xA5; out << "Wrote " << 0xA5 << " to MAIN.byByte and read " << (uint32_t)validation << " back\n"; simpleVar = 0x5A; out << "Wrote " << (uint32_t)simpleVar << " to MAIN.byByte and read " << (uint32_t)validation << " back\n"; }
|
应用
构建好之后在install目录下会生成相关的库文件、头文件,方便我们拿到自己的项目使用

在cmake里边包含和链接ads组件
1 2 3
| include_directories(${CMAKE_CURRENT_SOURCE_DIR}/ads)
target_link_libraries(demoApp PUBLIC ads)
|