介绍

倍福官方把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打开

img

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

img

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

img

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

img

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

img

然后点击重新构建就好

img

测试

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";

// uncomment and adjust if automatic AmsNetId deduction is not working as expected
//bhf::ads::SetLocalAddress({192, 168, 0, 1, 1, 1});

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;

/** Get handle to access AdsVariable by indexGroup/Offset */
AdsHandle GetHandle(uint32_t offset) const;

/** Get handle for access by symbol name */
AdsHandle GetHandle(const std::string& symbolName) const;

/** Get notification handle */
AdsHandle GetHandle(uint32_t indexGroup,
uint32_t indexOffset,
const AdsNotificationAttrib& notificationAttributes,
PAdsNotificationFuncEx callback,
uint32_t hUser) const;

/** Get handle to access files */
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目录下会生成相关的库文件、头文件,方便我们拿到自己的项目使用

img

在cmake里边包含和链接ads组件

1
2
3
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/ads)

target_link_libraries(demoApp PUBLIC ads)

© 2025 hywing 使用 Stellar 创建
总访问 113701 次 | 本页访问 326