import React, { useEffect, useState } from 'react';
import { AppBar, Toolbar, Typography, Box, Button, ThemeProvider, IconButton, Snackbar, Alert, Tooltip, FormControl, Select, MenuItem, InputLabel, Chip } from '@mui/material';
import MavlinkSession from './mavlink_session';
import dronecan from './dronecan';
import theme from './theme';
import NodeList from './NodeList';
import NodeLogs from './NodeLogs';
import NodeProperties from './NodeProperties';
import NodeParam from './NodeParam';
import About from './About';
import ToolsMenu from './ToolsMenu';
import PanelsMenu from './PanelsMenu';
import ConnectionSettingsModal from './ConnectionSettingsModal';
import DronecanLogo from './image/dronecan_logo.png';
import FileServer from './FileServer';
import './css/index.css';
import ConnectionIndicators from './ConnectionIndicators';
import DnsIcon from '@mui/icons-material/Dns';
import LanIcon from '@mui/icons-material/Lan';
import LanguageIcon from '@mui/icons-material/Language';
import CompactSidebar from './CompactSidebar';
import DynamicNodeIdServer from './services/DynamicNodeIdServer';
import { LanguageProvider, useTranslation } from './i18n/LanguageContext';
window.mavlinkSession = new MavlinkSession();
window.localNode = new dronecan.Node({name: "com.vimdrones.web_gui"});
localNode.on('sendFrame', (messageId, data, len) => {
mavlinkSession.sendCanFrame(localNode.bus, messageId, data, len);
});
localNode.on('uavcan.protocol.file.Read.Request', (transfer) => {
FileServer.handleReadRequest(transfer, localNode);
});
const App = () => {
return (
);
};
const AppContent = () => {
const { t, language, setLanguage } = useTranslation();
const [nodes, setNodes] = useState({});
const [nodesUpdateTimestamp, setNodesUpdateTimestamp] = useState(0);
const [isConnected, setIsConnected] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const [selectedNodeId, setSelectedNodeId] = useState(null);
const [multiNodeEditorEnable, setMultiNodeEditorEnable] = useState(false);
const [subWindowRef, setSubWindowRef] = useState({});
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState('');
const [snackbarSeverity, setSnackbarSeverity] = useState('info');
const [selectedBus, setSelectedBus] = useState(0);
const [dnaServerActive, setDnaServerActive] = useState(false);
const openWindow = (windowTitle, windowPath, windowSize) => {
if (subWindowRef[windowPath] && !subWindowRef[windowPath].closed) {
subWindowRef[windowPath].focus();
return;
}
const newWindow = window.open(windowPath, windowTitle, windowSize);
if (newWindow) {
setSubWindowRef((prev) => ({ ...prev, [windowPath]: newWindow }));
newWindow.addEventListener('beforeunload', () => {
setSubWindowRef((prev) => {
const next = { ...prev };
delete next[windowPath];
return next;
});
});
} else {
console.error(`Main: Failed to open ${windowTitle}`);
}
};
const showMessage = (message, severity = 'info') => {
setSnackbarMessage(message);
setSnackbarSeverity(severity);
setSnackbarOpen(true);
};
const handleConnectionStatusChange = (isConnected) => {
setIsConnected(isConnected);
showMessage(
isConnected ? t('app.connected') : t('app.disconnected'),
isConnected ? 'success' : 'info'
);
};
const handleBusChange = (event) => {
const newBus = event.target.value;
if (newBus === selectedBus) return;
setSelectedBus(newBus);
if (window.localNode) {
window.localNode.changeBus(newBus);
showMessage(t('app.bus_switched', { bus: newBus }), 'info');
}
};
const handleNodeList = (nodeList) => {
setNodes(nodeList);
setNodesUpdateTimestamp(Date.now());
};
useEffect(() => {
localNode.on('nodeList', handleNodeList);
return () => {
localNode.off('nodeList');
};
}, []);
const handleOpenModal = () => {
setModalOpen(true);
};
const handleCloseModal = () => {
setModalOpen(false);
};
const handleToggleDnaServer = () => {
if (!window.dnaServer) {
window.dnaServer = new DynamicNodeIdServer(window.localNode);
}
if (window.dnaServer.getStatus().isActive) {
window.dnaServer.stop();
setDnaServerActive(false);
showMessage(t('app.dna_stopped'), 'info');
} else {
const success = window.dnaServer.start(1, 125);
setDnaServerActive(success);
showMessage(success ? t('app.dna_started') : t('app.dna_failed'), success ? 'success' : 'error');
}
};
return (
<>
{t('app.title')}
:
}
onClick={handleToggleDnaServer}
sx={dnaServerActive ? {
borderColor: 'success.main',
'&:hover': {
backgroundColor: 'rgba(76, 175, 80, 0.08)',
borderColor: 'success.dark'
}
} : {}}
>
{t('app.dna')}
setLanguage(language === 'en' ? 'zh' : 'en')}
sx={{ mr: 0.5 }}
>
}
onClick={handleOpenModal}
>
{t('app.adapter')}
{selectedNodeId && (
)}
{selectedNodeId === null && (
)}
setSnackbarOpen(false)}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
>
setSnackbarOpen(false)} severity={snackbarSeverity}>
{snackbarMessage}
>
);
};
export default App;