Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加不占有所有权的 ServerView #102

Merged
merged 1 commit into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions cmake/RMVLCompilerOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,30 @@ if(ENABLE_PIC)
set(CMAKE_POSITION_INDEPENDENT_CODE ${ENABLE_PIC})
endif()

option(ENABLE_LTO "Enable Link Time Optimization" OFF)
if(ENABLE_LTO)
include(CheckCXXCompilerFlag)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
check_cxx_compiler_flag("-flto=auto" HAS_LTO_AUTO_FLAG)
if(HAS_LTO_AUTO_FLAG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=auto")
else()
check_cxx_compiler_flag("-flto" HAS_LTO_FLAG)
if(HAS_LTO_FLAG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto")
endif()
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
check_cxx_compiler_flag("/GL" COMPILER_SUPPORTS_LTO)
if(COMPILER_SUPPORTS_LTO)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GL")
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /LTCG")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /LTCG")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LTCG")
endif()
endif()
endif()

# ----------------------------------------------------------------------------
# Develop options
# ----------------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions cmake/templates/cmake_uninstall.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling: $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
execute_process(
"@CMAKE_COMMAND@" "-E" "remove" "$ENV{DESTDIR}${file}"
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
RESULT_VARIABLE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
Expand Down
64 changes: 45 additions & 19 deletions doc/tutorials/modules/tools/opcua.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

相关模块: @ref opcua

### 简介
## 1. 简介

#### OPC UA 是什么
### 1.1 OPC UA 是什么

[OPC UA](https://opcfoundation.org/about/opc-technologies/opc-ua/)(全称为 Open Platform Communications Unified Architecture)是一种用于工业和物联网(IoT)应用的开放通信协议和架构。它提供了一种统一的框架,用于在不同设备和系统之间实现数据传输、通信和集成。

Expand All @@ -32,7 +32,7 @@ OPC UA 的设计目标是建立一种通用的、独立于厂商和平台的通
| 信息建模 | OPC UA 使用统一的信息模型,将数据和功能以标准化的方式表示和描述,使不同系统之间的数据交换更加简化和一致 |
| 可靠性 | OPC UA 提供了可靠的通信机制,包括消息确认、重试和错误处理,以确保数据的可靠传输 |

#### 地址空间
### 1.2 地址空间

在 OPC UA 中,所有的数据都被组织成一个地址空间,地址空间中的每一个元素都被称为一个节点。每个节点都有一个唯一的节点号,在 @ref opcua 中表示为 rm::NodeId 。

Expand Down Expand Up @@ -63,11 +63,11 @@ OPC UA 的设计目标是建立一种通用的、独立于厂商和平台的通

8. 视图节点 rm::View :视图节点可将地址空间中感兴趣的节点提取出来,作为一个子集,视图节点作为该子集的入口,方便客户端浏览。

### 服务器/客户端 {#opcua_server_client}
## 2. 服务器/客户端 {#opcua_server_client}

基于服务器/客户端的方式是 OPC UA 最基本的一种通信方式,上文的地址空间在服务器/客户端通信的过程中完全展现出来。下面列举一些 opcua 模块中常用的服务器与客户端的成员方法。

#### 初始化
### 2.1 初始化

**服务器**

Expand Down Expand Up @@ -104,7 +104,7 @@ int main()
}
```

#### 变量
### 2.2 变量

在上文介绍了变量的 3 种访问方式,这里使用最简单的直接读写的方式。首先在服务器中添加变量节点。

Expand Down Expand Up @@ -158,7 +158,7 @@ int main()
}
```

#### 方法
### 2.3 方法

在服务器中添加两数之和的方法节点,供客户端调用。

Expand Down Expand Up @@ -227,7 +227,7 @@ int main()
}
```

#### 对象
### 2.4 对象

在服务器中添加对象节点:

Expand Down Expand Up @@ -302,7 +302,7 @@ int main()
}
```

#### 视图
### 2.5 视图

在 `nodeObjectsFolder` 中先添加 `A/num1`、`num2` 2 个变量节点,并将 `num1` 和 `num2` 加入视图,下面的示例演示在 **服务器** 中创建并添加视图节点。若要在客户端中进行此操作,创建并添加视图节点的步骤基本一致,这里不做展示。需要注意的是,在客户端中创建并添加视图节点,需要提前在服务器中加入对应的(变量、方法、对象……)节点

Expand Down Expand Up @@ -336,7 +336,7 @@ int main()
}
```

#### 监视
### 2.6 监视

在服务器中添加待监视的变量节点

Expand Down Expand Up @@ -407,7 +407,7 @@ int main()
}
```

### 发布/订阅 {#opcua_pub_sub}
## 3. 发布/订阅 {#opcua_pub_sub}

这是一段来自 [open62541 手册](https://www.open62541.org)中有关 PubSub 的介绍。

Expand Down Expand Up @@ -437,7 +437,7 @@ int main()

有关 API 使用的更多详细信息,请查看 [PubSub 教程](https://www.open62541.org/doc/master/pubsub.html)。

#### 无代理 Pub/Sub
### 3.1 无代理 Pub/Sub

RMVL 提供了基于 `UDP` 传输协议的 Broker-less 即无代理的发布订阅机制,目前支持 `UADP` 的消息映射方式,对应的枚举类型是 `TransportID::UDP_UADP`。

Expand Down Expand Up @@ -513,11 +513,15 @@ int main()
}
```

#### 有代理 Pub/Sub
### 3.2 有代理 Pub/Sub

@warning RMVL 目前暂不支持有代理的发布订阅机制。

### 参数加载 {#opcua_parameters}
## 4. 使用技巧

以下是 @ref opcua 的使用技巧。

### 4.1 参数加载 {#opcua_parameters}

@ref opcua 中提供了以下几个运行时可调节参数

Expand All @@ -533,9 +537,9 @@ int main()

具体调节方式可参考引言中的 @ref intro_parameters_manager 部分。

### 从 XML 配置 OPC UA {#opcua_nodeset_compiler}
### 4.2 从 XML 配置 OPC UA {#opcua_nodeset_compiler}

#### 安装 UaModeler
#### 4.2.1 安装 UaModeler

可使用 UaModeler 等软件进行可视化信息模型的建立,构建后可以导出为一个 `*.xml` 文件,首先先安装 UaModeler。

Expand All @@ -555,15 +559,15 @@ int main()

具体安装细节可参考 [opcua-modeler on Github](https://github.com/FreeOpcUa/opcua-modeler) 的 README。

#### 可视化配置 OPC UA 信息模型
#### 4.2.2 可视化配置 OPC UA 信息模型

对于项目创建或导出等内容,此处不做过多介绍,可参考[此博客](https://wanghao1314.blog.csdn.net/article/details/104092781)了解上述内容。

@note
- 一般的,定义对象、变量、方法等内容均按照在代码中的顺序进行定义即可,但需要注意的是,添加了方法节点后,还需要在代码中设置该方法节点执行的回调函数,可参见 `rm::Server::setMethodNodeCallBack`。
- `NamespaceArray` 的 `[1]` 的字符串需要更改为 `urn:open62541.server.application`

#### 生成 \*.c/\*.h 文件
#### 4.2.3 生成 \*.c/\*.h 文件

@note 以下生成 C/C++ 文件的介绍来自 [open62541 nodeset-compiler](https://www.open62541.org/doc/master/nodeset_compiler.html#getting-started)。

Expand All @@ -582,8 +586,30 @@ python3 ./nodeset_compiler.py \
myNodeSet # myNodeSet 是要生成的文件名,包含 myNodeSet.h 和 myNodeSet.c,请自行设置
```

### 4.3 不占有所有权的服务器视图

`rm::Server` 使用 RAII 进行设计,一个对象占有了服务器的所有权和生命周期,当对象析构时,会自动停止并结束服务器。使用 `rm::ServerView` 来获取不占有所有权的服务器视图,并进行变量读写、路径搜索的操作,下面用服务器视图的单元测试作为示例。

```cpp
rm::Method method;
method.browse_name = "plus";
method.display_name = "Input + Number";
method.description = "输入值加数";
method.func = [](UA_Server *p_server, const UA_NodeId *, void *, const UA_NodeId *, void *, const UA_NodeId *,
void *, size_t, const UA_Variant *inputs, size_t, UA_Variant *) -> UA_StatusCode {
rm::ServerView sv = p_server;
auto num_node = nodeObjectsFolder | sv.find("num");
int num = sv.read(num_node).cast<int>();
rm::Variable dst = *reinterpret_cast<int *>(inputs->data) + num;
sv.write(num_node, dst);
return UA_STATUSCODE_GOOD;
};
method.iargs = {{"input", UA_TYPES_INT32, 1, "输入值"}};
svr.addMethodNode(method);
```

---

### 引用
## 5. 引用

@cite ua-modeler UaModeler · FreeOpcUa/opcua-modeler · Github
26 changes: 13 additions & 13 deletions modules/opcua/include/rmvl/opcua/client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class Client
* @return 目标节点信息
* @retval fnic `[_client, browse_name]` 元组
*/
inline FindNodeInClient find(const std::string &browse_name, uint16_t ns = 1U) { return {_client, browse_name, ns}; }
inline FindNodeInClient find(const std::string &browse_name, uint16_t ns = 1U) const { return {_client, browse_name, ns}; }

/****************************** 功能配置 ******************************/

Expand All @@ -71,22 +71,22 @@ class Client
* @brief
* - 执行事件循环,等效于 ROS/ROS2 工具包中的 `ros::spin()` 以及 `rclcpp::spin()`
*/
void spin();
void spin() const;

/**
* @brief 在网络上监听并处理到达的异步响应,同时进行内部维护、安全通道的更新和订阅管理
* @brief
* - 处理当前已到来的事件,等效于 ROS/ROS2 工具包中的 `ros::spinOnce()` 以及 `rclcpp::spin_some()`
*/
void spinOnce();
void spinOnce() const;

/**
* @brief 从指定的变量节点读数据
*
* @param[in] node 既存的变量节点的 `NodeId`
* @return 读出的用 `rm::Variable` 表示的数据,未成功读取则返回空
*/
Variable read(const NodeId &node);
Variable read(const NodeId &node) const;

/**
* @brief 给指定的变量节点写数据
Expand All @@ -95,7 +95,7 @@ class Client
* @param[in] val 待写入的数据
* @return 是否写入成功
*/
bool write(const NodeId &node, const Variable &val);
bool write(const NodeId &node, const Variable &val) const;

/**
* @brief 在客户端调用指定对象节点中的方法
Expand All @@ -106,25 +106,25 @@ class Client
* @param[out] outputs 输出参数列表
* @return 是否成功完成当前操作
*/
bool call(const NodeId &obj_node, const std::string &name, const std::vector<Variable> &inputs, std::vector<Variable> &outputs);
bool call(const NodeId &obj_node, const std::string &name, const std::vector<Variable> &inputs, std::vector<Variable> &outputs) const;

/**
* @brief 在客户端调用 ObjectsFolder 中的方法
*
* @param[in] name 方法名
* @param[in] name 方法名 `browse_name`
* @param[in] inputs 输入参数列表
* @param[out] outputs 输出参数列表
* @return 是否成功完成当前操作
*/
inline bool call(const std::string &name, const std::vector<Variable> &inputs, std::vector<Variable> &outputs) { return call(nodeObjectsFolder, name, inputs, outputs); }
inline bool call(const std::string &name, const std::vector<Variable> &inputs, std::vector<Variable> &outputs) const { return call(nodeObjectsFolder, name, inputs, outputs); }

/**
* @brief 添加视图节点 ViewNode 至 `ViewsFolder` 中
* @brief 添加 OPC UA 视图节点 ViewNode 至 `ViewsFolder` 中
*
* @param[in] view `rm::View` 表示的视图
* @return 添加至服务器后,对应视图节点的唯一标识 `NodeId`
*/
NodeId addViewNode(const View &view);
NodeId addViewNode(const View &view) const;

/**
* @brief 创建变量节点监视项,以实现订阅节点的功能
Expand All @@ -151,7 +151,7 @@ class Client
* @param[in] queue_size 通知存放的队列大小,若队列已满,新的通知会覆盖旧的通知,默认为 `10`
* @return 变量节点监视创建成功?
*/
bool monitor(NodeId node, UA_Client_DataChangeNotificationCallback on_change, uint32_t queue_size = 10);
bool monitor(NodeId node, UA_Client_DataChangeNotificationCallback on_change, uint32_t queue_size = 10) const;

/**
* @brief 创建事件监视项,以实现事件的订阅功能
Expand All @@ -161,7 +161,7 @@ class Client
* @param[in] on_event 事件回调函数
* @return 事件监视创建成功?
*/
bool monitor(NodeId node, const std::vector<std::string> &names, UA_Client_EventNotificationCallback on_event);
bool monitor(NodeId node, const std::vector<std::string> &names, UA_Client_EventNotificationCallback on_event) const;

private:
/**
Expand All @@ -170,7 +170,7 @@ class Client
* @param[out] response 订阅请求的响应
* @return 是否成功完成当前操作
*/
bool createSubscription(UA_CreateSubscriptionResponse &responce);
bool createSubscription(UA_CreateSubscriptionResponse &responce) const;
};

//! @} opcua
Expand Down
Loading
Loading