设备监控-过滤

This commit is contained in:
flower_linux
2026-03-23 17:45:32 +08:00
parent 51fa0bb51b
commit bc07fa0e6e
23 changed files with 300 additions and 34 deletions

Binary file not shown.

View File

@@ -14,7 +14,7 @@ void signalHandler(int signal)
if (coreService) {
coreService->shutdown();
}
QCoreApplication::quit();
}
@@ -67,6 +67,14 @@ int main(int argc, char *argv[])
<< "valid roles are: CoreRouter, CollectorSerial, CollectorCan, Collector, SqlServer";
return 1;
}
// 开启coreserive的初始化工作|线程
// QThread* coreServiceThread = new QThread();
// CoreService* coreService = new CoreService();
// coreService->moveToThread(coreServiceThread);
// QObject::connect(coreServiceThread, &QThread::started, coreService, &CoreService::initialize);
// QObject::connect(coreServiceThread, &QThread::finished, coreService, &CoreService::deleteLater);
// QObject::connect(coreServiceThread, &QThread::finished, coreServiceThread, &QThread::deleteLater);
// coreServiceThread->start();
CoreService* coreService = CoreService::instance();
if (!coreService->initialize()) {

View File

@@ -52,15 +52,27 @@ bool DeviceBus::initialize(const QJsonObject &rootConfig) {
d->manager->setDeviceTreeModel(d->treeModel);
d->manager->setRegistry(d->registry.get());
// 1) Load persisted config and initialize manager
if (!d->manager->initialize(rootConfig)) {
return false;
}
// 2) Start monitor service
if (!d->monitorService) {
d->monitorService = std::make_unique<monitor::DeviceMonitorService>(nullptr);
d->monitorService->setRegistry(d->registry.get());
d->monitorService->setDirtyCallback([this]() { this->markDeviceTreeDirty(); });
if (!d->monitorService->start()) {
LOG_WARNING() << "DeviceBus: device monitor service start failed";
}
}
return d->manager->initialize(rootConfig);
if (!d->monitorService->start()) {
LOG_WARNING() << "DeviceBus: device monitor service start failed";
}
// 3) Initial snapshot scan (register existing devices)
d->monitorService->initialScan();
// 4) Reconcile runtime instances against latest tree model
d->manager->reconcileFromTreeModel();
return true;
}
void DeviceBus::shutdown() {

View File

@@ -4,6 +4,7 @@
#include <QJsonDocument>
#include <QSerialPort>
#include <QSet>
// #include <algorithm>
#include "hardware/drivers/serial/SerialQtDriver.h"
@@ -58,6 +59,44 @@ bool DeviceBusManager::initialize(const QJsonObject& rootConfig)
return true;
}
bool DeviceBusManager::reconcileFromTreeModel()
{
if (!m_treeModel) {
return false;
}
QSet<QString> desiredSerialEndpoints;
const auto nodes = m_treeModel->nodes();
for (const auto& n : nodes) {
if (!n.enabled) continue;
if (!n.params.value(QStringLiteral("online")).toBool(true)) continue;
auto kind = softbus::devices::kindFromType(n.type);
if (kind == softbus::devices::DeviceKind::Unknown) {
kind = softbus::devices::inferDeviceKindFromEndpoint(n.endpoint);
}
if (kind == softbus::devices::DeviceKind::Serial) {
desiredSerialEndpoints.insert(n.endpoint);
if (!m_serialDevicesByEndpoint.contains(n.endpoint)) {
initializeSerialDevice(n.endpoint, n.params);
}
m_deviceEndpointsMap.insert(n.endpoint, kind);
}
}
// Remove runtime devices that are no longer desired/offline
const auto currentKeys = m_serialDevicesByEndpoint.keys();
for (const auto& endpoint : currentKeys) {
if (!desiredSerialEndpoints.contains(endpoint)) {
shutdownSerialDevice(endpoint);
m_deviceEndpointsMap.remove(endpoint);
}
}
return true;
}
void DeviceBusManager::shutdown()
{
// 关闭并销毁所有串口设备

View File

@@ -20,6 +20,7 @@ public:
void setRegistry(softbus::device_bus::registry::IDeviceRegistry* registry);
bool initialize(const QJsonObject& rootConfig);
bool reconcileFromTreeModel();
void shutdown();
private:

View File

@@ -49,6 +49,19 @@ bool DeviceMonitorService::start()
return ok;
}
bool DeviceMonitorService::initialScan()
{
bool any = false;
for (auto& m : m_monitors) {
const auto snapshot = m->snapshotExisting();
for (const auto& ev : snapshot) {
onEvent(ev);
any = true;
}
}
return any;
}
void DeviceMonitorService::stop()
{
if (!m_started) return;

View File

@@ -23,6 +23,7 @@ public:
bool start();
void stop();
bool initialScan();
void setRegistry(softbus::device_bus::registry::IDeviceRegistry* registry);
void setDirtyCallback(std::function<void()> cb);

View File

@@ -1,6 +1,7 @@
#pragma once
#include <functional>
#include <vector>
#include <QObject>
@@ -20,6 +21,7 @@ public:
virtual bool start() = 0;
virtual void stop() = 0;
virtual std::vector<DeviceEvent> snapshotExisting() = 0;
void setCallback(EventCallback cb) { m_cb = std::move(cb); }

View File

@@ -5,6 +5,7 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <sys/socket.h>
#include <linux/netlink.h>
@@ -149,6 +150,38 @@ void NetlinkDeviceMonitor::onReadable()
}
}
std::vector<DeviceEvent> NetlinkDeviceMonitor::snapshotExisting()
{
std::vector<DeviceEvent> out;
ifaddrs* ifaddr = nullptr;
if (::getifaddrs(&ifaddr) != 0) {
return out;
}
QSet<QString> seen;
for (ifaddrs* it = ifaddr; it != nullptr; it = it->ifa_next) {
if (!it->ifa_name) continue;
const QString ifname = QString::fromUtf8(it->ifa_name);
if (ifname.isEmpty() || seen.contains(ifname)) continue;
seen.insert(ifname);
DeviceEvent ev;
ev.timestamp = QDateTime::currentDateTimeUtc();
ev.type = DeviceEventType::NetInterface;
ev.action = DeviceEventAction::Add;
ev.subsystem = QStringLiteral("net");
ev.ifname = ifname;
ev.ifindex = ::if_nametoindex(it->ifa_name);
ev.linkUp = (it->ifa_flags & IFF_RUNNING) != 0;
out.push_back(ev);
}
::freeifaddrs(ifaddr);
return out;
}
} // namespace softbus::device_bus::monitor
#endif // __linux__

View File

@@ -7,6 +7,7 @@
#include <QSocketNotifier>
#include <QSet>
#include <QHash>
#include <vector>
namespace softbus::device_bus::monitor
{
@@ -20,6 +21,7 @@ public:
bool start() override;
void stop() override;
std::vector<DeviceEvent> snapshotExisting() override;
private:
void onReadable();

View File

@@ -3,6 +3,8 @@
#if defined(__linux__)
#include <unistd.h>
#include <vector>
#include <QRegularExpression>
#include <QFileInfo>
@@ -10,6 +12,37 @@
namespace softbus::device_bus::monitor
{
namespace {
bool isWhitelistedSerialDevnode(const QString& devnode)
{
if (devnode.startsWith(QStringLiteral("/dev/ttyUSB"))) return true;
if (devnode.startsWith(QStringLiteral("/dev/ttyACM"))) return true;
// Board UART allowlist: ttyS1 ~ ttyS6
static const QRegularExpression kTtySRe(QStringLiteral("^/dev/ttyS(\\d+)$"));
const auto m = kTtySRe.match(devnode);
if (m.hasMatch()) {
bool ok = false;
const int n = m.captured(1).toInt(&ok);
if (ok && n >= 1 && n <= 6) return true;
}
return false;
}
bool isConsoleLikeTty(const QString& devnode)
{
if (devnode == QStringLiteral("/dev/console")) return true;
if (devnode == QStringLiteral("/dev/tty")) return true;
if (devnode == QStringLiteral("/dev/ptmx")) return true;
if (devnode.startsWith(QStringLiteral("/dev/pts/"))) return true;
// Linux VT consoles: /dev/tty0.../dev/tty63 (without 'S')
static const QRegularExpression kVtRe(QStringLiteral("^/dev/tty(\\d+)$"));
return kVtRe.match(devnode).hasMatch();
}
} // namespace
UdevDeviceMonitor::UdevDeviceMonitor(QObject* parent)
: IDeviceMonitor(parent)
@@ -94,35 +127,7 @@ void UdevDeviceMonitor::onReadable()
const char* action = udev_device_get_action(dev);
DeviceEvent ev = toEvent(dev, action);
// Drop unknown action (usually noise / incomplete events)
if (ev.action == DeviceEventAction::Unknown) {
udev_device_unref(dev);
continue;
}
// USB: only keep usb_device (drop interface-level noise)
if (ev.subsystem == QStringLiteral("usb")) {
const QString devtype = ev.props.value(QStringLiteral("DEVTYPE"));
if (!devtype.isEmpty() && devtype != QStringLiteral("usb_device")) {
udev_device_unref(dev);
continue;
}
// Drop most "change" spam for usb (keep add/remove)
if (ev.action == DeviceEventAction::Change) {
udev_device_unref(dev);
continue;
}
// If it carries no stable identifiers, it's not useful
if (ev.devnode.isEmpty() && ev.vid.isEmpty() && ev.pid.isEmpty() && ev.serial.isEmpty()) {
udev_device_unref(dev);
continue;
}
}
// Storage: keep add/remove; drop change spam
if (ev.type == DeviceEventType::Storage && ev.action == DeviceEventAction::Change) {
if (!shouldKeepEvent(ev)) {
udev_device_unref(dev);
continue;
}
@@ -132,6 +137,117 @@ void UdevDeviceMonitor::onReadable()
}
}
std::vector<DeviceEvent> UdevDeviceMonitor::snapshotExisting()
{
std::vector<DeviceEvent> out;
if (!m_udev) {
// Snapshot can be requested before start(); create temp context.
m_udev = udev_new();
if (!m_udev) {
LOG_ERROR() << "UdevDeviceMonitor: snapshot udev_new failed";
return out;
}
}
udev_enumerate* en = udev_enumerate_new(m_udev);
if (!en) {
return out;
}
udev_enumerate_add_match_subsystem(en, "tty");
udev_enumerate_add_match_subsystem(en, "usb");
udev_enumerate_add_match_subsystem(en, "block");
if (udev_enumerate_scan_devices(en) != 0) {
udev_enumerate_unref(en);
return out;
}
udev_list_entry* devices = udev_enumerate_get_list_entry(en);
udev_list_entry* entry = nullptr;
udev_list_entry_foreach(entry, devices) {
const char* path = udev_list_entry_get_name(entry);
if (!path) continue;
udev_device* dev = udev_device_new_from_syspath(m_udev, path);
if (!dev) continue;
DeviceEvent ev = toEvent(dev, "add");
if (shouldKeepEvent(ev)) {
out.push_back(ev);
}
udev_device_unref(dev);
}
udev_enumerate_unref(en);
return out;
}
bool UdevDeviceMonitor::shouldKeepEvent(const DeviceEvent& ev) const
{
// Drop unknown action (usually noise / incomplete events)
if (ev.action == DeviceEventAction::Unknown) {
return false;
}
// Serial tty: keep only business serial nodes.
if (ev.subsystem == QStringLiteral("tty")) {
if (ev.devnode.isEmpty()) {
LOG_ERROR() << "UdevDeviceMonitor: devnode is empty" << ev.devnode;
return false;
}
if (isConsoleLikeTty(ev.devnode)) {
LOG_INFO() << "UdevDeviceMonitor: console like tty" << ev.devnode;
return false;
}
if (!isWhitelistedSerialDevnode(ev.devnode)) {
LOG_INFO() << "UdevDeviceMonitor: not whitelisted serial devnode" << ev.devnode;
return false;
}
}
// USB: only keep usb_device (drop interface-level noise)
if (ev.subsystem == QStringLiteral("usb")) {
const QString devtype = ev.props.value(QStringLiteral("DEVTYPE"));
if (!devtype.isEmpty() && devtype != QStringLiteral("usb_device")) {
return false;
}
// Drop most "change" spam for usb (keep add/remove)
if (ev.action == DeviceEventAction::Change) {
return false;
}
// If it carries no stable identifiers, it's not useful
if (ev.devnode.isEmpty() && ev.vid.isEmpty() && ev.pid.isEmpty() && ev.serial.isEmpty()) {
return false;
}
// Drop Linux root hubs/controller pseudo devices.
if (ev.vid == QStringLiteral("1d6b")) {
return false;
}
const QString modelLower = ev.model.toLower();
if (modelLower.contains(QStringLiteral("hub")) ||
modelLower.contains(QStringLiteral("host_controller")) ||
modelLower.contains(QStringLiteral("xhci"))) {
return false;
}
}
// Block: keep only usb-backed storage add/remove, drop non-storage blocks.
if (ev.subsystem == QStringLiteral("block")) {
if (ev.type != DeviceEventType::Storage) {
return false;
}
if (ev.action == DeviceEventAction::Change) {
return false;
}
}
return true;
}
QString UdevDeviceMonitor::getProp(struct udev_device* dev, const char* key)
{
if (!dev || !key) return {};

View File

@@ -20,9 +20,11 @@ public:
bool start() override;
void stop() override;
std::vector<DeviceEvent> snapshotExisting() override;
private:
void onReadable();
bool shouldKeepEvent(const DeviceEvent& ev) const;
DeviceEvent toEvent(struct udev_device* dev, const char* action);
static QString getProp(struct udev_device* dev, const char* key);

View File

@@ -38,5 +38,11 @@ void WindowsDeviceMonitor::stop()
#endif
}
std::vector<DeviceEvent> WindowsDeviceMonitor::snapshotExisting()
{
// TODO: implement full snapshot on Windows.
return {};
}
} // namespace softbus::device_bus::monitor

View File

@@ -15,6 +15,7 @@ public:
bool start() override;
void stop() override;
std::vector<DeviceEvent> snapshotExisting() override;
private:
#if defined(_WIN32)

View File

@@ -85,6 +85,27 @@ QString DeviceRegistry::buildStableKey(const softbus::device_bus::monitor::Devic
{
const QString type = inferType(ev);
// Important: ttyACM/ttyUSB 可能“同一条物理设备多路口”但 serial 相同,
// 旧逻辑会导致 stableKey 冲突并相互覆盖 endpoint。
// tty 事件devnode 必须用于唯一标识ttyACM 可能多路口但 serial 可能相同)。
// 若具备 SYSPATH也一并拼接便于跨层级归并与排查。
if (ev.type == softbus::device_bus::monitor::DeviceEventType::Serial ||
ev.subsystem == QStringLiteral("tty")) {
const QString syspath = ev.props.value(QStringLiteral("SYSPATH"));
if (!ev.devnode.isEmpty()) {
if (!syspath.isEmpty()) {
return type + QStringLiteral("|syspath|") + syspath + QStringLiteral("|devnode|") + ev.devnode;
}
return type + QStringLiteral("|devnode|") + ev.devnode;
}
if (!syspath.isEmpty()) {
return type + QStringLiteral("|syspath|") + syspath;
}
if (!ev.serial.isEmpty()) {
return type + QStringLiteral("|serial|") + ev.serial;
}
}
if (!ev.serial.isEmpty()) {
return type + QStringLiteral("|serial|") + ev.serial;
}
@@ -109,6 +130,15 @@ QString DeviceRegistry::buildStableKey(const softbus::device_bus::monitor::Devic
QString DeviceRegistry::buildPreferredNodeId(const softbus::device_bus::monitor::DeviceEvent& ev, const QString& stableKey)
{
// For tty devices (ttyS/ttyUSB/ttyACM...), a single physical device may expose
// multiple ports that share the same serial. If we only use `serial` as id,
// upsert() will collide by preferredId and overwrite endpoints.
if (ev.type == softbus::device_bus::monitor::DeviceEventType::Serial ||
ev.subsystem == QStringLiteral("tty")) {
const QByteArray hash = QCryptographicHash::hash(stableKey.toUtf8(), QCryptographicHash::Sha1).toHex();
return QStringLiteral("dev_") + QString::fromUtf8(hash.left(12));
}
if (!ev.serial.isEmpty()) {
return QStringLiteral("dev_serial_") + ev.serial;
}