Add heartbeat endpoint message schema
This commit is contained in:
@@ -88,6 +88,7 @@ void convert_a_frame(MppFrame frame)
|
|||||||
}
|
}
|
||||||
base = (RK_U8 *)mpp_buffer_get_ptr(buffer);
|
base = (RK_U8 *)mpp_buffer_get_ptr(buffer);
|
||||||
rga_client.convert(base, width, height, h_stride, v_stride, out);
|
rga_client.convert(base, width, height, h_stride, v_stride, out);
|
||||||
|
send_inference_heartbeat_if_needed();
|
||||||
// printf("convert a frame ok");
|
// printf("convert a frame ok");
|
||||||
if (pthread_mutex_trylock(&frame_mutex) == 0)
|
if (pthread_mutex_trylock(&frame_mutex) == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -179,16 +179,18 @@ h264 分段文件会写到:
|
|||||||
|
|
||||||
## 上报逻辑
|
## 上报逻辑
|
||||||
|
|
||||||
程序向本地管理程序发送:
|
推理结果业务包向本地管理程序发送:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
POST http://127.0.0.1:<upload-port>/video/post
|
POST http://127.0.0.1:<upload-port>/video/post
|
||||||
```
|
```
|
||||||
|
|
||||||
JSON 结构保持兼容:
|
JSON 结构:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"message_type": "inference_result",
|
||||||
|
"schema_version": 1,
|
||||||
"serial": "camera001",
|
"serial": "camera001",
|
||||||
"type": 19,
|
"type": 19,
|
||||||
"at": 1778826272,
|
"at": 1778826272,
|
||||||
@@ -202,7 +204,59 @@ JSON 结构保持兼容:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
当前版本只主动上传 `number > 0` 的正例。目标未检出时不上传 `number=0`,只写日志。
|
字段说明:
|
||||||
|
|
||||||
|
- `message_type` 固定为 `inference_result`,用于和心跳包区分。
|
||||||
|
- `schema_version` 当前为 `1`。
|
||||||
|
- `serial` 来自启动参数 `-c`,在管理程序中可作为推理进程身份。
|
||||||
|
- `type` 来自启动参数 `-t`。
|
||||||
|
- `at` 为推理端当前时间戳。
|
||||||
|
- `params[].class_idx` 来自模型目录下 `<model>_code.txt`。
|
||||||
|
- `params[].name` 来自模型目录下 `<model>.txt`。
|
||||||
|
- `params[].number` 为当前帧同一 `class_idx` 的检测数量;多个 label 映射到同一个 `class_idx` 时会合并数量。
|
||||||
|
|
||||||
|
当前版本只主动上传 `number > 0` 的正例。目标未检出时不上传 `number=0`,只写日志。业务包不会承担心跳职责。
|
||||||
|
|
||||||
|
## 心跳机制
|
||||||
|
|
||||||
|
推理程序新增独立心跳接口:
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST http://127.0.0.1:<upload-port>/heartbeat
|
||||||
|
```
|
||||||
|
|
||||||
|
只有启动参数指定了有效 `-p <upload-port>`,并且 `-c <processUUID>` 非空时,才会发送心跳。当前心跳周期为 `HEARTBEAT_SECS = 60` 秒。
|
||||||
|
|
||||||
|
心跳包结构:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message_type": "heartbeat",
|
||||||
|
"schema_version": 1,
|
||||||
|
"serial": "camera001",
|
||||||
|
"process_uuid": "camera001",
|
||||||
|
"status": "running",
|
||||||
|
"timestamp": 1778826272
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
- `message_type` 固定为 `heartbeat`。
|
||||||
|
- `schema_version` 当前为 `1`。
|
||||||
|
- `serial` 来自启动参数 `-c`,必填且非空。
|
||||||
|
- `process_uuid` 同样来自启动参数 `-c`,用于管理程序校验端口对应的推理进程。
|
||||||
|
- `status` 当前固定为 `running`。
|
||||||
|
- `timestamp` 为推理端当前时间戳。
|
||||||
|
|
||||||
|
心跳发送点在解码帧完成 RGA 转换之后、`-P/--time-period` 时间段判断之前。因此即使当前时间段不执行识别,只要程序仍在正常读流解码,也会继续发送心跳。
|
||||||
|
|
||||||
|
管理程序建议按以下规则区分:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/heartbeat + message_type=heartbeat -> 只刷新心跳,不进入业务判断
|
||||||
|
/video/post + message_type=inference_result -> 推理业务结果
|
||||||
|
```
|
||||||
|
|
||||||
## G1000 截图通知客户端
|
## G1000 截图通知客户端
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# 2026-07-06 修改记录
|
||||||
|
|
||||||
|
## 推理程序心跳
|
||||||
|
|
||||||
|
新增独立心跳上报:
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST http://127.0.0.1:<upload-port>/heartbeat
|
||||||
|
```
|
||||||
|
|
||||||
|
心跳只在启动参数指定了有效 `-p <upload-port>`,且 `-c <processUUID>` 非空时发送。当前发送周期沿用 `HEARTBEAT_SECS = 60`。
|
||||||
|
|
||||||
|
心跳包结构:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message_type": "heartbeat",
|
||||||
|
"schema_version": 1,
|
||||||
|
"serial": "<processUUID>",
|
||||||
|
"process_uuid": "<processUUID>",
|
||||||
|
"status": "running",
|
||||||
|
"timestamp": 1778826272
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中 `serial` 和 `process_uuid` 都来自启动参数 `-c`。例如 `fire_check` 进程应传入 `deviceUUID_fire_check`。
|
||||||
|
|
||||||
|
心跳发送点放在解码帧完成 RGA 转换之后、时间段判断之前。因此即使 `-P/--time-period` 当前不执行识别,只要推理程序仍在读流解码,也会继续刷新心跳。
|
||||||
|
|
||||||
|
## 推理结果业务包
|
||||||
|
|
||||||
|
业务结果仍然走原接口:
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST http://127.0.0.1:<upload-port>/video/post
|
||||||
|
```
|
||||||
|
|
||||||
|
本次只新增顶层区分字段,不改变原有业务字段:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message_type": "inference_result",
|
||||||
|
"schema_version": 1,
|
||||||
|
"serial": "<processUUID>",
|
||||||
|
"type": 19,
|
||||||
|
"at": 1778826272,
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"class_idx": 1,
|
||||||
|
"name": "person",
|
||||||
|
"number": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
管理程序可按以下规则区分:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/heartbeat + message_type=heartbeat -> 只刷新心跳
|
||||||
|
/video/post + message_type=inference_result -> 业务推理结果
|
||||||
|
```
|
||||||
|
|
||||||
|
## 未修改内容
|
||||||
|
|
||||||
|
- 未修改识别阈值、workspace 过滤、类别判断、NMS、模型后处理逻辑。
|
||||||
|
- 未修改 `/video/post` 中 `serial/type/at/params` 原有字段含义。
|
||||||
|
- 未恢复旧的 `number=0` 心跳业务包。
|
||||||
|
- 当前推理程序缓存文件仍是 `.h264`,没有改成 `.mp4`。
|
||||||
|
|
||||||
|
## mp4-merge 代码确认
|
||||||
|
|
||||||
|
已查看本地 `mp4-merge/` 源码。该项目是 MP4 box 级合并工具,依赖输入文件已有 `moov/mdat/stbl` 等 MP4 容器结构,当前不能直接合并裸 `.h264`。
|
||||||
|
|
||||||
|
如果要支持 `.h264`,推荐在 `mp4-merge` 命令入口增加预处理:
|
||||||
|
|
||||||
|
```text
|
||||||
|
.h264 -> ffmpeg 无损封装为临时 .mp4 -> 复用现有 join_files 合并
|
||||||
|
```
|
||||||
|
|
||||||
|
不建议直接在现有 MP4 box 合并逻辑里解析裸 H264。
|
||||||
|
|
||||||
|
## 构建验证
|
||||||
|
|
||||||
|
已使用 WSL + Linaro 交叉编译通过,生成产物:
|
||||||
|
|
||||||
|
```text
|
||||||
|
C:\Users\Smos.DESKTOP-6MT98U8\Desktop\yolov5-inference\build-aarch64\yolov5
|
||||||
|
```
|
||||||
|
|
||||||
|
构建输出为 ARM aarch64 ELF。构建过程中出现 Windows/WSL 时间戳导致的 `Clock skew` warning,但目标文件已成功生成。
|
||||||
@@ -24,12 +24,14 @@
|
|||||||
#include "vertix.h"
|
#include "vertix.h"
|
||||||
#include "snapshot_notify.h"
|
#include "snapshot_notify.h"
|
||||||
#include "myrga.h"
|
#include "myrga.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
#ifdef mkdir
|
#ifdef mkdir
|
||||||
#undef mkdir
|
#undef mkdir
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void *upload_info(void *arg);
|
void *upload_info(void *arg);
|
||||||
|
static void detect_log_line(const char *fmt, ...);
|
||||||
|
|
||||||
typedef struct _upload_target_info_t
|
typedef struct _upload_target_info_t
|
||||||
{
|
{
|
||||||
@@ -59,7 +61,9 @@ int TypeID = 0;
|
|||||||
int UploadPort = 0;
|
int UploadPort = 0;
|
||||||
|
|
||||||
static char is_uploading = 0;
|
static char is_uploading = 0;
|
||||||
|
static char is_heartbeat_uploading = 0;
|
||||||
static time_t last_upload = 0;
|
static time_t last_upload = 0;
|
||||||
|
static time_t last_heartbeat_post = 0;
|
||||||
static time_t last_heartbeat_log = 0;
|
static time_t last_heartbeat_log = 0;
|
||||||
static int last_object_count[OBJ_CLASS_MAX] = {0};
|
static int last_object_count[OBJ_CLASS_MAX] = {0};
|
||||||
static pthread_mutex_t upload_state_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t upload_state_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
@@ -107,6 +111,80 @@ static void release_upload()
|
|||||||
pthread_mutex_unlock(&upload_state_mutex);
|
pthread_mutex_unlock(&upload_state_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int try_reserve_heartbeat_upload()
|
||||||
|
{
|
||||||
|
int reserved = 0;
|
||||||
|
pthread_mutex_lock(&upload_state_mutex);
|
||||||
|
if (is_heartbeat_uploading == 0)
|
||||||
|
{
|
||||||
|
is_heartbeat_uploading = 1;
|
||||||
|
reserved = 1;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&upload_state_mutex);
|
||||||
|
return reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_heartbeat_upload()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&upload_state_mutex);
|
||||||
|
is_heartbeat_uploading = 0;
|
||||||
|
pthread_mutex_unlock(&upload_state_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *upload_heartbeat_info(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
cJSON *heartbeat = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(heartbeat, "message_type", "heartbeat");
|
||||||
|
cJSON_AddNumberToObject(heartbeat, "schema_version", 1);
|
||||||
|
cJSON_AddStringToObject(heartbeat, "serial", CameraID);
|
||||||
|
cJSON_AddStringToObject(heartbeat, "process_uuid", CameraID);
|
||||||
|
cJSON_AddStringToObject(heartbeat, "status", "running");
|
||||||
|
cJSON_AddNumberToObject(heartbeat, "timestamp", current_time_stamp());
|
||||||
|
|
||||||
|
int ret = post_json("127.0.0.1", UploadPort, "localhost", "/heartbeat", heartbeat);
|
||||||
|
detect_log_line("heartbeat_post camera=%s port=%d result=%d",
|
||||||
|
CameraID ? CameraID : "", UploadPort, ret);
|
||||||
|
|
||||||
|
cJSON_Delete(heartbeat);
|
||||||
|
release_heartbeat_upload();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_inference_heartbeat_if_needed()
|
||||||
|
{
|
||||||
|
if (UploadPort <= 0 || CameraID == NULL || CameraID[0] == '\0')
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t now_timestamp;
|
||||||
|
time(&now_timestamp);
|
||||||
|
if (now_timestamp - last_heartbeat_post < HEARTBEAT_SECS)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!try_reserve_heartbeat_upload())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_heartbeat_post = now_timestamp;
|
||||||
|
pthread_t uploadth;
|
||||||
|
if (pthread_create(&uploadth, NULL, upload_heartbeat_info, NULL) == 0)
|
||||||
|
{
|
||||||
|
pthread_detach(uploadth);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
detect_log_line("heartbeat_thread_create_failed camera=%s port=%d",
|
||||||
|
CameraID ? CameraID : "", UploadPort);
|
||||||
|
release_heartbeat_upload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int is_edge_touch(int left, int top, int right, int bottom)
|
static int is_edge_touch(int left, int top, int right, int bottom)
|
||||||
{
|
{
|
||||||
return left <= 2 || top <= 2 || right >= DSTWidth - 3 || bottom >= DSTHeight - 3;
|
return left <= 2 || top <= 2 || right >= DSTWidth - 3 || bottom >= DSTHeight - 3;
|
||||||
@@ -823,6 +901,8 @@ void *upload_info(void *arg)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
cJSON *uploadinfo = cJSON_CreateObject();
|
cJSON *uploadinfo = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(uploadinfo, "message_type", "inference_result");
|
||||||
|
cJSON_AddNumberToObject(uploadinfo, "schema_version", 1);
|
||||||
cJSON_AddStringToObject(uploadinfo, "serial", CameraID);
|
cJSON_AddStringToObject(uploadinfo, "serial", CameraID);
|
||||||
// 内部事件编码,因为没法表示多个,所以,即将废弃
|
// 内部事件编码,因为没法表示多个,所以,即将废弃
|
||||||
cJSON_AddNumberToObject(uploadinfo, "type", TypeID);
|
cJSON_AddNumberToObject(uploadinfo, "type", TypeID);
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ rknn_t *do_init_model();
|
|||||||
rknn_t *reload_rknn(char *model_path);
|
rknn_t *reload_rknn(char *model_path);
|
||||||
*/
|
*/
|
||||||
int process_a_frame(char *buf, int sw, int sh);
|
int process_a_frame(char *buf, int sw, int sh);
|
||||||
|
void send_inference_heartbeat_if_needed();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
int post_process_u8(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w,
|
int post_process_u8(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w,
|
||||||
|
|||||||
Reference in New Issue
Block a user