Files
2026-04-09 10:14:20 +08:00

755 lines
26 KiB
C
Raw Permalink Blame History

#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);
}