一个传感器的SDK(C++)

  • Post author:
  • Post category:案例
  • Post comments:0评论

公司研发了个传感器样机要做演示,需要开发上位机,硬件跟上位机的通信是通过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 模拟是否打开的标识,用了 param1param2 模拟传感器内部参数,还通过回调模拟采集到的数据返回;实际应用这些操作都应该与硬件通信以实现同步;

接着实现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);
}

发表回复