/******************************************************************************
* Copyright (c) 2018-2020 TP-Link Systems Inc.
*
* 文件名称: record_plan.c
* 版 本: 1.0
* 摘 要: record_plan源文件
* 作 者: Deng Peng<dengpeng@tp-link.com.cn>
* 创建时间: 2019-2-22
******************************************************************************/
#include <nvmp_utils.h>
#include <error.h>
#include <unistd.h>
#include "slp_model.h"
#include "libds.h"
#include "record_plan.h"
#define DAY_LAST_MINUTES (24 * 60)
LOCAL S32 s_timer_id = -1; /* 定时器id */
/* 当前录像计划类型 */
static REC_TYPE_E rec_plan_type = REC_TYPE_MAX;
void rec_plan_timer_fun(S32 param); /* 定时器到期调用函数 */
/* 向录像模块发送录像动作 */
/* 注意这里type的枚举值是REC_TYPE_E,不是REC_PLAN_TYPE_E */
S32 notify_outer_module(REC_TYPE_E type)
{
REC_ACTION rec_action;
S32 ret = ERROR;
if (type < REC_TYPE_NULL || type >= REC_TYPE_MAX)
{
RECORD_ERROR("invalid rec_type");
return ERROR;
}
rec_action.rec_type = type;
ret = msg_send(REC_PLAN_ACTION_CHANGED,(U8 *) &rec_action, sizeof(REC_ACTION));
RECORD_DEBUG("REC_TYPE_E type[%d], return %d", type, ret);
return ret;
}
/******************************************************************************
* 函数名称: do_rec_plan()
* 函数描述: 执行录像计划,
若当前时间在录像时间段内,则向录像模块发送dms消息(告知:录像类型,录像/停止录像)
并设置定时器到下次启动时间再次调用本函数
* 输 出: N/A
* 返 回 值: ERROR/OK
* 备 注: 目前只处理当天内的录像计划,若当天没有计划,则定时到当天的最后一秒再次调用本函数
******************************************************************************/
static S32 do_rec_plan()
{
int i;
S32 ret = OK;
PTR_REC_PLAN ptr_rec_plan = NULL;
PTR_REC_PLAN_SECTION p_rec_plan_section = NULL;
time_t now;
struct tm *tm_now;
int now_minute;
WEEK_DAY wday = 0;
S32 next_start_seconds;
S32 next_start_minute;
ptr_rec_plan = get_rec_plan_cfg();
if (ptr_rec_plan == NULL)
{
RECORD_ERROR("get_rec_plan_cfg failed");
ret = ERROR;
goto end;
}
/* 如果“SD卡录像”选项关闭,则发送一个停止录像消息,然后返回 */
if (FALSE == ptr_rec_plan->b_enable)
{
rec_plan_type = REC_TYPE_NULL;
notify_outer_module(REC_TYPE_NULL);
return OK;
}
now = time(NULL);
tm_now = localtime(&now);
now_minute = tm_now->tm_hour * 60 + tm_now->tm_min;
/* 查找当前时间所在的录像计划时间段 */
wday = (tm_now->tm_wday + 6) % 7; /* tm_wday: 0-sunday, 1-monday,而录像计划数组的顺序为0-MONDAY,1-Tuesday,...*/
p_rec_plan_section = (PTR_REC_PLAN_SECTION) &(ptr_rec_plan->week_rec_plan->plan_section[wday * MAX_SECTION_PER_DAY]);
for (i = 0; i < MAX_SECTION_PER_DAY; i++)
{
RECORD_DEBUG("section[%d] type[%u] sm[%u] em[%u]", i, p_rec_plan_section[i].type,
p_rec_plan_section[i].start_minute, p_rec_plan_section[i].end_minute);
if (p_rec_plan_section[i].type == REC_PLAN_TYPE_NULL)
{
continue;
}
/* 找到了当前时间所在的录像计划的时间段,依次做3件事:
* 1、计算本次录像时段的剩余时间-next_start_seconds
* 2、根据录像类型向录像模块发送消息
* 3、设置定时器,next_start_seconds后再次调用本函数*/
if ((p_rec_plan_section[i].start_minute <= now_minute)
&& (p_rec_plan_section[i].end_minute > now_minute))
{
RECORD_DEBUG("section:%d start_minute:%d end_minute:%d now:%d",
i, p_rec_plan_section[i].start_minute,
p_rec_plan_section[i].end_minute, now_minute);
/* 计算距离本时间段结束的时间,单位(秒) */
next_start_seconds = (p_rec_plan_section[i].end_minute - now_minute - 1) * 60
+ (60 - tm_now->tm_sec);
switch (p_rec_plan_section[i].type)
{
case REC_PLAN_TYPE_EVENT:
rec_plan_type = REC_TYPE_MOTION;
/* 通知storage模块当前处于移动侦测录像计划;是否有移动侦测事件发生由storage自行判断 */
notify_outer_module(REC_TYPE_MOTION);
break;
case REC_PLAN_TYPE_TIME:
rec_plan_type = REC_TYPE_TIME;
/* 向录像模块发送 定时录像指令 */
notify_outer_module(REC_TYPE_TIME);
break;
default:
rec_plan_type = REC_TYPE_NULL;
/* 既不是移动侦测录像,也不是定时录像,则表明发生了错误 */
RECORD_ERROR("do_rec_plan: [%d-%d] invalid record type [%u]", p_rec_plan_section[i].start_minute,
p_rec_plan_section[i].end_minute, p_rec_plan_section[i].type);
return ERROR;
}
goto start_timer;
}
}
/* 当前时间不在录像时间段内,发送停止录像的消息 */
rec_plan_type = REC_TYPE_NULL;
notify_outer_module(REC_TYPE_NULL);
/* 查找最近的录像计划时间段的开始时间 */
next_start_minute = DAY_LAST_MINUTES;
for (i = 0; i < MAX_SECTION_PER_DAY; i++)
{
if (p_rec_plan_section[i].type != REC_PLAN_TYPE_NULL)
{
if ((p_rec_plan_section[i].start_minute > now_minute)
&& (p_rec_plan_section[i].start_minute < next_start_minute))
{
next_start_minute = p_rec_plan_section[i].start_minute;
}
}
}
/* 计算距离下一次录像时段开始时间的秒数 */
next_start_seconds = (next_start_minute - now_minute - 1) * 60 + 60 - tm_now->tm_sec;
start_timer:
s_timer_id = inet_add_timer(rec_plan_timer_fun, 0, next_start_seconds, EXECUTE_SINGLE);
if (ERROR == s_timer_id)
{
RECORD_ERROR("inet_add_timer return ERROR");
}
end:
return ret;
}
void rec_plan_timer_fun(S32 param)
{
do_rec_plan();
}
/* 系统时间更新消息的回调函数 */
S32 ntp_time_reload_cb(void *handler, U8 *mbuf, U32 mlen, U32 sender_dms_id)
{
TIME_CALIBRATION *time_cali = NULL;
if (mbuf == NULL || mlen != sizeof(TIME_CALIBRATION))
{
return ERROR;
}
time_cali = (TIME_CALIBRATION *)mbuf;
/* 校时失败什么都不做 */
if (time_cali->status != TIME_CALIBRATION_SUCC)
{
return OK;
}
/* 删除系统时间更新前设置的定时器 */
if (ERROR != s_timer_id)
{
inet_del_timer(s_timer_id);
s_timer_id = ERROR;
}
/* 系统时间更新后,立即执行一次录像计划 */
return do_rec_plan();
}
/*****************************************************
* 下面是本模块作为cap子模块的配套函数
*****************************************************/
LOCAL S32 record_plan_init()
{
S32 ret = OK;
/* 初始化录像计划结构体 */
ret = rec_plan_init();
if (ERROR == ret)
{
RECORD_INFO("rec_plan_init fail");
return ret;
}
/* 关注系统时间更新消息 */
ret = msg_attach_handler(TIME_CALIBRATION_MSG_ID, ntp_time_reload_cb);
if (ERROR == ret)
{
RECORD_INFO("msg_attach_handler fail");
return ret;
}
RECORD_INFO("init over, ret[%d]", ret);
return ret;
}
LOCAL S32 record_plan_start()
{
S32 ret = ERROR;
/* 读取本机上的录像计划配置表 */
ret = load_record_plan_cfg();
if (ERROR == ret)
{
RECORD_ERROR("load_record_plan_cfg error.");
return SLP_EGENERIC;
}
/* 如果尚未开启定时器,那么立即执行一次录像计划 */
if (ERROR == s_timer_id)
{
ret = do_rec_plan();
if (ERROR == ret)
{
RECORD_ERROR("load_record_plan_cfg error.");
return SLP_EGENERIC;
}
}
RECORD_INFO("start over, ret[%d]", ret);
return ret;
}
S32 record_plan_reload()
{
S32 ret = OK;
/* 读取新的录像计划配置表 */
ret = load_record_plan_cfg();
if (ERROR == ret)
{
RECORD_ERROR("load_record_plan_cfg error.");
return SLP_EGENERIC;
}
/* 更新录像计划配置表后,删除先前的定时器,然后执行一次录像计划 */
if (ERROR != s_timer_id)
{
inet_del_timer(s_timer_id);
s_timer_id = ERROR;
}
ret = do_rec_plan();
if (ERROR == ret)
{
RECORD_ERROR("load_record_plan_cfg error.");
return SLP_EGENERIC;
}
RECORD_INFO("reload over, ret[%d]", ret);
return SLP_ENONE;
}
LOCAL S32 record_plan_stop()
{
msg_detach_handler(TIME_CALIBRATION_MSG_ID, ntp_time_reload_cb);
if (ERROR != s_timer_id)
{
inet_del_timer(s_timer_id);
s_timer_id = ERROR;
}
RECORD_INFO("record plan stop over.");
return SLP_ENONE;
}
#define WEEK_PATH_SUFFIX_SIZE 5 /* listConfig路径后缀最大长度为4,加上\0占5字节 例如:_168 */
LOCAL S32 record_plan_check(BIN_AREA *bin_area)
{
REC_PLAN_SECTION plan_section;
char record_path_str[sizeof(REC_PLAN_WEEK_PATH) + WEEK_PATH_SUFFIX_SIZE] = {0};
S32 section_count = 0;
for (section_count = 0; section_count < WEEK_PLAN_LIST_SIZE; ++section_count)
{
memset(&plan_section, 0, sizeof(REC_PLAN_SECTION));
snprintf(record_path_str, sizeof(record_path_str), "%s_%d", REC_PLAN_WEEK_PATH, section_count + 1);
if (0 != ds_ext_read(bin_area, record_path_str, (U8 *)&plan_section, sizeof(REC_PLAN_SECTION)))
{
if (plan_section.start_minute > 1440 || plan_section.end_minute > 1440)
{
return SLP_EINVARG;
}
}
}
return SLP_ENONE;
}
LOCAL void record_plan_main()
{
/*************************************************************************************/
/********* Desc of /record_plan/chn1_channel *********************************************/
/*************************************************************************************/
DS_OPT_DESC chn1_channel_options[] =
{
DS_SWITCH_OPT(REC_PLAN_ENABLE, enabled, OPT_FLAG_NORM),
};
DS_SEG_DESC chn1_channel_segments[] =
{
DS_STRUCT_SEG("chn1_channel", SEG_LIM_RW, SEG_GROUP_ROOT, REC_PLAN_ENABLE, chn1_channel_options),
};
DS_SECT_DESC chn1_channel_sections[] =
{
DS_STRUCT_SECT("chn1_channel", chn1_channel_segments),
};
/*************************************************************************************/
/********* Desc of /record_plan/chn1_channel *********************************************/
/*************************************************************************************/
DS_OPT_DESC week_plan_options[] =
{
DS_U8_OPT(REC_PLAN_SECTION, type, OPT_FLAG_NORM),
DS_U16_OPT(REC_PLAN_SECTION, start_minute, OPT_FLAG_NORM),
DS_U16_OPT(REC_PLAN_SECTION, end_minute, OPT_FLAG_NORM),
};
DS_SEG_DESC week_plan_segments[] =
{
DS_STRUCT_SEG("week_plan", SEG_LIM_RW, SEG_GROUP_ROOT, REC_PLAN_SECTION, week_plan_options),
};
DS_TBL_DESC record_plan_tables[] =
{
DS_STRUCT_TBL("chn1_channel", TBL_ATTR_CFG, chn1_channel_sections),
DSLL_TBL("week_plan", TBL_ATTR_LSTCFG, TABLE_FLAG_PREALLOCATE, WEEK_PLAN_LIST_SIZE, segment, week_plan_segments, NELEMENTS(week_plan_segments))
};
DS_DAT_MON_DESC record_plan_data_monitor[] =
{
DS_DAT_MON(REC_PLAN_ENABLE_PATH, DATA_ATTRI_CHECK),
DS_DAT_MON(REC_PLAN_WEEK_PATH, DATA_ATTRI_CHECK),
};
DS_MOD_DESC record_plan_module =
DS_STRUCT_MOD("record_plan", record_plan_init, record_plan_check, NULL, record_plan_start, record_plan_stop,
record_plan_tables, record_plan_data_monitor);
MODULE *module_node = ds_register_module("record_plan", &record_plan_module);
CAP_ASSERT(NULL != module_node);
}
CAP_INIT(record_plan_main);
详细解析一下
最新发布