Skip to main content

设备状态监控

本例程演示如何使用 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);
}
}

分组管理

tip

实际应用中可以像下面这样将不同区域的设备分组管理,例如底盘、云台等,从而实现更灵活的状态监控和保护逻辑。

#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;
}
});
tip

设备名称最大支持 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 - 获取所有设备是否正常