C 语言应用开发中的注意事项 1.全局变量 2.源代码文件夹中 .c 模块文件的划分 3.多线程编程注意的问题 线程安全和互斥锁 线程间通信 4.深浅拷贝问题 总结 C 语言应用开发中的注意事项 C 语言作为一门经典的编程语言，广泛应用于系统编程、嵌入式开发和高性能计算等领域。然而，在进行 C 语言应用开发时，开发者需要关注一系列问题，以确保代码的正确性、性能和可维护性。 笔者在基于 C 语言开发 EdgeX Foundry 设备服务实践之中，遇到了一些问题，如全局变量的使用、.c 模块文件的划分、多线程编程注意事项、深浅拷贝问题以及编译链接时的多重定义问题。本文将对其展开讨论，并提供具体的代码示例和实用建议。 1.全局变量 全局变量是在函数外部定义的变量，其作用域从定义处开始到文件结束。它们在程序整个生命周期内存在，通常不会被轻易时放掉。可被程序中的任何函数访问。它能够方便多个函数共享数据，并减少函数参数传递的开销，但同时也不利于模块化编程和代码重用，增加调试难度。 以下展示的 global_pp 全局变量，在 EdgeX Foundry 设备服务中用于保存设备协议的详细信息，为部分关键代码： #include "device_openvino.h" #include "circular_buffer.h" /* protocols */ typedef struct { char *input_model; char *input_image_path; /* Uri */ char *device_name; /* CPU, GPU[0,1,...], NPU */ char *Score; } protocol_properties; protocol_properties *global_pp; // malloc a protocol_properties void func() { if (!global_pp) { iot_log_debug(driver->lc, "malloc a global_pp (protocol_properties)"); global_pp = (protocol_properties *)malloc(sizeof(protocol_properties)); global_pp->input_model = NULL; global_pp->input_image_path = NULL; global_pp->device_name = NULL; global_pp->Score = NULL; } } int main() { func(); return 0; } 注意事项： 尽量减少全局变量的使用，优先考虑局部变量或函数参数。 使用有意义的命名（如 g_config），避免冲突。 在多线程环境中，使用锁机制确保线程安全。 2.源代码文件夹中 .c 模块文件的划分 模块化编程将程序分解为独立的模块，每个模块负责特定功能。这种方法能提高代码的可读性、可维护性和可重用性，同时便于团队协作开发。划分 .c 文件的指导思想有以下几点： 每个 .c 文件应包含一组相关功能的实现，例如字符串处理或网络通信。 避免单个 .c 文件过大，建议控制在 1000 行以内。 使用头文件 (.h) 声明对外接口，.c 文件实现具体逻辑。 头文件用于声明函数、宏、类型和全局变量，供其他模块使用。 以下是 EdgeX Foundry 设备服务中环形 buffer 缓冲区的模块设计： //circular_buffer.h #pragma once #include <stddef.h> // 包含 size_t 的定义 // Circular buffer structure struct circular_buffer { struct infer_result* buffer; // storing data size_t capacity; // Maximum buffer capacity size_t size; // The current number of stored elements size_t head; // Head index (write location) size_t tail; // Tail index (read position) }; struct circular_buffer* create_circular_buffer(size_t capacity); ... //circular_buffer.c #include "circular_buffer.h" #include <stdlib.h> #include "edgex_common.h" // Initialize circular buffer struct circular_buffer* create_circular_buffer(size_t capacity) { //malloc a circular_buffer struct circular_buffer* cb = (struct circular_buffer*)malloc(sizeof(struct circular_buffer)); if (cb == NULL) { return NULL; } cb->buffer = (struct infer_result*)malloc(sizeof(struct infer_result) * capacity); if (cb->buffer == NULL) { free(cb); return NULL; } //Init cb->capacity = capacity; cb->size = 0; cb->head = 0; cb->tail = 0; return cb; } 要点： 使用 #ifndef 和 #define 或 #pragma once 防止头文件重复包含。 头文件仅声明，不定义变量或函数。 保持头文件简洁，避免引入不必要的依赖。 3.多线程编程注意的问题 线程安全和互斥锁 在多线程程序中，多个线程可能同时访问共享资源，导致数据不一致。互斥锁（mutex）可用于保护临界区，确保同一时间只有一个线程操作共享数据。在 EdgeX Foundry 设备服务 SDK 设计中，服务实例结构体包含有互斥锁指针，在设备服务初始化时一并初始化 mutex 以便于在整个设备服务声明周期中进行资源同步管理： /** * @brief Structure representing an OpenVINO driver instance for managing inference operations. */ typedef struct openvino_driver { iot_logger_t *lc; pthread_mutex_t mutex; // for synchronization pthread_t inference_thread; // 推理线程句柄 bool stop_inference; // 控制推理线程停止的标志 } openvino_driver; viod *openvino_inference_thread (){ ... // Thread-safe storage of results pthread_mutex_lock(&driver->mutex); push_result(global_cb, *results); pthread_mutex_unlock(&driver->mutex); } 线程间通信 线程间可通过共享内存、信号量或消息队列通信。例如，使用条件变量实现生产者-消费者模型。在 EdgeX Foundry 设备服务的后续开发中也会频繁出现。 4.深浅拷贝问题 浅拷贝： 只复制基本数据类型和指针，指针指向的内存不复制。 深拷贝： 复制所有数据，包括指针指向的内存。 在 EdgeX Foundry 设备服务开发中，协议资源 global_pp; 在 openvino_create_addr 中由深拷贝赋值，以此避免global_pp;在 SDK 回调函数中被错误释放资源： /** * @brief Creates an address structure for the OpenVINO device service. * @details This callback function is used by the EdgeX Foundry framework to create and populate a protocol_properties * structure with OpenVINO-specific configuration data extracted from the provided protocols. * @param impl Pointer to the implementation-specific data (cast to openvino_driver). * @param protocols Pointer to the protocol data containing OpenVINO configuration. * @param exception Pointer to store any exception data (not used in this implementation). * @return devsdk_address_t A pointer to the populated protocol_properties structure, cast to the EdgeX address type. */ static devsdk_address_t openvino_create_addr(void *impl, const devsdk_protocols *protocols, iot_data_t **exception) { ... const iot_data_t *props = devsdk_protocols_properties(protocols, "Openvino"); //从设备获取协议资源 if (props) { result = iot_data_string_map_get_string(props, "Uri"); pp->input_image_path = result; result = iot_data_string_map_get_string(props, "model"); pp->input_model = result; result = iot_data_string_map_get_string(props, "CPU_GPU"); pp->device_name = result; result = iot_data_string_map_get_string(props, "Score"); pp->Score = result; } // Allocate and populate new global protocol properties // 深拷贝 global_pp->input_model = malloc(strlen(pp->input_model) + 1); strcpy(global_pp->input_model,pp->input_model); global_pp->input_image_path = malloc(strlen(pp->input_image_path) + 1); strcpy(global_pp->input_image_path,pp->input_image_path); global_pp->device_name = malloc(strlen(pp->device_name) + 1); strcpy(global_pp->device_name,pp->device_name); global_pp->Score = malloc(strlen(pp->Score) + 1); strcpy(global_pp->Score,pp->Score); return (devsdk_address_t)pp; } 深拷贝的原因： 对象包含动态分配的内存。 需要独立副本，避免共享内存导致的问题。 总结 在 C 语言应用开发中，开发者需要综合考虑全局变量的合理使用、模块化设计的规范性、多线程编程的线程安全性、深浅拷贝的适用场景以及编译链接的正确性。 通过谨慎使用全局变量、合理划分模块、确保线程安全、正确处理拷贝和避免多重定义问题，可以编写出健壮、高效且易于维护的 C 语言程序。
EdgeX Foundry 开发设备服务的几个关键点 1.设备服务工作流 2.设备服务示例模板 2.1 SDK 相关工作流 2.2 template.c 的细节 3.删除设备的处理逻辑 4.增加设备 4.1 增加设备的方式 4.2 增加设备的处理逻辑 总结 EdgeX Foundry 开发设备服务的几个关键点 EdgeX Foundry 设备服务是与传感器/设备或物联网对象 (“物”) 交互的边缘连接器，它需要与各种设备交互，将数据传输到边缘计算系统，并根据需求执行控制命令。EdgeX Foundry 通过其模块化设计和丰富的工具集，使得设备服务的开发变得更加简单高效。我们还可以使用设备服务 SDK 来创建您自己的 EdgeX 设备服务，本文重点介绍使用 C-SDK 开发设备服务源代码的几个必须明确的关键点 。 1.设备服务工作流 以 设备服务 template.c 为例，作为一个虚拟设备服务，它可以将数据发送到核心数据微服务，用户可以通过命令和控制微服务发送命令并获得响应，虚拟设备服务的这些功能在执行功能或性能测试时不需要任何实际设备就可以使用。 注意点： 虚拟设备服务对核心数据和元数据微服务具有依赖性，因为初始化过程需要检查或注册配置文件，设备和值描述符，因此，核心数据和元数据服务必须在设备服务初始化之前完全启动。 服务启动时，服务会通过从 YAML 文件加载“值描述符”,“设备服务”,“设备描述”来初始化，并登记在元数据微服务中，YAML文件默认存在，也可以自行创建。虚拟设备服务为元数据微服务提供回调API来管理设备实例。 2.设备服务示例模板 2.1 SDK 相关工作流 EdgeX Foundry 的 C-SDK 提供了一个设备服务模板（位于 examples/template.c 强烈推荐先看一遍代码），它提供了一套规范化的开发示例，包括自定义回调函数、设备服务启动关闭、内存开辟释放等，是我们初次接触设备服务开发的良好开端。设备服务工作流在上一节有了一个大致的介绍，结合官方的指导文档 servicewritersguide.md (强烈推荐先阅读一遍)，这里进一步描述设备服务内部 SDK 相关的工作流，展示如下： 从根本上说，设备服务由多个回调组成，上图中的 SDK 内部工作流均在一系列回调函数中作处理。一个设备服务只有一个设备服务实例结构体指针 Implementation 每次调用回调时，该指针都会被传递，如初始化回调函数的第一个参数 (template_driver *) impl。 /* --- template.c ---- */ /* Initialize performs protocol-specific initialization for the device * service. */ static bool template_init ( void *impl, struct iot_logger_t *lc, const iot_data_t *config ) { template_driver *driver = (template_driver *) impl; iot_log_debug (lc, "Template Init."); return true; } 2.2 template.c 的细节 一个设备服务必须要自定义好的回调函数如下：每一个回调函数的实现正如上面提到的 template_init 那样。 /* Device Callbacks */ devsdk_callbacks *templateImpls = devsdk_callbacks_init ( template_init, template_get_handler, template_put_handler, template_stop, template_create_addr, template_free_addr, template_create_resource_attr, template_free_resource_attr ); 同时设备服务 SDK 还提供了一些非必要的回调函数，以便于开发者实现更灵活的需求，在 servicewritersguide.md 的 Optional handlers 部分有提到。举一个例子，一个设备服务实例可以实现 device_added、device_updated 和 device_removed 处理程序，然后它将收到服务管理的设备范围的更改通知。如果需要特殊的初始化/关闭操作来优化设备的使用，这可能会有所帮助。只需要将该函数实现，并注册进设备服务的回调初始化函数即可： //device_added 的具体实现 static void device_added( void *impl, const char *devname, const devsdk_protocols *protocols, const devsdk_device_resources *resources, bool adminEnabled) { openvino_driver *driver = (openvino_driver *)impl; iot_log_info(driver->lc, "New device added: %s", devname); } ... //主函数内的回调初始化 devsdk_callbacks *templateImpls = devsdk_callbacks_init ( template_init, template_get_handler, template_put_handler, template_stop, template_create_addr, template_free_addr, template_create_resource_attr, template_free_resource_attr ); //注册自定义的设备服务回调 devsdk_callbacks_set_listeners(templateImpls, device_added, NULL, NULL); 3.删除设备的处理逻辑 每删除一个设备，一定依次进入的回调函数是：template_free_resource_attr 和 template_free_addr 。 如果自定义回调 device_removed 已经初始化，则会优先进入该回调，然后再进入上面两个默认的释放内存处理回调之中。 4.增加设备 4.1 增加设备的方式 通过 EdgeX Foundry 官方 UI 界面（如下图所示）增加一个具体的设备，通过界面可视化引导，很容易完成以下步骤： SelectDeviceService SelectDeviceProfile DevicePrimary CreateAutoEvent CreateDeviceProtocol 注意点：CreateDeviceProtocol 为必须设置项，且需要跟自己的设备服务所规定的协议保持一致。 通过 API 控制命令，直接向 core-metadata 发送 POST 请求，即可一行命令生成新设备，如下所示： curl http://localhost:59881/api/v3/device -H "Content-Type: application/json" -X POST \ -d '[ { "apiVersion" : "v3", "device": { "name": "Device1", "description": "An Example Device", "adminState": "UNLOCKED", "operatingState": "UP", "serviceName": "device-template", "profileName": "template", "protocols": { "Others": { "key1": "xxxx", "key2": "yyyy", } } } } ]' 4.2 增加设备的处理逻辑 每增加一个设备，会先调用自定义回调 device_add （如果已经初始化） 然后在第一次获取设备资源，也就是 GET 请求时，会依次调用 template_create_resource_attr 、 template_create_addr 和 template_get_handler。 以后的 GET 请求只会直接调用 template_get_handler。 总结 本文深入探讨了使用 EdgeX Foundry C-SDK 开发设备服务的几个关键点，为开发者提供了一个清晰的指导框架。通过分析设备服务的工作流、模板代码的细节以及设备增删的处理逻辑，我们可以更高效地构建自定义设备服务。 首先，设备服务的工作流强调了其与核心数据和元数据微服务的依赖性，以及通过 YAML 文件初始化配置的重要性。其次，C-SDK 的模板（template.c）提供了一个规范化的开发起点，包含必备回调函数（如 init、get、put、stop）和可选回调（如 device_added），开发者可根据需求灵活扩展。再次，删除设备的逻辑涉及内存释放回调（如 template_free_addr），而增加设备则通过 UI 或 API 触发，并涉及初始化和资源获取的回调链。 总的来说，掌握这些关键点——理解工作流、熟悉模板结构、明确设备管理的回调逻辑——能够帮助开发者快速上手 EdgeX Foundry 设备服务开发，无论是构建虚拟服务还是连接真实设备。
EdgeX Foundry 在 Docker 容器微服务下的相关使用技巧 1.Device Services 注册问题 2.数据库服务的操作工具 3.docker-compose.yml 用法 4.Docker Network 容器互联 总结 EdgeX Foundry 在 Docker 容器微服务下的相关使用技巧 EdgeX Foundry 是一个开源的边缘计算框架，广泛用于物联网设备的开发与管理。它高度依赖 Docker 来实现服务的容器化部署。本文将分享一些在 Docker 容器中使用 EdgeX Foundry 的实用技巧，包括 Device Services 的注册、数据库服务操作、docker-compose.yml 的用法以及网络管理等方面的经验总结。希望能为开发者提供参考。 1.Device Services 注册问题 在开发或调试 EdgeX Foundry 的 Device Services 时，可能会遇到设备服务配置残留或配置未正确加载的问题，默认情况下在官方 UI 界面（如下图所示）中没有删除具体一个 Device Service 的接口。 这会导致我们在加载 修改后的设备服务(关键是 /res/configration.yml 文件)时很麻烦，Device Service 的配置信息是存放在官方数据库服务容器 postgres:16.3 中的，最简单干脆的清理办法是清除所有Docker 卷： 清理容器和卷 # 删除所有已停止和运行中的容器 docker rm -f $(docker ps -a -q) # 删除所有未使用的 Docker 卷 docker volume rm -f $(docker volume ls -q) 启动 EdgeX 服务 假设你已经下载了 EdgeX 的 edgex-compose 仓库，可以通过以下步骤重新启动服务，此时获得了全新的一套 EdgeX 微服务： # 进入 compose-builder 目录 cd /home/ubuntu/edgex/edgex-compose/compose-builder/ # 后台启动所有服务 docker compose up -d root@ubuntu:/home/ubuntu/edgex/edgex-compose/compose-builder# docker compose up -d [+] Running 17/17 ✔ Volume "edgex_db-data" Created 0.0s ✔ Volume "edgex_kuiper-data" Created 0.0s ... ✔ Container edgex-support-scheduler Started 2.4s ✔ Container edgex-app-rules-engine Started 2.6s ✔ Container edgex-core-command Started 2.6s 注意： 在执行清理命令前，建议检查是否有重要数据未备份。 如果遇到权限问题，可使用 sudo 提升权限。 2.数据库服务的操作工具 EdgeX 的 Compose 文件仓库会为我们生成数据库服务容器如下： ID IMAGE COMMAND PORTS NAMES ... 8xxx7a postgres:16.3-alpine3.20 "docker-entrypoint.s…" 127.0.0.1:5432->5432/tcp edgex-postgres ... 为了方便查看数据库内部一些关键数据表的状态和信息，建议把 Postgres 端口映射到本地，用 SQL 工具（推荐 DBeaver）连接上，进行增删改查 : 注意： 首次使用需要登陆数据库，在 docker-compose.yml 文件中有数据库用户名和密码： database: container_name: edgex-postgres environment: POSTGRES_DB: edgex_db POSTGRES_PASSWORD: postgres ... 3.docker-compose.yml 用法 Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose，您可以使用 YML 文件来配置应用程序需要的所有服务。然后，使用一个命令，就可以从 YML 文件配置中创建并启动所有服务。 EdgeX Foundry 部署服务的核心配置文件正是 docker-compose.yml , 同时也是自定义设备服务的重要部署工具。以下是一些常用操作和注意事项 : （在 docker-compose.yml 同级文件夹下运行） 停止并删除所有服务容器： docker compose down 查看运行中的容器： docker compose ps 后台启动服务： docker compose up -d 重启某一个具体的服务并查看日志： docker compose restart edgex-device-openvino docker logs -f edgex-device-openvino 可以添加自定义设备服务在现有的 docker-compose.yml 中，如添加 s7 设备服务,也可调整现有的端口映射或服务依赖关系。在调整 docker-compose.yml 时，也可能需要更改文件权限： chmod +w docker-compose.yml vim docker-compose.yml ... bind: create_host_path: true device-s7: command: - -cp=keeper.http://edgex-core-keeper:59890 - --registry ... read_only: true bind: create_host_path: true mqtt-broker: ... 常见问题： 命令未生效：检查是否在正确的目录下运行（包含 docker-compose.yml 的目录）。 服务未启动：运行 docker compose ps 检查状态，可能需要查看日志定位问题。 4.Docker Network 容器互联 Docker 有一个连接系统允许将多个容器连接在一起，共享连接信息。EdgeX Foundry 的服务之间通过 Docker 网络通信，默认网络名为 edgex-network。以下是网络相关的操作技巧： 列出所有 Docker 网络并过滤出 EdgeX 相关网络： docker network ls | grep edgex-network # docker network ls | grep edgex-network 399e879a892b edgex_edgex-network bridge local 检查容器是否加入正确网络： docker inspect edgex-device-openvino | grep Network 如果服务无法通信，可以尝试重建网络： docker network rm edgex-network docker compose up -d # 自动重新创建网络 注意：删除网络前，确保没有正在运行的服务依赖它。 总结 通过本文，我们介绍了在 EdgeX Foundry 中使用 Docker 的一些实用技巧，包括清理容器、操作数据库、管理 docker-compose.yml 文件以及网络排查。这些方法可以帮助开发者更高效地部署和调试 EdgeX 服务。Docker 的灵活性为 EdgeX 提供了强大的支持，但在实际使用中仍需注意资源管理和服务依赖关系。
在 ubuntu22.04 中 安装 EdgeX 并运行 device-sdk-c sample
在 ubuntu22.04 中 安装 EdgeX 并运行 device-sdk-c sample 1.克隆源码-EdgeX 2.安装工具 3.编译运行-EdgeX 4.克隆源码-SDK-C 5.添加第三方依赖库 6.编译-C SDK 7.编译运行 random 设备服务示例 总结 在 ubuntu22.04 中 安装 EdgeX 并运行 device-sdk-c sample EdgeX Foundry 作为一个开源的边缘计算框架，为开发者提供了一个灵活、可扩展的平台，用于构建和管理边缘设备及服务。然而，对于初次接触 EdgeX 的用户来说，从零开始安装并运行其相关组件（如 device-sdk-c 示例）可能会面临不少挑战。 无论是环境的配置、依赖的安装，还是编译与调试的过程，每一步都需要细致的操作和清晰的思路。本文将以 Ubuntu 22.04 为基础平台，详细记录如何从克隆 EdgeX 源码到成功运行 device-sdk-c 的 sample 示例的全过程。希望通过这篇博客，不仅能为新手提供一份实用的操作指南，也能让有经验的开发者在遇到问题时找到解决思路。 1.克隆源码-EdgeX 在开始安装 EdgeX 之前，我们需要先获取其源码，而 GitHub 是获取最新版本的最佳途径。为了让后续操作更加有条理，我们首先在用户主目录下创建一个专属的工作目录，用于存放 EdgeX 相关的文件。以下是具体操作： 打开终端，输入以下命令创建一个名为 edgex 的文件夹，并进入该目录： mkdir edgex && cd edgex 接下来，我们需要从 GitHub 上克隆 EdgeX 的 Compose 文件仓库，这是运行 EdgeX 服务的核心配置文件集合。执行以下命令： git clone https://github.com/edgexfoundry/edgex-compose.git cd edgex-compose 结果显示如下： root@ubuntu:/home/ubuntu# mkdir edgex && cd edgex root@ubuntu:/home/ubuntu/edgex# git clone https://github.com/edgexfoundry/edgex-compose.git Cloning into 'edgex-compose'... remote: Enumerating objects: 5826, done. remote: Counting objects: 100% (3563/3563), done. remote: Compressing objects: 100% (319/319), done. remote: Total 5826 (delta 3504), reused 3250 (delta 3244), pack-reused 2263 (from 3) Receiving objects: 100% (5826/5826), 1.59 MiB | 1.76 MiB/s, done. Resolving deltas: 100% (4949/4949), done. root@ubuntu:/home/ubuntu/edgex# ls edgex-compose 输出信息显示了克隆进度，最终 ls 命令确认目录中已存在 edgex-compose 文件夹，说明这一步成功完成。 2.安装工具 EdgeX 的部署离不开 Docker，因为它通过容器化技术简化了服务的管理和运行。因此，我们需要先在 Ubuntu 系统中安装 Docker。以下是详细的安装步骤： 使用官方脚本安装 Docker，并指定阿里云镜像源以加速下载： curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh --mirror Aliyun docker -v 第一条命令通过 curl 下载安装脚本并保存为 get-docker.sh 文件；第二条命令执行脚本，--mirror Aliyun 参数指定使用阿里云镜像源，避免默认源下载缓慢；最后通过 docker -v 检查版本，确保安装成功。安装过程的输出类似： root@ubuntu:/home/ubuntu# curl -fsSL https://get.docker.com -o get-docker.sh root@ubuntu:/home/ubuntu# sh get-docker.sh --mirror Aliyun # Executing docker install script, commit: 4c94a56999e10efcf48c5b8e3f6afea464f9108e ... Client: Docker Engine - Community Version: 28.0.1 API version: 1.48 ... root@ubuntu:/home/ubuntu# docker -v Docker version 28.0.1, build 068a01e 如果下载速度过慢，可能是镜像源的问题。这时可以调整 Ubuntu 的软件源以提高效率。首先检查系统版本： root@ubuntu:/home/ubuntu# more /etc/os-release PRETTY_NAME="Ubuntu 22.04.5 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04.5 LTS (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-poli cy" UBUNTU_CODENAME=jammy 确认版本为 jammy 后，访问南京大学镜像站（https://mirror.nju.edu.cn/mirrorz-help/ubuntu/）, 获取对应源配置并编辑 /etc/apt/sources.list： # vim /etc/apt/sources.list deb https://mirror.nju.edu.cn/ubuntu/ jammy main restricted universe multiverse deb https://mirror.nju.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse deb https://mirror.nju.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse 启动 docker 并查看状态： root@ubuntu:/home/ubuntu# systemctl restart docker root@ubuntu:/home/ubuntu# systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset> Active: active (running) since Thu 2025-03-13 12:21:05 CST; 6s ago TriggeredBy: ● docker.socket Docs: https://docs.docker.com Main PID: 28525 (dockerd) Tasks: 10 Memory: 22.1M CPU: 1.228s CGroup: /system.slice/docker.service └─28525 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/con> Active: active (running) 表示 Docker 已正常运行，为后续 EdgeX 部署奠定了基础。 3.编译运行-EdgeX 有了 Docker 支持后，我们可以开始部署 EdgeX 服务。EdgeX 使用 Docker Compose 管理多个容器，因此需要进入 compose-builder 目录执行构建和运行命令： 进入目录并运行无安全模式的 EdgeX： cd /home/ubuntu/edgex/edgex-compose/compose-builder/ make run no-secty echo MQTT_VERBOSE= MQTT_VERBOSE= docker compose -p edgex -f docker-compose-base.yml -f add-postgres.yml -f add-mqtt-messagebus.yml -f add-mqtt-broker-mosquitto.yml convert --output docker-compose.yml.gen if [ "0" = "0" ]; then rm -rf ./gen_ext_compose; fi echo "# Generated with: Docker Compose version v2.33.1" > docker-compose.yml cat docker-compose.yml.gen >> docker-compose.yml rm docker-compose.yml.gen docker compose -p edgex up -d [+] Running 12/12 ✔ Container edgex-ui-go Running 0.0s ✔ Container edgex-postgres Running 0.0s ✔ Container edgex-mqtt-broker Running 0.0s ✔ Container edgex-core-keeper Running 0.0s ✔ Container edgex-core-metadata Running 0.0s ✔ Container edgex-core-common-config-bootstrapper Running 0.0s ✔ Container edgex-app-rules-engine Running 0.0s ✔ Container edgex-support-scheduler Running 0.0s ✔ Container edgex-core-data Running 0.0s ✔ Container edgex-kuiper Running 0.0s ✔ Container edgex-support-notifications Running 0.0s ✔ Container edgex-core-command Started 0.4s 这表明所有容器已启动，包括 UI、数据库和核心服务等。 查看容器运行状态，显示如下，每个容器显示 Up 状态，说明 EdgeX 已成功运行： docker compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS edgex-app-rules-engine nexus3.edgexfoundry.org:10004/app-service-configurable:latest "/app-service-config…" app-rules-engine 7 minutes ago Up 7 minutes 48095/tcp, 127.0.0.1:59701->59701/tcp edgex-core-command nexus3.edgexfoundry.org:10004/core-command:latest "/core-command --reg…" core-command 7 minutes ago Up 5 minutes 127.0.0.1:59882->59882/tcp edgex-core-common-config-bootstrapper nexus3.edgexfoundry.org:10004/core-common-config-bootstrapper:latest "entrypoint.sh /core…" core-common-config-bootstrapper 7 minutes ago Up 7 minutes edgex-core-data nexus3.edgexfoundry.org:10004/core-data:latest "/core-data --regist…" core-data 7 minutes ago Up 7 minutes 127.0.0.1:59880->59880/tcp edgex-core-keeper nexus3.edgexfoundry.org:10004/core-keeper:latest "/core-keeper" core-keeper 7 minutes ago Up 7 minutes 127.0.0.1:59890->59890/tcp edgex-core-metadata nexus3.edgexfoundry.org:10004/core-metadata:latest "/core-metadata ... 4.克隆源码-SDK-C 为了运行 device-sdk-c 的示例，我们还需要获取其源码。device-sdk-c 包含了丰富的 C 语言示例，是开发自定义设备服务的好起点： 在 /edgex 目录下克隆仓库： git clone https://github.com/edgexfoundry/device-sdk-c.git root@ubuntu:/home/ubuntu/edgex/edgex-compose/compose-builder# cd ../.. root@ubuntu:/home/ubuntu/edgex# ls edgex-compose root@ubuntu:/home/ubuntu/edgex# git clone https://github.com/edgexfoundry/device-sdk-c.git Cloning into 'device-sdk-c'... remote: Enumerating objects: 5641, done. remote: Counting objects: 100% (527/527), done. remote: Compressing objects: 100% (213/213), done. remote: Total 5641 (delta 419), reused 337 (delta 307), pack-reused 5114 (from 4) Receiving objects: 100% (5641/5641), 1.93 MiB | 1.70 MiB/s, done. Resolving deltas: 100% (3210/3210), done. 5.添加第三方依赖库 编译 device-sdk-c 需要一系列依赖库，参考官方 README（https://github.com/edgexfoundry/device-sdk-c/blob/main/README.md） A Linux build host A version of GCC supporting C11. CMake version 3 or greater and make. Development libraries and headers for: curl (version 7.56 or later) microhttpd (version 0.9) libyaml (version 0.1.6 or later) libcbor (version 0.5) paho (version 1.3.x) libuuid (from util-linux v2.x) IOTech's C utilities package (version 1.5) apt-get install libcurl4-openssl-dev libmicrohttpd-dev libyaml-dev libcbor-dev libpaho-mqtt-dev apt-get install lsb-release apt-transport-https curl gnupg curl -fsSL https://iotech.jfrog.io/artifactory/api/gpg/key/public | gpg --dearmor -o /usr/share/keyrings/iotech.gpg echo "deb [signed-by=/usr/share/keyrings/iotech.gpg] https://iotech.jfrog.io/iotech/debian-release $(lsb_release -cs) main" | tee -a /etc/apt/sources.list.d/iotech.list apt-get update apt-get install iotech-iot-1.5-dev apt install uuid-dev 6.编译-C SDK 在C SDK 顶层目录下, 运行 make，最终结果显示如下： root@ubuntu:/home/ubuntu/edgex/device-sdk-c# make ... [100%] Built target device-file 7.编译运行 random 设备服务示例 编译前的准备工作，参考https://github.com/edgexfoundry/device-sdk-c/blob/main/src/c/examples/random/README.md 此示例设备服务模拟了一个具有两个随机数生成器和一个可读取和写入状态的开关的设备。先配置环境变量，注意自己的路径： export CSDK_DIR=/home/logan/edgex/device-sdk-c/build/release/_CPack_Packages/Linux/TGZ/csdk-0.0.0 export LD_LIBRARY_PATH=$CSDK_DIR/lib:/opt/iotech/iot/1.5/lib 涉及头文件和库文件的环境变量很重要，并且运行程序的时候也需要用到。 在/random 目录下执行编译命令： gcc -I$CSDK_DIR/include -I/opt/iotech/iot/1.5/include -L$CSDK_DIR/lib -L/opt/iotech/iot/1.5/lib -o device-random device-random.c -lcsdk -liot 将编译好的可执行文件 device-random 复制到以下源文件目录，因为源文件目录提供了/res 里面包含预设的配置文件： cp device-sdk-c/build/release/c/examples/random/device-random device-sdk-c/src/c/examples/random/ cd /home/ubuntu/edgex/device-sdk-c/src/c/examples/random/ 执行以下命令：-o 表示覆盖默认配置，-cp 指定配置提供者 ./device-random -o -cp=keeper.http://localhost:59890 如果遇到报错，按以下方法解决： 安全 Token 报错：设置环境变量禁用安全服务： root@ubuntu:/home/ubuntu/edgex/device-sdk-c/src/c/examples/random# ./device-random -o -cp=keeper.http://localhost:59890 level=INFO ts=2025-03-13T06:44:41Z app=device-random msg="iot_threadpool_alloc (threads: 8 max_jobs: 0 default_priority: -1 affinity: -1)" level=INFO ts=2025-03-13T06:44:41Z app=device-random msg="iot_scheduler_alloc (priority: -1 affinity: -1)" level=ERROR ts=2025-03-13T06:44:41Z app=device-random msg="vault: Token file /tmp/edgex/secrets/device-random/secrets-token.json doesn't parse as JSON" Error: 4: Configuration is invalid 解决：需要关闭Edges安全服务： 参考https://docs.edgexfoundry.org/4.0/microservices/configuration/CommonEnvironmentVariables/#edgex_security_secret_store export EDGEX_SECURITY_SECRET_STORE="false" Curl 解析失败：编辑 /etc/hosts： level=DEBUG ts=2025-03-13T06:45:41Z app=device-random msg="mqtt: subscribing to edgex/configs/edgex/v4/core-common-config-bootstrapper/all-services/#" level=DEBUG ts=2025-03-13T06:45:41Z app=device-random msg="mqtt: subscribing to edgex/configs/edgex/v4/core-common-config-bootstrapper/device-services/#" level=ERROR ts=2025-03-13T06:45:42Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:43Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:44Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:45Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:46Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" level=ERROR ts=2025-03-13T06:45:47Z app=device-random msg="Curl failed with code 6 (Couldn't resolve host name)" 解决：在/etc/hosts 中添加127.0.0.1 edgex-core-metadata： /etc/hosts 127.0.0.1 localhost 127.0.0.1 edgex-core-metadata # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters ff02::3 ip6-allhosts 健康检查间隔缺失：修改 res/configuration.yml： level=DEBUG ts=2025-03-13T06:50:39Z app=device-random msg="HTTP response: 404" level=DEBUG ts=2025-03-13T06:50:39Z app=device-random msg="HTTP response: 400" level=ERROR ts=2025-03-13T06:50:39Z app=device-random msg="Register service failed: {"apiVersion":"v3","message":"AddRegistrationRequest.Registration.HealthCheck.Interval field is required","statusCode":400} level=ERROR ts=2025-03-13T06:50:39Z app=device-random msg="Unable to register service in registry" Error: 21: HTTP request failed level=DEBUG ts=2025-03-13T06:50:39Z app=device-random msg="Scheduler thread stopping" 解决：在res/configration.yml 中添加 HealthCheckInterval: 10s： Writable: LogLevel: DEBUG Service: Host: localhost Port: 59999 StartupMsg: Example random device service started HealthCheckInterval: 10s ​ 或者在/etc/hosts 中添加127.0.0.1 edgex-mqtt-broker,然后在执行命令 ./device-random -o -cp=keeper.http://localhost:59890 中去掉 -o： /etc/hosts 127.0.0.1 localhost 127.0.0.1 edgex-core-metadata 127.0.0.1 edgex-mqtt-broker # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters ff02::3 ip6-allhosts 如果程序运行成功，将显示如下： ... level=INFO ts=2025-03-13T07:36:45Z app=device-random correlation-id=c26ef1f7-755c-4952-8091-5c7c436e039b msg="AutoEvent: RandomDevice1/SensorOne" level=INFO ts=2025-03-13T07:36:50Z app=device-random correlation-id=dc222997-3113-465b-9c5c-26829ce24901 msg="AutoEvent: RandomDevice1/SensorTwo" level=INFO ts=2025-03-13T07:36:55Z app=device-random correlation-id=049d560d-896a-4e5a-8101-f3364c2767f1 msg="AutoEvent: RandomDevice1/SensorOne" 打开浏览器进入：localhost:4000 可发现一个设备服务，可成功读取随机数，如下： 总结 通过以上步骤，我们成功地在 Ubuntu 22.04 上完成了 EdgeX 的安装，并运行了 device-sdk-c 的 random 设备服务示例。从源码的克隆到 Docker 环境的搭建，再到依赖库的安装和配置文件的调整，每一个环节都环环相扣，考验着我们的耐心和解决问题的能力。 过程中可能遇到的报错，如安全性 token 问题、Curl 解析失败或健康检查间隔配置缺失，也提醒我们在操作复杂系统时，细节的重要性不容忽视。 最终，当我们在浏览器中看到 localhost:4000 界面上显示的设备服务和随机数据时，那份成就感无疑是对所有努力的最好回报。这不仅是一个技术实现的过程，更是一次对 EdgeX 架构和边缘计算理念的深入学习之旅。未来，我们还可以基于此基础，进一步扩展功能、优化性能，让边缘计算的潜力在更多场景中得以释放！
亿琪软件携手 EdgeX Foundry 发布设备服务 Device-OPC-UA，助力工业互联网互联互通
2024 年 3 月 26 日 – 上海 – 致力于推动边缘计算技术创新和应用的开源软件社区 EdgeX Foundry 今日宣布，正式发布由亿琪软件参与贡献的设备服务 Device-OPC-UA。该服务将为工业互联网领域提供更加便捷、高效的设备连接和数据采集方案，助力实现工业设备的互联互通和智能化管理。 GitHub 仓库地址：https://github.com/edgexfoundry/device-opc-ua Device-OPC-UA 简介 Device-OPC-UA 是基于 OPC-UA 标准的设备服务，可实现 EdgeX Foundry 与 OPC-UA 兼容设备的无缝连接。OPC-UA 是工业互联网领域重要的通信协议，具有开放、统一、安全等特点，广泛应用于工业自动化、智能制造等领域。 Device-OPC-UA 的发布，将为 EdgeX Foundry 社区带来以下优势： 扩展设备支持范围: 支持 OPC-UA 协议的设备，可通过 Device-OPC-UA 服务轻松接入 EdgeX Foundry 平台，丰富平台设备资源。 简化开发流程: Device-OPC-UA 提供标准化的 API 接口，开发人员可快速便捷地开发边缘应用，降低开发成本和难度。 提升数据互操作性: Device-OPC-UA 遵循 OPC-UA 标准，可实现不同厂商、不同型号设备之间的数据互通互享，打破数据孤岛。 亿琪软件积极贡献开源社区 亿琪软件作为 EdgeX Foundry 社区的活跃成员，一直积极参与社区建设和技术贡献。此次发布的 Device-OPC-UA 服务，是亿琪软件在边缘计算领域的技术积累和实践成果的体现。 亿琪软件表示：“Device-OPC-UA 的发布，将进一步推动 EdgeX Foundry 在工业互联网领域的应用落地。亿琪软件将持续参与开源社区建设，携手合作伙伴共同打造开放、互通、智能的工业互联网生态。” 关于 EdgeX Foundry EdgeX Foundry 是一个由 LF Edge 主导的开源项目，旨在为边缘计算提供统一的框架和标准。EdgeX Foundry 汇聚了来自全球的领先技术公司、学术机构和开源社区，共同推动边缘计算技术的创新和发展。 关于我们 !!! success "亿琪软件" 上海亿琪软件有限公司成立于 2016 年，专注于 5G 通信、AI 人工智能、边缘计算和大数据网络安全多项技术领域，致力于物联网领域前沿技术的创新，为用户提供全方位、智能化和安全的物联网解决方案。 2023 年，公司发布“YiFUSION ｜工业边缘智能融合一体机”产品，为工业客户提供一整套的边缘计算+AI 能力：高性能数据采集、多类型数据融合、AI 算法集成、云端业务对接。在边缘网关的基础上，集成了 IoT 平台的边缘协同能力、本地 Web SCADA 和 HMI 功能、本地数据存储、边缘 AI 视频分析、行业应用集成等。 2022 年，公司推出 “YiCLOUD ｜亿琪云”一站式物联网应用解决方案。公司的业务涵盖了智慧城市、智慧农业、智能工厂和智慧园区等多个领域，公司软硬件产品和解决方案获得华为技术认证，得到中国移动 OCP 认证，公司还是边缘计算产业联盟 ECC 成员。 !!! tip "关注我们" !!! warning "联系我们--商业服务" 网站：http://yiqisoft.cn 邮件：support@yiqisoft.cn 电话：021-68863086 手机：186-1666-9123
文档｜EdgeX 设备微服务之 OPC-UA -- device-opcua 完整使用手册
EdgeX Foundry （以下统一简称：EdgeX）设备微服务 device-opcua 由我司根据已有代码优化后贡献给社区，文档也是必不可少的环节，社区要求英文，现翻译成中文，方便国内客户阅读和使用。 目的 入门 配置 协议属性 API 参考 源代码 目的 该服务的目的是将 OPC-UA 设备连接到 EdgeX。 入门 本页介绍如何将 OPC-UA 设备连接到 EdgeX。 在此示例中，我们使用模拟器来实现（OPC UA Simulation Server）。 这提供了一种测试设备服务功能的简单方法。 环境 您可以使用任何可以安装 docker 和 docker-compose 的操作系统。 在本例中，我们使用 Ubuntu 使用 docker 部署 EdgeX。 设置 Prosys OPC UA Simulation Server 设备 !!! warning "警告" 本页面并不是设置 OPC-UA 设备的完整指南，仅介绍了几个重要步骤供参考。 下载 Prosys OPC UA 模拟服务器 从 OPC UA 模拟服务器下载 下载最新版本的 OPC UA Simulation Server Download。 安装模拟服务器 启动模拟服务器 验证模拟服务器 Status 复制连接地址(UA TCP)值：opc.tcp://jiekemacbookpro14.lan:53530/OPCUA/SimulationServer Objects 启动微服务之前 以下部分介绍如何在启动服务之前完成设置。 使用预定义的配置进行测试 设置新配置 使用预定义的配置进行测试 文件夹中的预定义配置：cmd/res/ 预定义 Device Profile 设备配置 Device Profile profiles/Simple-Driver.yaml 描述了一个 OPC-UA 设备，它包含 3 个资源，如下所示： name: OPCUA-Server manufacturer: Prosys model: Simlulator labels: - OPCUA - TEST description: Simulation OPCUA Server deviceResources: - name: Constant description: generated constant number properties: valueType: Float64 readWrite: RW attributes: nodeId: ns=3;i=1001 - name: Counter description: generated counter number properties: valueType: Int32 readWrite: R attributes: nodeId: ns=3;i=1002 - name: Random description: generated random number properties: valueType: Float64 readWrite: R attributes: nodeId: ns=3;i=1003 deviceCommands: - name: ConstantNum isHidden: false readWrite: RW resourceOperations: - deviceResource: Constant defaultValue: 0.0 - name: CounterNum isHidden: false readWrite: R resourceOperations: - deviceResource: Counter defaultValue: 0 - name: RandomNum isHidden: false readWrite: R resourceOperations: - deviceResource: Random defaultValue: 0.0 该表描述了设备配置文件的每个资源： Resource Name valueType of EdgeX nodeId Description Constant Float64 ns=3;i=1001 generated constant number Counter Int32 ns=3;i=1002 generated counter number Random Float64 ns=3;i=1003 generated random number 预定义设备配置 Device Configuration 设备配置 Device Configuration devices/Simple-Devices.yaml 描述了如下所示的两种设备。 deviceList: - name: SimulationServer profileName: OPCUA-Server description: OPCUA device labels: - OPCUA - TEST protocols: opcua: Endpoint: 'opc.tcp://192.168.123.21:53530/OPCUA/SimulationServer' 设置一个新的配置 Configuration 创建自定义配置文件夹 运行以下命令： mkdir -p custom-config 设置新的设备配置 Device Profile 运行以下命令来创建您的设备配置文件： cd custom-config vi new-device-profile.yaml 插入您的设备配置文件定义，这取决于您的 OPC-UA 设备配置。 设置设备服务配置 Configuration cd custom-config vi new-device-config.yaml 填写 new-device-config.yaml 文件。 准备 docker-compose 文件 生成 docker compose 文件 Clone edgex-compose git clone https://github.com/edgexfoundry/edgex-compose.git 生成 docker-compose.yml 文件 cd edgex-compose make gen no-secty ds-opcua 将自定义配置添加到 docker-compose 文件 将准备好的配置文件添加到 docker-compose 文件中，您可以使用卷挂载它们并更改 device-opcua 内部使用的环境。 打开 docker-compose.yml 文件，然后添加卷路径和环境，如下所示： device-opcua: ... environment: ... DEVICE_PROFILESDIR: /custom-config DEVICE_DEVICESDIR: /custom-config volumes: ... - /path/to/custom-config:/custom-config 在 Docker 上启动 EdgeX Foundry 由于我们在上一步中生成了 docker-compose.yml 文件，因此我们可以部署 EdgeX，如下所示： cd edgex-compose/compose-builder docker compose -p edgex up -d [+] Running 12/12 ✔ Container edgex-core-consul Started 0.0s ✔ Container edgex-ui-go Started 0.0s ✔ Container edgex-redis Started 0.0s ✔ Container edgex-core-common-config-bootstrapper Started 0.1s ✔ Container edgex-support-scheduler Started 0.0s ✔ Container edgex-kuiper Started 0.0s ✔ Container edgex-support-notifications Started 0.1s ✔ Container edgex-core-metadata Started 0.1s ✔ Container edgex-core-command Started 0.0s ✔ Container edgex-core-data Started 0.0s ✔ Container edgex-app-rules-engine Started 0.0s ✔ Container edgex-device-opcua Started 0.0s 启动服务后设置 如果服务已在运行并且您想要添加设备，则可以使用本节中概述的“核心元数据 API”。 如果您按照启动服务之前的设置中的说明设置设备配置文件和服务，则可以跳过此部分。 要在启动服务后添加设备，请完成以下步骤： 使用 POST 将上面的设备配置文件上传到元数据：http://localhost:59881/api/v3/deviceprofile/uploadfile ，并以表单数据格式将文件作为密钥“文件”添加到正文，以及创建的 ID 将被退回。 以下示例命令使用 curl 发送请求： curl http://localhost:59881/api/v3/deviceprofile/uploadfile -F "file=@new-device-profile.yml" 确保 OPC-UA 设备服务正在运行，如有必要或使用其他设备服务，请调整下面的服务名称以匹配。 使用 POST 将设备添加到 http://localhost:59881/api/v3/device，正文将类似于： curl http://localhost:59881/api/v3/device -H "Content-Type:application/json" -X POST \ -d '[ { "apiVersion" : "{{api_version}}", "device" : { "adminState" : "UNLOCKED", "description" : "OPCUA device", "labels" : [ "OPCUA", "TEST" ], "name" : "SimulationServer1", "operatingState" : "UP", "profileName" : "OPCUA-Server", "protocols" : { "opcua" : { "Endpoint" : "opc.tcp://192.168.123.21:53530/OPCUA/SimulationServer" } }, "serviceName" : "device-opcua" } } ]' 服务名称必须匹配/引用目标设备服务（“device-opcua”），并且配置文件名称必须与前面步骤中的设备配置文件名称("OPCUA-Server") 执行命令 现在我们准备运行一些命令。 查找可执行命令 使用以下查询查找可执行命令： curl http://localhost:59882/api/v3/device/name/SimulationServer1 | json_pp { "apiVersion": "v3", "deviceCoreCommand": { "coreCommands": [ { "get": true, "name": "ConstantNum", "parameters": [ { "resourceName": "Constant", "valueType": "Float64" } ], "path": "/api/v3/device/name/SimulationServer1/ConstantNum", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "CounterNum", "parameters": [ { "resourceName": "Counter", "valueType": "Int32" } ], "path": "/api/v3/device/name/SimulationServer1/CounterNum", "url": "http://edgex-core-command:59882" }, { "get": true, "name": "RandomNum", "parameters": [ { "resourceName": "Random", "valueType": "Float64" } ], "path": "/api/v3/device/name/SimulationServer1/RandomNum", "url": "http://edgex-core-command:59882" }, { "get": true, "name": "Constant", "parameters": [ { "resourceName": "Constant", "valueType": "Float64" } ], "path": "/api/v3/device/name/SimulationServer1/Constant", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "Counter", "parameters": [ { "resourceName": "Counter", "valueType": "Int32" } ], "path": "/api/v3/device/name/SimulationServer1/Counter", "url": "http://edgex-core-command:59882" }, { "get": true, "name": "Random", "parameters": [ { "resourceName": "Random", "valueType": "Float64" } ], "path": "/api/v3/device/name/SimulationServer1/Random", "url": "http://edgex-core-command:59882" } ], "deviceName": "SimulationServer1", "profileName": "OPCUA-Server" }, "statusCode": 200 } 执行设置命令 根据url和parameterNames执行 SET 命令，运行 SET 命令时将localhost替换为服务器 IP。 这里我们使用“heartbeat”命令： curl http://localhost:59882/api/v3/device/name/SimulationServer1/Constant \ -H "Content-Type:application/json" -X PUT \ -d '{"Constant": "199.99"}' {"apiVersion":"v3","statusCode":200} 执行获取命令 运行 GET 命令时，将 localhost 替换为服务器 IP。 这里我们使用“heartbeat”命令： curl http://localhost:59882/api/v3/device/name/SimulationServer1/Constant { "apiVersion": "v3", "event": { "apiVersion": "v3", "deviceName": "SimulationServer1", "id": "cc8ea84d-0a5f-4d5b-b4db-9126368f4aa3", "origin": 1702613269923352589, "profileName": "OPCUA-Server", "readings": [ { "deviceName": "SimulationServer1", "id": "5c1ea763-bb50-4837-b78c-8ef1c2ed0035", "origin": 1702613269921, "profileName": "OPCUA-Server", "resourceName": "Constant", "value": "1.999900e+02", "valueType": "Float64" } ], "sourceName": "Constant" }, "statusCode": 200 } 自动事件 AutoEvent 在设备定义文件的 autoEvents 部分中定义： deviceList: autoEvents: interval: '30s' onChange: false sourceName: 'Constant' 服务启动后，查询 core-data API。 结果显示该服务每“30 秒”自动执行一次命令。 配置 OPC-UA 设备服务具有以下配置来实现 OPCUAServer 协议。 OPCUAServer 示例如下图： # configuration.yml OPCUAServer: DeviceName: SimulationServer Policy: None Mode: None CertFile: '' KeyFile: '' Writable: Resources: 'Counter,Random' Configuration Default Value Description DeviceName SimmulationServer Device name of OPC-UA Policy None Security Policy of OPC-UA- None- Basic128Rsa15- Basic256- Basic256Sha256- Aes128Sha256RsaOaep- Aes256Sha256RsaPss Mode None Security Mode of OPC-UA-None- Sign- SignAndEncrypt CertFile none Cert file of OPC-UA, path to cert.pem KeyFile none Key file of OPC-UA, path to private key.pem Writable.Resources none Subscritions of OPC-UA 协议属性 设备为 opcua 定义以下协议属性。 这些属性位于每个设备定义的协议部分中的“opcua”键下。 Property Description Endpoint The Connection Address(UA TCP) of OPC-UA server !!! example "opcua 协议属性示例" ```yaml ... protocols: opcua: Endpoint: "opc.tcp://jiekemacbookpro14.lan:53530/OPCUA/SimulationServer" ``` API 参考 该服务继承了 SDK 中的通用设备服务 API API。 源代码 Device Service OPC-UA 的源代码可以在 https://github.com/edgexfoundry/device-opcua 中找到（社区还未合并，目前在 https://github.com/yiqisoft/device-opcua-go/tree/refactor-v3 ）。 关于我们 !!! success "亿琪软件" 上海亿琪软件有限公司成立于 2016 年，专注于 5G 通信、AI 人工智能、边缘计算和大数据网络安全多项技术领域，致力于物联网领域前沿技术的创新，为用户提供全方位、智能化和安全的物联网解决方案。 2023 年，公司发布“YiFUSION ｜工业边缘智能融合一体机”产品，为工业客户提供一整套的边缘计算+AI 能力：高性能数据采集、多类型数据融合、AI 算法集成、云端业务对接。在边缘网关的基础上，集成了 IoT 平台的边缘协同能力、本地 Web SCADA 和 HMI 功能、本地数据存储、边缘 AI 视频分析、行业应用集成等。 2022 年，公司推出 “YiCLOUD ｜亿琪云”一站式物联网应用解决方案。公司的业务涵盖了智慧城市、智慧农业、智能工厂和智慧园区等多个领域，公司软硬件产品和解决方案获得华为技术认证，得到中国移动 OCP 认证，公司还是边缘计算产业联盟 ECC 成员。 !!! tip "关注我们" !!! warning "联系我们--商业服务" 网站：http://yiqisoft.cn 邮件：support@yiqisoft.cn 电话：021-68863086 手机：186-1666-9123
EdgeX Foundry 边缘计算框架的分层和服务为边缘设备/节点和云/企业应用之间提供了一个双向转换引擎。EdgeX 作为边缘计算平台是非常不错的选择。 OPC-UA Micro Service - 用于 OPC-UA 设备连接到 EdgeX 的设备服务。 01 介绍 EdgeX Foundry 边缘计算框架的分层和服务为边缘设备/节点和云/企业应用之间提供了一个双向转换引擎。EdgeX 作为边缘计算平台是非常不错的选择。 OPC-UA Micro Service - 用于 OPC-UA 设备连接到 EdgeX 的设备服务。 02 技术要点 工业互联网应用场景中，OPC-UA 是非常常见的工业控制设备，边缘计算框架 EdgeX 对于其驱动一直在 EdgeX Foundry 社区没有开源支持，我司经过项目的验证和长期的维护，在社区原有项目基础之上进行了 V3.2.0 版本驱动的移植和扩展。 本开源 EdgeX 设备驱动使用 github.com/goopcua/opcua 作为基础库，在基于 EdgeX device-sdk-go 的框架下，完成了驱动的开发和验证。 03 版本支持 本次开源的 EdgeX 驱动支持 EdgeX v3.x（最新版本，且持续更新和维护）。 开源代码：https://github.com/edgexfoundry-holding/device-opcua-go 04 使用文档 预计发布在：https://docs.edgexfoundry.org/3.1/microservices/device/services/device-opcua，敬请期待。 05 产品应用 EdgeX 框架应用于我司的边缘计算产品 YiEDGE（边缘计算软件） 和 YiFUSION（工业边缘智能融合网关）。 YiFUSION: 边缘融合智能网关是指具备边缘计算、智能分析、数据融合等功能的智能网关，能够在边缘侧实现数据采集、处理、分析、融合和应用，为用户提供更加智能、高效和低成本的服务。 简洁定义 英文名称：YiFUSION 中文名称：工业边缘智能融合网关 产品形态：软硬件一体机 硬件架构：X86/ARM/超融合/国产化 软件架构：全微服务化 关于我们 "亿琪软件" 上海亿琪软件有限公司成立于 2016 年，专注于 5G 通信、AI 人工智能、边缘计算和大数据网络安全多项技术领域，致力于物联网领域前沿技术的创新，为用户提供全方位、智能化和安全的物联网解决方案。 2023 年，公司发布“YiFUSION ｜工业边缘智能融合一体机”产品，为工业客户提供一整套的边缘计算+AI 能力：高性能数据采集、多类型数据融合、AI 算法集成、云端业务对接。在边缘网关的基础上，集成了 IoT 平台的边缘协同能力、本地 Web SCADA 和 HMI 功能、本地数据存储、边缘 AI 视频分析、行业应用集成等。 2022 年，公司推出 “YiCLOUD ｜亿琪云”一站式物联网应用解决方案。公司的业务涵盖了智慧城市、智慧农业、智能工厂和智慧园区等多个领域，公司软硬件产品和解决方案获得华为技术认证，得到中国移动 OCP 认证，公司还是边缘计算产业联盟 ECC 成员。 "联系我们--商业服务" 网站：http://yiqisoft.cn 邮件：support@yiqisoft.cn 电话：021-68863086 手机：186-1666-9123
文档｜EdgeX 设备微服务之西门子 S7 -- device-s7 完整使用手册
EdgeX Foundry （以下统一简称：EdgeX）设备微服务 device-s7 由我司贡献给社区，文档也是必不可少的环节，社区要求英文，现翻译成中文，方便国内客户阅读和使用。 目录 目的 入门 环境 设置 S7 设备 设置 ISO-on-TCP 访问 IP protocol 启用 S7 Communication 远程访问 (可选) 保留 S7 communication 设置数据块 Data Blocks 启动微服务之前 使用预定义的配置进行测试 预定义 Device Profile 预定义设备配置 Device Configuration 设置一个新的配置 Configuration 创建自定义配置文件夹 设置新的设备配置 Device Profile 设置设备服务配置 Configuration 准备 docker-compose 文件 生成 docker compose 文件 将自定义配置添加到 docker-compose 文件 在 Docker 上启动 EdgeX Foundry 启动服务后设置 执行命令 查找可执行命令 执行设置命令 执行获取命令 自动事件 配置 协议属性 API 参考 源代码 额外细节 读命令 Read Command 写命令 Write Command 何时转换数据 支持的转换 "亿琪软件" "联系我们--商业服务" 目的 该服务的目的是将西门子 S7 PLC 设备连接到 EdgeX。 入门 本页介绍如何将 S7 设备连接到 EdgeX。 在此示例中，我们使用真实设备（例如 S7-1200）。 这提供了一种测试设备服务功能的简单方法。 环境 您可以使用任何可以安装 docker 和 docker-compose 的操作系统。 在本例中，我们使用 Ubuntu 使用 docker 部署 EdgeX。 设置 S7 设备 !!! warning "警告" 本页面并不是设置 S7 设备的完整指南，仅介绍了几个重要步骤。 设置 ISO-on-TCP 访问 IP protocol 设置 Ethernet addresses -> IP protocol, 例如： 192.168.123.199/255.255.255.0 启用 S7 Communication 远程访问 启用 Permit access with PUT/GET communication from remote partner. (可选) 保留 S7 communication S7 Communication 保留和使用的资源数量，“Configured” 为 0，表示 EdgeX 最多可以建立 8 个连接。 !!! warning "提示" 重要提示：不要与单个 S7 设备创建超过“6”个连接！ 我们需要为 WINCC 保留 “2” 个连接。 设置数据块 Data Blocks Data Block 1 在本例子中，只需使用 Clcok 偏移量 160.0 中的进行测试即可。 Data Block 4 在本例子中, 使用 bool, byte, word, dword, int, dint, real 作为测试. 启动微服务之前 以下部分介绍如何在启动服务之前完成设置。 使用预定义的配置进行测试 设置新配置 使用预定义的配置进行测试 文件夹中的预定义配置：cmd/res/ 预定义 Device Profile 设备配置 Device Profile profiles/Simple-Driver.yaml 描述了一个 S7-1200 设备，它包含 8 个资源，如下所示： name: S7-Device manufacturer: YIQISOFT description: Example of S7 Device model: Siemens S7 labels: [ISO-on-TCP] deviceResources: - name: bool description: PLC bool isHidden: false properties: valueType: Bool readWrite: RW attributes: NodeName: DB4.DBX0.0 - name: byte description: PLC byte isHidden: false properties: valueType: Uint8 readWrite: RW attributes: NodeName: DB4.DBB1 - name: word description: PLC word isHidden: false properties: valueType: Int16 readWrite: RW attributes: NodeName: DB4.DBW2 - name: dword description: PLC dword isHidden: false properties: valueType: Int32 readWrite: RW attributes: NodeName: DB4.DBD4 - name: int description: PLC int isHidden: false properties: valueType: Int16 readWrite: RW attributes: NodeName: DB4.DBW8 - name: dint description: PLC dint isHidden: false properties: valueType: Int32 readWrite: RW attributes: NodeName: DB4.DBW10 - name: real description: PLC real isHidden: false properties: valueType: Float32 readWrite: RW attributes: NodeName: DB4.DBD14 - name: heartbeat description: PLC heartbeat isHidden: false properties: valueType: Int16 readWrite: RW attributes: NodeName: DB1.DBW160 deviceCommands: - name: AllResource isHidden: false readWrite: RW resourceOperations: - deviceResource: bool defaultValue: 'false' - deviceResource: byte defaultValue: '0' - deviceResource: word defaultValue: '0' - deviceResource: dword defaultValue: '0' - deviceResource: int defaultValue: '0' - deviceResource: dint defaultValue: '0' - deviceResource: real defaultValue: '0' - deviceResource: heartbeat defaultValue: '0' 该表描述了设备配置文件的每个资源： Resource Name valueType of EdgeX NodeName Note(DB: Data Block) bool Bool DB4.DBX0.0 DB4: DBX0.0: (BOOL) bit 0 of offset 0 byte Uint8 DB4.DBB1 DB4, DBB1: (BYTE) one byte of offset 1 word Int16 DB4.DBW2 DB4, DBW2: (WORD) two bytes of offset 2 dword Int32 DB4.DBD4 DB4, DBD4: (DWORD) four bytes of offset 4 int Int16 DB4.DBW8 DB4, DBW8: (WORD) two bytes of offset 8 dint Int32 DB4.DBW10 DB4, DBW10: (WORD) two bytes of offset 10 real Float32 DB4.DBD14 DB4, DBD14: (REAL) four bytes of offset 14 heartbeat Int16 DB1.DBW160 DB1, DBW160: (WORD) two bytes of offset 160 预定义设备配置 Device Configuration 设备配置 Device Configuration devices/Simple-Device.yaml 描述了如下所示的两种设备。 S7-Device01：自动事件“interval”值(10s) < IdleTimeout(30s)，只有一个 tcp 连接，直到连接失败。 S7-Device02：自动事件“interval”值（10s）> IdleTimeout（5s），因此每个读/写命令都会重新连接到 S7 设备。 deviceList: - name: S7-Device01 profileName: S7-Device description: Example of S7 Device labels: [industrial] protocols: s7: Host: 192.168.123.199 Port: 102 Rack: 0 Slot: 1 Timeout: 30 IdleTimeout: 30 autoEvents: - interval: 10s onChange: false sourceName: AllResource - name: S7-Device02 profileName: S7-Device description: Example of S7 Device labels: [industrial] protocols: s7: Host: 192.168.123.199 Port: 102 Rack: 0 Slot: 1 Timeout: 5 IdleTimeout: 5 autoEvents: - interval: 10s onChange: false sourceName: heartbeat 设置一个新的配置 Configuration 创建自定义配置文件夹 运行以下命令： mkdir -p custom-config 设置新的设备配置 Device Profile 运行以下命令来创建您的设备配置文件： cd custom-config vi new-device-profile.yaml 插入您的设备配置文件定义，这取决于您的 S7 设备配置。 设置设备服务配置 Configuration cd custom-config vi new-device-config.yaml 填写 new-device-config.yaml 文件。 准备 docker-compose 文件 生成 docker compose 文件 Clone edgex-compose git clone https://github.com/edgexfoundry/edgex-compose.git 生成 docker-compose.yml 文件 cd edgex-compose make gen ds-s7 将自定义配置添加到 docker-compose 文件 将准备好的配置文件添加到 docker-compose 文件中，您可以使用卷挂载它们并更改 device-s7 内部使用的环境。 打开 docker-compose.yml 文件，然后添加卷路径和环境，如下所示： device-s7: ... environment: ... DEVICE_PROFILESDIR: /custom-config DEVICE_DEVICESDIR: /custom-config volumes: ... - /path/to/custom-config:/custom-config 在 Docker 上启动 EdgeX Foundry 由于我们在上一步中生成了 docker-compose.yml 文件，因此我们可以部署 EdgeX，如下所示： cd edgex-compose/compose-builder docker compose -p edgex up -d [+] Running 12/12 ✔ Container edgex-core-consul Started 0.0s ✔ Container edgex-ui-go Started 0.0s ✔ Container edgex-redis Started 0.0s ✔ Container edgex-core-common-config-bootstrapper Started 0.1s ✔ Container edgex-support-scheduler Started 0.0s ✔ Container edgex-kuiper Started 0.0s ✔ Container edgex-support-notifications Started 0.1s ✔ Container edgex-core-metadata Started 0.1s ✔ Container edgex-core-command Started 0.0s ✔ Container edgex-core-data Started 0.0s ✔ Container edgex-app-rules-engine Started 0.0s ✔ Container edgex-device-s7 Started 0.0s 启动服务后设置 如果服务已在运行并且您想要添加设备，则可以使用本节中概述的“核心元数据 API”。 如果您按照启动服务之前的设置中的说明设置设备配置文件和服务，则可以跳过此部分。 要在启动服务后添加设备，请完成以下步骤： 使用 POST 将上面的设备配置文件上传到元数据：http://localhost:59881/api/v3/deviceprofile/uploadfile ，并以表单数据格式将文件作为密钥“文件”添加到正文，以及创建的 ID 将被退回。 以下示例命令使用 curl 发送请求： curl http://localhost:59881/api/v3/deviceprofile/uploadfile -F "file=@new-device-profile.yml" 确保 S7 设备服务正在运行，如有必要或使用其他设备服务，请调整下面的服务名称以匹配。 使用 POST 将设备添加到 http://localhost:59881/api/v3/device，正文将类似于： curl http://localhost:59881/api/v3/device -H "Content-Type:application/json" -X POST \ -d '[ { "apiVersion" : "v3", "device": { "name": "S7-Device03", "description": "Example of S7 Device", "adminState": "UNLOCKED", "operatingState": "UP", "serviceName": "device-s7", "profileName": "S7-Device", "protocols": { "s7": { "Host": "192.168.123.199", "Port": 102, "Rack": 0, "Slot": 1, "Timeout": 30, "IdleTimeout": 30 } }, "autoEvents": [ { "interval": "10s", "onChange": false, "sourceName": "AllResource" } ] } } ]' 服务名称必须匹配/引用目标设备服务（“device-s7”），并且配置文件名称必须与前面步骤中的设备配置文件名称（“S7-Device”）匹配。 执行命令 现在我们准备运行一些命令。 查找可执行命令 使用以下查询查找可执行命令： curl http://localhost:59882/api/v3/device/name/S7-Device03 | json_pp { "apiVersion": "v3", "deviceCoreCommand": { "coreCommands": [ { "get": true, "name": "word", "parameters": [ { "resourceName": "word", "valueType": "Int16" } ], "path": "/api/v3/device/name/S7-Device03/word", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "int", "parameters": [ { "resourceName": "int", "valueType": "Int16" } ], "path": "/api/v3/device/name/S7-Device03/int", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "real", "parameters": [ { "resourceName": "real", "valueType": "Float32" } ], "path": "/api/v3/device/name/S7-Device03/real", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "heartbeat", "parameters": [ { "resourceName": "heartbeat", "valueType": "Int16" } ], "path": "/api/v3/device/name/S7-Device03/heartbeat", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "AllResource", "parameters": [ { "resourceName": "bool", "valueType": "Bool" }, { "resourceName": "byte", "valueType": "Uint8" }, { "resourceName": "word", "valueType": "Int16" }, { "resourceName": "dword", "valueType": "Int32" }, { "resourceName": "int", "valueType": "Int16" }, { "resourceName": "dint", "valueType": "Int32" }, { "resourceName": "real", "valueType": "Float32" }, { "resourceName": "heartbeat", "valueType": "Int16" } ], "path": "/api/v3/device/name/S7-Device03/AllResource", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "bool", "parameters": [ { "resourceName": "bool", "valueType": "Bool" } ], "path": "/api/v3/device/name/S7-Device03/bool", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "byte", "parameters": [ { "resourceName": "byte", "valueType": "Uint8" } ], "path": "/api/v3/device/name/S7-Device03/byte", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "dword", "parameters": [ { "resourceName": "dword", "valueType": "Int32" } ], "path": "/api/v3/device/name/S7-Device03/dword", "set": true, "url": "http://edgex-core-command:59882" }, { "get": true, "name": "dint", "parameters": [ { "resourceName": "dint", "valueType": "Int32" } ], "path": "/api/v3/device/name/S7-Device03/dint", "set": true, "url": "http://edgex-core-command:59882" } ], "deviceName": "S7-Device03", "profileName": "S7-Device" }, "statusCode": 200 } 执行设置命令 根据url和parameterNames执行 SET 命令，运行 SET 命令时将localhost替换为服务器 IP。 这里我们使用“heartbeat”命令： curl http://localhost:59882/api/v3/device/name/S7-Device03/heartbeat \ -H "Content-Type:application/json" -X PUT \ -d '{"heartbeat": "1"}' {"apiVersion":"v3","statusCode":200} 执行获取命令 运行 GET 命令时，将 localhost 替换为服务器 IP。 这里我们使用“heartbeat”命令： curl http://localhost:59882/api/v3/device/name/S7-Device03/heartbeat { "apiVersion": "v3", "statusCode": 200, "event": { "apiVersion": "v3", "id": "dd56ed66-2874-4d78-af02-753783164b3c", "deviceName": "S7-Device03", "profileName": "S7-Device", "sourceName": "heartbeat", "origin": 1702103303160428120, "readings": [ { "id": "8324105a-1843-415d-bea7-b61e6d12142c", "origin": 1702103303159722671, "deviceName": "S7-Device03", "resourceName": "heartbeat", "profileName": "S7-Device", "valueType": "Int16", "value": "1" } ] } } 自动事件 AutoEvent 在设备定义文件的 autoEvents 部分中定义： deviceList: autoEvents: interval: '30s' onChange: false sourceName: 'heartbeat' 服务启动后，查询 core-data API。 结果显示该服务每“30 秒”自动执行一次命令。 配置 该服务没有设备 SDK 提供的附加配置。 常用设备服务配置请参见设备服务配置部分。 协议属性 设备 S7 为 ISO-on-TCP 定义以下协议属性。 这些属性位于每个设备定义的协议部分中的“s7”键下。 Properity Default value Note Host N/A S7 ip address, e.g. 192.168.123.199 Port N/A S7 port, e.g. 102 Rack N/A Rack number, e.g. 0 Slot N/A Slot number, e.g. 1 Timeout 30 connect to S7 timeout, seconds IdleTimeout 30 connection idle timeout, seconds !!! example "S7 协议属性示例" ```json ... "protocols": { "s7": { "Host": "192.168.123.199", "Port": 102, "Rack": 0, "Slot": 1, "Timeout": 30, "IdleTimeout": 30 } } ... ``` API 参考 该服务继承了 SDK 中的通用设备服务 API API。 源代码 Device S7 的源代码可以在 https://github.com/edgexfoundry/device-s7 中找到。 额外细节 在 S7 设备资源使用带有“Int16”数字的“WORD”数据类型的用例中，“WORD”有两个字节，应转换为整数。 以下来自设备配置文件的摘录将“NodeName”DB4.DBW2 定义为“valueType”Int16： !!! example "示例 - 设备配置文件" ```yaml name: S7-Device manufacturer: YIQISOFT description: Example of S7 Device model: Siemens S7 labels: [ISO-on-TCP] deviceResources: - name: word description: PLC word isHidden: false properties: valueType: Int16 readWrite: RW attributes: NodeName: DB4.DBW2 ``` 读命令 Read Command 读取命令执行如下： Client 执行 Read Command； 设备服务发送 Read Command 读取一个‘WORD’数据； S7 设备返回一个两字节数据； 设备服务解析数据； 设备服务将数据转换为 Int16 值； 设备服务返回 json 格式给 Client。 graph LR A[Client] -->|1. Execute Read Command| B[Device Service] B -->|2. Send Request - WORD| C[S7 Device] C -->|3. Return 2 bytes| B B -->|4. Parse bytes| B B -->|5. Cast to Int16| B B -->|6. Return json| A 写命令 Write Command 写入命令执行如下： Client 执行 Write Command 写入一个 In16 值； 设备服务将 Int16 值转换为两个字节； 设备服务转换为二进制； 设备服务向 S7 设备发送写请求； 设备服务返回结果 json 给 Client。 graph LR A[Client] -->|1. Execute Write Command| B[Device Service] B -->|2. Cast to Int16| B B -->|3. Convert to binary| B B -->|4. Send Request - WORD| C[S7 Device] B -->|5. Return json| A 何时转换数据 您通常需要随时转换数据，因为 S7 设备仅接收其特定的数据类型。 支持的转换 支持的转换如下： From NodeName Bit(s) To valueType Address Sample Bool 1 Bool DB1.DBX0.0 Byte 8 Uint DB1.DBB1 Word 16 Int16 DB1.DBW2 DWord 32 Int32 DB1.DBD4 Int 16 Int16 DB1.DBW6 DInt 32 Int32 DB1.DBD8 Real 32 Float32 DB1.DBD20 "亿琪软件" 上海亿琪软件有限公司成立于 2016 年，专注于 5G 通信、AI 人工智能、边缘计算和大数据网络安全多项技术领域，致力于物联网领域前沿技术的创新，为用户提供全方位、智能化和安全的物联网解决方案。 2023 年，公司发布“YiFUSION ｜工业边缘智能融合一体机”产品，为工业客户提供一整套的边缘计算+AI 能力：高性能数据采集、多类型数据融合、AI 算法集成、云端业务对接。在边缘网关的基础上，集成了 IoT 平台的边缘协同能力、本地 Web SCADA 和 HMI 功能、本地数据存储、边缘 AI 视频分析、行业应用集成等。 2022 年，公司推出 “YiCLOUD ｜亿琪云”一站式物联网应用解决方案。公司的业务涵盖了智慧城市、智慧农业、智能工厂和智慧园区等多个领域，公司软硬件产品和解决方案获得华为技术认证，得到中国移动 OCP 认证，公司还是边缘计算产业联盟 ECC 成员。 "联系我们--商业服务" 网站：http://yiqisoft.cn 邮件：support@yiqisoft.cn 电话：021-68863086 手机：186-1666-9123
分享｜EdgeX 案例之 BACnet 设备读取，并通过物联网平台展示分析
物联网边缘端协议很多，BACnet 就是一种比较常见的监控协议。今天，就来跟大家分享一下 BACnet 基于 EdgeX 框架数据采集、控制和分析。 目录 BACnet 是什么？ 准备工作 EdgeX 微服务 EdgeX 基本微服务 EdgeX BACnet 微服务 配置 Device Profile 和 Device 获取 BACnet 设备数据 了解 BACnet 工作机制，可采用多种方法来做 数据上传 亿琪软件物联网平台 YiCONNECT 物联网平台展示和分析数据 关于我们 "亿琪软件" "联系我们--商业服务" BACnet 是什么？ !!! info "BACnet 协议" BACnet是用于智能建筑的通信协议，是国际标准化组织（ISO）、美国国家标准协会（ANSI）及美国采暖、制冷与空调工程师学会（ASHRAE）定义的通信协议1。BACnet针对智能建筑及控制系统的应用所设计的通信，可用在暖通空调系统（HVAC，包括暖气、通风、空气调节），也可以用在照明控制、门禁系统、火警侦测系统及其相关的设备。优点在于能降低维护系统所需成本并且安装比一般工业通信协议更为简易，而且提供有五种业界常用的标准协议，此可防止设备供应商及系统业者的垄断，也因此未来系统扩展性与兼容性大为增加。 !!! tip "最常见的 BACnet 协议有两种" BACnet IP：网络层通讯，基于以太网，使用 IP 通信，==本案例使用此协议== BACnet MS/TP：主从站通讯，就是串口，比如RS-485 !!! warning "什么是 BACnet BBMD？" BACnet/IP 使用 UDP/IP 在现有的 IP 网络中进行通信，在多个IP子网中如果需要对 BACnet 的广播消息进行管理的时候就需要一种特殊的BBMD设备（Broadcast Management Device）。 更多信息，请查阅其他相关资源，非本案例的重点内容。 准备工作 !!! info "BACnet 模拟器" 本案例中，使用 Yabe (Yet Another Bacnet Explorer)，请自行通过互联网下载。 其中，我们会用到一个子工具：==Bacnet.Room.Simulator==，将作为 BACnet 模拟设备来获取数据和控制。 软件打开后，类似如下，请记住右下角的 Id: ==2179272== !!! info "BACnet 查询工具" 本案例中，使用两种查询工具，都可以从互联网下载来使用。 Yabe：新建一个 BACnet/IP over UDP，自动出现 Device BACnetSCAN：双击 ==BACnet IP== 条目，就会出现 Device。 !!! warning "注意" BACnetSCAN 既可以作为查询工具，也可以作为 到目前为止，都是 BACnet 相关的准备和设置。 EdgeX 微服务 EdgeX 基本微服务 !!! info "启动你的 EdgeX 核心模块" 下载 docker compose 运行环境 git clone -b v3.1.0 https://github.com/edgexfoundry/edgex-compose.git cd edgex-compose/compose-builder make run no-secty 保证你的 EdgeX 基本微服务启动成功 ✔ Container edgex-core-consul Running ✔ Container edgex-redis Running ✔ Container edgex-ui-go Running ✔ Container edgex-core-common-config-bootstrapper Running ✔ Container edgex-core-metadata Running ✔ Container edgex-kuiper Running ✔ Container edgex-support-notifications Running ✔ Container edgex-support-scheduler Running ✔ Container edgex-core-command Running ✔ Container edgex-core-data Running ✔ Container edgex-app-rules-engine Running EdgeX BACnet 微服务 !!! info "增加 BACnet 微服务" !!! warning "注意" 官方版本不能直接使用，拿不到 BACnet 设备信息，需要根据以下步骤执行 重新编译你的微服务，增加 ==ds-bacnet-ip== 即可 make run no-secty ds-bacnet-ip 修改 docker-compose.yml，保证 ==UDP:47808== 端口被映射到容器 device-bacnet-ip: ... ports: - mode: ingress host_ip: 127.0.0.1 target: 59980 published: "59980" protocol: tcp - mode: ingress host_ip: 0.0.0.0 target: 47808 published: "47808" protocol: udp ... 重启全部微服务 docker compose -p edgex up -d 确保 BACnet 微服务运行正常 curl -s http://localhost:59980/api/v3/ping|jq { "apiVersion": "v3", "requestId": "98059fe6-4b81-4d79-a851-4d0db85dbdf5", "statusCode": 200, "timestamp": "Fri, 01 Dec 2023 11:13:45 UTC", "serviceName": "device-bacnet-ip" } 配置 Device Profile 和 Device !!! info "Device Profile 配置信息" name: TemperatureSimple manufacturer: YIQISOFT description: BACnet device simulator provided with YIQISOFT model: BACnet Stack labels: [BACnet] deviceResources: - description: The current analog input value. name: Temperature.Indoor isHidden: false properties: valueType: Float32 readWrite: RW attributes: instance: 0 property: present-value type: analog-input - description: The current analog input value. name: Temperature.Water isHidden: false properties: valueType: Float32 readWrite: RW attributes: instance: 1 property: present-value type: analog-input - description: The current analog input value. name: Temperature.Outdoor isHidden: false properties: valueType: Float32 readWrite: RW attributes: instance: 2 property: present-value type: analog-input deviceCommands: - name: Query isHidden: false readWrite: R resourceOperations: - deviceResource: Temperature.Indoor - deviceResource: Temperature.Water - deviceResource: Temperature.Outdoor !!! info "Device 配置信息" 在 EdgeX UI 增加一个设备，以下信息必须保证一样，还记得前面的数字： ==2179272== 吗？就是填写在这里 至此，设备配置相关内容就完成了 获取 BACnet 设备数据 了解 BACnet 工作机制，可采用多种方法来做 !!! warning "是否采用 BBMD？" 前面说过 BBMD ，这个概念很重要，因为我们下面的方法用到了相关内容。 方法一：不采用 BBMD 方法二：采用外部 BBMD !!! info "方法一" 以上所有的配置，只是用 EdgeX BACnet 微服务本地来管理 BACnet 设备。前提是，你需要手动触发 ==BACnet who-Is== 请求，使用上述两个查询工具都可以完成这个操作。 Yabe：选中 Udp:47808 后，按键盘：F2 即可； BACnetSCAN：选中 BACnet IP 后，右键：搜索设备； 此时，在 EdgeX BACnet 微服务 log 下会出现如下信息，证明 BACnet 微服务已经收到 who-Is 广播包： level=DEBUG ts=2023-12-01T11:51:36Z app=device-bacnet-ip msg="Processing I-Am Request from 2179272" 查询 BACnet 设备 2179272 设备数据： curl http://localhost:59881/api/v3/device/name/hello/Query 结果如下： { "apiVersion": "v3", "statusCode": 200, "event": { "apiVersion": "v3", "id": "799292fc-565e-4b90-945c-18383dfafbcc", "deviceName": "hello", "profileName": "TemperatureSimple", "sourceName": "Query", "origin": 1701431871437505753, "readings": [ { "id": "6c0e2297-3b63-436e-b908-b6f9b563cc03", "origin": 1701431871437505753, "deviceName": "hello", "resourceName": "Temperature.Indoor", "profileName": "TemperatureSimple", "valueType": "Float32", "value": "2.07999992e+01" }, { "id": "8aef6bec-710f-4216-a1f6-1e7e4607599a", "origin": 1701431871437505753, "deviceName": "hello", "resourceName": "Temperature.Water", "profileName": "TemperatureSimple", "valueType": "Float32", "value": "3.87999992e+01" }, { "id": "8ef8d25b-c77f-44df-90cd-ec1e4e365e4c", "origin": 1701431871437505753, "deviceName": "hello", "resourceName": "Temperature.Outdoor", "profileName": "TemperatureSimple", "valueType": "Float32", "value": "1.20000000e+01" } ] } } !!! warning "这种方法的弊端就是需要 EdgeX BACnet 微服务来管理这些广播包" !!! info "方法二" 需要修改 EdgeX BACnet 微服务配置文件： configuration.yaml，启用外部 BBMD ... Driver: BBMD_ADDRESS: '192.168.123.11' BBMD_PORT: '47808' ... 重启微服务 make clean docker compose -p edgex up -d Docker 容器管理器可以看到 AutoEvents 日志，一切工作正常： level=INFO ts=2023-12-01T12:14:49Z app=device-bacnet-ip correlation-id=72a29f26-9d4c-4fee-a037-20827cf522c7 msg="AutoEvent: TemperatureDevice/Query" level=DEBUG ts=2023-12-01T12:14:49Z app=device-bacnet-ip correlation-id=72a29f26-9d4c-4fee-a037-20827cf522c7 msg="GET on device: TemperatureDevice" level=INFO ts=2023-12-01T12:14:50Z app=device-bacnet-ip correlation-id=1ca57207-8d0c-4094-a384-e48da5140e80 msg="AutoEvent: TemperatureDevice/Query" level=DEBUG ts=2023-12-01T12:14:50Z app=device-bacnet-ip correlation-id=1ca57207-8d0c-4094-a384-e48da5140e80 msg="GET on device: TemperatureDevice" level=INFO ts=2023-12-01T12:14:51Z app=device-bacnet-ip correlation-id=0a5ddc91-cc04-472b-b20a-64deaa16530d msg="AutoEvent: TemperatureDevice/Query" level=DEBUG ts=2023-12-01T12:14:51Z app=device-bacnet-ip correlation-id=0a5ddc91-cc04-472b-b20a-64deaa16530d msg="GET on device: TemperatureDevice" level=INFO ts=2023-12-01T12:14:52Z app=device-bacnet-ip correlation-id=e8a19615-2caa-4295-913b-aff37cdadbba msg="AutoEvent: TemperatureDevice/Query" level=DEBUG ts=2023-12-01T12:14:52Z app=device-bacnet-ip correlation-id=e8a19615-2caa-4295-913b-aff37cdadbba msg="GET on device: TemperatureDevice" level=INFO ts=2023-12-01T12:14:53Z app=device-bacnet-ip correlation-id=da1f321e-2780-40d9-af38-b4387a141b2f msg="AutoEvent: TemperatureDevice/Query" level=DEBUG ts=2023-12-01T12:14:53Z app=device-bacnet-ip correlation-id=da1f321e-2780-40d9-af38-b4387a141b2f msg="GET on device: TemperatureDevice" 数据上传 亿琪软件物联网平台 YiCONNECT !!! info "配置 eKuiper 规则引擎" 创建 Stream CREATE STREAM EdgeXStream () WITH (format = "json", type = "edgex", shared = "true") 创建 Rule { "triggered": true, "id": "RuleToYiCONNECT", "sql": "SELECT * , meta(deviceName) as deviceName, tstamp() as ts FROM EdgeXStream", "actions": [ { "mqtt": { "dataTemplate": "{\"{{.deviceName}}\":[{\"ts\":{{.ts}}, \"values\":{{json .}}}]}", "insecureSkipVerify": true, "protocolVersion": "3.1", "qos": 0, "retained": false, "sendSingle": true, "server": "tcp://demo.yiqisoft.cn:1883", "topic": "v1/gateway/telemetry", "username": "************" } } ], "options": { "debug": false, "logFilename": "", "isEventTime": false, "lateTolerance": 1000, "concurrency": 1, "bufferLength": 1024, "sendMetaToSink": false, "sendError": true, "qos": 0, "checkpointInterval": 300000, "restartStrategy": { "attempts": 0, "delay": 1000, "multiplier": 2, "maxDelay": 30000, "jitter": 0.1 }, "cron": "", "duration": "", "cronDatetimeRange": null } } 确保服务器连接正常，且数据传输正确。数据输入和输出数量一致，各方面的延迟都很正常。 { "status": "running", "source_EdgeXStream_0_records_in_total": 557, "source_EdgeXStream_0_records_out_total": 557, "source_EdgeXStream_0_process_latency_us": 138, "source_EdgeXStream_0_buffer_length": 0, "source_EdgeXStream_0_last_invocation": "2023-12-01T12:32:49.03012", "source_EdgeXStream_0_exceptions_total": 0, "source_EdgeXStream_0_last_exception": "", "source_EdgeXStream_0_last_exception_time": 0, "op_2_project_0_records_in_total": 557, "op_2_project_0_records_out_total": 557, "op_2_project_0_process_latency_us": 37, "op_2_project_0_buffer_length": 0, "op_2_project_0_last_invocation": "2023-12-01T12:32:49.03027", "op_2_project_0_exceptions_total": 0, "op_2_project_0_last_exception": "", "op_2_project_0_last_exception_time": 0, "sink_mqtt_0_0_records_in_total": 557, "sink_mqtt_0_0_records_out_total": 557, "sink_mqtt_0_0_process_latency_us": 262, "sink_mqtt_0_0_buffer_length": 0, "sink_mqtt_0_0_last_invocation": "2023-12-01T12:32:49.030321", "sink_mqtt_0_0_exceptions_total": 0, "sink_mqtt_0_0_last_exception": "", "sink_mqtt_0_0_last_exception_time": 0 } 物联网平台展示和分析数据 !!! info "Yabe 数据可视化效果" !!! info "物联网平台动态展示" 关于我们 "亿琪软件" 上海亿琪软件有限公司成立于 2016 年，专注于 5G 通信、AI 人工智能、边缘计算和大数据网络安全多项技术领域，致力于物联网领域前沿技术的创新，为用户提供全方位、智能化和安全的物联网解决方案。 2023 年，公司发布“YiFUSION ｜工业边缘智能融合一体机”产品，为工业客户提供一整套的边缘计算+AI 能力：高性能数据采集、多类型数据融合、AI 算法集成、云端业务对接。在边缘网关的基础上，集成了 IoT 平台的边缘协同能力、本地 Web SCADA 和 HMI 功能、本地数据存储、边缘 AI 视频分析、行业应用集成等。 2022 年，公司推出 “YiCLOUD ｜亿琪云”一站式物联网应用解决方案。公司的业务涵盖了智慧城市、智慧农业、智能工厂和智慧园区等多个领域，公司软硬件产品和解决方案获得华为技术认证，得到中国移动 OCP 认证，公司还是边缘计算产业联盟 ECC 成员。 "联系我们--商业服务" 网站：http://yiqisoft.cn 邮件：support@yiqisoft.cn 电话：021-68863086 手机：186-1666-9123
EdgeX 爱好者社区 在使用和开发过程中，收到很多使用者和开发者的提问和反馈，我们将在一系列文章中给大家分享出来。 EdgeX Foundry 在下面将统一简称为 EdgeX。 系列文章四： 如何集成 RBAC 到 EdgeX？ 如何一次获取多个数据测点？ EdgeX 有 API Gateway 吗？ 如何接入云或企业网络？ 常见问题(FAQ) "如何集成 RBAC 到 EdgeX？" 这个问题是由老外在 GitHub 社区 discussions 里面提出来，他希望 EdgeX 能够集成第三方认证软件，比如 Keycloak，以便完成权限对接。 RBAC，其实不适合 EdgeX，这只是一个边缘计算框架而已，不是一个完整的业务系统； EdgeX 本身有 token 机制来保证安全，控制中心与 EdgeX 交互是安全可靠的就好了，无需 RBAC； 当然，你也可以改造 EdgeX 使得其能够支持 RBAC，这个工作量社区无法作出评估； "如何一次获取多个数据测点？" 当然可以，而且数量不限制。主要的问题就是每次 core-command 能承受的数据量需要手工进行调整，以达到你的数量请求。 定义一个 command，包含你所需要的 resource，即可进行绑定，EdgeX 会一次性获取完所有的数据，返回到 message bus 中； 修改 Service - MaxResultCount ，以便容纳你的 command； "EdgeX 有 API Gateway 吗？" GitHub 社区里，经常有人问关于 API Gateway 的事情，总的来说，基本都是没有好好读官方文档的人，不了解 EdgeX 的机制，所以不清楚。 EdgeX API Gateway 使用 nginx 来实现，并且集成了 Secure 机制，从 v3 版本开始，微服务之间也需要 JWT 认证，这是为了防止有应用绕过 API Gateway； 也可以自行修改 API Gateway 配置，完成更多集成和扩展，就是 nginx 配置而已； kong 安全机制已经从 EdgeX 被移除，相对简单了很多； "如何接入云或企业网络？" 这个问题，也是 GitHub 社区问的比较多的，尤其是那些不需要接入公有云服务的客户，他们有自己的云或企业网络，要接入进去。 用 eKuiper 规则引擎，可以通过简单的配置和命令完成简单的云对接，而且 eKuiper 已经内置了很多云服务的对接； eKuiper 除了一些云资源对接，也需要接入企业网络，比如数据库，时序数据库等，都可以完美支持； 自己开发一个 Application Service 来对接，官方提供了很多例子可以参考，主要是修改对接协议的数据格式皆可完成。 关于我们 "亿琪软件" 上海亿琪软件有限公司成立于 2016 年，专注于 5G 通信、AI 人工智能、边缘计算和大数据网络安全多项技术领域，致力于物联网领域前沿技术的创新，为用户提供全方位、智能化和安全的物联网解决方案。 2023 年，公司发布“YiFUSION ｜工业边缘智能融合一体机”产品，为工业客户提供一整套的边缘计算+AI 能力：高性能数据采集、多类型数据融合、AI 算法集成、云端业务对接。在边缘网关的基础上，集成了 IoT 平台的边缘协同能力、本地 Web SCADA 和 HMI 功能、本地数据存储、边缘 AI 视频分析、行业应用集成等。 2022 年，公司推出 “YiCLOUD ｜亿琪云”一站式物联网应用解决方案。公司的业务涵盖了智慧城市、智慧农业、智能工厂和智慧园区等多个领域，公司软硬件产品和解决方案获得华为技术认证，得到中国移动 OCP 认证，公司还是边缘计算产业联盟 ECC 成员。 "联系我们--商业服务" 网站：http://yiqisoft.cn 邮件：support@yiqisoft.cn 电话：021-68863086 手机：186-1666-9123