设备状态监控
本例程演示如何使用 librm 中的 Device 基类和 DeviceManager 设备管理器来监控设备的在线状态和工作状态。
设备状态监控框架通过心跳机制自动检测设备是否离线,并在设备状态发生变化时触发回调函数。设备有三种状态:kOk(正常)、kFault(故障)、kOffline(离线,超过超时时间未收到状态上报)。
librm 中的大部分设备驱动类都基于这套机制实现了状态上报逻辑,可以直接添加到 DeviceManager。
代码示例
基本用法
#include "cmsis_os.h"
#include <librm.hpp>
extern "C" void DeviceMonitorTask(const void *pv_arg) {
rm::hal::Can can1(hcan1);
rm::device::M3508 motor1(can1, 1);
rm::device::M3508 motor2(can1, 2);
rm::device::GM6020 motor3(can1, 3);
can1.Begin();
// 为设备设置易于识别的名称(可选)
motor1.SetName("chassis_fl");
motor2.SetName("chassis_fr");
motor3.SetName("gimbal_yaw");
// 创建设备管理器,最多可以添加 10 个设备
rm::device::DeviceManager<10> device_manager;
// 将设备添加到管理器
device_manager << &motor1 << &motor2 << &motor3;
// 注册设备状态变化回调
device_manager.OnDeviceStatusChange([](rm::device::Device *device) {
// 设备状态变化时的处理逻辑
// 可以通过 device->online_status() 获取具体状态
// 可以通过 device->name() 获取设备名称,便于调试和日志记录
if (device->online_status() == rm::device::Device::kOk) {
// 设备恢复在线
// 例如:LOG_INFO("Device %s is back online", device->name().c_str());
} else {
// 设备故障或离线,例如:停止所有运动、触发蜂鸣器报警、LED 指示等
// 例如:LOG_ERROR("Device %s is offline/fault", device->name().c_str());
}
});
for (;;) {
// 更新所有设备状态并检查
bool all_ok = device_manager.Update();
if (all_ok) {
// 所有设备正常
} else {
// 存在故障或离线设备
}
osDelay(100); // 100ms 检查一次
}
}
自定义设备类
如果新编写了一个设备驱动,想要整合进这套状态监控框架,继承 Device 基类并在适当时机调用 ReportStatus 方法报告设备状态:
#include <librm.hpp>
// 自定义传感器设备
class CustomSensor : public rm::device::Device {
public:
void Update() {
// 读取传感器数据
bool data_valid = ReadSensorData();
if (data_valid) {
// 数据有效,报告设备正常
ReportStatus(Status::kOk);
} else {
// 数据异常,报告设备故障
ReportStatus(Status::kFault);
}
}
float GetValue() const { return value_; }
private:
bool ReadSensorData() {
// 实际的传感器读取逻辑
// 返回 true 表示数据有效,false 表示数据异常
return true;
}
float value_{0.0f};
};
// 使用自定义传感器
CustomSensor imu_sensor;
imu_sensor.SetName("imu"); // 设置设备名称
imu_sensor.SetHeartbeatTimeout(std::chrono::milliseconds(500)); // 设置500ms超时
rm::device::DeviceManager<5> sensors;
sensors << &imu_sensor;
extern "C" void SensorTask(const void *pv_arg) {
for (;;) {
// 更新传感器数据
imu_sensor.Update();
// 检查传感器状态
if (imu_sensor.online_status() == rm::device::Device::kOk) {
float value = imu_sensor.GetValue();
// 使用传感器数据
}
osDelay(10);
}
}
分组管理
实际应用中可以像下面这样将不同区域的设备分组管理,例如底盘、云台等,从而实现更灵活的状态监控和保护逻辑。
#include "cmsis_os.h"
#include <librm.hpp>
rm::device::DeviceManager<8> chassis_motors;
rm::device::DeviceManager<4> gimbal_motors;
extern "C" void SafetyMonitorTask(const void *pv_arg) {
chassis_motors.OnDeviceStatusChange([](rm::device::Device *device) {
if (device->online_status() != rm::device::Device::kOk) {
// 底盘电机故障或离线时停止所有底盘电机
}
});
gimbal_motors.OnDeviceStatusChange([](rm::device::Device *device) {
if (device->online_status() != rm::device::Device::kOk) {
// 云台电机故障或离线时停止云台运动
}
});
for (;;) {
bool chassis_ok = chassis_motors.Update();
bool gimbal_ok = gimbal_motors.Update();
// 根据状态执行保护逻辑
osDelay(50);
}
}
获取设备状态列表
DeviceManager 提供了 GetDeviceListByStatus 方法,可以按状态分类获取设备列表。该列表在每次调用 Update() 时自动更新。
#include <librm.hpp>
rm::device::DeviceManager<10> device_manager;
// ... 添加设备 ...
extern "C" void MonitorTask(const void *pv_arg) {
for (;;) {
device_manager.Update();
// 获取离线设备列表
const auto &offline_devices = device_manager.GetDeviceListByStatus(rm::device::Device::kOffline);
if (!offline_devices.empty()) {
// 处理离线设备
for (auto *device : offline_devices) {
printf("Device '%s' is offline\n", device->name().c_str());
}
}
// 获取故障设备列表
const auto &fault_devices = device_manager.GetDeviceListByStatus(rm::device::Device::kFault);
if (!fault_devices.empty()) {
// 处理故障设备
for (auto *device : fault_devices) {
printf("Device '%s' has fault\n", device->name().c_str());
}
}
osDelay(100);
}
}
获取状态摘要字符串
DeviceManager 提供了 GetSummaryString 方法,可以快速生成所有设备的状态摘要字符串,方便日志记录和调试。
#include <librm.hpp>
rm::device::DeviceManager<10> device_manager;
// ... 添加设备 ...
extern "C" void StatusReportTask(const void *pv_arg) {
for (;;) {
device_manager.Update();
// 获取状态摘要字符串
auto summary = device_manager.GetSummaryString();
// 如果所有设备正常,返回 "All devices OK."
// 否则返回类似 "Offline: motor1, rc; Fault: sensor1; " 的字符串
printf("System Status: %s\n", summary.c_str());
// 也可以在状态变化回调中使用
osDelay(1000);
}
}
// 在状态变化回调中使用摘要字符串
device_manager.OnDeviceStatusChange([&](rm::device::Device *device) {
auto status_summary = device_manager.GetSummaryString();
// 将状态摘要发送给上位机或记录到日志
SendToHost(status_summary);
});
GetSummaryString 方法返回的字符串默认最大长度为 512 字节,适合用于日志记录、调试输出和与上位机通信。
如果设备数量很多(例如超过 20 个)或设备名称较长,摘要字符串可能会被截断。此时可以通过调整 DeviceManager 的第三个模板参数来增大字符串长度:
// 使用 1024 字节的摘要字符串(可管理更多设备)
rm::device::DeviceManager<50, true, 1024> device_manager;
当字符串因长度限制而被截断时,末尾会自动添加 "..." 标记,提示信息不完整。
设备命名
设备可以设置一个易于识别的名称,这在调试和日志记录时非常有用。如果不设置名称,设备会自动使用其对象地址作为默认名称。
#include <librm.hpp>
// 创建设备并设置名称
rm::hal::Can can1(hcan1);
rm::device::M3508 motor1(can1, 1);
rm::device::M3508 motor2(can1, 2);
motor1.SetName("wheel_left");
motor2.SetName("wheel_right");
rm::device::DeviceManager<2> motors;
motors << &motor1 << &motor2;
// 在回调中使用设备名称
motors.OnDeviceStatusChange([](rm::device::Device *device) {
auto status = device->online_status();
auto name = device->name();
switch (status) {
case rm::device::Device::kOk:
printf("[INFO] Device '%s' is online\n", name.c_str());
break;
case rm::device::Device::kOffline:
printf("[ERROR] Device '%s' is offline\n", name.c_str());
break;
case rm::device::Device::kFault:
printf("[ERROR] Device '%s' has fault\n", name.c_str());
break;
default:
break;
}
});
设备名称最大支持 32 字节(包含结尾的空字符),建议使用简短且有意义的名称,如 "motor_fl"(前左电机)、"imu"等。
主要 API
Device 基类
void SetHeartbeatTimeout(duration timeout)- 设置心跳超时时间(默认 1 秒)Status online_status()- 获取设备的当前状态(kUnknown/kOffline/kFault/kOk)time_point last_seen() const- 获取设备最后一次上报状态的时间点void SetName(etl::string<32> name)- 设置设备名称(最多 32 字节)etl::string<32> name() const- 获取设备名称(如果不设置,默认为设备对象的地址)protected void ReportStatus(Status status)- 由设备驱动Device类调用,报告当前的设备状态
DeviceManager 设备管理器
模板参数:
kMaxDevices- 最大容纳的设备数量,按需设置kUseStdFunctionCallback- 是否使用std::function作为回调类型(默认true)。设置为false则使用etl::delegate,无动态内存分配但使用起来不如std::function方便kMaxSummaryStringLength- 设备状态摘要字符串的最大长度(默认 512 字节)。如果设备数量很多或名称很长,可以适当增大此值
成员函数:
DeviceManager& operator<<(Device *device)- 添加设备到管理器bool Update()- 更新所有设备状态,同时更新按状态分类的设备列表,返回是否所有设备都正常void OnDeviceStatusChange(CallbackType callback)- 注册设备状态变化回调(当这个DeviceManager对象管理的任一设备的状态发生任何变化时触发)const etl::vector<Device *, kMaxDevices>& GetDeviceListByStatus(Device::Status status) const- 根据状态获取设备列表(在每次调用Update()时更新)etl::string<kMaxSummaryStringLength> GetSummaryString() const- 获取设备状态摘要字符串,方便日志记录和调试。字符串超出长度限制时会自动截断并添加 "..." 标记bool all_device_ok() const- 获取所有设备是否正常