React Leaflet 应用程序未收到 React signalr 消息 - 旧版 signalr 版本
7
0
我的 react、leaflet、singalr(旧版)应用程序有两个 signalr 集线器。一个用于跟踪,一个用于更改。在跟踪中,我能够接收跟踪集线器消息并查看全局变量...
我的 react、leaflet、singalr(旧版)应用程序有两个 signalr 集线器。一个用于跟踪,一个用于更改。在跟踪中,我能够接收跟踪中心消息并看到上下文中的全局变量实时更改 ui。我正尝试在我的更改中心做类似的事情。当有消息进来时,它会从数据库中获取数据,然后更新上下文中的全局变量,因此 ui 应该更新。但是,我在代码的其他部分做了一些更改,这些更改对于其他功能是必要的,但这样做时,该特定集线器不再接收消息。现在我主要关心的只是像以前一样接收消息。我稍后会担心 ui。我将包括与 signalr 处理以及跟踪中心相关的代码以供比较。此外,我必须使用旧版 signalr,因为这是工作要求,而不是选择,所以没有 ms signalr。这反映在导入“signalr”中
Signalr 服务文件:
import 'signalr';
import { EventEmitter } from 'events';
import log from '../logger';
import { UpdatePatrolsFunction } from '../Context/PatrolContext';
declare var jQuery: any;
const ConnectionState = {
Connecting: 0,
Connected: 1,
Reconnecting: 2,
Disconnected: 4,
};
interface HubConnectionOptions {
url: string;
qs?: any;
hub: string;
listenToMethods: string[];
}
class SignalRService {
private static instance: SignalRService | null = null;
public trackingEvent: EventEmitter;
public connectionStateChanged: EventEmitter;
private connections: { [hub: string]: SignalR.Hub.Connection } = {};
private proxies: { [hub: string]: SignalR.Hub.Proxy } = {};
private connectionStates: { [hub: string]: number } = {};
private isConnectedPromises: { [hub: string]: Promise<boolean> } = {}; // Renamed for clarity
private connectedResolvers: { [hub: string]: (value: boolean) => void } = {};
private updatePatrolsCallback: UpdatePatrolsFunction | null = null;
private trackingSubscribed: boolean = false;
private pendingTrackingSetup: (() => void) | null = null;
private trackedDeviceIds: string[] = [];
private constructor() {
this.trackingEvent = new EventEmitter();
this.connectionStateChanged = new EventEmitter();
}
public static getInstance(): SignalRService {
if (!SignalRService.instance) {
SignalRService.instance = new SignalRService();
}
return SignalRService.instance;
}
public setUpdatePatrolsCallback(callback: UpdatePatrolsFunction) {
this.updatePatrolsCallback = callback;
log.info('updatePatrols callback has been set');
}
public async connect(options: HubConnectionOptions): Promise<void> {
if (this.connections[options.hub] && this.connections[options.hub].state === ConnectionState.Connected) {
log.info(`Connection to ${options.hub} already exists, skipping connection`);
return;
}
const connection = jQuery.hubConnection();
connection.url = options.url;
if (options.qs) {
connection.qs = options.qs;
}
this.isConnectedPromises[options.hub] = new Promise<boolean>((resolve) => {
this.connectedResolvers[options.hub] = resolve;
});
connection.reconnected(() => {
this.connectedResolvers[options.hub](true);
log.info(`Now reconnected to hub ${options.hub}, connection ID= ${connection.id}`);
if (options.hub === 'trackingHub' && this.pendingTrackingSetup) {
this.pendingTrackingSetup();
}
});
connection.disconnected(() => {
this.connectedResolvers[options.hub](false);
log.info(`Disconnected from hub ${options.hub}, attempting to reconnect...`);
this.attemptReconnect(options);
});
connection.stateChanged((state: SignalR.StateChanged) => {
this.connectionStates[options.hub] = state.newState;
log.info(`Connection state changed for ${options.hub} to: ${state.newState}`);
this.connectionStateChanged.emit('stateChanged', { hub: options.hub, state });
});
this.proxies[options.hub] = connection.createHubProxy(options.hub);
options.listenToMethods.forEach(method => {
this.proxies[options.hub].on(method, (message: any) => this.onMessageReceived(options.hub, method, message));
});
try {
await connection.start();
this.connections[options.hub] = connection;
this.connectedResolvers[options.hub](true);
log.info(`Inside Connect Method. Now connected to hub ${options.hub}, connection ID= ${connection.id}`);
if (options.hub === 'trackingHub') {
this.setupTrackingHub(options);
}
} catch (error) {
log.error(`Could not connect to ${options.hub}`, error);
this.connectedResolvers[options.hub](false);
throw error;
}
}
private setupTrackingHub(options: HubConnectionOptions) {
if (!this.connections[options.hub]) {
log.warn('Tracking hub connection not established yet, deferring setup');
this.pendingTrackingSetup = () => this.setupTrackingHub(options);
return;
}
this.proxies[options.hub].on('trackingReceived', (message: any) => {
this.onTrackingReceived(message);
});
// Resubscribe to tracking on reconnection
this.connections[options.hub].reconnected(() => {
this.resubscribeToTracking(options.hub);
});
log.info('Tracking hub setup completed');
}
private resubscribeToTracking(hub: string) {
if (this.trackingSubscribed && hub === 'trackingHub') {
const devices = this.getTrackedDevices();
this.trackDevices(devices);
}
}
public getTrackedDevices(): Array<string> {
return [];
}
private onTrackingReceived(message: any) {
this.trackingEvent.emit('trackingReceived', message);
if (this.updatePatrolsCallback) {
const updatedMessage = {
...message,
GroupName: message.GroupName || 'Unknown Group'
};
this.updatePatrolsCallback(updatedMessage);
} else {
log.warn('updatePatrols callback not set');
}
}
private attemptReconnect(options: HubConnectionOptions) {
const maxAttempts = 5;
const retryDelay = 1000; // 1 second delay between attempts
let attempt = 1;
const reconnect = async () => {
if (attempt > maxAttempts) {
log.error(`Failed to reconnect to ${options.hub} after ${maxAttempts} attempts`);
return;
}
if (this.connections[options.hub]?.state === ConnectionState.Disconnected) {
log.info(`Attempting to reconnect to ${options.hub} (attempt ${attempt})...`);
try {
await this.connections[options.hub].start();
log.info(`Reconnected to hub ${options.hub}, connection ID= ${this.connections[options.hub]?.id || 'unknown'}`);
this.connectedResolvers[options.hub](true);
} catch (error) {
log.warn(`Reconnect attempt ${attempt} failed for ${options.hub}. Retrying in ${retryDelay}ms...`);
attempt += 1;
setTimeout(reconnect, retryDelay); // Delay before next attempt
}
}
};
reconnect();
}
public isConnectionEstablished(hub: string): Promise<boolean> {
return this.isConnectedPromises[hub];
}
private onMessageReceived(hub: string, method: string, message: any) {
// log.info(`Received ${method} data from ${hub}:`, message);
this.trackingEvent.emit(method, message);
if (this.updatePatrolsCallback) {
this.updatePatrolsCallback(message);
} else {
log.warn('updatePatrols callback not set');
}
}
public disconnect(hub: string) {
if (this.connections[hub]) {
this.connections[hub].stop();
log.info(`Disconnected from hub ${hub}`);
delete this.connections[hub];
delete this.proxies[hub];
delete this.connectionStates[hub];
}
}
public async trackDevices(devices: string[]): Promise<void> {
await this.isConnectionEstablished('trackingHub');
const trackingHub = this.connections['trackingHub'];
if (trackingHub && trackingHub.state === ConnectionState.Connected && devices && devices.length > 0) {
try {
await this.proxies['trackingHub'].invoke('trackDevices', devices);
log.info('Successfully subscribed to device tracking');
this.trackedDeviceIds = Array.from(new Set([...this.trackedDeviceIds, ...devices]));
this.trackingSubscribed = true;
} catch (error) {
log.error('Failed to subscribe to device tracking:', error);
throw error;
}
} else {
const error = new Error('Cannot track devices: trackingHub connection not established or no devices provided');
log.warn(error.message);
throw error;
}
}
public subscribe(eventName: string, listener: (...args: any[]) => void) {
log.info(`Subscribing to event ${eventName}`);
this.trackingEvent.on(eventName, listener);
}
public removeAllListeners(eventName: string) {
log.info(`Removing all listeners for event ${eventName}`);
this.trackingEvent.removeAllListeners(eventName);
}
public async untrackDevices(devices: string[]): Promise<void> {
log.info('untrackDevices called with devices:', devices);
await this.isConnectionEstablished('trackingHub');
const trackingHub = this.connections['trackingHub'];
if (trackingHub && trackingHub.state === ConnectionState.Connected && devices && devices.length > 0) {
try {
await this.proxies['trackingHub'].invoke('UntrackDevices', devices);
log.info('Successfully unsubscribed from device tracking');
this.trackedDeviceIds = this.trackedDeviceIds.filter(id => !devices.includes(id));
} catch (error) {
log.error('Failed to unsubscribe from device tracking:', error);
throw error;
}
} else {
const error = new Error('Cannot untrack devices: trackingHub connection not established or no devices provided');
log.warn(error.message);
throw error;
}
}
}
export default SignalRService.getInstance();
signalRChangesService 文件:
import 'signalr';
import { EventEmitter } from 'events';
import log from '../logger';
import { UpdatePatrolsFunction } from '../Context/PatrolContext';
declare var jQuery: any;
const ConnectionState = {
Connecting: 0,
Connected: 1,
Reconnecting: 2,
Disconnected: 4,
};
interface HubConnectionOptions {
url: string;
qs?: any;
hub: string;
listenToMethods: string[];
}
class SignalRChangesService {
public connectionStateChanged: EventEmitter;
public connection: SignalR.Hub.Connection | null = null;
private connections: { [hub: string]: SignalR.Hub.Connection };
private proxy: SignalR.Hub.Proxy | null = null;
private isConnected: { [hub: string]: Promise<boolean>; };
private connectedResolvers: { [hub: string]: (value: boolean) => void } = {};
private updatePatrolsCallback: UpdatePatrolsFunction | null = null;
private eventEmitter: EventEmitter;
constructor() {
this.connectionStateChanged = new EventEmitter();
this.connections = {};
this.proxy = null;
this.isConnected = {};
this.connectedResolvers = {};
this.updatePatrolsCallback = null;
this.eventEmitter = new EventEmitter();
}
public setUpdatePatrolsCallback(callback: UpdatePatrolsFunction) {
this.updatePatrolsCallback = callback;
}
public async connect(options: HubConnectionOptions): Promise<void> {
log.info(`Attempting to connect to hub: ${options.hub}`);
if (this.connections[options.hub] && this.connections[options.hub].state === ConnectionState.Connected) {
log.info(`Already connected to ${options.hub}, skipping connection`);
return;
}
const connection = jQuery.hubConnection();
connection.url = options.url;
log.info(`Connection URL set: ${connection.url}`);
if (options.qs) {
connection.qs = options.qs;
log.info(`Query string set: ${JSON.stringify(options.qs)}`);
}
this.isConnected[options.hub] = new Promise<boolean>((resolve) => {
this.connectedResolvers[options.hub] = resolve;
});
connection.reconnected(() => {
this.connectedResolvers[options.hub](true);
log.info(`Reconnected to hub ${options.hub}, connection ID= ${connection.id}`);
});
connection.disconnected(() => {
this.connectedResolvers[options.hub](false);
log.info(`Disconnected from hub ${options.hub}, attempting to reconnect...`);
this.attemptReconnect(options);
});
connection.stateChanged((state: SignalR.StateChanged) => {
log.info(`Connection state changed for ${options.hub} to: ${state.newState}`);
this.connectionStateChanged.emit('stateChanged', { hub: options.hub, state });
});
this.proxy = connection.createHubProxy(options.hub);
log.info(`Proxy created for hub: ${options.hub}`);
this.proxy?.on("patrolReceived", (message) => {
log.info('Received patrolReceived event:', message); // Ensure this log is hit
this.onMessageReceived(message);
});
try {
await connection.start();
this.connections[options.hub] = connection;
this.connectedResolvers[options.hub](true);
log.info(`Connected to hub ${options.hub}, connection ID= ${connection.id}`);
} catch (error) {
log.error(`Could not connect to ${options.hub}`, error);
this.connectedResolvers[options.hub](false);
throw error;
}
}
public async isConnectionEstablished(hub: string): Promise<boolean> {
return this.isConnected[hub] !== undefined ? await this.isConnected[hub] : false;
}
private attemptReconnect(options: HubConnectionOptions) {
const maxAttempts = 5;
let attempt = 1;
const reconnect = () => {
if (attempt > maxAttempts) {
log.error(`Failed to reconnect to ${options.hub} after maximum attempts`);
return;
}
if (this.connections[options.hub]?.state === ConnectionState.Disconnected) {
log.info(`Attempting to reconnect to ${options.hub} (attempt ${attempt})...`);
this.connections[options.hub].start()
.done(() => {
log.info(`Reconnected to hub ${options.hub}, connection ID= ${this.connections[options.hub].id || 'unknown'}`);
this.connectedResolvers[options.hub](true);
})
.fail(() => {
attempt += 1;
reconnect();
});
}
};
reconnect();
}
public disconnect(hub: string) {
if (this.connections[hub]) {
this.connections[hub].stop();
log.info(`Disconnected from hub ${hub}`);
delete this.connections[hub];
}
}
private async onMessageReceived(message: any) {
log.info(`Received message:`, message);
const patrolId = typeof message === 'string' ? message : message?.Data;
if (!patrolId) {
log.warn('Received message without patrol ID:', message);
return;
}
log.info(`Received patrol ID: ${patrolId}`);
try {
const response = await fetch(`${process.env.REACT_APP_API_URL}/api/v1/patrols/getPatrolById/${patrolId}`);
const patrolData = await response.json();
if (!response.ok || !patrolData) {
log.warn(`No data found for patrol ID ${patrolId} or failed to fetch data.`);
return;
}
log.info(`Fetched data for patrol ID ${patrolId}:`, patrolData);
if (this.updatePatrolsCallback) {
this.updatePatrolsCallback(patrolData);
} else {
log.warn('updatePatrols callback not set for patrol ID:', patrolId);
}
} catch (error) {
log.error(`Failed to fetch patrol data for ID ${patrolId}:`, error);
}
}
public subscribe(eventName: string, listener: (...args: any[]) => void) {
log.info(`Subscribing to event ${eventName}`);
this.eventEmitter.on(eventName, listener);
}
}
const signalRChangesService = new SignalRChangesService();
export default signalRChangesService;
SignalRHandler 文件:
import React, { useEffect, useRef, useCallback } from 'react';
import { Patrol } from '../types';
import signalRService from '../services/signalRService';
import signalRChangesService from '../services/signalRChangesService';
import { usePatrolContext } from '../Context/PatrolContext';
import log from '../logger';
import { Semaphore } from '../utils/Samaphore';
import { ReverseGeocode } from '../utils/reverseGeocode';
import { updatePatrolData } from '../utils/patrolUtils';
interface SignalRHandlerProps {
selectedPatrolRef: React.MutableRefObject<Patrol | null>;
setSelectedPatrol: React.Dispatch<React.SetStateAction<Patrol | null>>;
signalrConnection: typeof signalRService;
handleRefreshClick: () => void;
currentFilter?: string;
}
const SignalRHandler: React.FC<SignalRHandlerProps> = ({ selectedPatrolRef, setSelectedPatrol, signalrConnection, handleRefreshClick, currentFilter }) => {
const { patrols, updatePatrols, getUpdatedPatrol, setUpdatedPatrols, loading } = usePatrolContext();
const semaphore = useRef(new Semaphore(1));
const signalRInitializedRef = useRef(false);
const changesHubInitializedRef = useRef(false);
const changesHubConnectedRef = useRef(false);
const handleSignalRData = useCallback(async (data: any) => {
await semaphore.current.acquire();
try {
const updatedData = { ...data }; // Clone the data received
if (updatedData.Latitude && updatedData.Longitude && !updatedData.address) {
try {
const address = await ReverseGeocode(updatedData.Latitude, updatedData.Longitude);
updatedData.address = address; // Attach the reverse geocoded address to the patrol data
} catch (error) {
log.error('Error in reverse geocoding:', error);
updatedData.address = 'Address not available'; // Fallback if geocoding fails
}
}
setUpdatedPatrols(prevPatrols => {
const newPatrols = new Map(prevPatrols);
const patrolId = updatedData.GpsId || updatedData.DeviceId;
if (patrolId) {
const existingPatrol = newPatrols.get(patrolId) || {
GpsId: patrolId,
latitude: updatedData.Latitude || 0,
longitude: updatedData.Longitude || 0,
speed: 0,
gps_date: new Date().toISOString(),
address: '', // Set default address
};
const updatedPatrol = {
...existingPatrol,
...updatedData,
// Only update the address if the new one is different from the old one
address: updatedData.address !== existingPatrol.address ? updatedData.address : existingPatrol.address,
};
newPatrols.set(patrolId, updatedPatrol); // Update patrol data in the Map
}
return newPatrols; // Return the updated Map to update the global state
});
// If the selected patrol is being tracked, update its details as well
if (selectedPatrolRef.current) {
const updatedSelectedPatrol = Array.isArray(updatedData)
? updatedData.find(p => p.DeviceId === selectedPatrolRef.current?.GpsId)
: updatedData.DeviceId === selectedPatrolRef.current.GpsId ? updatedData : null;
if (updatedSelectedPatrol) {
setSelectedPatrol(prev => {
if (!prev) return prev;
return {
...prev,
...updatedSelectedPatrol,
GpsId: prev.GpsId, // Ensure GpsId remains unchanged
};
});
// log.info('Selected patrol updated:', updatedSelectedPatrol);
}
}
} finally {
semaphore.current.release();
}
}, [getUpdatedPatrol, selectedPatrolRef, setUpdatedPatrols, setSelectedPatrol]);
const trackDevicesWithRetry = useCallback(async (retryCount = 0) => {
const deviceIds = patrols.map(patrol => patrol.GpsId).filter(Boolean);
log.info(deviceIds.length);
if (deviceIds.length > 0) {
await signalrConnection.trackDevices(deviceIds);
log.info('Devices tracked successfully:', deviceIds);
} else if (retryCount < 5) {
log.warn('No patrols available to track, retrying...');
setTimeout(() => trackDevicesWithRetry(retryCount + 1), 500);
} else {
log.error('Failed to track devices after 5 retries.');
}
}, [patrols, signalrConnection]);
const handleChangesHubData = useCallback(async (data: any) => {
await semaphore.current.acquire();
try {
log.info('ChangesHub data received:', data);
const patrolId = data.patrolId || data.GpsId;
const response = await fetch(`${process.env.REACT_APP_API_URL}/api/v1/patrols/getPatrolById/${patrolId}`);
const patrolData = await response.json();
if (response.ok && patrolData) {
log.info(`Patrol ${patrolId} found, updating with real-time data...`);
const updatedPatrol = { ...patrolData, ...data };
setUpdatedPatrols(prevPatrols => {
return updatePatrolData(updatedPatrol, getUpdatedPatrol, prevPatrols);
});
if (selectedPatrolRef.current?.GpsId === patrolId) {
setSelectedPatrol(prev => {
if (!prev) return prev;
return {
...prev,
...updatedPatrol,
GpsId: prev.GpsId,
};
});
log.info('Selected patrol updated from ChangeHub:', updatedPatrol);
}
} else {
log.info(`Patrol ${patrolId} not found in backend, creating using real-time data...`);
const newPatrol: Patrol = {
GpsId: patrolId,
name: data.name || 'Unknown Patrol',
latitude: data.latitude || 0,
longitude: data.longitude || 0,
gps_date: new Date().toISOString(),
statusId: data.statusId || 1,
statusName: data.statusName || 'Unknown',
statusPatrol: data.statusPatrol || null,
address: data.address || 'Unknown Address',
patrolType: data.patrolType || 'Unknown Type',
icon: data.icon || 'default-icon.png',
speed: data.speed || 0,
folio: data.folio || null,
reactionStatus: data.reactionStatus || null,
customer: data.customer || null,
serviceAddress: data.serviceAddress || null,
transportLine: data.transportLine || null,
plates: data.plates || null,
comments: data.comments || null,
battery: data.battery || null,
kmOdometer: data.kmOdometer || null,
degrees: data.degrees || null,
altitude: data.altitude || null,
ambientLightValue: data.ambientLightValue || null,
satellite: data.satellite || null,
signalValid: data.signalValid || false,
historyData: data.historyData || null,
};
setUpdatedPatrols(prevPatrols => {
return updatePatrolData(newPatrol, getUpdatedPatrol, prevPatrols);
});
}
} catch (error) {
log.error(`Error processing ChangeHub data for patrol ${data.patrolId || data.GpsId}:`, error);
} finally {
semaphore.current.release();
}
}, [selectedPatrolRef, setSelectedPatrol, getUpdatedPatrol, setUpdatedPatrols]);
const initializeSignalR = useCallback(async () => {
if (signalRInitializedRef.current || loading) return;
signalRInitializedRef.current = true;
signalrConnection.setUpdatePatrolsCallback(updatePatrols);
try {
await signalrConnection.connect({
url: process.env.REACT_APP_SIGNALR_URL || 'http://localhost:5000/signalr',
hub: 'trackingHub',
qs: { username: '*********' },
listenToMethods: ['trackingReceived'],
});
signalrConnection.subscribe('trackingReceived', handleSignalRData);
log.info('SignalR tracking connected and patrols subscribed');
const deviceIds = patrols.map(patrol => patrol.GpsId).filter(Boolean);
if (deviceIds.length > 0) {
await signalrConnection.trackDevices(deviceIds);
log.info('Devices tracked successfully:', deviceIds);
} else {
log.warn('No devices to track after successful SignalR connection');
throw new Error('No devices to track after successful SignalR connection');
}
} catch (error) {
log.error('Error initializing SignalR tracking:', error);
signalRInitializedRef.current = false;
handleRefreshClick();
}
}, [handleSignalRData, patrols, signalrConnection, handleRefreshClick, loading, updatePatrols]);
const initializeChangesHub = useCallback(async (retryCount = 0) => {
if (changesHubConnectedRef.current) {
// log.info('ChangesHub already connected, skipping initialization');
return;
}
log.info('Connecting to ChangesHub');
try {
await signalRChangesService.connect({
url: process.env.REACT_APP_SIGNALR_URL || 'http://localhost:5000/signalr',
hub: 'changesHub',
qs: { username: '*********' },
listenToMethods: ['patrolReceived'],
});
changesHubConnectedRef.current = true;
log.info('ChangesHub connected successfully');
} catch (error) {
log.error('Error initializing ChangesHub:', error);
if (retryCount < 5) {
setTimeout(() => initializeChangesHub(retryCount + 1), 5000);
} else {
log.error('Failed to connect to ChangesHub after 5 attempts');
}
}
}, []);
const handleMaxReconnectAttemptsReached = useCallback(() => {
log.warn('Max reconnect attempts reached for ChangesHub. Attempting to reinitialize...');
changesHubInitializedRef.current = false;
initializeChangesHub();
}, [initializeChangesHub]);
useEffect(() => {
signalRChangesService.setUpdatePatrolsCallback(updatePatrols);
initializeSignalR();
initializeChangesHub();
}, [initializeSignalR, initializeChangesHub, handleSignalRData, handleChangesHubData, updatePatrols]);
return null;
};
export default React.memo(SignalRHandler);
如果我遗漏了任何文件,或者您想查看我未能提供的内容,请告诉我,我很乐意在此问题的更新中提供。谢谢。
React Leaflet 应用程序未收到 React signalr 消息 - 旧版 signalr 版本
下载声明: 本站所有软件和资料均为软件作者提供或网友推荐发布而来,仅供学习和研究使用,不得用于任何商业用途。如本站不慎侵犯你的版权请联系我,我将及时处理,并撤下相关内容!
下载声明: 本站所有软件和资料均为软件作者提供或网友推荐发布而来,仅供学习和研究使用,不得用于任何商业用途。如本站不慎侵犯你的版权请联系我,我将及时处理,并撤下相关内容!
收藏的用户(0)
X
正在加载信息~