288 lines
10 KiB
Python
288 lines
10 KiB
Python
|
|
import os
|
||
|
|
R = r"C:\Projects\CMSCore"
|
||
|
|
def w(rel, txt):
|
||
|
|
p = os.path.join(R, rel)
|
||
|
|
os.makedirs(os.path.dirname(p), exist_ok=True)
|
||
|
|
with open(p, "w", encoding="utf-8", newline="\r\n") as f:
|
||
|
|
f.write(txt.lstrip("\n"))
|
||
|
|
print(f" OK: {rel}")
|
||
|
|
|
||
|
|
# ============================================================================
|
||
|
|
# 9. device_agent_c.cpp
|
||
|
|
# ============================================================================
|
||
|
|
w("anscloud-device/src/device_agent_c.cpp", r'''#include <anscloud/device/device_agent_c.h>
|
||
|
|
#include <anscloud/device/device_agent.h>
|
||
|
|
#include <anscloud/common/json_serializer.h>
|
||
|
|
#include <cstring>
|
||
|
|
#include <string>
|
||
|
|
|
||
|
|
using namespace anscloud;
|
||
|
|
|
||
|
|
static char* alloc_copy(const std::string& s) {
|
||
|
|
char* p = new char[s.size() + 1];
|
||
|
|
memcpy(p, s.c_str(), s.size() + 1);
|
||
|
|
return p;
|
||
|
|
}
|
||
|
|
|
||
|
|
extern "C" {
|
||
|
|
|
||
|
|
AnsDeviceHandle ansdevice_create(void) {
|
||
|
|
return new DeviceAgent();
|
||
|
|
}
|
||
|
|
|
||
|
|
void ansdevice_destroy(AnsDeviceHandle h) {
|
||
|
|
delete static_cast<DeviceAgent*>(h);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ansdevice_configure(AnsDeviceHandle h, void* broker_ptr, const char* config_json) {
|
||
|
|
if (!h || !broker_ptr) return false;
|
||
|
|
auto* agent = static_cast<DeviceAgent*>(h);
|
||
|
|
auto* broker = static_cast<IMessageBroker*>(broker_ptr);
|
||
|
|
// TODO: parse config_json into DeviceAgentConfig
|
||
|
|
DeviceAgentConfig cfg;
|
||
|
|
// For now just a minimal config
|
||
|
|
cfg.device_id = "device";
|
||
|
|
cfg.verbose_logging = true;
|
||
|
|
agent->configure(broker, cfg);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ansdevice_set_command_handler(AnsDeviceHandle h, AnsDeviceCmdHandlerFn fn, void* user_data) {
|
||
|
|
if (!h || !fn) return;
|
||
|
|
auto* agent = static_cast<DeviceAgent*>(h);
|
||
|
|
agent->set_command_handler([fn, user_data](const Command& cmd) -> CommandResponse {
|
||
|
|
std::string cmd_json = json::serialize(cmd);
|
||
|
|
const char* resp_str = fn(user_data, cmd_json.c_str());
|
||
|
|
CommandResponse resp;
|
||
|
|
if (resp_str) {
|
||
|
|
resp = json::deserialize_response(resp_str);
|
||
|
|
ansdevice_free_string(resp_str);
|
||
|
|
}
|
||
|
|
return resp;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void ansdevice_on_connection_changed(AnsDeviceHandle h, AnsDeviceConnectionFn fn, void* ud) {
|
||
|
|
if (!h || !fn) return;
|
||
|
|
static_cast<DeviceAgent*>(h)->on_connection_changed(
|
||
|
|
[fn, ud](bool c, const std::string& info) { fn(ud, c, info.c_str()); });
|
||
|
|
}
|
||
|
|
|
||
|
|
void ansdevice_on_error(AnsDeviceHandle h, AnsDeviceErrorFn fn, void* ud) {
|
||
|
|
if (!h || !fn) return;
|
||
|
|
static_cast<DeviceAgent*>(h)->on_error(
|
||
|
|
[fn, ud](int code, const std::string& msg) { fn(ud, code, msg.c_str()); });
|
||
|
|
}
|
||
|
|
|
||
|
|
void ansdevice_on_broadcast(AnsDeviceHandle h, AnsDeviceBroadcastFn fn, void* ud) {
|
||
|
|
if (!h || !fn) return;
|
||
|
|
static_cast<DeviceAgent*>(h)->on_broadcast(
|
||
|
|
[fn, ud](const std::string& msg) { fn(ud, msg.c_str()); });
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ansdevice_start(AnsDeviceHandle h) { return h ? static_cast<DeviceAgent*>(h)->start() : false; }
|
||
|
|
void ansdevice_stop(AnsDeviceHandle h) { if (h) static_cast<DeviceAgent*>(h)->stop(); }
|
||
|
|
bool ansdevice_is_running(AnsDeviceHandle h) { return h ? static_cast<DeviceAgent*>(h)->is_running() : false; }
|
||
|
|
bool ansdevice_is_connected(AnsDeviceHandle h){ return h ? static_cast<DeviceAgent*>(h)->is_connected() : false; }
|
||
|
|
|
||
|
|
bool ansdevice_publish_event(AnsDeviceHandle h, const char* event_json) {
|
||
|
|
if (!h || !event_json) return false;
|
||
|
|
DeviceEvent evt = json::deserialize_event(event_json);
|
||
|
|
return static_cast<DeviceAgent*>(h)->publish_event(evt);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ansdevice_send_heartbeat(AnsDeviceHandle h) { return h ? static_cast<DeviceAgent*>(h)->send_heartbeat() : false; }
|
||
|
|
bool ansdevice_send_metrics(AnsDeviceHandle h) { return h ? static_cast<DeviceAgent*>(h)->send_metrics() : false; }
|
||
|
|
|
||
|
|
const char* ansdevice_get_status(AnsDeviceHandle h) {
|
||
|
|
if (!h) return alloc_copy("{}");
|
||
|
|
auto s = static_cast<DeviceAgent*>(h)->get_status();
|
||
|
|
// Simple JSON
|
||
|
|
std::string j = "{\"connected\":" + std::string(s.connected ? "true" : "false")
|
||
|
|
+ ",\"running\":" + std::string(s.running ? "true" : "false")
|
||
|
|
+ ",\"uptime_seconds\":" + std::to_string(s.uptime_seconds)
|
||
|
|
+ ",\"commands_received\":" + std::to_string(s.commands_received)
|
||
|
|
+ ",\"commands_executed\":" + std::to_string(s.commands_executed)
|
||
|
|
+ ",\"heartbeats_sent\":" + std::to_string(s.heartbeats_sent) + "}";
|
||
|
|
return alloc_copy(j);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ansdevice_free_string(const char* str) {
|
||
|
|
delete[] str;
|
||
|
|
}
|
||
|
|
|
||
|
|
const char* ansdevice_version(void) {
|
||
|
|
return "1.0.0";
|
||
|
|
}
|
||
|
|
|
||
|
|
} // extern "C"
|
||
|
|
''')
|
||
|
|
|
||
|
|
# ============================================================================
|
||
|
|
# 10. gateway_agent.h
|
||
|
|
# ============================================================================
|
||
|
|
w("anscloud-gateway/include/anscloud/gateway/gateway_agent.h", r'''#pragma once
|
||
|
|
// ==========================================================================
|
||
|
|
// ANSCloud SDK - Gateway Agent (CMS side)
|
||
|
|
//
|
||
|
|
// Manages CMS gateway lifecycle:
|
||
|
|
// - Connects to broker via IMessageBroker (your existing RabbitMQ API)
|
||
|
|
// - Consumes telemetry, heartbeats, events, status from all devices
|
||
|
|
// - Sends commands to devices (synchronous RPC via correlation_id)
|
||
|
|
// - Tracks device online/offline state
|
||
|
|
// ==========================================================================
|
||
|
|
|
||
|
|
#include <anscloud/common/types.h>
|
||
|
|
#include <anscloud/common/i_message_broker.h>
|
||
|
|
#include <string>
|
||
|
|
#include <vector>
|
||
|
|
#include <memory>
|
||
|
|
#include <future>
|
||
|
|
|
||
|
|
namespace anscloud {
|
||
|
|
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
// Configuration
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
struct GatewayAgentConfig {
|
||
|
|
int device_timeout_seconds = 90; // mark offline if no heartbeat
|
||
|
|
int default_command_timeout_seconds = 30;
|
||
|
|
int max_pending_commands = 1000;
|
||
|
|
bool verbose_logging = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
// Command result (from synchronous RPC)
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
struct CommandResult {
|
||
|
|
bool success = false;
|
||
|
|
CommandStatus status = CommandStatus::Timeout;
|
||
|
|
std::string result_json;
|
||
|
|
std::string error_message;
|
||
|
|
double execution_time_ms = 0.0;
|
||
|
|
};
|
||
|
|
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
// Device state as tracked by the gateway
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
struct DeviceState {
|
||
|
|
std::string device_id;
|
||
|
|
DeviceStatus status = DeviceStatus::Unknown;
|
||
|
|
std::string firmware_version;
|
||
|
|
std::string ansvis_version;
|
||
|
|
std::string ip_address;
|
||
|
|
std::string last_heartbeat; // ISO 8601 timestamp
|
||
|
|
std::string last_telemetry;
|
||
|
|
uint64_t uptime_seconds = 0;
|
||
|
|
SystemMetrics last_metrics;
|
||
|
|
uint32_t camera_count = 0;
|
||
|
|
};
|
||
|
|
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
// Batch command result
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
struct BatchResult {
|
||
|
|
std::vector<std::pair<std::string, CommandResult>> results; // device_id -> result
|
||
|
|
int succeeded = 0;
|
||
|
|
int failed = 0;
|
||
|
|
int timed_out = 0;
|
||
|
|
};
|
||
|
|
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
// Gateway statistics
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
struct GatewayStats {
|
||
|
|
uint64_t commands_sent = 0;
|
||
|
|
uint64_t responses_received = 0;
|
||
|
|
uint64_t timeouts = 0;
|
||
|
|
uint64_t telemetry_received = 0;
|
||
|
|
uint64_t heartbeats_received= 0;
|
||
|
|
uint64_t events_received = 0;
|
||
|
|
uint32_t devices_online = 0;
|
||
|
|
uint32_t devices_total = 0;
|
||
|
|
uint32_t pending_commands = 0;
|
||
|
|
uint64_t uptime_seconds = 0;
|
||
|
|
};
|
||
|
|
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
// GatewayAgent
|
||
|
|
// --------------------------------------------------------------------------
|
||
|
|
class GatewayAgent {
|
||
|
|
public:
|
||
|
|
GatewayAgent();
|
||
|
|
~GatewayAgent();
|
||
|
|
|
||
|
|
GatewayAgent(const GatewayAgent&) = delete;
|
||
|
|
GatewayAgent& operator=(const GatewayAgent&) = delete;
|
||
|
|
|
||
|
|
// --- Setup ---
|
||
|
|
void configure(IMessageBroker* broker, const GatewayAgentConfig& config);
|
||
|
|
|
||
|
|
// --- Lifecycle ---
|
||
|
|
bool start();
|
||
|
|
void stop();
|
||
|
|
bool is_running() const;
|
||
|
|
bool is_connected() const;
|
||
|
|
|
||
|
|
// --- Commands ---
|
||
|
|
// Synchronous RPC: sends command, blocks until response or timeout
|
||
|
|
CommandResult send_command(
|
||
|
|
const std::string& device_id,
|
||
|
|
CommandType type,
|
||
|
|
const std::string& params_json = "{}",
|
||
|
|
int timeout_seconds = 0 // 0 = use default
|
||
|
|
);
|
||
|
|
|
||
|
|
// Async version
|
||
|
|
std::future<CommandResult> send_command_async(
|
||
|
|
const std::string& device_id,
|
||
|
|
CommandType type,
|
||
|
|
const std::string& params_json = "{}",
|
||
|
|
int timeout_seconds = 0
|
||
|
|
);
|
||
|
|
|
||
|
|
// Fire-and-forget (no response expected)
|
||
|
|
bool send_command_fire_and_forget(
|
||
|
|
const std::string& device_id,
|
||
|
|
CommandType type,
|
||
|
|
const std::string& params_json = "{}"
|
||
|
|
);
|
||
|
|
|
||
|
|
// Send to multiple devices
|
||
|
|
BatchResult send_batch_command(
|
||
|
|
const std::vector<std::string>& device_ids,
|
||
|
|
CommandType type,
|
||
|
|
const std::string& params_json = "{}",
|
||
|
|
int timeout_seconds = 0
|
||
|
|
);
|
||
|
|
|
||
|
|
// Broadcast to all devices
|
||
|
|
bool send_broadcast(const std::string& message_json);
|
||
|
|
|
||
|
|
// --- Device tracking ---
|
||
|
|
std::vector<DeviceState> get_all_devices() const;
|
||
|
|
DeviceState get_device(const std::string& device_id) const;
|
||
|
|
std::vector<DeviceState> get_online_devices() const;
|
||
|
|
bool is_device_online(const std::string& device_id) const;
|
||
|
|
|
||
|
|
// --- Callbacks ---
|
||
|
|
void on_telemetry(TelemetryCallback cb);
|
||
|
|
void on_heartbeat(HeartbeatCallback cb);
|
||
|
|
void on_event(EventCallback cb);
|
||
|
|
void on_device_status_changed(StatusCallback cb);
|
||
|
|
void on_connection_changed(ConnectionCallback cb);
|
||
|
|
void on_error(ErrorCallback cb);
|
||
|
|
|
||
|
|
// --- Stats ---
|
||
|
|
GatewayStats get_stats() const;
|
||
|
|
|
||
|
|
private:
|
||
|
|
struct Impl;
|
||
|
|
std::unique_ptr<Impl> m_impl;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace anscloud
|
||
|
|
''')
|
||
|
|
|
||
|
|
print(" [9-10] device_agent_c.cpp, gateway_agent.h done")
|