erpc/erpc_core.c

451 lines
14 KiB
C
Raw Normal View History

2024-10-29 08:42:21 +00:00
#include "erpc_core.h"
#include "crc16.h"
2024-11-20 15:03:26 +00:00
/**
*
*/
erpc_sleep erpc_sleep_tick = NULL;
static list_t erpc_hw;
2024-11-21 08:59:31 +00:00
void clean_cache(erpc_hw_cfg_t *hw) {
for (int i = 0; i < MAX_REC_CMD_CACHE_NUM; i++) {
2024-11-20 15:03:26 +00:00
hw->rec_cache->state = ERPC_CMD_NO_ERROR;
}
2024-11-21 08:59:31 +00:00
for (int i = 0; i < MAX_SEND_CMD_CACHE_NUM; i++) {
2024-11-20 15:03:26 +00:00
hw->send_cache[i].state = ERPC_CMD_NO_ERROR;
}
}
/**
* hardware
* 线
*/
2024-11-21 08:59:31 +00:00
u32 erpc_hw_add(erpc_hw_cfg_t *hw) {
2024-11-20 15:03:26 +00:00
list_t *list = &erpc_hw;
2024-11-21 08:59:31 +00:00
if (list->data == NULL) {
2024-11-20 15:03:26 +00:00
list->data = (void *)hw;
clean_cache(hw);
return ERPC_NO_ERROR;
}
list_t *list_node = malloc(sizeof(list_t));
2024-11-21 08:59:31 +00:00
if (list_node == NULL) {
2024-11-20 15:03:26 +00:00
return ERPC_ERR_MALLOC_ERROR;
}
list_node->data = (void *)hw;
u8 hw_ord = hw->ord;
// 检查hw是否已经存在
2024-11-21 08:59:31 +00:00
while (list != NULL) {
2024-11-20 15:03:26 +00:00
erpc_hw_cfg_t *hw_cfg = (erpc_hw_cfg_t *)list->data;
2024-11-21 08:59:31 +00:00
if (hw_cfg->ord == hw_ord) {
2024-11-20 15:03:26 +00:00
return ERPC_ERR_HW_EXIST;
}
list = list->next;
}
list_t *result = list_append(&erpc_hw, hw);
2024-11-21 08:59:31 +00:00
if (result == NULL) {
2024-11-20 15:03:26 +00:00
return ERPC_ERR_MALLOC_ERROR;
}
clean_cache(hw);
return ERPC_NO_ERROR;
}
/**
*
* 线
*/
2024-11-21 08:59:31 +00:00
u32 erpc_add_cmd_list(erpc_hw_cfg_t *hw, erpc_cmd_list_t *cmd_list) {
2024-11-20 15:03:26 +00:00
list_t *list = &hw->cmd_list;
2024-11-21 08:59:31 +00:00
if (list->data == NULL) {
2024-11-20 15:03:26 +00:00
list->data = (void *)cmd_list;
return ERPC_NO_ERROR;
}
list = list_append(&hw->cmd_list, cmd_list);
2024-11-21 08:59:31 +00:00
if (list == NULL) {
2024-11-20 15:03:26 +00:00
return ERPC_ERR_MALLOC_ERROR;
}
return ERPC_NO_ERROR;
}
/**
* hardware
* 线
*/
2024-11-21 08:59:31 +00:00
u32 erpc_hw_remove(u8 hw) {
2024-11-20 15:03:26 +00:00
list_t *list = &erpc_hw;
2024-11-21 08:59:31 +00:00
if (list->data == NULL) {
2024-11-20 15:03:26 +00:00
return ERPC_ERR_NOFOND_HW;
}
2024-11-21 08:59:31 +00:00
while (list != NULL) {
2024-11-20 15:03:26 +00:00
erpc_hw_cfg_t *hw_cfg = (erpc_hw_cfg_t *)list->data;
2024-11-21 08:59:31 +00:00
if (hw_cfg->ord == hw) {
2024-11-20 15:03:26 +00:00
list_delete(&erpc_hw, list);
free(list);
return ERPC_NO_ERROR;
}
list = list->next;
}
return ERPC_ERR_NOFOND_HW;
}
/**
* hardware
*/
2024-11-21 08:59:31 +00:00
erpc_hw_cfg_t *erpc_hw_get(u8 hw) {
2024-11-20 15:03:26 +00:00
list_t *list = &erpc_hw;
2024-11-21 08:59:31 +00:00
if (list->data == NULL) {
2024-11-20 15:03:26 +00:00
return NULL;
}
2024-11-21 08:59:31 +00:00
while (list != NULL) {
2024-11-20 15:03:26 +00:00
erpc_hw_cfg_t *hw_cfg = (erpc_hw_cfg_t *)list->data;
2024-11-21 08:59:31 +00:00
if (hw_cfg->ord == hw) {
2024-11-20 15:03:26 +00:00
return hw_cfg;
}
}
return NULL;
2024-10-29 08:42:21 +00:00
}
2024-11-21 08:59:31 +00:00
u32 erpc_send_base(erpc_hw_cfg_t *hw_cfg, u8 dest_id, u16 port, u8 package_type,
u8 *data, u16 len) {
2024-11-20 15:03:26 +00:00
CHECK_IF_ERROR(hw_cfg == NULL, ERPC_ERR_NOFOND_HW);
u8 cache_ord = 255;
// 查找可用的发送缓存
2024-11-21 08:59:31 +00:00
for (int i = 0; i < MAX_SEND_CMD_CACHE_NUM; i++) {
if (hw_cfg->send_cache[i].state == ERPC_CMD_NO_ERROR) {
2024-11-20 16:12:48 +00:00
printf("find send cache %d\n", i);
2024-11-20 15:03:26 +00:00
cache_ord = i;
break;
}
}
2024-11-21 08:59:31 +00:00
2024-11-20 15:03:26 +00:00
CHECK_IF_ERROR(cache_ord == 255, ERPC_ERR_SEND_CACHE_FULL);
// 准备数据
hw_cfg->send_cache[cache_ord].state = ERPC_CMD_DATA_DEAL;
2024-11-21 08:59:31 +00:00
erpc_cmd_def_t *cmd =
(erpc_cmd_def_t *)&hw_cfg->send_cache[cache_ord].data[0];
2024-11-20 15:03:26 +00:00
cmd->head.dest_id = dest_id;
cmd->head.port = port;
cmd->head.msg_len = len;
2024-11-21 08:59:31 +00:00
if (data != NULL) {
cmd->head.msg_len = 0;
}
cmd->head.type = package_type;
2024-11-20 15:03:26 +00:00
cmd->head.src_id = hw_cfg->local_id;
2024-11-21 08:59:31 +00:00
if (len > 0 && data != NULL) {
memcpy(cmd->data, data, len);
}
2024-11-20 15:03:26 +00:00
// 计算校验和
cmd_cal_crc16(cmd);
2024-11-21 08:59:31 +00:00
2024-11-20 15:03:26 +00:00
// 发送数据
hw_cfg->send_cache[cache_ord].state = ERPC_CMD_WAIT_SEND;
// 等待数据发送完成
int wait_time = CMD_TIMEOUT;
2024-11-21 08:59:31 +00:00
while (wait_time > 0) {
if (hw_cfg->send_cache[cache_ord].state == ERPC_CMD_SEND_OK) {
2024-11-20 15:03:26 +00:00
break;
}
2024-11-21 08:59:31 +00:00
if (hw_cfg->send_cache[cache_ord].state == ERPC_CMD_DEST_BUSY) {
2024-11-20 15:03:26 +00:00
return ERPC_ERR_DEST_BUSY;
}
2024-11-21 08:59:31 +00:00
if (erpc_sleep_tick != NULL) {
2024-11-20 15:03:26 +00:00
erpc_sleep_tick(1);
}
wait_time--;
}
2024-11-20 16:12:48 +00:00
u32 ret = ERPC_NO_ERROR;
2024-11-21 08:59:31 +00:00
do {
if (wait_time == 0) {
2024-11-20 16:26:15 +00:00
ret = ERPC_ERR_SEND_TIMEOUT;
break;
}
2024-11-21 08:59:31 +00:00
if (hw_cfg->send_cache[cache_ord].state == ERPC_CMD_SEND_ONCE) {
2024-11-20 16:26:15 +00:00
ret = ERPC_ERR_DEST_NO_RESPONSE;
break;
}
2024-11-21 08:59:31 +00:00
if (hw_cfg->send_cache[cache_ord].state == ERPC_CMD_SEND_REPEAT) {
2024-11-20 16:26:15 +00:00
ret = ERPC_ERR_DEST_NO_RESPONSE;
break;
}
2024-11-20 16:12:48 +00:00
} while (0);
hw_cfg->send_cache[cache_ord].state = ERPC_CMD_NO_ERROR;
return ret;
2024-10-29 08:42:21 +00:00
}
2024-11-21 08:59:31 +00:00
u32 erpc_send(u8 hw, u8 dest_id, u16 port, u8 *data, u16 len) {
erpc_hw_cfg_t *hw_cfg = erpc_hw_get(hw);
u32 ret =
erpc_send_base(hw_cfg, dest_id, port, PACKAGE_TYPE_CMD_REQ, data, len);
return ret;
}
2024-10-29 08:42:21 +00:00
2024-11-21 08:59:31 +00:00
u32 erpc_wait_resp_package(u8 hw, u8 dest_id, u16 port, u8 *reply_data,
u16 *reply_len, u32 time_out) {
erpc_hw_cfg_t *hw_cfg = erpc_hw_get(hw);
CHECK_IF_ERROR(hw_cfg == NULL, ERPC_ERR_NOFOND_HW);
while (time_out > 0) {
for (int i = 0; i < MAX_REC_CMD_CACHE_NUM; i++) {
erpc_data_cache_t *rec_cahce = &hw_cfg->rec_cache[i];
if (rec_cahce->state != ERPC_CMD_RESP_OK) {
continue;
}
erpc_cmd_def_t *cmd = (erpc_cmd_def_t *)&rec_cahce->data[0];
if (cmd->head.port != port) {
continue;
}
if (cmd->head.src_id != dest_id) {
continue;
}
if (reply_data != NULL) {
memccpy(reply_data, cmd->data, 0, cmd->head.msg_len);
*reply_len = cmd->head.msg_len;
}
rec_cahce->state = ERPC_CMD_NO_ERROR;
return ERPC_NO_ERROR;
}
if (erpc_sleep_tick != NULL) {
erpc_sleep_tick(1);
}
time_out--;
}
CHECK_IF_ERROR(time_out == 0, ERPC_ERR_DEST_NO_REPLY);
return ERPC_NO_ERROR;
}
u32 erpc_send_wait_reply(u8 hw, u8 dest_id, u16 port, u8 *data, u16 len,
u8 *reply_data, u16 *reply_len, u32 timeout) {
u32 ret = erpc_send(hw, dest_id, port, data, len);
if (reply_len != NULL) {
*reply_len = 0;
}
CHECK_IF_ERROR(ret != ERPC_NO_ERROR, ret);
printf("send ok\n");
ret =
erpc_wait_resp_package(hw, dest_id, port, reply_data, reply_len, timeout);
CHECK_IF_ERROR(ret != ERPC_NO_ERROR, ret);
return ERPC_NO_ERROR;
}
u32 erpc_replay(u8 hw, u8 dest_id, u16 port, u8 *data_out, u16 len) {
erpc_hw_cfg_t *hw_cfg = erpc_hw_get(hw);
u32 ret = erpc_send_base(hw_cfg, dest_id, port, PACKAGE_TYPE_CMD_REPEAT,
data_out, len);
return ret;
}
u32 erpc_send_data(erpc_hw_cfg_t *hw) {
for (int i = 0; i < MAX_SEND_CMD_CACHE_NUM; i++) {
if (hw->send_cache[i].state == ERPC_CMD_WAIT_SEND) {
2024-11-20 15:03:26 +00:00
erpc_cmd_def_t *cmd = (erpc_cmd_def_t *)&hw->send_cache[i].data[0];
u32 len = sizeof(erpc_cmd_head_t) + 2 + cmd->head.msg_len;
hw->send_lock();
u8 ret = hw->write((u8 *)hw->send_cache[i].data, len);
hw->send_unlock();
CHECK_IF_ERROR(ret != 0, ERPC_ERR_HW_SEND_FAIL);
hw->send_cache[i].state = ERPC_CMD_SEND_ONCE;
}
}
2024-11-21 08:59:31 +00:00
return ERPC_NO_ERROR;
2024-10-29 08:42:21 +00:00
}
2024-11-21 08:59:31 +00:00
u32 erpc_rev_ack_package(erpc_hw_cfg_t *hw, erpc_cmd_def_t *cmd_rev) {
for (int i = 0; i < MAX_SEND_CMD_CACHE_NUM; i++) {
if (hw->send_cache[i].state == ERPC_CMD_SEND_ONCE ||
hw->send_cache[i].state == ERPC_CMD_SEND_REPEAT) {
2024-11-20 15:03:26 +00:00
erpc_cmd_def_t *cmd_send = (erpc_cmd_def_t *)&hw->send_cache[i].data[0];
2024-11-21 08:59:31 +00:00
if (cmd_rev->head.port == cmd_send->head.port) {
2024-11-20 15:03:26 +00:00
hw->send_cache[i].state = ERPC_CMD_SEND_OK;
return ERPC_NO_ERROR;
}
}
}
return ERPC_NO_ERROR;
}
2024-11-21 08:59:31 +00:00
u32 erpc_rev_resp_package(erpc_hw_cfg_t *hw, erpc_cmd_def_t *cmd_rev) {
erpc_cmd_def_t cmd_send;
cmd_send.head.dest_id = cmd_rev->head.src_id;
cmd_send.head.port = cmd_rev->head.port;
cmd_send.head.msg_len = 0;
cmd_send.head.type = PACKAGE_TYPE_CMD_RESP_ACK;
cmd_send.head.src_id = hw->local_id;
cmd_cal_crc16(&cmd_send);
u32 len = sizeof(erpc_cmd_head_t) + 2 + cmd_send.head.msg_len;
hw->send_lock();
u8 ret = hw->write((u8 *)&cmd_send, len);
hw->send_unlock();
CHECK_IF_ERROR(ret != 0, ERPC_ERR_HW_SEND_FAIL);
return ERPC_NO_ERROR;
}
2024-11-20 15:03:26 +00:00
// 重发数据包
2024-11-21 08:59:31 +00:00
u32 erpc_rev_repeat_package(erpc_hw_cfg_t *hw, erpc_cmd_def_t *cmd_rev) {
for (int i = 0; i < MAX_SEND_CMD_CACHE_NUM; i++) {
if (hw->send_cache[i].state == ERPC_CMD_SEND_ONCE ||
hw->send_cache[i].state == ERPC_CMD_SEND_REPEAT) {
2024-11-20 15:03:26 +00:00
erpc_cmd_def_t *cmd_send = (erpc_cmd_def_t *)&hw->send_cache[i].data[0];
2024-11-21 08:59:31 +00:00
if (cmd_rev->head.port == cmd_send->head.port) {
2024-11-20 15:03:26 +00:00
u32 len = sizeof(erpc_cmd_head_t) + 2 + cmd_send->head.msg_len;
hw->send_lock();
2024-11-21 08:59:31 +00:00
u32 ret = hw->write((u8 *)hw->send_cache[i].data, len);
2024-11-20 15:03:26 +00:00
hw->send_unlock();
hw->send_cache[i].state = ERPC_CMD_SEND_REPEAT;
2024-11-21 08:59:31 +00:00
CHECK_IF_ERROR(ret, ERPC_ERR_HW_SEND_FAIL); //
2024-11-20 15:03:26 +00:00
return ERPC_NO_ERROR;
}
}
}
return ERPC_NO_ERROR;
}
2024-11-21 08:59:31 +00:00
u32 erpc_send_ack_package(erpc_hw_cfg_t *hw, erpc_cmd_def_t *cmd_rev) {
2024-10-29 08:42:21 +00:00
erpc_cmd_def_t cmd_send;
2024-11-20 15:03:26 +00:00
cmd_send.head.dest_id = cmd_rev->head.src_id;
cmd_send.head.port = cmd_rev->head.port;
cmd_send.head.msg_len = 0;
cmd_send.head.type = PACKAGE_TYPE_CMD_REQ_ACK;
cmd_send.head.src_id = hw->local_id;
2024-10-29 08:42:21 +00:00
cmd_cal_crc16(&cmd_send);
2024-11-20 15:03:26 +00:00
u32 len = sizeof(erpc_cmd_head_t) + 2 + cmd_send.head.msg_len;
hw->send_lock();
u8 ret = hw->write((u8 *)&cmd_send, len);
hw->send_unlock();
CHECK_IF_ERROR(ret != 0, ERPC_ERR_HW_SEND_FAIL);
return ERPC_NO_ERROR;
2024-10-29 08:42:21 +00:00
}
2024-11-21 08:59:31 +00:00
u32 erpc_rev_package(erpc_hw_cfg_t *hw) {
for (int i = 0; i < MAX_REC_CMD_CACHE_NUM; i++) {
if (hw->rec_cache[i].state == ERPC_CMD_WAIT_SERVER_DEAL) {
2024-11-20 15:03:26 +00:00
erpc_cmd_def_t *cmd = (erpc_cmd_def_t *)&hw->rec_cache[i].data[0];
// 检查crc
u8 crc_result = cmd_check_crc16(cmd);
// 丢弃错误数据包
2024-11-21 08:59:31 +00:00
if (crc_result) {
2024-11-20 15:03:26 +00:00
hw->rec_cache[i].state = ERPC_CMD_NO_ERROR;
continue;
}
// 丢弃不是本地数据包
2024-11-21 08:59:31 +00:00
if (cmd->head.dest_id != hw->local_id) {
2024-11-20 15:03:26 +00:00
hw->rec_cache[i].state = ERPC_CMD_NO_ERROR;
continue;
}
// 处理数据包
2024-11-21 08:59:31 +00:00
switch (cmd->head.type) {
case PACKAGE_TYPE_CMD_REQ:
printf("{REQ}\n");
erpc_send_ack_package(hw, cmd);
hw->rec_cache[i].state = ERPC_CMD_WAIT_TASK_DEAL;
break;
case PACKAGE_TYPE_CMD_REQ_ACK:
case PACKAGE_TYPE_CMD_RESP_ACK:
printf("{ACK}\n");
erpc_rev_ack_package(hw, cmd);
hw->rec_cache[i].state = ERPC_CMD_NO_ERROR;
break;
case PACKAGE_TYPE_CMD_REPEAT:
erpc_rev_repeat_package(hw, cmd);
hw->rec_cache[i].state = ERPC_CMD_NO_ERROR;
break;
case PACKAGE_TYPE_CMD_RESP:
printf("{RESP}\n");
erpc_rev_resp_package(hw, cmd);
hw->rec_cache[i].state = ERPC_CMD_RESP_OK;
break;
default:
2024-11-20 15:03:26 +00:00
2024-11-21 08:59:31 +00:00
break;
2024-11-20 15:03:26 +00:00
}
}
}
2024-11-21 08:59:31 +00:00
return ERPC_NO_ERROR;
2024-10-29 08:42:21 +00:00
}
2024-11-21 08:59:31 +00:00
void erpc_rev_package_core() {
2024-11-20 15:03:26 +00:00
list_t *list = &erpc_hw;
2024-11-21 08:59:31 +00:00
if (list->data == NULL) {
2024-11-20 15:03:26 +00:00
return;
}
2024-11-21 08:59:31 +00:00
while (list != NULL) {
2024-11-20 15:03:26 +00:00
erpc_hw_cfg_t *hw = (erpc_hw_cfg_t *)list->data;
erpc_rev_package(hw);
list = list->next;
}
2024-10-29 08:42:21 +00:00
}
2024-11-21 08:59:31 +00:00
u32 erpc_set_rev_cahce(u8 hw, u8 *data, u16 len) {
2024-11-20 15:03:26 +00:00
erpc_hw_cfg_t *hw_cfg = erpc_hw_get(hw);
CHECK_IF_ERROR(hw_cfg == NULL, ERPC_ERR_NOFOND_HW);
2024-11-21 08:59:31 +00:00
for (int i = 0; i < MAX_REC_CMD_CACHE_NUM; i++) {
if (hw_cfg->rec_cache[i].state == ERPC_CMD_NO_ERROR) {
2024-11-20 16:12:48 +00:00
printf("set rev cache %d\r\n", i);
2024-11-20 15:03:26 +00:00
memcpy(hw_cfg->rec_cache[i].data, data, len);
hw_cfg->rec_cache[i].state = ERPC_CMD_WAIT_SERVER_DEAL;
return ERPC_NO_ERROR;
}
}
return ERPC_ERR_REC_CACHE_FULL;
}
2024-11-21 08:59:31 +00:00
void erpc_send_deal_core() {
2024-11-20 15:03:26 +00:00
list_t *list = &erpc_hw;
2024-11-21 08:59:31 +00:00
if (list->data == NULL) {
2024-11-20 15:03:26 +00:00
return;
}
2024-11-21 08:59:31 +00:00
while (list != NULL) {
2024-11-20 15:03:26 +00:00
erpc_hw_cfg_t *hw = (erpc_hw_cfg_t *)list->data;
erpc_send_data(hw);
}
2024-11-21 08:59:31 +00:00
if (erpc_sleep_tick != NULL) {
2024-11-20 15:03:26 +00:00
erpc_sleep_tick(1);
}
}
2024-11-21 08:59:31 +00:00
void erpc_rev_deal_core() {
2024-11-20 15:03:26 +00:00
list_t *list = &erpc_hw;
2024-11-21 08:59:31 +00:00
if (list->data == NULL) {
2024-11-20 15:03:26 +00:00
return;
}
2024-11-21 08:59:31 +00:00
while (list != NULL) {
2024-11-20 15:03:26 +00:00
erpc_hw_cfg_t *hw = (erpc_hw_cfg_t *)list->data;
2024-11-21 08:59:31 +00:00
{ // 遍历接收缓存
for (int j = 0; j < MAX_REC_CMD_CACHE_NUM; j++) {
{ // 发现等待处理的数据包
if (hw->rec_cache[j].state != ERPC_CMD_WAIT_TASK_DEAL) {
2024-11-20 16:26:15 +00:00
continue;
}
2024-11-20 15:03:26 +00:00
}
2024-11-21 08:59:31 +00:00
{ // 多线程下,抢占处理权限
2024-11-20 16:26:15 +00:00
hw->deal_lock();
2024-11-21 08:59:31 +00:00
if (hw->rec_cache[j].state == ERPC_CMD_WAIT_TASK_DEAL) {
2024-11-20 16:26:15 +00:00
// 获取指令的处理权限
hw->rec_cache[j].state = ERPC_CMD_WAIT_TASK_DEAL_FINISH;
2024-11-21 08:59:31 +00:00
} else {
2024-11-20 16:26:15 +00:00
// 已经有线程抢先执行了
hw->deal_unlock();
continue;
}
2024-11-20 15:03:26 +00:00
hw->deal_unlock();
}
2024-11-21 08:59:31 +00:00
{ // 处理指令
2024-11-20 16:26:15 +00:00
// 搜索指令列表
list_t *cmd_list = &hw->cmd_list;
// 链表是空的
2024-11-21 08:59:31 +00:00
if (cmd_list->data == NULL) {
2024-11-20 16:26:15 +00:00
continue;
}
// 获取指令指针
erpc_cmd_def_t *cmd_def = (erpc_cmd_def_t *)hw->rec_cache[j].data;
2024-11-21 08:59:31 +00:00
while (cmd_list != NULL) { // 搜索指令列表
2024-11-20 16:26:15 +00:00
erpc_cmd_list_t *cmd_obj = (erpc_cmd_list_t *)cmd_list->data;
2024-11-21 08:59:31 +00:00
if (cmd_obj->cmd == cmd_def->head.port) {
if (cmd_obj->handle != NULL) {
2024-11-20 16:26:15 +00:00
// 指令调用
2024-11-21 08:59:31 +00:00
cmd_obj->handle(hw->ord, cmd_def->head.src_id,
cmd_def->head.dest_id, cmd_def->head.port,
cmd_def->data, cmd_def->head.msg_len);
2024-11-20 16:26:15 +00:00
}
break;
2024-11-20 15:03:26 +00:00
}
2024-11-20 16:26:15 +00:00
cmd_list = cmd_list->next;
2024-11-20 15:03:26 +00:00
}
}
2024-11-20 16:26:15 +00:00
// 处理完成 丢弃缓存 , 无论是否执行指令,都需要把数据包丢弃
hw->rec_cache[j].state = ERPC_CMD_NO_ERROR;
2024-11-20 15:03:26 +00:00
}
}
2024-11-20 16:12:48 +00:00
list = list->next;
2024-11-20 15:03:26 +00:00
}
2024-11-21 08:59:31 +00:00
if (erpc_sleep_tick != NULL) {
2024-11-20 15:03:26 +00:00
erpc_sleep_tick(1);
}
2024-10-29 08:42:21 +00:00
}