修复mqtt消息历史分页问题
This commit is contained in:
@@ -31,24 +31,45 @@ const MqttCommunicationLogPage: React.FC = () => {
|
|||||||
showTotal: (total) => `共 ${total} 条记录`,
|
showTotal: (total) => `共 ${total} 条记录`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchData = useCallback(async (params: MqttLogQueryRequest = {}) => {
|
const fetchData = useCallback(async (params: Partial<MqttLogQueryRequest & { dateRange?: [dayjs.Dayjs, dayjs.Dayjs] | null, antdOrder?: 'ascend' | 'descend' | null }> = {}) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const queryParams: MqttLogQueryRequest = {
|
|
||||||
current: pagination.current,
|
const formValues = form.getFieldsValue();
|
||||||
pageSize: pagination.pageSize,
|
|
||||||
...form.getFieldsValue(), // Get values from the form
|
const currentPage = params.current !== undefined ? params.current : pagination.current;
|
||||||
...params, // Override with any explicitly passed params (like sorters)
|
const currentPageSize = params.pageSize !== undefined ? params.pageSize : pagination.pageSize;
|
||||||
|
|
||||||
|
const queryParamsBuild: Partial<MqttLogQueryRequest> = {
|
||||||
|
...formValues,
|
||||||
|
...params,
|
||||||
|
current: currentPage,
|
||||||
|
pageSize: currentPageSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle dateRange from AntD RangePicker
|
if (params.antdOrder) {
|
||||||
if (queryParams.dateRange && queryParams.dateRange[0] && queryParams.dateRange[1]) {
|
queryParamsBuild.sortOrder = params.antdOrder;
|
||||||
queryParams.startTime = queryParams.dateRange[0].toISOString();
|
} else if (params.hasOwnProperty('antdOrder') && params.antdOrder === null) {
|
||||||
queryParams.endTime = queryParams.dateRange[1].toISOString();
|
queryParamsBuild.sortOrder = undefined;
|
||||||
delete queryParams.dateRange; // Remove from params sent to backend
|
queryParamsBuild.sortField = undefined;
|
||||||
|
} else if (!params.sortField) {
|
||||||
|
queryParamsBuild.sortOrder = undefined;
|
||||||
}
|
}
|
||||||
|
delete (queryParamsBuild as any).antdOrder;
|
||||||
|
|
||||||
|
const effectiveDateRange = params.dateRange !== undefined ? params.dateRange : formValues.dateRange;
|
||||||
|
if (effectiveDateRange && effectiveDateRange[0] && effectiveDateRange[1]) {
|
||||||
|
queryParamsBuild.startTime = effectiveDateRange[0].toISOString();
|
||||||
|
queryParamsBuild.endTime = effectiveDateRange[1].toISOString();
|
||||||
|
} else {
|
||||||
|
queryParamsBuild.startTime = undefined;
|
||||||
|
queryParamsBuild.endTime = undefined;
|
||||||
|
}
|
||||||
|
delete (queryParamsBuild as any).dateRange;
|
||||||
|
|
||||||
|
const finalQueryParams = queryParamsBuild as MqttLogQueryRequest;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response: BaseResponse<PageResponse<MqttCommunicationLog>> = await fetchMqttLogs(queryParams);
|
const response: BaseResponse<PageResponse<MqttCommunicationLog>> = await fetchMqttLogs(finalQueryParams);
|
||||||
if (response.code === 0 && response.data) {
|
if (response.code === 0 && response.data) {
|
||||||
setLogs(response.data.records || []);
|
setLogs(response.data.records || []);
|
||||||
setPagination(prev => ({
|
setPagination(prev => ({
|
||||||
@@ -84,10 +105,17 @@ const MqttCommunicationLogPage: React.FC = () => {
|
|||||||
sorter: SorterResult<MqttCommunicationLog> | SorterResult<MqttCommunicationLog>[],
|
sorter: SorterResult<MqttCommunicationLog> | SorterResult<MqttCommunicationLog>[],
|
||||||
) => {
|
) => {
|
||||||
const currentSorter = Array.isArray(sorter) ? sorter[0] : sorter;
|
const currentSorter = Array.isArray(sorter) ? sorter[0] : sorter;
|
||||||
const sortParams: MqttLogQueryRequest = {};
|
const paramsToFetch: Partial<MqttLogQueryRequest & { antdOrder?: 'ascend' | 'descend' | null }> = {
|
||||||
if (currentSorter && currentSorter.field && currentSorter.order) {
|
current: newPagination.current,
|
||||||
sortParams.sortField = String(currentSorter.field);
|
pageSize: newPagination.pageSize,
|
||||||
sortParams.sortOrder = currentSorter.order;
|
};
|
||||||
|
|
||||||
|
if (currentSorter && currentSorter.field) {
|
||||||
|
paramsToFetch.sortField = String(currentSorter.field);
|
||||||
|
paramsToFetch.antdOrder = currentSorter.order;
|
||||||
|
} else {
|
||||||
|
paramsToFetch.sortField = undefined;
|
||||||
|
paramsToFetch.antdOrder = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPagination(prev => ({
|
setPagination(prev => ({
|
||||||
@@ -96,22 +124,33 @@ const MqttCommunicationLogPage: React.FC = () => {
|
|||||||
pageSize: newPagination.pageSize,
|
pageSize: newPagination.pageSize,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// fetchData will pick up new pagination from state
|
fetchData(paramsToFetch);
|
||||||
// and form values, and will merge sortParams
|
|
||||||
fetchData(sortParams);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFinish = () => {
|
const onFinish = (values: MqttLogQueryRequest & { dateRange?: [dayjs.Dayjs, dayjs.Dayjs] | null }) => {
|
||||||
// Reset to first page on new search
|
|
||||||
setPagination(prev => ({ ...prev, current: 1 }));
|
setPagination(prev => ({ ...prev, current: 1 }));
|
||||||
fetchData(); // fetchData will use current form values and new pagination
|
fetchData({
|
||||||
|
...values,
|
||||||
|
current: 1,
|
||||||
|
sortField: undefined,
|
||||||
|
antdOrder: null,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
setPagination(prev => ({ ...prev, current: 1 }));
|
setPagination(prev => ({ ...prev, current: 1, pageSize: 20 }));
|
||||||
// After reset, fetch with default (cleared) form values
|
fetchData({
|
||||||
fetchData({}); // Pass empty object to ensure sortField/sortOrder are not persisted from previous state if any
|
current: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: undefined,
|
||||||
|
antdOrder: null,
|
||||||
|
clientId: undefined,
|
||||||
|
topic: undefined,
|
||||||
|
direction: undefined,
|
||||||
|
payloadContains: undefined,
|
||||||
|
dateRange: undefined,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!user && !loading) {
|
if (!user && !loading) {
|
||||||
@@ -254,7 +293,7 @@ const MqttCommunicationLogPage: React.FC = () => {
|
|||||||
pagination={pagination}
|
pagination={pagination}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
scroll={{ x: 1500 }} // Enable horizontal scroll if content is too wide
|
scroll={{ x: 1500 }}
|
||||||
bordered
|
bordered
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -27,47 +27,53 @@ void handle_mqtt_command(const char* commandType, const char* taskId, const char
|
|||||||
Serial.println();
|
Serial.println();
|
||||||
|
|
||||||
current_active_task_id = String(taskId);
|
current_active_task_id = String(taskId);
|
||||||
|
const char* cmd_ack_val = nullptr; // 用于存储指令的中文ACK值
|
||||||
|
|
||||||
if (strcmp(commandType, "MOVE") == 0 || strcmp(commandType, "MOVE_TO_SPOT") == 0) {
|
if (strcmp(commandType, "MOVE") == 0 || strcmp(commandType, "MOVE_TO_SPOT") == 0) {
|
||||||
|
cmd_ack_val = "移动到指定点";
|
||||||
if (targetSpotId && strlen(targetSpotId) > 0) {
|
if (targetSpotId && strlen(targetSpotId) > 0) {
|
||||||
if (current_robot_status == IDLE || current_robot_status == COMPLETED || current_robot_status == FAULTED) {
|
if (current_robot_status == IDLE || current_robot_status == COMPLETED || current_robot_status == FAULTED) {
|
||||||
Serial.print("指令有效: 准备移动到 "); Serial.println(targetSpotId);
|
Serial.print("指令有效: 准备移动到 "); Serial.println(targetSpotId);
|
||||||
target_spot_for_move = String(targetSpotId);
|
target_spot_for_move = String(targetSpotId);
|
||||||
// 状态将在navigation_move_to_spot开始时变为MOVING
|
// 状态将在navigation_move_to_spot开始时变为MOVING
|
||||||
// navigation_move_to_spot会阻塞,直到完成、失败或超时
|
// 注意:这里的ACK是针对指令接收无效的情况,移动完成的ACK在loop中处理
|
||||||
} else {
|
} else {
|
||||||
Serial.println("指令无效: 机器人当前不处于可接收MOVE指令的状态。");
|
Serial.println("指令无效: 机器人当前不处于可接收MOVE指令的状态。");
|
||||||
mqtt_publish_ack(taskId, false, "Robot busy or not in correct state for MOVE", current_robot_status, nullptr);
|
mqtt_publish_ack(taskId, false, "Robot busy or not in correct state for MOVE", current_robot_status, nullptr, cmd_ack_val);
|
||||||
current_active_task_id = "";
|
current_active_task_id = "";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Serial.println("指令错误: MOVE 指令缺少 targetSpotId/target_spot_uid。");
|
Serial.println("指令错误: MOVE 指令缺少 targetSpotId/target_spot_uid。");
|
||||||
mqtt_publish_ack(taskId, false, "MOVE command missing target spot ID", current_robot_status, nullptr);
|
mqtt_publish_ack(taskId, false, "MOVE command missing target spot ID", current_robot_status, nullptr, cmd_ack_val);
|
||||||
current_active_task_id = "";
|
current_active_task_id = "";
|
||||||
}
|
}
|
||||||
} else if (strcmp(commandType, "STOP_CHARGE") == 0) {
|
} else if (strcmp(commandType, "STOP_CHARGE") == 0) {
|
||||||
|
cmd_ack_val = "停止充电";
|
||||||
if (current_robot_status == CHARGING) {
|
if (current_robot_status == CHARGING) {
|
||||||
Serial.println("指令有效: 停止充电。");
|
Serial.println("指令有效: 停止充电。");
|
||||||
current_robot_status = COMPLETED; // 立即改变状态
|
current_robot_status = COMPLETED; // 立即改变状态
|
||||||
relay_stop_charging();
|
relay_stop_charging();
|
||||||
unsigned long charge_duration = (robot_charge_start_time_ms > 0) ? (millis() - robot_charge_start_time_ms) / 1000 : 0;
|
unsigned long charge_duration = (robot_charge_start_time_ms > 0) ? (millis() - robot_charge_start_time_ms) / 1000 : 0;
|
||||||
robot_charge_start_time_ms = 0;
|
robot_charge_start_time_ms = 0;
|
||||||
robot_current_location = navigation_get_current_spot_count() > 0 ? String(SPOT_IDS[navigation_get_current_spot_count()-1]) : robot_current_location; // 假设停在最后一个检测到的桩
|
// 假设停在最后一个检测到的桩或已知位置
|
||||||
mqtt_publish_ack(taskId, true, ("Charging stopped. Duration: " + String(charge_duration) + "s").c_str(), current_robot_status, robot_current_location.c_str());
|
// robot_current_location 更新逻辑可以更精细,例如,如果充电时就知道spotId
|
||||||
// 立即发布一次状态更新
|
mqtt_publish_ack(taskId, true, ("Charging stopped. Duration: " + String(charge_duration) + "s").c_str(), current_robot_status, robot_current_location.c_str(), cmd_ack_val);
|
||||||
mqtt_publish_status_update(current_robot_status, robot_current_location.c_str(), robot_battery_level, nullptr, nullptr, robot_current_location.c_str(), 0, 0, nullptr);
|
mqtt_publish_status_update(current_robot_status, robot_current_location.c_str(), robot_battery_level, nullptr, nullptr, robot_current_location.c_str(), 0, 0, nullptr, nullptr);
|
||||||
} else {
|
} else {
|
||||||
Serial.println("指令无效: 机器人未在充电状态。");
|
Serial.println("指令无效: 机器人未在充电状态。");
|
||||||
mqtt_publish_ack(taskId, false, "Robot not in CHARGING state for STOP_CHARGE", current_robot_status, nullptr);
|
mqtt_publish_ack(taskId, false, "Robot not in CHARGING state for STOP_CHARGE", current_robot_status, nullptr, cmd_ack_val);
|
||||||
}
|
}
|
||||||
current_active_task_id = ""; // STOP_CHARGE任务通常是瞬时完成
|
current_active_task_id = ""; // STOP_CHARGE任务通常是瞬时完成
|
||||||
} else if (strcmp(commandType, "START_CHARGE") == 0) { // 保留旧指令处理的ACK,但业务上已忽略
|
} else if (strcmp(commandType, "START_CHARGE") == 0) {
|
||||||
|
cmd_ack_val = "开始充电";
|
||||||
Serial.println("收到 START_CHARGE 指令,但业务逻辑为到达即充,仅ACK。");
|
Serial.println("收到 START_CHARGE 指令,但业务逻辑为到达即充,仅ACK。");
|
||||||
mqtt_publish_ack(taskId, true, "START_CHARGE acknowledged (auto-charge on arrival)", current_robot_status, robot_current_location.c_str());
|
mqtt_publish_ack(taskId, true, "START_CHARGE acknowledged (auto-charge on arrival)", current_robot_status, robot_current_location.c_str(), cmd_ack_val);
|
||||||
current_active_task_id = "";
|
current_active_task_id = "";
|
||||||
} else {
|
} else {
|
||||||
Serial.print("未知指令类型: "); Serial.println(commandType);
|
Serial.print("未知指令类型: "); Serial.println(commandType);
|
||||||
mqtt_publish_ack(taskId, false, "Unknown command type", current_robot_status, nullptr);
|
// 对于未知指令,不确定后端是否能处理没有正确 command_ack 的ACK,或者是否应该发送ACK
|
||||||
|
// 为安全起见,可以不发送,或发送一个带通用错误信息的(但后端可能不认)
|
||||||
|
// mqtt_publish_ack(taskId, false, "Unknown command type", current_robot_status, nullptr, "未知指令");
|
||||||
current_active_task_id = "";
|
current_active_task_id = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,31 +115,40 @@ void loop() {
|
|||||||
// --- 状态机和任务处理 ---
|
// --- 状态机和任务处理 ---
|
||||||
if (target_spot_for_move.length() > 0 && (current_robot_status == IDLE || current_robot_status == COMPLETED || current_robot_status == FAULTED) ) {
|
if (target_spot_for_move.length() > 0 && (current_robot_status == IDLE || current_robot_status == COMPLETED || current_robot_status == FAULTED) ) {
|
||||||
Serial.print("开始执行移动任务到: "); Serial.println(target_spot_for_move);
|
Serial.print("开始执行移动任务到: "); Serial.println(target_spot_for_move);
|
||||||
|
current_robot_status = MOVING; // 明确在开始导航前设置状态为MOVING
|
||||||
|
mqtt_publish_status_update(current_robot_status, robot_current_location.c_str(), robot_battery_level, current_active_task_id.c_str(), target_spot_for_move.c_str(), nullptr, 0, 0, nullptr, nullptr); // 上报MOVING状态
|
||||||
|
|
||||||
bool move_success = navigation_move_to_spot(target_spot_for_move.c_str(), ¤t_robot_status, &robot_current_location);
|
bool move_success = navigation_move_to_spot(target_spot_for_move.c_str(), ¤t_robot_status, &robot_current_location);
|
||||||
|
|
||||||
|
const char* move_cmd_ack_val = "移动到指定点"; // MOVE 指令的中文ACK值
|
||||||
|
|
||||||
if (move_success) {
|
if (move_success) {
|
||||||
Serial.println("移动成功,准备充电。");
|
Serial.println("移动成功,准备充电。");
|
||||||
current_robot_status = CHARGING; // 到达即充电
|
// current_robot_status 已在 navigation_move_to_spot 中被设为 CHARGING (如果它内部这么做)
|
||||||
|
// 或者在这里强制设置,确保到达即充逻辑
|
||||||
|
current_robot_status = CHARGING;
|
||||||
robot_charge_start_time_ms = millis();
|
robot_charge_start_time_ms = millis();
|
||||||
relay_start_charging();
|
relay_start_charging();
|
||||||
mqtt_publish_ack(current_active_task_id.c_str(), true, "Robot arrived and started charging.", current_robot_status, robot_current_location.c_str());
|
// robot_current_location 应该由 navigation_move_to_spot 更新为实际到达的spotId
|
||||||
|
mqtt_publish_ack(current_active_task_id.c_str(), true, "Robot arrived and started charging.", current_robot_status, robot_current_location.c_str(), move_cmd_ack_val);
|
||||||
} else {
|
} else {
|
||||||
Serial.println("移动失败或超时。");
|
Serial.println("移动失败或超时。");
|
||||||
// current_robot_status 已经在 navigation_move_to_spot 中被设为 FAULTED (如果超时)
|
// current_robot_status 应该已在 navigation_move_to_spot 中被设为 FAULTED
|
||||||
// 如果不是超时而是其他导航错误,可能需要在这里显式设置为FAULTED
|
if(current_robot_status != FAULTED) { // 以防万一,如果导航模块未正确设置
|
||||||
if(current_robot_status != FAULTED) current_robot_status = FAULTED;
|
current_robot_status = FAULTED;
|
||||||
mqtt_publish_ack(current_active_task_id.c_str(), false, "Failed to reach target spot.", current_robot_status, robot_current_location.c_str());
|
// robot_current_location 可能还是旧的或 EN_ROUTE_TO_...
|
||||||
|
}
|
||||||
|
mqtt_publish_ack(current_active_task_id.c_str(), false, "Failed to reach target spot.", current_robot_status, robot_current_location.c_str(), move_cmd_ack_val);
|
||||||
}
|
}
|
||||||
target_spot_for_move = ""; // 清除移动目标
|
target_spot_for_move = ""; // 清除移动目标
|
||||||
current_active_task_id = ""; // 清除任务ID
|
// current_active_task_id = ""; // 在ACK发送后清除,确保ACK能使用正确的ID。如果ACK失败,任务ID可能还需要。
|
||||||
// 立即上报一次状态
|
// 考虑到ACK是即时发送,可以在这里清除
|
||||||
mqtt_publish_status_update(current_robot_status, robot_current_location.c_str(), robot_battery_level, nullptr,
|
if(current_active_task_id.length() > 0 && (strcmp(current_active_task_id.c_str(), "") != 0) ) {
|
||||||
nullptr, // target_spot (已到达或失败)
|
// 只有在 active_task_id 有效时才清除,避免重复清除或错误清除
|
||||||
(current_robot_status == CHARGING) ? robot_current_location.c_str() : nullptr, // current_spot
|
}
|
||||||
0, // duration (刚开始充电是0)
|
current_active_task_id = ""; // 移动任务完成后清除
|
||||||
(current_robot_status == FAULTED) ? 1 : 0, // errorCode
|
// 立即发布一次最终状态
|
||||||
(current_robot_status == FAULTED) ? "Navigation failed" : nullptr // errorMessage
|
mqtt_publish_status_update(current_robot_status, robot_current_location.c_str(), robot_battery_level, nullptr, nullptr, (current_robot_status == CHARGING || current_robot_status == COMPLETED || current_robot_status == FAULTED) ? robot_current_location.c_str() : nullptr, (current_robot_status == CHARGING) ? (millis()-robot_charge_start_time_ms)/1000 : 0, (current_robot_status == FAULTED) ? 1 : 0 /*示例错误码*/, (current_robot_status == FAULTED) ? "Navigation or other error" : nullptr, nullptr);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 定期任务 ---
|
// --- 定期任务 ---
|
||||||
|
|||||||
@@ -196,22 +196,35 @@ void mqtt_publish_ack(
|
|||||||
bool success,
|
bool success,
|
||||||
const char* message,
|
const char* message,
|
||||||
RobotStatus current_robot_status_at_ack,
|
RobotStatus current_robot_status_at_ack,
|
||||||
const char* ack_context_spot_id
|
const char* ack_context_spot_id,
|
||||||
|
const char* command_ack_chinese_value
|
||||||
) {
|
) {
|
||||||
if (!task_id) {
|
if (!task_id) {
|
||||||
Serial.println("无法发送ACK: taskId 为空");
|
Serial.println("无法发送ACK: taskId 为空");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StaticJsonDocument<256> doc;
|
if (!command_ack_chinese_value) {
|
||||||
|
Serial.println("无法发送ACK: command_ack_chinese_value 为空");
|
||||||
|
// 或者根据实际情况决定是否发送一个不带 command_ack 的ACK,但后端可能不认
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticJsonDocument<384> doc; // 适当调整JSON文档大小
|
||||||
doc["robotUid"] = DEVICE_UID;
|
doc["robotUid"] = DEVICE_UID;
|
||||||
doc["taskId"] = task_id;
|
|
||||||
doc["status"] = success ? "SUCCESS" : "FAILURE";
|
// 关键修改:符合后端期望的ACK格式
|
||||||
|
doc["command_ack"] = command_ack_chinese_value; // 使用传入的中文描述
|
||||||
|
doc["task_id"] = atol(task_id); // 将 task_id (字符串) 转换为 long (数字类型)
|
||||||
|
// 注意:如果 task_id 本身就应该是数字类型传来,则更好
|
||||||
|
// 但当前task_id通常作为字符串处理,这里做转换
|
||||||
|
// 后端期望 task_id 是数字
|
||||||
|
doc["success"] = success; // 布尔类型
|
||||||
|
|
||||||
if (message) doc["message"] = message;
|
if (message) doc["message"] = message;
|
||||||
doc["actualRobotStatus"] = robotStatusToString(current_robot_status_at_ack);
|
doc["actualRobotStatus"] = robotStatusToString(current_robot_status_at_ack);
|
||||||
if (ack_context_spot_id) {
|
if (ack_context_spot_id) {
|
||||||
doc["spotId"] = ack_context_spot_id; // 在ACK上下文中关联spotId
|
doc["spotId"] = ack_context_spot_id;
|
||||||
}
|
}
|
||||||
// 根据之前的讨论,ACK中不发送errorCode字段
|
|
||||||
|
|
||||||
String jsonBuffer;
|
String jsonBuffer;
|
||||||
serializeJson(doc, jsonBuffer);
|
serializeJson(doc, jsonBuffer);
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ void mqtt_publish_ack(
|
|||||||
bool success,
|
bool success,
|
||||||
const char* message,
|
const char* message,
|
||||||
RobotStatus current_robot_status_at_ack,
|
RobotStatus current_robot_status_at_ack,
|
||||||
const char* ack_context_spot_id // 例如,MOVE_TO_SPOT完成后的spotId
|
const char* ack_context_spot_id,
|
||||||
|
const char* command_ack_chinese_value // 新增参数
|
||||||
); // 发布ACK消息
|
); // 发布ACK消息
|
||||||
|
|
||||||
// 内部状态,供其他模块查询 (可选,或者通过回调传递)
|
// 内部状态,供其他模块查询 (可选,或者通过回调传递)
|
||||||
|
|||||||
Reference in New Issue
Block a user