feat: 添加时间同步管理功能
- 新增time_manager模块用于统一管理时间同步状态 - 实现BLE时间同步GATT服务(time_sync_event和ble_time_sync_module) - 添加time_sync_protocol定义统一的协议帧格式 - 支持UTC时间戳、时区偏移和精度信息的时间同步 - 实现settings持久化存储时间校准数据 - 提供time_manager快照API供其他模块查询当前时间状态 - 增加对BLE/USB/手动三种同步源的支持和区分
This commit is contained in:
175
src/modules/ble_time_sync_module.c
Normal file
175
src/modules/ble_time_sync_module.c
Normal file
@@ -0,0 +1,175 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include <app_event_manager.h>
|
||||
|
||||
#define MODULE ble_time_sync
|
||||
#include <caf/events/module_state_event.h>
|
||||
|
||||
#include "time_manager.h"
|
||||
#include "time_sync_event.h"
|
||||
#include "time_sync_protocol.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
|
||||
#define BT_UUID_TIME_SYNC_SERVICE_VAL \
|
||||
BT_UUID_128_ENCODE(0x0b7f5000, 0x38d2, 0x4f62, 0x8f6f, 0x36c4fd73a110)
|
||||
#define BT_UUID_TIME_SYNC_WRITE_CHAR_VAL \
|
||||
BT_UUID_128_ENCODE(0x0b7f5001, 0x38d2, 0x4f62, 0x8f6f, 0x36c4fd73a110)
|
||||
|
||||
#define BT_UUID_TIME_SYNC_SERVICE \
|
||||
BT_UUID_DECLARE_128(BT_UUID_TIME_SYNC_SERVICE_VAL)
|
||||
#define BT_UUID_TIME_SYNC_WRITE_CHAR \
|
||||
BT_UUID_DECLARE_128(BT_UUID_TIME_SYNC_WRITE_CHAR_VAL)
|
||||
|
||||
struct ble_time_sync_ctx {
|
||||
bool ble_stack_ready;
|
||||
bool time_manager_ready;
|
||||
bool module_ready;
|
||||
};
|
||||
|
||||
static struct ble_time_sync_ctx ble_time_sync;
|
||||
|
||||
/* 统一检查协议版本和长度,避免在回调里分散出现偏移判断。 */
|
||||
static bool ble_time_sync_payload_is_valid(const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
if (!buf || (len != TIME_SYNC_PROTOCOL_PAYLOAD_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buf[TIME_SYNC_PROTOCOL_OFFSET_VERSION] != TIME_SYNC_PROTOCOL_VERSION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((buf[TIME_SYNC_PROTOCOL_OFFSET_FLAGS] &
|
||||
TIME_SYNC_PROTOCOL_FLAG_TIMEZONE_VALID) == 0U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 把私有 GATT payload 解码为统一的 time_sync_update:
|
||||
* - BLE 只负责协议适配;
|
||||
* - 传输无关的时间语义都转成公共结构体后再交给事件层。
|
||||
*/
|
||||
static void ble_time_sync_decode_payload(const uint8_t *buf,
|
||||
struct time_sync_update *update)
|
||||
{
|
||||
update->utc_ms = sys_get_le64(&buf[TIME_SYNC_PROTOCOL_OFFSET_UTC_MS]);
|
||||
update->timezone_min =
|
||||
(int16_t)sys_get_le16(&buf[TIME_SYNC_PROTOCOL_OFFSET_TIMEZONE]);
|
||||
update->accuracy_ms =
|
||||
sys_get_le32(&buf[TIME_SYNC_PROTOCOL_OFFSET_ACCURACY_MS]);
|
||||
update->source = TIME_SYNC_SOURCE_BLE;
|
||||
}
|
||||
|
||||
/*
|
||||
* GATT 写回调必须尽量短:
|
||||
* - 不支持 offset/prepare write,避免被拆成长写事务;
|
||||
* - 校验和解码完成后直接提交统一事件,不在这里做耗时存储。
|
||||
*/
|
||||
static ssize_t write_time_sync(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr,
|
||||
const void *buf,
|
||||
uint16_t len,
|
||||
uint16_t offset,
|
||||
uint8_t flags)
|
||||
{
|
||||
struct time_sync_update update;
|
||||
|
||||
ARG_UNUSED(conn);
|
||||
ARG_UNUSED(attr);
|
||||
|
||||
if (!ble_time_sync.module_ready || !time_manager_is_ready()) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
|
||||
}
|
||||
|
||||
if (offset != 0U) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
|
||||
if ((flags & BT_GATT_WRITE_FLAG_PREPARE) != 0U) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_ATTRIBUTE_NOT_LONG);
|
||||
}
|
||||
|
||||
if (!ble_time_sync_payload_is_valid(buf, len)) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
ble_time_sync_decode_payload(buf, &update);
|
||||
time_sync_event_submit(&update);
|
||||
|
||||
LOG_INF("Accepted BLE time sync utc_ms=%llu tz=%d acc=%u",
|
||||
(unsigned long long)update.utc_ms,
|
||||
update.timezone_min,
|
||||
update.accuracy_ms);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(ble_time_sync_svc,
|
||||
BT_GATT_PRIMARY_SERVICE(BT_UUID_TIME_SYNC_SERVICE),
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_TIME_SYNC_WRITE_CHAR,
|
||||
BT_GATT_CHRC_WRITE |
|
||||
BT_GATT_CHRC_WRITE_WITHOUT_RESP,
|
||||
BT_GATT_PERM_WRITE_ENCRYPT,
|
||||
NULL,
|
||||
write_time_sync,
|
||||
NULL),
|
||||
);
|
||||
|
||||
/*
|
||||
* 只有 BLE 栈和 time_manager 都 ready 后,才把模块状态标记为 READY:
|
||||
* - 虽然静态 GATT service 会跟随蓝牙栈注册;
|
||||
* - 但真正是否接受写入,仍由 module_ready 再做一层保护。
|
||||
*/
|
||||
static void ble_time_sync_update_ready_state(void)
|
||||
{
|
||||
bool should_be_ready = ble_time_sync.ble_stack_ready &&
|
||||
ble_time_sync.time_manager_ready;
|
||||
|
||||
if (should_be_ready == ble_time_sync.module_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
ble_time_sync.module_ready = should_be_ready;
|
||||
module_set_state(should_be_ready ? MODULE_STATE_READY : MODULE_STATE_STANDBY);
|
||||
|
||||
LOG_INF("BLE time sync %s", should_be_ready ? "ready" : "standby");
|
||||
}
|
||||
|
||||
/* 模块依赖只来自 ble_state 和 time_manager,两者 READY 顺序不做假设。 */
|
||||
static bool handle_module_state_event(const struct module_state_event *event)
|
||||
{
|
||||
if (check_state(event, MODULE_ID(ble_state), MODULE_STATE_READY)) {
|
||||
ble_time_sync.ble_stack_ready = true;
|
||||
ble_time_sync_update_ready_state();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (check_state(event, MODULE_ID(time_manager), MODULE_STATE_READY)) {
|
||||
ble_time_sync.time_manager_ready = true;
|
||||
ble_time_sync_update_ready_state();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool app_event_handler(const struct app_event_header *aeh)
|
||||
{
|
||||
if (is_module_state_event(aeh)) {
|
||||
return handle_module_state_event(cast_module_state_event(aeh));
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
|
||||
407
src/modules/time_manager_module.c
Normal file
407
src/modules/time_manager_module.c
Normal file
@@ -0,0 +1,407 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include <zephyr/spinlock.h>
|
||||
|
||||
#include <app_event_manager.h>
|
||||
|
||||
#define MODULE time_manager
|
||||
#include <caf/events/module_state_event.h>
|
||||
#include <caf/events/power_event.h>
|
||||
|
||||
#include "time_manager.h"
|
||||
#include "time_sync_event.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
|
||||
#define TIME_MANAGER_SAVE_DELAY_MS 1000
|
||||
#define TIME_MANAGER_STORAGE_KEY "state"
|
||||
|
||||
/*
|
||||
* 持久化数据只记录“最近一次成功校时”的元数据:
|
||||
* - 它可以帮助我们知道设备曾经被校时过;
|
||||
* - 但由于当前没有独立 RTC,重启后不能把这份数据当作“当前仍准确”的时间。
|
||||
*/
|
||||
struct time_manager_storage_data {
|
||||
uint64_t utc_ms;
|
||||
int16_t timezone_min;
|
||||
uint32_t accuracy_ms;
|
||||
uint8_t source;
|
||||
uint8_t valid_marker;
|
||||
};
|
||||
|
||||
struct time_manager_ctx {
|
||||
struct k_spinlock lock;
|
||||
struct k_work_delayable save_work;
|
||||
struct time_manager_storage_data persisted;
|
||||
uint64_t base_utc_ms;
|
||||
int64_t base_uptime_ms;
|
||||
int16_t timezone_min;
|
||||
uint32_t accuracy_ms;
|
||||
enum time_sync_source source;
|
||||
bool ready;
|
||||
bool synchronized;
|
||||
bool has_persisted_time;
|
||||
bool storage_dirty;
|
||||
bool storage_loaded;
|
||||
};
|
||||
|
||||
static struct time_manager_ctx time_ctx;
|
||||
|
||||
/* 统一判断来源是否合法,避免把损坏配置或“未设置来源”写进时间状态。 */
|
||||
static bool time_manager_source_is_valid(enum time_sync_source source)
|
||||
{
|
||||
switch (source) {
|
||||
case TIME_SYNC_SOURCE_BLE:
|
||||
case TIME_SYNC_SOURCE_USB:
|
||||
case TIME_SYNC_SOURCE_MANUAL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 对时区做保守校验,避免异常包把显示层带到离谱偏移。 */
|
||||
static bool time_manager_timezone_is_valid(int16_t timezone_min)
|
||||
{
|
||||
return (timezone_min >= -(24 * 60)) && (timezone_min <= (24 * 60));
|
||||
}
|
||||
|
||||
/*
|
||||
* 保存工作在系统工作队列里执行:
|
||||
* - 这样 GATT 写回调和事件派发路径都只做内存更新;
|
||||
* - 真正可能触发 flash 擦写的 settings_save_one 被挪到异步上下文。
|
||||
*/
|
||||
static int time_manager_store_state(const struct time_manager_storage_data *storage)
|
||||
{
|
||||
char key[] = MODULE_NAME "/" TIME_MANAGER_STORAGE_KEY;
|
||||
int err = settings_save_one(key, storage, sizeof(*storage));
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to save time state err=%d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
LOG_INF("Stored time state src=%u utc_ms=%llu",
|
||||
storage->source,
|
||||
(unsigned long long)storage->utc_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 从运行态复制一份稳定快照,再在锁外执行 flash 写入:
|
||||
* - 锁内只做小块 memcpy,避免长时间持锁;
|
||||
* - 即使保存失败,也不回滚内存时间状态,避免影响当前功能。
|
||||
*/
|
||||
static void time_manager_save_work_fn(struct k_work *work)
|
||||
{
|
||||
struct time_manager_storage_data storage;
|
||||
bool should_store;
|
||||
k_spinlock_key_t key;
|
||||
|
||||
ARG_UNUSED(work);
|
||||
|
||||
key = k_spin_lock(&time_ctx.lock);
|
||||
should_store = time_ctx.storage_dirty && time_ctx.has_persisted_time;
|
||||
storage = time_ctx.persisted;
|
||||
time_ctx.storage_dirty = false;
|
||||
k_spin_unlock(&time_ctx.lock, key);
|
||||
|
||||
if (!should_store) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)time_manager_store_state(&storage);
|
||||
}
|
||||
|
||||
/*
|
||||
* 把一次同步结果写入运行态:
|
||||
* - base_utc_ms + base_uptime_ms 组成当前时间基准;
|
||||
* - persisted 只保存“最近一次同步结果”,后续异步落盘。
|
||||
*/
|
||||
static void time_manager_apply_update(const struct time_sync_update *update)
|
||||
{
|
||||
k_spinlock_key_t key = k_spin_lock(&time_ctx.lock);
|
||||
|
||||
time_ctx.base_utc_ms = update->utc_ms;
|
||||
time_ctx.base_uptime_ms = k_uptime_get();
|
||||
time_ctx.timezone_min = update->timezone_min;
|
||||
time_ctx.accuracy_ms = update->accuracy_ms;
|
||||
time_ctx.source = update->source;
|
||||
time_ctx.synchronized = true;
|
||||
time_ctx.has_persisted_time = true;
|
||||
time_ctx.persisted.utc_ms = update->utc_ms;
|
||||
time_ctx.persisted.timezone_min = update->timezone_min;
|
||||
time_ctx.persisted.accuracy_ms = update->accuracy_ms;
|
||||
time_ctx.persisted.source = (uint8_t)update->source;
|
||||
time_ctx.persisted.valid_marker = 1U;
|
||||
time_ctx.storage_dirty = true;
|
||||
|
||||
k_spin_unlock(&time_ctx.lock, key);
|
||||
|
||||
/*
|
||||
* 保存延迟 1 秒:
|
||||
* - 避免未来 BLE/USB 连续校时时反复触发 flash 写入;
|
||||
* - 也避免在同步入口上下文里直接阻塞等待存储完成。
|
||||
*/
|
||||
k_work_reschedule(&time_ctx.save_work, K_MSEC(TIME_MANAGER_SAVE_DELAY_MS));
|
||||
|
||||
LOG_INF("Time synchronized src=%u tz=%d utc_ms=%llu acc=%u",
|
||||
update->source,
|
||||
update->timezone_min,
|
||||
(unsigned long long)update->utc_ms,
|
||||
update->accuracy_ms);
|
||||
}
|
||||
|
||||
/*
|
||||
* settings 子系统回调只负责恢复原始持久化结构:
|
||||
* - 不在这里决定“当前时间是否有效”;
|
||||
* - 运行态初始化放到 settings_loader ready 之后统一完成。
|
||||
*/
|
||||
static int settings_set(const char *key,
|
||||
size_t len_rd,
|
||||
settings_read_cb read_cb,
|
||||
void *cb_arg)
|
||||
{
|
||||
ssize_t rc;
|
||||
|
||||
if (strcmp(key, TIME_MANAGER_STORAGE_KEY) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len_rd != sizeof(time_ctx.persisted)) {
|
||||
LOG_WRN("Time state size mismatch got=%u expect=%u",
|
||||
(uint32_t)len_rd,
|
||||
sizeof(time_ctx.persisted));
|
||||
time_ctx.storage_loaded = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = read_cb(cb_arg, &time_ctx.persisted, sizeof(time_ctx.persisted));
|
||||
time_ctx.storage_loaded = (rc == sizeof(time_ctx.persisted));
|
||||
|
||||
if (!time_ctx.storage_loaded) {
|
||||
LOG_WRN("Time state read failed rc=%d", (int)rc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SETTINGS_STATIC_HANDLER_DEFINE(time_manager,
|
||||
MODULE_NAME,
|
||||
NULL,
|
||||
settings_set,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
/*
|
||||
* settings 恢复完成后初始化运行态:
|
||||
* - 有持久化历史仅表示“以前同步过”,不能直接把时间标记成有效;
|
||||
* - 当前 boot 仍然需要新的同步事件来建立可信时间基准。
|
||||
*/
|
||||
static void time_manager_init_after_settings_loaded(void)
|
||||
{
|
||||
k_spinlock_key_t key = k_spin_lock(&time_ctx.lock);
|
||||
|
||||
time_ctx.ready = true;
|
||||
time_ctx.synchronized = false;
|
||||
time_ctx.source = TIME_SYNC_SOURCE_NONE;
|
||||
time_ctx.base_utc_ms = 0U;
|
||||
time_ctx.base_uptime_ms = 0;
|
||||
time_ctx.timezone_min = 0;
|
||||
time_ctx.accuracy_ms = 0U;
|
||||
time_ctx.has_persisted_time = time_ctx.storage_loaded &&
|
||||
(time_ctx.persisted.valid_marker == 1U) &&
|
||||
time_manager_source_is_valid(
|
||||
(enum time_sync_source)time_ctx.persisted.source) &&
|
||||
time_manager_timezone_is_valid(
|
||||
time_ctx.persisted.timezone_min);
|
||||
time_ctx.storage_dirty = false;
|
||||
|
||||
k_spin_unlock(&time_ctx.lock, key);
|
||||
|
||||
if (time_ctx.has_persisted_time) {
|
||||
LOG_INF("Loaded persisted time metadata tz=%d src=%u utc_ms=%llu",
|
||||
time_ctx.persisted.timezone_min,
|
||||
time_ctx.persisted.source,
|
||||
(unsigned long long)time_ctx.persisted.utc_ms);
|
||||
} else {
|
||||
LOG_INF("No valid persisted time metadata");
|
||||
}
|
||||
|
||||
module_set_state(MODULE_STATE_READY);
|
||||
}
|
||||
|
||||
/*
|
||||
* 校时事件入口只做快速校验和内存更新:
|
||||
* - 数据格式错误直接丢弃;
|
||||
* - flash 持久化交给延迟工作处理。
|
||||
*/
|
||||
static bool handle_time_sync_event(const struct time_sync_event *event)
|
||||
{
|
||||
const struct time_sync_update *update = time_sync_event_get_update(event);
|
||||
|
||||
if (!time_ctx.ready) {
|
||||
LOG_WRN("Drop time sync before manager ready");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!time_manager_source_is_valid(update->source)) {
|
||||
LOG_WRN("Drop time sync invalid source=%u", update->source);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!time_manager_timezone_is_valid(update->timezone_min)) {
|
||||
LOG_WRN("Drop time sync invalid timezone=%d", update->timezone_min);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (update->utc_ms == 0U) {
|
||||
LOG_WRN("Drop time sync utc_ms=0");
|
||||
return false;
|
||||
}
|
||||
|
||||
time_manager_apply_update(update);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 掉电前尽量把最后一次同步结果落盘:
|
||||
* - 如果没有脏数据,立即返回;
|
||||
* - 如果有待保存数据,则同步执行一次保存,降低突然掉电时的丢失概率。
|
||||
*/
|
||||
static bool handle_power_down_event(void)
|
||||
{
|
||||
struct time_manager_storage_data storage;
|
||||
bool should_store;
|
||||
k_spinlock_key_t key;
|
||||
|
||||
if (!time_manager_is_ready()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(void)k_work_cancel_delayable(&time_ctx.save_work);
|
||||
|
||||
key = k_spin_lock(&time_ctx.lock);
|
||||
should_store = time_ctx.storage_dirty && time_ctx.has_persisted_time;
|
||||
storage = time_ctx.persisted;
|
||||
time_ctx.storage_dirty = false;
|
||||
k_spin_unlock(&time_ctx.lock, key);
|
||||
|
||||
if (!should_store) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(void)time_manager_store_state(&storage);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 仅在 settings_loader 完成后宣布 READY,保证外部读取到的是稳定状态。 */
|
||||
static bool handle_module_state_event(const struct module_state_event *event)
|
||||
{
|
||||
if (!check_state(event, MODULE_ID(settings_loader), MODULE_STATE_READY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (time_ctx.ready) {
|
||||
return false;
|
||||
}
|
||||
|
||||
k_work_init_delayable(&time_ctx.save_work, time_manager_save_work_fn);
|
||||
time_manager_init_after_settings_loaded();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool time_manager_is_ready(void)
|
||||
{
|
||||
k_spinlock_key_t key = k_spin_lock(&time_ctx.lock);
|
||||
bool ready = time_ctx.ready;
|
||||
|
||||
k_spin_unlock(&time_ctx.lock, key);
|
||||
return ready;
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取快照时只复制一次基准,再在锁外计算当前 UTC:
|
||||
* - 这样不会让调用者在锁里做时间换算;
|
||||
* - 也避免 64 位字段在 32 位 MCU 上被撕裂读取。
|
||||
*/
|
||||
int time_manager_get_snapshot(struct time_manager_snapshot *snapshot)
|
||||
{
|
||||
uint64_t base_utc_ms;
|
||||
int64_t base_uptime_ms;
|
||||
int16_t timezone_min;
|
||||
uint32_t accuracy_ms;
|
||||
enum time_sync_source source;
|
||||
bool ready;
|
||||
bool synchronized;
|
||||
bool has_persisted_time;
|
||||
k_spinlock_key_t key;
|
||||
int64_t elapsed_ms;
|
||||
|
||||
if (!snapshot) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key = k_spin_lock(&time_ctx.lock);
|
||||
ready = time_ctx.ready;
|
||||
synchronized = time_ctx.synchronized;
|
||||
has_persisted_time = time_ctx.has_persisted_time;
|
||||
base_utc_ms = time_ctx.base_utc_ms;
|
||||
base_uptime_ms = time_ctx.base_uptime_ms;
|
||||
timezone_min = time_ctx.timezone_min;
|
||||
accuracy_ms = time_ctx.accuracy_ms;
|
||||
source = time_ctx.source;
|
||||
k_spin_unlock(&time_ctx.lock, key);
|
||||
|
||||
snapshot->ready = ready;
|
||||
snapshot->synchronized = synchronized;
|
||||
snapshot->has_persisted_time = has_persisted_time;
|
||||
snapshot->timezone_min = timezone_min;
|
||||
snapshot->accuracy_ms = accuracy_ms;
|
||||
snapshot->source = source;
|
||||
snapshot->utc_ms = 0U;
|
||||
|
||||
if (!ready) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (!synchronized) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
elapsed_ms = k_uptime_get() - base_uptime_ms;
|
||||
if (elapsed_ms < 0) {
|
||||
elapsed_ms = 0;
|
||||
}
|
||||
|
||||
snapshot->utc_ms = base_utc_ms + (uint64_t)elapsed_ms;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool app_event_handler(const struct app_event_header *aeh)
|
||||
{
|
||||
if (is_module_state_event(aeh)) {
|
||||
return handle_module_state_event(cast_module_state_event(aeh));
|
||||
}
|
||||
|
||||
if (is_time_sync_event(aeh)) {
|
||||
return handle_time_sync_event(cast_time_sync_event(aeh));
|
||||
}
|
||||
|
||||
if (is_power_down_event(aeh)) {
|
||||
return handle_power_down_event();
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
|
||||
APP_EVENT_SUBSCRIBE(MODULE, time_sync_event);
|
||||
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
|
||||
Reference in New Issue
Block a user