| | |
| | | SpeedDial, |
| | | SpeedDialAction, |
| | | useTheme, |
| | | Snackbar, |
| | | } from '@mui/material'; |
| | | import { |
| | | MoreVert as MoreVertIcon, |
| | |
| | | Share as ShareIcon, |
| | | } from '@mui/icons-material'; |
| | | import Player from './player'; |
| | | import * as Tool from './tool'; |
| | | import { NotificationProvider, useNotification } from './Notification'; |
| | | |
| | | let player; |
| | | |
| | | const MapPage = () => { |
| | | const Map = () => { |
| | | const mapRef = useRef(); |
| | | const contentRef = React.useRef(); |
| | | const [app, setApp] = useState(null); |
| | | const [mapContainer, setMapContainer] = React.useState(null); |
| | | const notify = useNotification(); |
| | | |
| | | const [mode, setMode] = useState('monitoring'); |
| | | const [mode, setMode] = useState(MapModel.OBSERVER_MODEL); |
| | | |
| | | const theme = useTheme(); |
| | | const themeMode = theme.palette.mode; |
| | |
| | | player = new Player(mapRef.current, themeMode); |
| | | setApp(player.app); |
| | | setMapContainer(player.mapContainer); |
| | | Tool.setApp(player.app); |
| | | Tool.setMapContainer(player.mapContainer); |
| | | |
| | | } |
| | | initialize(); |
| | |
| | | }; |
| | | handleResize(); |
| | | window.addEventListener('resize', handleResize); |
| | | |
| | | notify('Welcome to Rcs', 'info'); |
| | | |
| | | return () => { |
| | | player.destroy(); |
| | |
| | | borderRadius: 1, |
| | | }} |
| | | > |
| | | <MenuItem value="monitoring">监控模式</MenuItem> |
| | | <MenuItem value="edit">编辑模式</MenuItem> |
| | | <MenuItem value="configuration">配置模式</MenuItem> |
| | | <MenuItem value={MapModel.OBSERVER_MODEL}>监控模式</MenuItem> |
| | | <MenuItem value={MapModel.MOVABLE_MODEL}>编辑模式</MenuItem> |
| | | <MenuItem value={MapModel.SETTINGS_MODEL}>配置模式</MenuItem> |
| | | </Select> |
| | | <Button |
| | | variant="contained" |
| | |
| | | ); |
| | | } |
| | | |
| | | const MapPage = () => { |
| | | return ( |
| | | <NotificationProvider> |
| | | <Map /> |
| | | </NotificationProvider> |
| | | ) |
| | | } |
| | | |
| | | export const MapModel = Object.freeze({ |
| | | OBSERVER_MODEL: "1", |
| | | MOVABLE_MODEL: "2", |
| | | SETTINGS_MODEL: "3", |
| | | }) |
| | | |
| | | export default MapPage; |
New file |
| | |
| | | import React, { createContext, useContext, useState, useCallback } from 'react'; |
| | | import { Snackbar, Alert, Portal, Slide } from '@mui/material'; |
| | | |
| | | const NotificationContext = createContext(); |
| | | |
| | | const SlideTransition = (props) => { |
| | | return <Slide {...props} direction="up" />; |
| | | } |
| | | |
| | | export const NotificationProvider = ({ children }) => { |
| | | const [notification, setNotification] = useState({ |
| | | open: false, |
| | | message: '', |
| | | severity: 'info', |
| | | }); |
| | | |
| | | const showNotification = useCallback((message, severity = 'info') => { |
| | | setNotification({ open: true, message, severity }); |
| | | }, []); |
| | | |
| | | const handleClose = () => { |
| | | setNotification({ ...notification, open: false }); |
| | | }; |
| | | |
| | | return ( |
| | | <NotificationContext.Provider value={showNotification}> |
| | | {children} |
| | | <Portal> |
| | | <Snackbar |
| | | open={notification.open} |
| | | autoHideDuration={4000} |
| | | onClose={handleClose} |
| | | TransitionComponent={SlideTransition} |
| | | anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} |
| | | sx={{ |
| | | // mt: 12, |
| | | zIndex: 2000, |
| | | }} |
| | | > |
| | | <Alert onClose={handleClose} severity={notification.severity} sx={{ width: '300px', zIndex: 2000 }}> |
| | | {notification.message} |
| | | </Alert> |
| | | </Snackbar> |
| | | </Portal> |
| | | </NotificationContext.Provider> |
| | | ); |
| | | }; |
| | | |
| | | export const useNotification = () => { |
| | | return useContext(NotificationContext); |
| | | }; |
New file |
| | |
| | | import * as PIXI from 'pixi.js'; |
| | | import * as TWEEDLE from 'tweedle.js'; |
| | | |
| | | let app, mapContainer; |
| | | |
| | | export function setApp(param) { |
| | | app = param; |
| | | } |
| | | |
| | | export function setMapContainer(param) { |
| | | mapContainer = param; |
| | | } |
| | | |
| | | export function getApp() { |
| | | return app; |
| | | } |
| | | |
| | | export function getMapContainer() { |
| | | return mapContainer; |
| | | } |
New file |
| | |
| | | import { WEBSOCKET_BASE_URL } from '@/config/setting'; |
| | | |
| | | export default class WebSocketClient { |
| | | |
| | | constructor(path) { |
| | | this.url = WEBSOCKET_BASE_URL + path; |
| | | this.webSocket = null; |
| | | this.heartbeatInterval = null; // Store the interval ID |
| | | this.heartbeatFrequency = 30000; // Heartbeat every 30 seconds |
| | | } |
| | | |
| | | connect() { |
| | | if (!this.url) { |
| | | console.error('WebSocketClient: Cannot connect without url.'); |
| | | return; |
| | | } |
| | | |
| | | this.webSocket = new WebSocket(this.url); |
| | | |
| | | this.webSocket.onopen = (event) => { |
| | | console.log('websocket connection opened.'); |
| | | // Start the heartbeat |
| | | this.startHeartbeat(); |
| | | }; |
| | | |
| | | this.webSocket.onmessage = (event) => { |
| | | // console.log('websocket message received:', event.data); |
| | | this.onMessage(event.data); |
| | | }; |
| | | |
| | | this.webSocket.onerror = (event) => { |
| | | console.error('websocket error observed:', event); |
| | | }; |
| | | |
| | | this.webSocket.onclose = (event) => { |
| | | console.log('websocket connection closed!'); |
| | | // Clear the heartbeat |
| | | this.stopHeartbeat(); |
| | | this.reconnect(); |
| | | }; |
| | | } |
| | | |
| | | sendMessage(message) { |
| | | if (this.webSocket && this.webSocket.readyState === WebSocket.OPEN) { |
| | | this.webSocket.send(message); |
| | | } else { |
| | | console.error('WebSocketClient: Cannot send message, WebSocket connection is not open.'); |
| | | } |
| | | } |
| | | |
| | | // Override |
| | | onMessage(data) { |
| | | } |
| | | |
| | | close() { |
| | | if (this.webSocket && this.webSocket.readyState === WebSocket.OPEN) { |
| | | this.webSocket.close(); |
| | | } |
| | | } |
| | | |
| | | reconnect() { |
| | | setTimeout(() => { |
| | | console.log('WebSocketClient: Attempting to reconnect...'); |
| | | this.connect(); |
| | | }, 3000); |
| | | } |
| | | |
| | | startHeartbeat() { |
| | | if (this.heartbeatInterval) { |
| | | clearInterval(this.heartbeatInterval); |
| | | } |
| | | this.heartbeatInterval = setInterval(() => { |
| | | this.sendMessage('1'); |
| | | }, this.heartbeatFrequency); |
| | | } |
| | | |
| | | stopHeartbeat() { |
| | | if (this.heartbeatInterval) { |
| | | clearInterval(this.heartbeatInterval); |
| | | this.heartbeatInterval = null; |
| | | } |
| | | } |
| | | } |