石墨仪设备 前端仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
3.0 KiB

6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
  1. import { Subject } from "rxjs";
  2. import type { Datagram } from "./wsTypes";
  3. export type SocketState = "open" | "close" | "error";
  4. class WebSocketClient {
  5. private ws: WebSocket | null = null;
  6. private url: string;
  7. private reconnectAttempts: number = -1;
  8. private maxReconnectAttempts: number = 5;
  9. private reconnectInterval: number = 3000;
  10. private dataSub = new Subject<Datagram>();
  11. get dataOb() {
  12. return this.dataSub.asObservable();
  13. }
  14. private stateSub = new Subject<SocketState>();
  15. get stateOb() {
  16. return this.stateSub.asObservable();
  17. }
  18. constructor(url: string) {
  19. this.url = url;
  20. }
  21. // 连接 WebSocket
  22. connect(): void {
  23. try {
  24. // WebSocket.CONNECTING (0) WebSocket.OPEN (1)
  25. if (this.ws && this.ws.readyState <= 1) {
  26. // 已连接
  27. console.log(`${this.url} 正在连接或已连接,无需重复连接`);
  28. } else {
  29. this.ws = new WebSocket(this.url);
  30. this.bindEvents();
  31. }
  32. } catch (error) {
  33. console.error("WebSocket 连接失败:", error);
  34. this.reconnect();
  35. }
  36. }
  37. // 绑定事件
  38. private bindEvents(): void {
  39. if (!this.ws) return;
  40. // 连接建立时的处理
  41. this.ws.onopen = () => {
  42. console.log("WebSocket 连接已建立");
  43. this.reconnectAttempts = -1; // 重置重连次数
  44. this.stateSub.next("open");
  45. };
  46. // 接收消息的处理
  47. this.ws.onmessage = (event: MessageEvent) => {
  48. try {
  49. const data = JSON.parse(event.data) as Datagram;
  50. // console.log("🚀 ~ WebSocketClient ~ bindEvents ~ data:", data);
  51. if (data.type === "cmd") {
  52. this.dataSub.next({ type: data.type, data: { ...data.data, success: data.data.status === "D0000" } });
  53. } else {
  54. this.dataSub.next(data);
  55. }
  56. } catch (error) {
  57. console.error("消息解析错误:", error);
  58. }
  59. };
  60. this.ws.onclose = () => {
  61. this.stateSub.next("close");
  62. console.log("WebSocket 连接已关闭");
  63. this.reconnect();
  64. };
  65. this.ws.onerror = error => {
  66. this.stateSub.next("error");
  67. console.error("WebSocket 错误:", error);
  68. };
  69. }
  70. // 重连机制
  71. private reconnect(): void {
  72. if (this.reconnectAttempts === -1) {
  73. this.reconnectAttempts = 0;
  74. }
  75. if (this.reconnectAttempts >= this.maxReconnectAttempts) {
  76. console.log("达到最大重连次数,停止重连");
  77. this.reconnectAttempts = -1;
  78. return;
  79. }
  80. setTimeout(() => {
  81. console.log(`尝试第 ${this.reconnectAttempts + 1} 次重连...`);
  82. this.reconnectAttempts++;
  83. this.connect();
  84. }, this.reconnectInterval);
  85. }
  86. // 关闭连接
  87. disconnect(): void {
  88. if (this.ws) {
  89. this.ws.close();
  90. this.ws = null;
  91. }
  92. }
  93. }
  94. const urlSocketMap = new Map<string, WebSocketClient>();
  95. // 导出 WebSocket 客户端
  96. export const createWebSocket = (url: string): WebSocketClient => {
  97. if (urlSocketMap.has(url)) {
  98. return urlSocketMap.get(url)!;
  99. } else {
  100. const client = new WebSocketClient(url);
  101. urlSocketMap.set(url, client);
  102. return client;
  103. }
  104. };
  105. // export const sharedWsUrl = `ws://${window.location.hostname}:8080/ws`;
  106. export const sharedWsUrl = `ws://${import.meta.env.VITE_API_HOST}:${import.meta.env.VITE_API_PORT}${
  107. import.meta.env.VITE_WS_PATH
  108. }`;