FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
ScenarioIO.tsx
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2-0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
3
4import FileDownloadIcon from '@mui/icons-material/FileDownload';
5import FileUploadIcon from '@mui/icons-material/FileUpload';
6import { IconButton, Tooltip } from '@mui/material';
7import { invoke } from '@tauri-apps/api/core';
8import { dirname, join } from '@tauri-apps/api/path';
9import { open, save } from '@tauri-apps/plugin-dialog';
10import { writeTextFile } from '@tauri-apps/plugin-fs';
11import { useState } from 'react';
12import { useScenarioStore } from '@/stores/scenarioStore';
13import ConfirmDialog from './ConfirmDialog';
14
15export default function ScenarioIO() {
16 const loadScenario = useScenarioStore((state) => state.loadScenario);
17 const isDirty = useScenarioStore((state) => state.isDirty);
18 const resetScenario = useScenarioStore((state) => state.resetScenario);
19 const showError = useScenarioStore((state) => state.showError);
20 const showWarning = useScenarioStore((state) => state.showWarning);
21 const setScenarioFilePath = useScenarioStore(
22 (state) => state.setScenarioFilePath
23 );
24 const scenarioFilePath = useScenarioStore(
25 (state) => state.scenarioFilePath
26 );
27
28 const [isConfirmOpen, setConfirmOpen] = useState(false);
29
30 const handleExport = async () => {
31 try {
32 const state = useScenarioStore.getState();
33 await state.syncBackend();
34
35 const xmlContent = await invoke<string>('get_scenario_as_xml');
36
37 // 1. Determine default directory
38 let defaultDir = '.';
39 if (scenarioFilePath) {
40 defaultDir = await dirname(scenarioFilePath);
41 }
42
43 // 2. Determine suggested filename
44 const simName =
45 state.globalParameters.simulation_name || 'scenario';
46 const suggestedFileName = `${simName.replace(/[^a-z0-9]/gi, '_')}.fersxml`;
47
48 // 3. Combine for dialog
49 const defaultPath = await join(defaultDir, suggestedFileName);
50
51 const filePath = await save({
52 title: 'Export Scenario',
53 defaultPath: defaultPath,
54 filters: [
55 {
56 name: 'FERS XML Scenario',
57 extensions: ['fersxml', 'xml'],
58 },
59 ],
60 });
61
62 if (filePath) {
63 await writeTextFile(filePath, xmlContent);
64 setScenarioFilePath(filePath);
65 console.log('Scenario exported successfully to:', filePath);
66 }
67 } catch (error) {
68 const errorMessage =
69 error instanceof Error ? error.message : String(error);
70 console.error('Failed to export scenario:', errorMessage);
71 showError(`Export failed: ${errorMessage}`);
72 }
73 };
74
75 const performImport = async () => {
76 try {
77 const selectedPath = await open({
78 title: 'Import Scenario',
79 multiple: false,
80 filters: [
81 {
82 name: 'FERS XML Scenario',
83 extensions: ['xml', 'fersxml'],
84 },
85 ],
86 });
87
88 if (typeof selectedPath === 'string') {
89 // Load the XML file into the C++ core
90 const warnings = await invoke<string[]>(
91 'load_scenario_from_xml_file',
92 {
93 filepath: selectedPath,
94 }
95 );
96
97 // Fetch the new state as JSON from the C++ core
98 const jsonState = await invoke<string>('get_scenario_as_json');
99 const scenarioData = JSON.parse(jsonState);
100
101 // Update the UI's Zustand store with the new state after resetting the current state
102 resetScenario();
103 loadScenario(scenarioData);
104 setScenarioFilePath(selectedPath);
105 warnings.forEach((warning) => showWarning(warning));
106
107 console.log(
108 'Scenario imported and synchronized successfully from:',
109 selectedPath
110 );
111 }
112 } catch (error) {
113 const errorMessage =
114 error instanceof Error ? error.message : String(error);
115 console.error('Failed to import scenario:', errorMessage);
116 showError(`Import failed: ${errorMessage}`);
117 }
118 };
119
120 const handleImport = () => {
121 if (isDirty) {
122 setConfirmOpen(true);
123 } else {
124 void performImport();
125 }
126 };
127
128 const handleConfirmImport = () => {
129 setConfirmOpen(false);
130 void performImport();
131 };
132
133 const handleCancelImport = () => {
134 setConfirmOpen(false);
135 };
136
137 return (
138 <>
139 <Tooltip title="Import Scenario (XML)">
140 <IconButton size="small" onClick={handleImport}>
141 <FileUploadIcon fontSize="inherit" />
142 </IconButton>
143 </Tooltip>
144 <Tooltip title="Export Scenario (XML)">
145 <IconButton size="small" onClick={handleExport}>
146 <FileDownloadIcon fontSize="inherit" />
147 </IconButton>
148 </Tooltip>
149 <ConfirmDialog
150 open={isConfirmOpen}
151 onConfirm={handleConfirmImport}
152 onCancel={handleCancelImport}
153 title="Overwrite Current Scenario?"
154 message="Importing a new scenario will discard all unsaved changes. Are you sure you want to proceed?"
155 />
156 </>
157 );
158}