#include #include #include #include #include #include #include "node.h" #include "rule_engine.h" #include "frame_ring_buffer.h" #include "actions/action_base.h" #include "actions/log_action.h" #include "actions/http_action.h" #include "actions/snapshot_action.h" #include "actions/clip_action.h" namespace rk3588 { class AlarmNode : public INode { public: std::string Id() const override { return id_; } std::string Type() const override { return "alarm"; } bool Init(const SimpleJson& config, const NodeContext& ctx) override { id_ = config.ValueOr("id", "alarm"); // Parse labels for class name mapping if (const SimpleJson* labels_cfg = config.Find("labels")) { for (const auto& label : labels_cfg->AsArray()) { labels_.push_back(label.AsString("")); } } // Initialize rule engine if (const SimpleJson* rules_cfg = config.Find("rules")) { if (!rule_engine_.Init(*rules_cfg, labels_)) { std::cerr << "[alarm] failed to init rule engine\n"; return false; } } // Get pre-event buffer settings from clip action config int pre_sec = 5; int fps_hint = 25; if (const SimpleJson* actions_cfg = config.Find("actions")) { if (const SimpleJson* clip_cfg = actions_cfg->Find("clip")) { pre_sec = clip_cfg->ValueOr("pre_sec", 5); fps_hint = clip_cfg->ValueOr("fps", 25); } } frame_buffer_ = std::make_shared(pre_sec, fps_hint); // Initialize actions if (const SimpleJson* actions_cfg = config.Find("actions")) { // Log action if (const SimpleJson* log_cfg = actions_cfg->Find("log")) { if (log_cfg->ValueOr("enable", true)) { auto action = std::make_unique(); if (action->Init(*log_cfg)) { actions_.push_back(std::move(action)); } } } // Snapshot action (must be before HTTP to fill snapshot_url) if (const SimpleJson* snap_cfg = actions_cfg->Find("snapshot")) { if (snap_cfg->ValueOr("enable", false)) { auto action = std::make_unique(); if (action->Init(*snap_cfg)) { actions_.push_back(std::move(action)); } } } // Clip action (must be before HTTP to fill clip_url) if (const SimpleJson* clip_cfg = actions_cfg->Find("clip")) { if (clip_cfg->ValueOr("enable", false)) { auto action = std::make_unique(); if (action->Init(*clip_cfg)) { auto* clip_ptr = static_cast(action.get()); clip_ptr->SetFrameBuffer(frame_buffer_); clip_action_ = clip_ptr; actions_.push_back(std::move(action)); } } } // HTTP action (should be last to include media URLs) if (const SimpleJson* http_cfg = actions_cfg->Find("http")) { if (http_cfg->ValueOr("enable", false)) { auto action = std::make_unique(); if (action->Init(*http_cfg)) { actions_.push_back(std::move(action)); } } } } else { // Default: just log action auto action = std::make_unique(); SimpleJson empty_cfg; action->Init(empty_cfg); actions_.push_back(std::move(action)); } input_queue_ = ctx.input_queue; if (!input_queue_) { std::cerr << "[alarm] no input queue for node " << id_ << "\n"; return false; } std::cout << "[alarm] initialized with " << actions_.size() << " actions\n"; return true; } bool Start() override { std::cout << "[alarm] started\n"; return true; } void Stop() override { // Drain all actions for (auto& action : actions_) { action->Drain(); } std::cout << "[alarm] stopped, processed " << processed_frames_ << " frames, triggered " << alarm_count_ << " alarms\n"; } void Drain() override { for (auto& action : actions_) { action->Drain(); } } NodeStatus Process(FramePtr frame) override { if (!frame) return NodeStatus::DROP; // Always push to ring buffer for pre-event recording frame_buffer_->Push(frame); // Push to clip action for post-event collection if active if (clip_action_) { clip_action_->PushPostEventFrame(frame); } // Evaluate rules auto result = rule_engine_.Evaluate(frame); if (result.matched) { TriggerAlarm(result, frame); } ++processed_frames_; return NodeStatus::OK; } private: void TriggerAlarm(const RuleMatchResult& result, FramePtr frame) { ++alarm_count_; AlarmEvent event; event.node_id = id_; event.rule_name = result.rule_name; event.timestamp_ms = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); event.frame_id = frame->frame_id; event.detections = result.matched_detections; // Execute all actions in order // Snapshot and Clip actions will fill snapshot_url and clip_url // HTTP action should be last to include all URLs for (auto& action : actions_) { action->Execute(event, frame); } } std::string id_; std::vector labels_; RuleEngine rule_engine_; std::shared_ptr frame_buffer_; std::vector> actions_; ClipAction* clip_action_ = nullptr; std::shared_ptr> input_queue_; uint64_t processed_frames_ = 0; uint64_t alarm_count_ = 0; }; REGISTER_NODE(AlarmNode, "alarm"); } // namespace rk3588