#pragma once #include #include #include #include #include #include #include // 轻量级设备树模型:从 daemon_config.json 的 device_tree 字段加载 struct DeviceTreeNode { QString id; QString name; QString type; QString endpoint; QString driver; QString capabilitiesRef; QJsonObject params; QVector childrenIds; bool enabled{true}; }; class DeviceTreeModel { public: // 从给定的 JSON 根对象(包含 device_tree 字段)加载 bool loadFromDaemonConfig(const QJsonObject& root, QString* errorMessage = nullptr); const QVector& nodes() const { return m_nodes; } QVector& nodesMutable() { return m_nodes; } const DeviceTreeNode* findById(const QString& id) const; int findIndexById(const QString& id) const; int findIndexByEndpoint(const QString& endpoint) const; int findIndexByStableKey(const QString& stableKey) const; // Monitor/register path: upsert or mark online/offline QString upsertNodeByStableKey(const QString& stableKey, const QString& preferredId, const QString& type, const QString& endpoint, const QString& name, const QJsonObject& extraParams, bool online, const QString& lastSeenTs); bool markOfflineByStableKey(const QString& stableKey, const QString& lastSeenTs); // 序列化为 daemon_config.json 所需格式 QJsonArray toDeviceTreeJson() const; QJsonObject toDaemonConfigRoot() const; // 当外部直接修改 nodesMutable() 后,需要重建索引 void rebuildIndex(); // 与 D-Bus patch / 边缘 CTRL_TREE_PROPOSE 共用的写锁与 revision(见 docs/EDGE_UPLINK_PROTOCOL_v1.md) QMutex& writeMutex() { return m_writeMutex; } qint64 treeRevision() const { return m_treeRevision.load(std::memory_order_relaxed); } void bumpTreeRevision(); bool applyPatchToNodeById(const QString& id, const QJsonObject& patch); struct EdgeProposeResult { bool ok{false}; QString code; // OK, CONFLICT, FORBIDDEN_SCOPE, NODE_NOT_FOUND, INVALID_JSON QString message; qint64 newRevision{0}; qint64 currentRevision{0}; }; EdgeProposeResult applyEdgePropose(const QString& edgeId, qint64 baseRevision, const QJsonArray& entries); /** 导出属于该 edge 的节点子集(与 applyEdgePropose 作用域规则一致),用于下行 TREE_FULL。 * 若传入 outRevisionAtExport,则写入导出时刻的 treeRevision(与 nodes 同一快照)。 */ QJsonArray exportSubtreeForEdge(const QString& edgeId, qint64* outRevisionAtExport = nullptr) const; /** 节点若处于边缘作用域(params.edgeId 或 id 前缀 edges//...),返回应尝试下行 TREE_FULL 的 edgeId(去重)。 */ QVector downlinkEdgeIdsForNode(const DeviceTreeNode& node) const; private: static bool nodeBelongsToEdge(const DeviceTreeNode& node, const QString& edgeId); QVector m_nodes; QHash m_indexById; mutable QMutex m_writeMutex; std::atomic m_treeRevision{1}; };