refactor: 移除任务表单中的不必要的选择器,简化任务上下文,更新配置文件以支持新的请求头
This commit is contained in:
parent
f9564796ab
commit
37492f0890
40
config.json
40
config.json
@ -1,42 +1,10 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"locations": [
|
||||
{ "label": "AP-1", "value": "AP-1" },
|
||||
{ "label": "AP-2", "value": "AP-2" },
|
||||
{ "label": "AP-3", "value": "AP-3" },
|
||||
{ "label": "AP-4", "value": "AP-4" },
|
||||
{ "label": "AP-5", "value": "AP-5" },
|
||||
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
|
||||
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
|
||||
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
|
||||
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
|
||||
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
|
||||
],
|
||||
"locationsBays": [
|
||||
{ "label": "AS2_2_001", "value": "AS2_2_001" },
|
||||
{ "label": "AS2_2_002", "value": "AS2_2_002" },
|
||||
{ "label": "AS2_2_003", "value": "AS2_2_003" },
|
||||
{ "label": "AS2_2_004", "value": "AS2_2_004" },
|
||||
{ "label": "AS2_2_005", "value": "AS2_2_005" }
|
||||
],
|
||||
"payloads": [
|
||||
{ "label": "满料架-A1", "value": "满料架-A1" },
|
||||
{ "label": "满料架-A2", "value": "满料架-A2" },
|
||||
{ "label": "空料架-B1", "value": "空料架-B1" },
|
||||
{ "label": "空料架-B2", "value": "空料架-B2" },
|
||||
{ "label": "空车", "value": "空车" },
|
||||
{ "label": "钢材-Q235", "value": "钢材-Q235" },
|
||||
{ "label": "铝合金-6061", "value": "铝合金-6061" }
|
||||
],
|
||||
"robotActions": [
|
||||
{ "label": "运输", "value": "TRANSPORT" },
|
||||
{ "label": "取货", "value": "PICKUP" },
|
||||
{ "label": "卸货", "value": "DROPOFF" },
|
||||
{ "label": "等待", "value": "WAIT" },
|
||||
{ "label": "充电", "value": "CHARGE" },
|
||||
{ "label": "清洁", "value": "CLEAN" }
|
||||
],
|
||||
"serverUrl": "http://192.168.189.206:8000",
|
||||
"headers": {
|
||||
"x-access-token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTIzMzQ5MjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.dKvKIJOU5FxFeFNI3ucJTPLhHmUFZkv8HA2S_VO6klY",
|
||||
"x-tenant-id": 1000
|
||||
},
|
||||
"apiEndpoints": {
|
||||
"getTasks": "/api/vwed-task/list",
|
||||
"getTaskDetail": "/api/vwed-task/{taskId}",
|
||||
|
@ -1,9 +1,6 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { Input, Slider } from '@rneui/themed';
|
||||
// @ts-ignore
|
||||
import { Picker } from '@react-native-picker/picker';
|
||||
import { useTasks } from '../context/TasksContext';
|
||||
import { Task, RobotAction, InputParam } from '../types/task';
|
||||
|
||||
interface TaskFormProps {
|
||||
@ -12,8 +9,6 @@ interface TaskFormProps {
|
||||
}
|
||||
|
||||
const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
|
||||
const { locations, payloads, robotActions, locationsBays } = useTasks();
|
||||
|
||||
const handleParamChange = (
|
||||
field: string,
|
||||
value: string | number | RobotAction,
|
||||
@ -58,92 +53,6 @@ const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
case 'location':
|
||||
case 'location_bay':
|
||||
const locationData =
|
||||
param.type.toLowerCase() === 'location' ? locations : locationsBays;
|
||||
return (
|
||||
<View key={param.name} style={styles.inputGroup}>
|
||||
{label}
|
||||
<View style={styles.pickerContainer}>
|
||||
<Picker
|
||||
selectedValue={value as string}
|
||||
onValueChange={(itemValue: any) =>
|
||||
handleParamChange(param.name, itemValue)
|
||||
}
|
||||
style={styles.picker}
|
||||
dropdownIconColor="#00ff00"
|
||||
>
|
||||
{locationData.map(loc => (
|
||||
<Picker.Item
|
||||
key={loc.value}
|
||||
label={loc.label}
|
||||
value={loc.value}
|
||||
color="#000000"
|
||||
/>
|
||||
))}
|
||||
</Picker>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
case 'payload':
|
||||
return (
|
||||
<View key={param.name} style={styles.inputGroup}>
|
||||
{label}
|
||||
<View style={styles.pickerContainer}>
|
||||
<Picker
|
||||
selectedValue={value as string}
|
||||
onValueChange={(itemValue: any) =>
|
||||
handleParamChange(param.name, itemValue)
|
||||
}
|
||||
style={styles.picker}
|
||||
dropdownIconColor="#00ff00"
|
||||
>
|
||||
{payloads.map(p => (
|
||||
<Picker.Item
|
||||
key={p.value}
|
||||
label={p.label}
|
||||
value={p.value}
|
||||
color="#000000"
|
||||
/>
|
||||
))}
|
||||
</Picker>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
case 'robotaction':
|
||||
return (
|
||||
<View key={param.name} style={styles.inputGroup}>
|
||||
{label}
|
||||
<View style={styles.pickerContainer}>
|
||||
<Picker
|
||||
selectedValue={(value as RobotAction)?.name}
|
||||
onValueChange={(itemValue: any) => {
|
||||
const selectedAction = robotActions.find(
|
||||
a => a.value === itemValue,
|
||||
);
|
||||
if (selectedAction) {
|
||||
handleParamChange(param.name, {
|
||||
name: selectedAction.label,
|
||||
actionId: selectedAction.actionId,
|
||||
});
|
||||
}
|
||||
}}
|
||||
style={styles.picker}
|
||||
dropdownIconColor="#00ff00"
|
||||
>
|
||||
{robotActions.map(action => (
|
||||
<Picker.Item
|
||||
key={action.actionId}
|
||||
label={action.label}
|
||||
value={action.value}
|
||||
color="#000000"
|
||||
/>
|
||||
))}
|
||||
</Picker>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
case 'number':
|
||||
return (
|
||||
<View key={param.name} style={styles.inputGroup}>
|
||||
|
@ -8,24 +8,18 @@ import React, {
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import { Task } from '../types/task';
|
||||
import { AppConfig } from '../types/config';
|
||||
import { getConfig } from '../services/configService';
|
||||
import {
|
||||
AppConfig,
|
||||
LocationOption,
|
||||
PayloadOption,
|
||||
RobotActionOption,
|
||||
} from '../types/config';
|
||||
import { getConfig, executeTask } from '../services/configService';
|
||||
getTasks as getTasksService,
|
||||
getTaskDetail as getTaskDetailService,
|
||||
} from '../services/taskService';
|
||||
|
||||
interface TasksContextData {
|
||||
tasks: Task[];
|
||||
locations: LocationOption[];
|
||||
locationsBays: LocationOption[];
|
||||
payloads: PayloadOption[];
|
||||
robotActions: RobotActionOption[];
|
||||
serverUrl: string | null;
|
||||
getTaskById: (id: string) => Task | undefined;
|
||||
updateTask: (updatedTask: Task) => void;
|
||||
runTask: (id: string) => void;
|
||||
refreshConfig: () => Promise<void>;
|
||||
isConfigLoaded: boolean;
|
||||
fetchTaskDetail: (taskId: string) => Promise<void>;
|
||||
@ -37,59 +31,19 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [tasks, setTasks] = useState<Task[]>([]);
|
||||
const [locations, setLocations] = useState<LocationOption[]>([]);
|
||||
const [locationsBays, setLocationsBays] = useState<LocationOption[]>([]);
|
||||
const [payloads, setPayloads] = useState<PayloadOption[]>([]);
|
||||
const [robotActions, setRobotActions] = useState<RobotActionOption[]>([]);
|
||||
const [isConfigLoaded, setIsConfigLoaded] = useState(false);
|
||||
const [serverUrl, setServerUrl] = useState<string | null>(null);
|
||||
|
||||
const fetchTasks = useCallback(async (baseUrl: string, endpoint: string) => {
|
||||
const fetchTasks = useCallback(async () => {
|
||||
if (!isConfigLoaded) return;
|
||||
try {
|
||||
if (baseUrl && endpoint) {
|
||||
const fetchUrl = `${baseUrl}${endpoint}?pageNum=1&pageSize=100`;
|
||||
console.log('Fetching tasks from:', fetchUrl);
|
||||
const response = await fetch(fetchUrl);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const responseData = await response.json();
|
||||
console.log('responseData', responseData);
|
||||
|
||||
if (responseData && responseData.code === 200) {
|
||||
const fetchedTasks = responseData.data.list.map((task: any) => {
|
||||
let detail = task.detail;
|
||||
if (detail && typeof detail === 'string') {
|
||||
try {
|
||||
detail = JSON.parse(detail);
|
||||
} catch (e) {
|
||||
console.error('解析任务详情失败 (list):', e);
|
||||
detail = null;
|
||||
}
|
||||
}
|
||||
return {
|
||||
...task,
|
||||
name: task.label,
|
||||
parameters: task.parameters || {},
|
||||
detail: detail,
|
||||
};
|
||||
});
|
||||
setTasks(fetchedTasks);
|
||||
} else {
|
||||
console.error('获取任务列表失败: responseData.code is not 200');
|
||||
setTasks([]);
|
||||
}
|
||||
}
|
||||
const fetchedTasks = await getTasksService({ pageNum: 1, pageSize: 100 });
|
||||
setTasks(fetchedTasks);
|
||||
} catch (error) {
|
||||
console.error('获取任务列表失败:', error);
|
||||
if (error instanceof Error) {
|
||||
console.error('Error message:', error.message);
|
||||
}
|
||||
console.error('获取任务列表失败 (context):', error);
|
||||
setTasks([]);
|
||||
}
|
||||
}, []); // Empty dependency array as it has no external dependencies from component scope
|
||||
}, [isConfigLoaded]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadApp = async () => {
|
||||
@ -98,9 +52,6 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
||||
if (config) {
|
||||
applyConfig(config);
|
||||
setIsConfigLoaded(true);
|
||||
if (config.serverUrl && config.apiEndpoints) {
|
||||
await fetchTasks(config.serverUrl, config.apiEndpoints.getTasks);
|
||||
}
|
||||
} else {
|
||||
console.log('没有找到配置文件,使用空数据');
|
||||
setIsConfigLoaded(false);
|
||||
@ -112,37 +63,26 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
||||
};
|
||||
|
||||
loadApp();
|
||||
}, [fetchTasks]);
|
||||
}, []);
|
||||
|
||||
const [apiEndpoints, setApiEndpoints] = useState<{
|
||||
getTasks: string;
|
||||
getTaskDetail: string;
|
||||
runTask: string;
|
||||
} | null>(null);
|
||||
useEffect(() => {
|
||||
if (isConfigLoaded) {
|
||||
fetchTasks();
|
||||
}
|
||||
}, [isConfigLoaded, fetchTasks]);
|
||||
|
||||
const applyConfig = (config: AppConfig) => {
|
||||
setLocations(config.locations || []);
|
||||
setLocationsBays(config.locationsBays || []);
|
||||
setPayloads(config.payloads || []);
|
||||
setRobotActions(config.robotActions || []);
|
||||
if (config.serverUrl) {
|
||||
setServerUrl(config.serverUrl);
|
||||
}
|
||||
if (config.apiEndpoints) {
|
||||
setApiEndpoints(config.apiEndpoints);
|
||||
}
|
||||
};
|
||||
|
||||
const refreshConfig = useCallback(async () => {
|
||||
try {
|
||||
// await clearCachedConfig(); // 通常刷新不需要清除缓存
|
||||
const config = await getConfig();
|
||||
if (config) {
|
||||
applyConfig(config);
|
||||
setIsConfigLoaded(true);
|
||||
if (config.serverUrl && config.apiEndpoints) {
|
||||
await fetchTasks(config.serverUrl, config.apiEndpoints.getTasks);
|
||||
}
|
||||
setIsConfigLoaded(true); // 确保配置加载后再次触发任务获取
|
||||
} else {
|
||||
console.log('刷新配置时没有找到配置文件');
|
||||
setIsConfigLoaded(false);
|
||||
@ -151,63 +91,28 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
||||
console.error('刷新配置失败:', error);
|
||||
setIsConfigLoaded(false);
|
||||
}
|
||||
}, [fetchTasks]);
|
||||
}, []);
|
||||
|
||||
const getTaskById = useCallback(
|
||||
(id: string) => {
|
||||
const task = tasks.find(t => t.id === id);
|
||||
if (task && !task.detail) {
|
||||
// fetchTaskDetail(id); // 详情在编辑页面获取
|
||||
}
|
||||
return task;
|
||||
},
|
||||
[tasks],
|
||||
);
|
||||
|
||||
const fetchTaskDetail = useCallback(
|
||||
async (taskId: string) => {
|
||||
try {
|
||||
if (serverUrl && apiEndpoints) {
|
||||
const endpoint = apiEndpoints.getTaskDetail.replace(
|
||||
'{taskId}',
|
||||
taskId,
|
||||
);
|
||||
const url = `${serverUrl}${endpoint}`;
|
||||
console.log('Fetching task detail from:', url);
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const responseData = await response.json();
|
||||
console.log('task detail responseData', responseData);
|
||||
|
||||
if (responseData && responseData.code === 200) {
|
||||
let taskDetail = responseData.data.detail;
|
||||
if (taskDetail && typeof taskDetail === 'string') {
|
||||
try {
|
||||
taskDetail = JSON.parse(taskDetail);
|
||||
} catch (e) {
|
||||
console.error('解析任务详情失败 (detail):', e);
|
||||
taskDetail = null;
|
||||
}
|
||||
}
|
||||
setTasks(prevTasks =>
|
||||
prevTasks.map(task =>
|
||||
task.id === taskId ? { ...task, detail: taskDetail } : task,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
console.error('获取任务详情失败: responseData.code is not 200');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取任务详情失败:', error);
|
||||
}
|
||||
},
|
||||
[serverUrl, apiEndpoints],
|
||||
);
|
||||
const fetchTaskDetail = useCallback(async (taskId: string) => {
|
||||
try {
|
||||
const taskDetail = await getTaskDetailService(taskId);
|
||||
setTasks(prevTasks =>
|
||||
prevTasks.map(task =>
|
||||
task.id === taskId ? { ...task, detail: taskDetail.detail } : task,
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('获取任务详情失败 (context):', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateTask = useCallback((updatedTask: Task) => {
|
||||
setTasks(prevTasks =>
|
||||
@ -215,72 +120,21 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
||||
);
|
||||
}, []);
|
||||
|
||||
const runTask = useCallback(
|
||||
async (id: string) => {
|
||||
const task = tasks.find(t => t.id === id);
|
||||
if (!task || !serverUrl || !apiEndpoints) return;
|
||||
|
||||
setTasks(prevTasks =>
|
||||
prevTasks.map(t => (t.id === id ? { ...t, status: 1 } : t)),
|
||||
);
|
||||
|
||||
try {
|
||||
const parameters = Object.entries(task.parameters).reduce(
|
||||
(acc, [key, param]) => {
|
||||
if (param) {
|
||||
acc[key] = param.value;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: any },
|
||||
);
|
||||
|
||||
const endpoint = apiEndpoints.runTask.replace('{taskId}', task.id);
|
||||
await executeTask(serverUrl, endpoint, {
|
||||
name: task.name,
|
||||
parameters: parameters,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
setTasks(prevTasks =>
|
||||
prevTasks.map(t => (t.id === id ? { ...t, status: 2 } : t)),
|
||||
);
|
||||
}, 5000);
|
||||
} catch (error) {
|
||||
console.error('任务执行失败:', error);
|
||||
setTasks(prevTasks =>
|
||||
prevTasks.map(t => (t.id === id ? { ...t, status: 3 } : t)),
|
||||
);
|
||||
}
|
||||
},
|
||||
[tasks, serverUrl, apiEndpoints],
|
||||
);
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
tasks,
|
||||
locations,
|
||||
locationsBays,
|
||||
payloads,
|
||||
robotActions,
|
||||
serverUrl,
|
||||
getTaskById,
|
||||
updateTask,
|
||||
runTask,
|
||||
refreshConfig,
|
||||
isConfigLoaded,
|
||||
fetchTaskDetail,
|
||||
}),
|
||||
[
|
||||
tasks,
|
||||
locations,
|
||||
locationsBays,
|
||||
payloads,
|
||||
robotActions,
|
||||
serverUrl,
|
||||
getTaskById,
|
||||
updateTask,
|
||||
runTask,
|
||||
refreshConfig,
|
||||
isConfigLoaded,
|
||||
fetchTaskDetail,
|
||||
|
@ -17,6 +17,8 @@ type TaskListNavigationProp = StackNavigationProp<
|
||||
|
||||
export default function TaskListScreen() {
|
||||
const { tasks } = useTasks();
|
||||
console.log('tasks', tasks);
|
||||
|
||||
const navigation = useNavigation<TaskListNavigationProp>();
|
||||
|
||||
const handlePressTask = (id: string) => {
|
||||
|
@ -1,8 +1,20 @@
|
||||
import { getSettings, getConfig } from './configService';
|
||||
import { RunTaskRequest, RunTaskApiResponse, Task } from '../types/task';
|
||||
|
||||
// 获取通用请求头
|
||||
const getHeaders = async () => {
|
||||
const config = await getConfig();
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
...config?.headers,
|
||||
};
|
||||
};
|
||||
|
||||
// 获取任务列表
|
||||
export const getTasks = async (): Promise<Task[]> => {
|
||||
export const getTasks = async (params: {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
}): Promise<Task[]> => {
|
||||
try {
|
||||
const config = await getConfig();
|
||||
const endpoint = config?.apiEndpoints?.getTasks;
|
||||
@ -18,10 +30,14 @@ export const getTasks = async (): Promise<Task[]> => {
|
||||
throw new Error('服务器地址未配置');
|
||||
}
|
||||
|
||||
const url = `${serverUrl.replace(/\/$/, '')}${endpoint}`;
|
||||
const urlObj = new URL(`${serverUrl.replace(/\/$/, '')}${endpoint}`);
|
||||
urlObj.searchParams.append('pageNum', String(params.pageNum));
|
||||
urlObj.searchParams.append('pageSize', String(params.pageSize));
|
||||
const url = urlObj.toString();
|
||||
console.log('获取任务列表:', url);
|
||||
|
||||
const response = await fetch(url);
|
||||
const headers = await getHeaders();
|
||||
const response = await fetch(url, { headers });
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
@ -31,7 +47,15 @@ export const getTasks = async (): Promise<Task[]> => {
|
||||
throw new Error(`API 错误: ${result.message}`);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
// 兼容两种可能的数据结构: result.data.list or result.data
|
||||
if (result.data && Array.isArray(result.data.list)) {
|
||||
return result.data.list;
|
||||
}
|
||||
if (Array.isArray(result.data)) {
|
||||
return result.data;
|
||||
}
|
||||
console.warn('任务列表数据格式不正确', result.data);
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('获取任务列表失败:', error);
|
||||
throw error;
|
||||
@ -59,7 +83,8 @@ export const getTaskDetail = async (taskId: string): Promise<Task> => {
|
||||
const url = `${serverUrl.replace(/\/$/, '')}${endpoint}`;
|
||||
console.log('获取任务详情:', url);
|
||||
|
||||
const response = await fetch(url);
|
||||
const headers = await getHeaders();
|
||||
const response = await fetch(url, { headers });
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
@ -98,11 +123,10 @@ export const runTask = async (
|
||||
const url = `${serverUrl.replace(/\/$/, '')}${endpoint}`;
|
||||
console.log('运行任务请求:', url, JSON.stringify(taskData, null, 2));
|
||||
|
||||
const headers = await getHeaders();
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers: headers,
|
||||
body: JSON.stringify(taskData),
|
||||
});
|
||||
|
||||
|
@ -1,35 +1,15 @@
|
||||
// 配置文件中的位置选项
|
||||
export interface LocationOption {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
// 配置文件中的载荷选项
|
||||
export interface PayloadOption {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
// 配置文件中的机器人动作选项
|
||||
export interface RobotActionOption {
|
||||
label: string;
|
||||
value: string; // 动作的名称,作为 Picker 的 value
|
||||
actionId: string;
|
||||
}
|
||||
|
||||
// 完整的配置文件结构
|
||||
export interface AppConfig {
|
||||
version: string;
|
||||
locations: LocationOption[];
|
||||
locationsBays: LocationOption[];
|
||||
payloads: PayloadOption[];
|
||||
robotActions: RobotActionOption[];
|
||||
serverUrl?: string; // 服务器地址
|
||||
apiEndpoints?: {
|
||||
getTasks: string;
|
||||
getTaskDetail: string;
|
||||
runTask: string;
|
||||
};
|
||||
headers?: {
|
||||
[key: string]: string | number;
|
||||
};
|
||||
}
|
||||
|
||||
// 设置存储接口
|
||||
|
Loading…
x
Reference in New Issue
Block a user