755 lines
26 KiB
C
755 lines
26 KiB
C
#include "app_mozen_handler.h"
|
||
#include "mozen_tunnel.h"
|
||
#include "mx_log.h"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include "app_calibration.h"
|
||
#include "main.h"
|
||
#include "cJSON.h"
|
||
|
||
// Define logs if not using MX_LOG directly
|
||
#ifndef MX_LOGD
|
||
#define MX_LOGD MOZEN_LOGD
|
||
#define MX_LOGI MOZEN_LOGI
|
||
#define MX_LOGW MOZEN_LOGW
|
||
#define MX_LOGE MOZEN_LOGE
|
||
#endif
|
||
|
||
#define MX_PTO_TAG "MOZEN_APP"
|
||
|
||
volatile uint16_t g_sensor_frames_to_send = 0;
|
||
volatile uint8_t g_sensor_data_type = SENSOR_BIT_16;
|
||
|
||
static mozen_protocol_t *s_prot_instance = NULL;
|
||
static mozen_tunnel_t s_tunnel_instance;
|
||
|
||
// --- Tunnel Stream State ---
|
||
static struct
|
||
{
|
||
bool is_streaming_write;
|
||
uint8_t* write_ptr;
|
||
uint8_t target_cmd;
|
||
uint8_t target_sub_class;
|
||
} s_stream_state;
|
||
|
||
// --- Response Utilities ---
|
||
#pragma pack(1)
|
||
typedef struct
|
||
{
|
||
uint8_t target_id;
|
||
uint8_t sub_class;
|
||
uint8_t operation;
|
||
} mozen_cmd_header_t;
|
||
#pragma pack()
|
||
|
||
typedef void (*cmd_sub_handler_t)(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
|
||
|
||
typedef struct
|
||
{
|
||
uint8_t target_id;
|
||
cmd_sub_handler_t handler;
|
||
} cmd_handler_entry_t;
|
||
|
||
// --- Forward Declarations ---
|
||
static void param_handle_report_mode(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
|
||
static void param_handle_device_info(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
|
||
static void param_handle_cali_display(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
|
||
static void prod_handle_sys_cmd(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
|
||
static void prod_handle_map_cali_cmd(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len);
|
||
|
||
// --- param setters ---
|
||
static const cmd_handler_entry_t s_param_handlers[] =
|
||
{
|
||
{MOZEN_PARAM_TGT_REPORT_MODE, param_handle_report_mode}, // 0x01
|
||
{MOZEN_PARAM_TGT_DEVICE_INFO, param_handle_device_info}, // 0x02
|
||
{MOZEN_PARAM_TGT_CALI_DISPLAY, param_handle_cali_display}, // 0x03
|
||
};
|
||
|
||
// --- Production Test Targets ---
|
||
static const cmd_handler_entry_t s_prod_handlers[] =
|
||
{
|
||
{MOZEN_PROD_TGT_SYS, prod_handle_sys_cmd},
|
||
{MOZEN_PROD_TGT_MAP_CALI, prod_handle_map_cali_cmd},
|
||
};
|
||
|
||
// --- Init ---
|
||
mozen_protocol_t g_mozen_prot; // Global mozen protocol instance
|
||
#define RX_BUFFER_SIZE 1024 // RX buffer size
|
||
#define TX_BUFFER_SIZE 1024 // TX buffer size
|
||
static uint8_t s_rx_buf[RX_BUFFER_SIZE]; // RX buffer
|
||
static uint8_t s_tx_buf_main[TX_BUFFER_SIZE]; // TX buffer
|
||
static uint8_t s_tx_buf_irq[TX_BUFFER_SIZE]; // TX buffer
|
||
|
||
// --- Legacy Compatibility Functions ---
|
||
static uint8_t is_legacy_config_payload(const uint8_t *data, uint16_t len)
|
||
{
|
||
if ((data == NULL) || (len < 3U)) return 0U;
|
||
if ((data[0] != 0x3DU) && (data[0] != 0x3FU)) return 0U;
|
||
if ((data[1] != '{') || (data[len - 1U] != '}')) return 0U;
|
||
|
||
return 1U;
|
||
}
|
||
|
||
static int handle_old_protocol_device_status(uint8_t *data, uint16_t length)
|
||
{
|
||
if ((data == NULL) || (length < 2U)) return -1;
|
||
|
||
if (data[0] == 0x3D) // "=" <20><><EFBFBD>ò<EFBFBD><C3B2><EFBFBD>
|
||
{
|
||
cJSON *root = cJSON_Parse((char *)&data[1]);
|
||
if (!root)
|
||
{
|
||
MX_LOGE(MX_PTO_TAG, "old protocol json parse failed");
|
||
return -1;
|
||
}
|
||
|
||
MX_LOGD(MX_PTO_TAG, "old protocol json parsed");
|
||
|
||
// 1. <20><>Ӧ<EFBFBD>ò<EFBFBD><C3B2><EFBFBD>ȡ<EFBFBD><C8A1>ǰ״̬<D7B4><CCAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>и<EFBFBD><D0B8><EFBFBD>
|
||
app_device_status_t new_status = *app_device_status_get();
|
||
|
||
// 2. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֶ<EFBFBD>
|
||
cJSON *led_sw = cJSON_GetObjectItem(root, "led_sw");
|
||
if (led_sw) new_status.led_sw = (uint8_t)led_sw->valueint;
|
||
|
||
cJSON *tx_sw = cJSON_GetObjectItem(root, "tx_sw");
|
||
if (tx_sw)
|
||
{
|
||
int tx_sw_val = tx_sw->valueint;
|
||
if (new_status.sensor_tx_sw == 1 && tx_sw_val == 0)
|
||
{
|
||
new_status.tx_stopping = 1;
|
||
}
|
||
new_status.sensor_tx_sw = (uint8_t)tx_sw_val;
|
||
}
|
||
|
||
cJSON *pick_sw = cJSON_GetObjectItem(root, "pick_sw");
|
||
if (pick_sw) new_status.output_pick = (uint8_t)pick_sw->valueint;
|
||
// printf("pick_sw: %d\n", new_status.output_pick);
|
||
|
||
// 3. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѧ<EFBFBD>궨<EFBFBD><EAB6A8><EFBFBD><EFBFBD> (math / math_full)
|
||
cJSON *math_arr = cJSON_GetObjectItem(root, "math");
|
||
cJSON *extra_flag = cJSON_GetObjectItem(root, "math_full");
|
||
|
||
if (math_arr && cJSON_IsArray(math_arr))
|
||
{
|
||
int size = cJSON_GetArraySize(math_arr);
|
||
app_math_cali_t temp_params;
|
||
|
||
if (app_math_get_temp_params(&temp_params))
|
||
{
|
||
if (size >= 1) {
|
||
temp_params.max_trigger_res_value = (uint16_t)cJSON_GetArrayItem(math_arr, 0)->valueint;
|
||
}
|
||
if (size >= 2) {
|
||
temp_params.min_trigger_res_value = (uint16_t)cJSON_GetArrayItem(math_arr, 1)->valueint;
|
||
}
|
||
|
||
if (extra_flag && extra_flag->valueint && size >= 5) {
|
||
temp_params.max_display_value = (uint16_t)cJSON_GetArrayItem(math_arr, 4)->valueint;
|
||
}
|
||
|
||
app_math_set_temp_params(&temp_params);
|
||
}
|
||
}
|
||
|
||
// 4. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
cJSON *creep_strength = cJSON_GetObjectItem(root, "creep_strength");
|
||
cJSON *creep_level = cJSON_GetObjectItem(root, "creep_level");
|
||
|
||
if (creep_strength || creep_level)
|
||
{
|
||
app_creep_params temp_creep;
|
||
app_creep_read_params(&temp_creep);
|
||
|
||
if (creep_strength) {
|
||
temp_creep.creep_strength = (uint8_t)creep_strength->valueint;
|
||
}
|
||
if (creep_level) {
|
||
temp_creep.creep_level = (uint8_t)creep_level->valueint;
|
||
}
|
||
|
||
app_creep_write_temp_params(&temp_creep);
|
||
}
|
||
|
||
// 5. Flash <20>־û<D6BE><C3BB><EFBFBD><EFBFBD><EFBFBD>
|
||
cJSON *flash_cmd = cJSON_GetObjectItem(root, "flash");
|
||
if(flash_cmd && cJSON_IsString(flash_cmd))
|
||
{
|
||
if (strcmp(flash_cmd->valuestring, "creep_write") == 0)
|
||
{
|
||
app_creep_params temp_creep;
|
||
app_creep_read_params(&temp_creep);
|
||
app_creep_save_params(&temp_creep);
|
||
}
|
||
}
|
||
|
||
// 6. <20><><EFBFBD><EFBFBD><EFBFBD>º<EFBFBD><C2BA><EFBFBD> Legacy ״̬<D7B4><CCAC>д<EFBFBD><D0B4>Ӧ<EFBFBD>ò<EFBFBD>
|
||
app_device_status_set(&new_status);
|
||
|
||
cJSON_Delete(root);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static bool app_mozen_raw_frame_handler(void *context, const uint8_t *raw_frame, uint16_t length)
|
||
{
|
||
mozen_frame_header_t header;
|
||
if (length < sizeof(mozen_frame_header_t) + 2) return false;
|
||
|
||
memcpy(&header, raw_frame, sizeof(mozen_frame_header_t));
|
||
if (header.command_id == MOZEN_CMD_ID_PARAM_CONFIG) // 0x02
|
||
{
|
||
uint16_t full_frame_len = header.frame_length;
|
||
uint16_t data_len = full_frame_len - sizeof(mozen_frame_header_t);
|
||
const uint8_t *data_ptr = raw_frame + sizeof(mozen_frame_header_t);
|
||
uint16_t received_crc;
|
||
memcpy(&received_crc, raw_frame + full_frame_len, sizeof(uint16_t));
|
||
|
||
uint16_t calc_crc = mozen_protocol_checksum(raw_frame, full_frame_len);
|
||
if ((calc_crc == received_crc) || is_legacy_config_payload(data_ptr, data_len))
|
||
{
|
||
handle_old_protocol_device_status((uint8_t *)data_ptr, data_len);
|
||
return true; // Handled
|
||
}
|
||
}
|
||
return false; // Not handled, fallback to standard parsing
|
||
}
|
||
|
||
// --- Command Handlers ---
|
||
/*
|
||
* @brief handle sensor data
|
||
* @param context: context
|
||
* @param cmd_id: command id
|
||
* @param data: data
|
||
* @param length: length
|
||
* @retval none
|
||
*/
|
||
static void handle_sensor_data(void *context, uint8_t cmd_id, const uint8_t *data, uint16_t length)
|
||
{
|
||
if (length < 3)
|
||
{
|
||
MX_LOGE(MX_PTO_TAG, "sensor command length invalid: %u", (unsigned int)length);
|
||
return;
|
||
}
|
||
uint8_t new_data_type = data[0];
|
||
if (new_data_type != g_sensor_data_type)
|
||
{
|
||
g_sensor_data_type = new_data_type;
|
||
app_math_cali_t temp_params;
|
||
bool got_params = false;
|
||
|
||
got_params = app_math_get_temp_params(&temp_params);
|
||
|
||
if (got_params)
|
||
{
|
||
temp_params.max_display_value = 4095;
|
||
temp_params.sensor_data_type = g_sensor_data_type;
|
||
|
||
app_math_set_temp_params(&temp_params);
|
||
}
|
||
}
|
||
g_sensor_frames_to_send = *(uint16_t*)(&data[1]);
|
||
}
|
||
|
||
// --- Response Utilities ---
|
||
/*
|
||
* @brief send mozen command response
|
||
* @param cmd_id: command id
|
||
* @param req_header: request header
|
||
* @param payload: payload
|
||
* @param payload_len: payload length
|
||
* @retval none
|
||
*/
|
||
static void send_mozen_cmd_response(uint8_t cmd_id, const mozen_cmd_header_t* req_header, const uint8_t* payload, uint16_t payload_len)
|
||
{
|
||
static uint8_t response_buf[1024];
|
||
mozen_cmd_header_t* res_header = (mozen_cmd_header_t*)response_buf;
|
||
res_header->target_id = req_header->target_id;
|
||
res_header->sub_class = req_header->sub_class;
|
||
res_header->operation = req_header->operation;
|
||
if (payload && payload_len > 0)
|
||
{
|
||
memcpy(response_buf + sizeof(mozen_cmd_header_t), payload, payload_len);
|
||
}
|
||
mozen_protocol_send_frame(s_prot_instance, cmd_id, response_buf, sizeof(mozen_cmd_header_t) + payload_len, false);
|
||
}
|
||
|
||
/*
|
||
* @brief send mozen command ok or ng
|
||
* @param cmd_id: command id
|
||
* @param req_header: request header
|
||
* @param success: success or not
|
||
* @retval none
|
||
*/
|
||
static inline void send_mozen_cmd_ok_ng(uint8_t cmd_id, const mozen_cmd_header_t* req_header, bool success)
|
||
{
|
||
uint8_t payload[2] = { success ? 'o' : 'N', success ? 'k' : 'G' };
|
||
send_mozen_cmd_response(cmd_id, req_header, payload, 2);
|
||
}
|
||
|
||
// --- Parameter Config Handlers (Cmd ID: 0x02) ---
|
||
/*
|
||
* @brief handle report mode
|
||
* @param header: header
|
||
* @param payload: payload
|
||
* @param payload_len: payload length
|
||
* @retval none
|
||
*/
|
||
static void param_handle_report_mode(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
|
||
{
|
||
if (header->sub_class == 0x01 && header->operation == 0x01 && payload_len >= 1)
|
||
{
|
||
if (payload[0] == 0x00) g_is_active_reporting = false;
|
||
else if (payload[0] == 0x01) g_is_active_reporting = true;
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, true);
|
||
}
|
||
}
|
||
/*
|
||
* @brief handle device info
|
||
* @param header: header
|
||
* @param payload: payload
|
||
* @param payload_len: payload length
|
||
* @retval none
|
||
*/
|
||
static void param_handle_device_info(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
|
||
{
|
||
if (header->operation == 0x00)
|
||
{
|
||
app_dev_info dev_info;
|
||
char sensor_size_text[sizeof(dev_info.sensor_size) + 1U] = {0};
|
||
char protocol_ver_text[sizeof(dev_info.protocol_ver) + 1U] = {0};
|
||
|
||
if (app_device_get_info(&dev_info))
|
||
{
|
||
memcpy(sensor_size_text, dev_info.sensor_size, sizeof(dev_info.sensor_size));
|
||
memcpy(protocol_ver_text, dev_info.protocol_ver, sizeof(dev_info.protocol_ver));
|
||
|
||
uint8_t resp_payload[256];
|
||
resp_payload[0] = 'o';
|
||
resp_payload[1] = 'k';
|
||
int json_len = snprintf((char *)(resp_payload + 2),
|
||
sizeof(resp_payload) - 2,
|
||
"{\"PN\":\"%s\",\"SN\":\"%s\",\"SV\":\"%s\",\"HVI\":\"%s\",\"XY\":\"%s\",\"Protocol\":\"%s\"}",
|
||
dev_info.pn, dev_info.sn, dev_info.sw_ver, dev_info.hw_ver, sensor_size_text, protocol_ver_text);
|
||
if (json_len > 0) {
|
||
send_mozen_cmd_response(MOZEN_CMD_ID_PARAM_CONFIG, header, resp_payload, 2 + (uint16_t)json_len);
|
||
} else {
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, false);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, false);
|
||
}
|
||
}
|
||
}
|
||
/*
|
||
* @brief handle calibration display
|
||
* @param header: header
|
||
* @param payload: payload
|
||
* @param payload_len: payload length
|
||
* @retval none
|
||
*/
|
||
static void param_handle_cali_display(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
|
||
{
|
||
if (header->operation == 0x00)
|
||
{
|
||
uint8_t success = 0;
|
||
uint16_t data_len = 0;
|
||
uint8_t resp_payload[256];
|
||
|
||
if (header->sub_class == 0x01)
|
||
{
|
||
success = app_math_read_temp_params(resp_payload + 2, &data_len);
|
||
}
|
||
else if (header->sub_class == 0x02)
|
||
{
|
||
success = app_math_read_solidified_params(resp_payload + 2, &data_len);
|
||
}
|
||
|
||
if (success)
|
||
{
|
||
resp_payload[0] = 'o'; resp_payload[1] = 'k';
|
||
send_mozen_cmd_response(MOZEN_CMD_ID_PARAM_CONFIG, header, resp_payload, 2 + data_len);
|
||
}
|
||
else
|
||
{
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, false);
|
||
}
|
||
}
|
||
else if (header->operation == 0x01)
|
||
{
|
||
uint8_t success = 0;
|
||
if (header->sub_class == 0x01)
|
||
{
|
||
success = app_math_write_temp_params(payload, payload_len);
|
||
}
|
||
else if (header->sub_class == 0x02)
|
||
{
|
||
success = app_math_save_params(payload, payload_len);
|
||
}
|
||
else if (header->sub_class == 0x03)
|
||
{
|
||
success = app_math_clear_params();
|
||
app_calibration_invalidate_runtime();
|
||
}
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PARAM_CONFIG, header, success);
|
||
}
|
||
}
|
||
/*
|
||
* @brief handle param config
|
||
* @param context: context
|
||
* @param cmd_id: cmd id
|
||
* @param data: data
|
||
* @param length: length
|
||
* @retval none
|
||
*/
|
||
static void handle_param_config(void *context, uint8_t cmd_id, const uint8_t *data, uint16_t length)
|
||
{
|
||
if (length < sizeof(mozen_cmd_header_t)) return;
|
||
|
||
const mozen_cmd_header_t* cmd_header = (const mozen_cmd_header_t*)data;
|
||
const uint8_t* cmd_payload = data + sizeof(mozen_cmd_header_t);
|
||
uint16_t payload_len = length - sizeof(mozen_cmd_header_t);
|
||
|
||
for (size_t i = 0; i < sizeof(s_param_handlers) / sizeof(s_param_handlers[0]); i++)
|
||
{
|
||
if (s_param_handlers[i].target_id == cmd_header->target_id)
|
||
{
|
||
s_param_handlers[i].handler(cmd_header, cmd_payload, payload_len);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
// --- Production Test Handlers (Cmd ID: 0xAA) ---
|
||
|
||
/*
|
||
* @brief handle system command
|
||
* @param header: header
|
||
* @param payload: payload
|
||
* @param payload_len: payload length
|
||
* @retval none
|
||
*/
|
||
static void prod_handle_sys_cmd(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
|
||
{
|
||
if (header->operation == 0x01)
|
||
{
|
||
if (header->sub_class == 0x02)
|
||
{
|
||
if(payload_len > 0)
|
||
{
|
||
g_is_creep_enable = (payload[0] == 0x01);
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, true);
|
||
}
|
||
}
|
||
else if (header->sub_class == 0x01)
|
||
{
|
||
app_dev_info dev_info;
|
||
if (payload_len == 0U || payload_len >= sizeof(dev_info.sn))
|
||
{
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
|
||
return;
|
||
}
|
||
if (!app_device_get_info(&dev_info))
|
||
{
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
|
||
return;
|
||
}
|
||
memset(dev_info.sn, 0, sizeof(dev_info.sn));
|
||
memcpy(dev_info.sn, payload, payload_len);
|
||
if (!app_device_set_info(&dev_info))
|
||
{
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
|
||
return;
|
||
}
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, true);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* @brief handle map calibration
|
||
* @param header: header
|
||
* @param payload: payload
|
||
* @param payload_len: payload length
|
||
* @retval none
|
||
*/
|
||
static void prod_handle_map_cali_cmd(const mozen_cmd_header_t* header, const uint8_t* payload, uint16_t payload_len)
|
||
{
|
||
uint8_t resp_payload[260];
|
||
|
||
// First, handle map temp params (operation 0x01, sub_class 0x3F / 0x3D) which return no response
|
||
// First, handle map temp params (operation 0x01, sub_class 0x3F / 0x3D) which return no response
|
||
if (header->operation == 0x01)
|
||
{
|
||
if (header->sub_class == 0x3F)
|
||
{
|
||
app_map_write_temp_params(payload, payload_len);
|
||
return;
|
||
}
|
||
else if (header->sub_class == 0x3D)
|
||
{
|
||
app_map_save_params(payload, payload_len);
|
||
return;
|
||
}
|
||
}
|
||
|
||
switch (header->sub_class)
|
||
{
|
||
case 0x4F:
|
||
if (header->operation == 0x00)
|
||
{
|
||
uint16_t d_len;
|
||
if(app_pressure_read_temp_params(resp_payload + 2, &d_len))
|
||
{
|
||
resp_payload[0] = 'o'; resp_payload[1] = 'k';
|
||
send_mozen_cmd_response(MOZEN_CMD_ID_PROD_TEST, header, resp_payload, 2 + d_len);
|
||
} else {
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
|
||
}
|
||
} else if (header->operation == 0x01) {
|
||
bool success = app_pressure_write_temp_params(payload, payload_len);
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, success);
|
||
}
|
||
break;
|
||
case 0x4D:
|
||
if (header->operation == 0x00)
|
||
{
|
||
uint16_t d_len;
|
||
if(app_pressure_read_solidified_params(resp_payload + 2, &d_len))
|
||
{
|
||
resp_payload[0] = 'o'; resp_payload[1] = 'k';
|
||
send_mozen_cmd_response(MOZEN_CMD_ID_PROD_TEST, header, resp_payload, 2 + d_len);
|
||
} else {
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
|
||
}
|
||
} else if (header->operation == 0x01)
|
||
{
|
||
bool success = app_pressure_save_params(payload, payload_len);
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, success);
|
||
}
|
||
break;
|
||
case 0x3F:
|
||
case 0x3D:
|
||
if (header->operation == 0x00)
|
||
{
|
||
uint32_t total_data_len = app_map_data_size();
|
||
uint16_t total_packets = (total_data_len + 240 - 1) / 240;
|
||
memcpy(resp_payload, &total_data_len, sizeof(uint32_t));
|
||
memcpy(resp_payload + 4, &total_packets, sizeof(uint16_t));
|
||
send_mozen_cmd_response(MOZEN_CMD_ID_PROD_TEST, header, resp_payload, 6);
|
||
}
|
||
else if (header->operation == 0x02)
|
||
{
|
||
if (payload_len < 2)
|
||
{
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
|
||
break;
|
||
}
|
||
uint16_t packet_index;
|
||
memcpy(&packet_index, payload, sizeof(uint16_t));
|
||
uint32_t total_data_len = app_map_data_size();
|
||
uint32_t offset = (packet_index - 1) * 240;
|
||
|
||
if (offset >= total_data_len)
|
||
{
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, false);
|
||
break;
|
||
}
|
||
uint16_t chunk_len = 240;
|
||
if (offset + chunk_len > total_data_len) chunk_len = total_data_len - offset;
|
||
|
||
memcpy(resp_payload, &packet_index, sizeof(uint16_t));
|
||
memcpy(resp_payload + 2, app_map_data_ptr() + offset, chunk_len);
|
||
send_mozen_cmd_response(MOZEN_CMD_ID_PROD_TEST, header, resp_payload, 2 + chunk_len);
|
||
}
|
||
break;
|
||
case 0x00:
|
||
if (header->operation == 0x01)
|
||
{
|
||
bool success = app_pressure_clear_params();
|
||
success = success && app_map_clear_params();
|
||
send_mozen_cmd_ok_ng(MOZEN_CMD_ID_PROD_TEST, header, success);
|
||
app_calibration_invalidate_runtime();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* This function handles the production test command.
|
||
* It checks the target ID and calls the appropriate handler function.
|
||
* @param context The context pointer.
|
||
* @param cmd_id The command ID.
|
||
* @param data The command data.
|
||
* @param length The length of the command data.
|
||
*/
|
||
static void handle_production_test(void *context, uint8_t cmd_id, const uint8_t *data, uint16_t length)
|
||
{
|
||
if (length < sizeof(mozen_cmd_header_t)) return;
|
||
|
||
const mozen_cmd_header_t* cmd_header = (const mozen_cmd_header_t*)data;
|
||
const uint8_t* cmd_payload = data + sizeof(mozen_cmd_header_t);
|
||
uint16_t payload_len = length - sizeof(mozen_cmd_header_t);
|
||
|
||
for (size_t i = 0; i < sizeof(s_prod_handlers) / sizeof(s_prod_handlers[0]); i++)
|
||
{
|
||
if (s_prod_handlers[i].target_id == cmd_header->target_id) {
|
||
s_prod_handlers[i].handler(cmd_header, cmd_payload, payload_len);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* This function handles the tunnel command.
|
||
* It calls the tunnel handle command function with the command data.
|
||
* @param context The context pointer.
|
||
* @param cmd_id The command ID.
|
||
* @param data The command data.
|
||
* @param length The length of the command data.
|
||
*/
|
||
static void handle_tunnel_command(void *context, uint8_t cmd_id, const uint8_t *data, uint16_t length)
|
||
{
|
||
mozen_tunnel_handle_command(&s_tunnel_instance, data, length);
|
||
}
|
||
|
||
// --- Tunnel Callbacks ---
|
||
/*
|
||
* This function is called when a stream starts.
|
||
* It checks if the target command is production test and the sub class is 0x3F or 0x3D.
|
||
* If so, it sets the stream state to streaming write and initializes the write pointer.
|
||
* @param context The context pointer.
|
||
* @param target_cmd The target command.
|
||
* @param first_chunk_data The first chunk of data.
|
||
* @param chunk_len The length of the chunk.
|
||
* @return true if the stream is started, false otherwise.
|
||
*/
|
||
static bool tunnel_on_stream_start(void *context, uint8_t target_cmd, const uint8_t *first_chunk_data, uint16_t chunk_len)
|
||
{
|
||
if (target_cmd == MOZEN_CMD_ID_PROD_TEST) {
|
||
const mozen_cmd_header_t* pt_header = (const mozen_cmd_header_t*)first_chunk_data;
|
||
if (chunk_len >= sizeof(mozen_cmd_header_t) && pt_header->target_id == MOZEN_PROD_TGT_MAP_CALI && pt_header->operation == 0x01)
|
||
{
|
||
if (pt_header->sub_class == 0x3F || pt_header->sub_class == 0x3D)
|
||
{
|
||
s_stream_state.is_streaming_write = true;
|
||
s_stream_state.target_sub_class = pt_header->sub_class;
|
||
s_stream_state.target_cmd = target_cmd;
|
||
s_stream_state.write_ptr = app_map_data_ptr();
|
||
|
||
uint16_t data_offset = sizeof(mozen_cmd_header_t);
|
||
uint16_t data_to_write = chunk_len - data_offset;
|
||
uint8_t* end_ptr = (app_map_data_ptr() + app_map_data_size());
|
||
|
||
if (s_stream_state.write_ptr + data_to_write > end_ptr) {
|
||
data_to_write = end_ptr - s_stream_state.write_ptr;
|
||
}
|
||
|
||
if (data_to_write > 0) {
|
||
memcpy(s_stream_state.write_ptr, first_chunk_data + data_offset, data_to_write);
|
||
s_stream_state.write_ptr += data_to_write;
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
* This function is called when data is received during a stream.
|
||
* It checks if the stream is in streaming write mode and writes the data to the map data buffer.
|
||
* @param context The context pointer.
|
||
* @param data The data.
|
||
* @param len The length of the data.
|
||
* @return true if the data is handled, false otherwise.
|
||
*/
|
||
static bool tunnel_on_stream_data(void *context, const uint8_t *data, uint16_t len)
|
||
{
|
||
if (s_stream_state.is_streaming_write) {
|
||
uint8_t* end_ptr = (app_map_data_ptr() + app_map_data_size());
|
||
uint16_t data_to_write = len;
|
||
|
||
if (s_stream_state.write_ptr >= end_ptr) {
|
||
data_to_write = 0;
|
||
} else if (s_stream_state.write_ptr + data_to_write > end_ptr) {
|
||
data_to_write = end_ptr - s_stream_state.write_ptr;
|
||
}
|
||
|
||
if (data_to_write > 0) {
|
||
memcpy(s_stream_state.write_ptr, data, data_to_write);
|
||
s_stream_state.write_ptr += data_to_write;
|
||
}
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
* This function is called when a stream finishes.
|
||
* It checks if the stream is in streaming write mode and saves the map parameters if the target command is production test and the sub class is 0x3F or 0x3D.
|
||
* @param context The context pointer.
|
||
* @param success The success flag.
|
||
* @return None.
|
||
*/
|
||
static void tunnel_on_stream_finish(void *context, bool success)
|
||
{
|
||
if (s_stream_state.is_streaming_write && success)
|
||
{
|
||
uint32_t expected_bytes = app_map_data_size();
|
||
uint32_t written_bytes = (uint32_t)(s_stream_state.write_ptr - app_map_data_ptr());
|
||
|
||
if (written_bytes >= expected_bytes)
|
||
{
|
||
if (s_stream_state.target_cmd == MOZEN_CMD_ID_PROD_TEST && s_stream_state.target_sub_class == 0x3D)
|
||
{
|
||
app_map_save_params(app_map_data_ptr(), (uint16_t)expected_bytes);
|
||
app_calibration_request_reset_effect();
|
||
}
|
||
}
|
||
}
|
||
s_stream_state.is_streaming_write = false;
|
||
s_stream_state.write_ptr = NULL;
|
||
}
|
||
|
||
// --- Init ---
|
||
|
||
/*
|
||
* @brief: Initialize the mozen handler.
|
||
* @param tx_fn: The function to send data.
|
||
* @return None.
|
||
*/
|
||
void app_mozen_init(mozen_tx_fn_t tx_fn)
|
||
{
|
||
mozen_protocol_init(&g_mozen_prot, s_rx_buf, sizeof(s_rx_buf),
|
||
s_tx_buf_main, s_tx_buf_irq, sizeof(s_tx_buf_main),
|
||
tx_fn, NULL);
|
||
app_mozen_handler_init(&g_mozen_prot);
|
||
}
|
||
|
||
/*
|
||
* @brief: Handle raw frame from mozen protocol
|
||
* @param: prot - Protocol instance
|
||
* @return: None
|
||
*/
|
||
void app_mozen_handler_init(mozen_protocol_t *prot)
|
||
{
|
||
s_prot_instance = prot;
|
||
|
||
// Register commands
|
||
mozen_protocol_register_handler(prot, MOZEN_CMD_ID_SENSOR_DATA, handle_sensor_data);
|
||
mozen_protocol_register_handler(prot, MOZEN_CMD_ID_PARAM_CONFIG, handle_param_config);
|
||
mozen_protocol_register_handler(prot, MOZEN_CMD_ID_PROD_TEST, handle_production_test);
|
||
mozen_protocol_register_handler(prot, MOZEN_TUNNEL_COMMAND_ID, handle_tunnel_command);
|
||
|
||
// Legacy handler
|
||
mozen_protocol_set_raw_handler(prot, app_mozen_raw_frame_handler);
|
||
|
||
// Init tunnel
|
||
mozen_tunnel_init(&s_tunnel_instance, prot, 240, NULL);
|
||
mozen_tunnel_set_callbacks(&s_tunnel_instance, tunnel_on_stream_start, tunnel_on_stream_data, tunnel_on_stream_finish);
|
||
}
|