公司研发了个传感器样机要做演示,需要开发上位机,硬件跟上位机的通信是通过tcp/ip,UI用WPF,原本计划做个简单的通信协议嵌到UI凑合着用,后来考虑到传感器或许会被用到一些实际项目上,最后还是封出了c++和.net的库来,以后用起来也方便;封完之后发现应用场景还挺多,像是算法封装底层逻辑封装之类的都可以按这个模式来,这里记录一下大概实现。
首先分析一下传感器提供些什么功能:
- 作为传感器,根本功能肯定是采集或测量数据,要能够采集到数据
- 需要能够做一些简单的控制,例如开关、采集等
- 能够读写传感器内部参数,可以通过调整参数提升数据质量
- 能够知道传感器内部运行状态,这里先简单的从传感器里输出一些日志
因为要供其他项目使用,为了方便灵活,这个项目创建成c++动态链接库(关于动态链接库的创建,参考:演练:创建和使用自己的动态链接库 (C++) | Microsoft Learn);
对外开放的api如下:
sensor_api.h
#pragma once #ifdef SENSORSDK_EXPORTS #define SENSORSDK_API __declspec(dllexport) #else #define SENSORSDK_API __declspec(dllimport) #endif constexpr auto sensor_ok = 1; constexpr auto sensor_err = 0; #define SENSOR_RETURN_OK return sensor_ok #define SENSOR_RETURN_ERR return sensor_err /// <summary> /// 创建传感器 /// </summary> /// <param name="handle">创建的传感器句柄</param> /// <returns>是否创建成功</returns> SENSORSDK_API int sensor_create(void** handle); /// <summary> /// 销毁传感器 /// </summary> /// <param name="handle">待销毁传感器的句柄</param> /// <returns>是否销毁成功</returns> SENSORSDK_API int sensor_destroy(void* handle); /// <summary> /// 打开传感器 /// </summary> /// <param name="handle">待操作传感器的句柄</param> /// <returns>是否打开成功</returns> SENSORSDK_API int sensor_open(void* handle); /// <summary> /// 关闭传感器 /// </summary> /// <param name="handle">待操作传感器的句柄</param> /// <returns>是否关闭成功</returns> SENSORSDK_API int sensor_close(void* handle); /// <summary> /// 执行命令 /// </summary> /// <param name="handle">待操作传感器的句柄</param> /// <param name="cmd_str">命令内容</param> /// <returns>是否执行成功</returns> SENSORSDK_API int sensor_execute_cmd(void* handle, const char* cmd_str); /// <summary> /// 获取传感器int类型的参数值 /// </summary> /// <param name="handle">待操作传感器的句柄</param> /// <param name="param_str">参数名</param> /// <param name="n_value">获取的值</param> /// <returns>是否获取成功</returns> SENSORSDK_API int sensor_get_int_value(void* handle, const char* param_str, int* n_value); /// <summary> /// 设置传感器int类型的参数值 /// </summary> /// <param name="handle">待操作传感器的句柄</param> /// <param name="param_str">参数名</param> /// <param name="n_value">待设置的值</param> /// <returns>是否设置成功</returns> SENSORSDK_API int sensor_set_int_value(void* handle, const char* param_str, int n_value); /// <summary> /// 注册采集数据回调 /// </summary> /// <param name="handle">待操作传感器的句柄</param> /// <param name="cb_output">回调函数</param> /// <param name="p_user">用户指针</param> /// <returns>是否注册成功</returns> SENSORSDK_API int sensor_register_data_callback(void* handle, void(__stdcall* cb_output)(int sensor_data, void* p_user), void* p_user); /// <summary> /// 注册采集数据回调 /// </summary> /// <param name="handle">待操作传感器的句柄</param> /// <param name="cb_log">回调函数</param> /// <param name="p_user">用户指针</param> /// <returns>是否注册成功</returns> SENSORSDK_API int sensor_register_log_callback(void* handle, void(__stdcall* cb_log)(char* sensor_log, void* p_user), void* p_user);
api中提供 sensor_create
和 sensor_destroy
两个函数用于创建和销毁传感器对象,用户对传感器的操作都基于创建出来的句柄(void*
)接着实现一个传感器提供上面列出来的功能;
sensor.h
#pragma once constexpr auto max_one_log_length = 512; #define SENSOR_LOG_CALLBACK(P,...) if(my_cb_log_!=nullptr){ char buf[max_one_log_length]; _snprintf_s(buf, _TRUNCATE, __VA_ARGS__); my_cb_log_(buf,P);} #define SENSOR_LOG(...) SENSOR_LOG_CALLBACK(my_log_p_user_ , __VA_ARGS__) class sensor { public: int open(); int close(); int execute_cmd(const char* cmd_str) const; int get_int_value(const char* param_str, int* n_value) const; int set_int_value(const char* param_str, int n_value); int register_data_callback(void(__stdcall* cb_output)(int sensor_data, void* p_user), void* p_user); int register_log_callback(void(__stdcall* cb_log)(char* sensor_log, void* p_user), void* p_user); private: static int get_random_int_number(); private: int is_open_ = 0; int param1_ = 10; int param2_ = 20; void(__stdcall* my_cb_output_)(int sensor_data, void* p_user) = nullptr; void(__stdcall* my_cb_log_)(char* sensor_log, void* p_user) = nullptr; void* my_output_p_user_ = nullptr; void* my_log_p_user_ = nullptr; };
sensor.cpp
#include <cstring> #include <random> #include "sensor.h" #include "sensor_api.h" int sensor::open() { if (is_open_) { //不允许重复打开 SENSOR_LOG("The sensor was opened!"); SENSOR_RETURN_ERR; } //模拟打开 is_open_ = 1; SENSOR_RETURN_OK; } int sensor::close() { if (!is_open_) { //不允许重复关闭 SENSOR_LOG("The sensor was closed!"); SENSOR_RETURN_ERR; } //模拟关闭 is_open_ = 0; SENSOR_RETURN_OK; } int sensor::execute_cmd(const char* cmd_str) const { if (!is_open_) { //模拟未打开无法许执行命令 SENSOR_LOG("Can not operate before sensor open!"); SENSOR_RETURN_ERR; } SENSOR_LOG("Recv cmd - %s", cmd_str); if (!strcmp(cmd_str, "collect")) { //返回一个随机数作为采集结果 if (my_cb_output_ != nullptr) my_cb_output_(get_random_int_number(), my_output_p_user_); SENSOR_RETURN_OK; } SENSOR_LOG("Can not identify command - [%s]", cmd_str); SENSOR_RETURN_ERR; } int sensor::get_int_value(const char* param_str, int* n_value) const { if (!is_open_) { //模拟未打开无法读取参数 SENSOR_LOG("Can not operate before sensor open!"); SENSOR_RETURN_ERR; } SENSOR_LOG("Get param - Name: %s", param_str); //读参数1 if (!strcmp(param_str, "param1")) { *n_value = param1_; SENSOR_RETURN_OK; } //读参数2 if (!strcmp(param_str, "param2")) { *n_value = param2_; SENSOR_RETURN_OK; } SENSOR_LOG("Can not idenfity parameter name - [%s]", param_str); SENSOR_RETURN_ERR; } int sensor::set_int_value(const char* param_str, const int n_value) { if (!is_open_) { //模拟未打开无法变更参数 SENSOR_LOG("Can not operate before sensor open!"); SENSOR_RETURN_ERR; } SENSOR_LOG("Set param - Name: %s Value: %i", param_str, n_value); //写参数1 if (!strcmp(param_str, "param1")) { param1_ = n_value; SENSOR_RETURN_OK; } //写参数2 if (!strcmp(param_str, "param2")) { param2_ = n_value; SENSOR_RETURN_OK; } //参数无法识别 SENSOR_LOG("Can not idenfity parameter name - [%s]", param_str); SENSOR_RETURN_ERR; } int sensor::register_data_callback(void(__stdcall* cb_output)(int sensor_data, void* p_user), void* p_user) { my_cb_output_ = cb_output; my_output_p_user_ = p_user; SENSOR_RETURN_OK; } int sensor::register_log_callback(void(__stdcall* cb_log)(char* sensor_log, void* p_user), void* p_user) { my_cb_log_ = cb_log; my_log_p_user_ = p_user; SENSOR_RETURN_OK; } int sensor::get_random_int_number() { std::random_device rd; // 随机数生成器 std::mt19937 mt(rd()); // 使用 Mennen Twister 算法生成随机数 const std::uniform_int_distribution<int> dist(1, 100); // 定义随机数范围 const int random_number = dist(mt); // 生成随机数 return random_number; }
上面用了一个 is_open
模拟是否打开的标识,用了 param1
、param2
模拟传感器内部参数,还通过回调模拟采集到的数据返回;实际应用这些操作都应该与硬件通信以实现同步;
接着实现api,sdk就算基本可用了
sensor_api.cpp
#include "sensor_api.h" #include "sensor.h" /** * \brief 创建传感器 * \param handle 创建的传感器句柄 * \return 是否创建成功 */ int sensor_create(void** handle) { const auto my_sensor = new sensor; *handle = my_sensor; SENSOR_RETURN_OK; } /** * \brief 销毁传感器 * \param handle 待销毁传感器的句柄 * \return 是否销毁成功 */ int sensor_destroy(void* handle) { const auto my_sensor = static_cast<sensor*>(handle); delete my_sensor; SENSOR_RETURN_OK; } /** * \brief 打开传感器 * \param handle 待操作传感器的句柄 * \return 是否打开成功 */ int sensor_open(void* handle) { const auto my_sensor = static_cast<sensor*>(handle); return my_sensor->open(); } /** * \brief 关闭传感器 * \param handle 待操作传感器的句柄 * \return 是否关闭成功 */ int sensor_close(void* handle) { const auto my_sensor = static_cast<sensor*>(handle); return my_sensor->close(); } /** * \brief 执行命令 * \param handle 待操作传感器的句柄 * \param cmd_str 命令内容 * \return 是否执行成功 */ int sensor_execute_cmd(void* handle, const char* cmd_str) { const auto my_sensor = static_cast<sensor*>(handle); return my_sensor->execute_cmd(cmd_str); } /** * \brief 获取传感器int类型的参数值 * \param handle 待操作传感器的句柄 * \param param_str 参数名 * \param n_value 获取的值 * \return 是否获取成功 */ int sensor_get_int_value(void* handle, const char* param_str, int* n_value) { const auto my_sensor = static_cast<sensor*>(handle); return my_sensor->get_int_value(param_str, n_value); } /** * \brief 设置传感器int类型的参数值 * \param handle 待操作传感器的句柄 * \param param_str 参数名 * \param n_value 待设置的值 * \return 是否设置成功 */ int sensor_set_int_value(void* handle, const char* param_str, const int n_value) { const auto my_sensor = static_cast<sensor*>(handle); return my_sensor->set_int_value(param_str, n_value); } /** * \brief 注册采集数据回调 * \param handle 待操作传感器的句柄 * \param cb_output 回调函数 * \param p_user 用户指针 * \return 是否注册成功 */ int sensor_register_data_callback(void* handle, void(__stdcall* cb_output)(int sensor_data, void* p_user), void* p_user) { const auto my_sensor = static_cast<sensor*>(handle); return my_sensor->register_data_callback(cb_output, p_user); } /** * \brief 注册日志回调 * \param handle 待操作传感器的句柄 * \param cb_log 回调函数 * \param p_user 用户指针 * \return 是否注册成功 */ int sensor_register_log_callback(void* handle, void(__stdcall* cb_log)(char* sensor_log, void* p_user), void* p_user) { const auto my_sensor = static_cast<sensor*>(handle); return my_sensor->register_log_callback(cb_log, p_user); }
创建一个C++控制台项目进行测试
CppDemo.cpp
#include <cstdio> #include <iostream> #include <string> #include "../SharedLibs/SensorSDK/include/sensor_api.h" void _stdcall sensor_data_callback_func(int data, void* p_user) { printf("传感器数据:%i\n", data); } void _stdcall sensor_log_callback_func(char* sensor_log, void* p_user) { printf("传感器日志:%s\n", sensor_log); } int main() { void* sensor; //创建传感器 const auto is_succeed = sensor_create(&sensor); if (!is_succeed) { printf("创建传感器失败"); return 0; } //注册传感器数据回调 sensor_register_data_callback(sensor, sensor_data_callback_func, nullptr); //注册传感器日志回调 sensor_register_log_callback(sensor, sensor_log_callback_func, nullptr); std::string typed_str; printf("q - 退出\n"); printf("open - 打开传感器\n"); printf("close - 关闭传感器\n"); printf("cmd - 执行命令(当前支持 collect 命令)\n"); printf("read - 读参数\n"); printf("write - 写参数\n"); printf("键入指令:"); std::getline(std::cin, typed_str); while (true) { if (typed_str == "q") break; if (typed_str == "open") sensor_open(sensor); if (typed_str == "close") sensor_close(sensor); if (typed_str == "cmd") { printf("命令:"); std::getline(std::cin, typed_str); sensor_execute_cmd(sensor, typed_str.c_str()); } if (typed_str == "read") { printf("输入参数名:"); std::getline(std::cin, typed_str); int value; const auto succeed = sensor_get_int_value(sensor, typed_str.c_str(), &value); if (succeed) printf("Get param %s value is %i\n", typed_str.c_str(), value); } if (typed_str == "write") { printf("输入参数名:"); std::getline(std::cin, typed_str); auto param_name = typed_str; printf("输入参数值:"); std::getline(std::cin, typed_str); const auto param_value = atoi(typed_str.c_str()); const auto succeed = sensor_set_int_value(sensor, param_name.c_str(), param_value); if (succeed) printf("Set param %s value to %i\n", param_name.c_str(), param_value); } printf("键入指令:"); std::getline(std::cin, typed_str); } //销毁传感器 sensor_destroy(sensor); }