ws.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. import io from 'socket.io-client';
  2. const address: any = import.meta.env.VITE_APP_BASE_API;
  3. const token: any = localStorage.getItem('token');
  4. const myInfo: any = localStorage.getItem('userInfo');
  5. let socket: any = {}; //ws实例对象
  6. let timerManager: any = {}; //计时器管理
  7. let parameter: any = {}; //参数
  8. let testTime: number = 0; //默认时长
  9. let userInfo: any = JSON.parse(myInfo); //用户信息
  10. let beatTime: number = 10000; //心跳频率
  11. let loading: any = null; //遮罩层
  12. let version: string = ''; //ws接口版本v2表示单ws多项目
  13. let examStateList: any = []; //当前状态码
  14. let testList: any = []; //区列表
  15. let wkList: any = []; //工作站列表
  16. socket = io(address + '/midexam', {
  17. transports: ['websocket', 'polling'],
  18. query: {
  19. Authorization: token,
  20. sysuuid: token
  21. }
  22. });
  23. export const initWs = (data: any, callback: any) => {
  24. examStateList = []; //当前状态码
  25. testList = []; //区列表
  26. wkList = []; //工作站列表
  27. loading = ElLoading.service({ text: '正在初始化,请稍候', background: 'rgba(0, 0, 0, 0.8)', customClass: `sports ${data.parameter.project}` });
  28. parameter = data.parameter;
  29. testTime = data.testTime;
  30. version = data.version || '';
  31. testList = data.parameter.area.split(',');
  32. examStateList = testList.map((item: any) => {
  33. let examId = `${parameter.project}_${item}`;
  34. let obj = {
  35. examState: 0,
  36. examId: examId,
  37. beatNumber: 0
  38. };
  39. return obj;
  40. });
  41. let loadingTime = setTimeout(() => {
  42. //30秒还在0状态就算超时
  43. let list = examStateList.filter((item: any) => {
  44. return item.examState == 0;
  45. });
  46. //考虑到多开只有一个在线也有效的
  47. if (list.length == testList.length) {
  48. clearTimeout(loadingTime);
  49. callback({ cmd: 'disconnect_request', data: { message: 'WS连接超时' } });
  50. }
  51. }, 20000);
  52. socket.on('connect', (e: any) => {
  53. callback({ cmd: 'mySid', data: { sid: socket.id.replace('/midexam#', '') } });
  54. if (testList.length > 1) {
  55. //单WS多区
  56. testList.forEach((item: any) => {
  57. let examId = `${parameter.project}_${item}`;
  58. if (parameter.taskId) {
  59. getTaskStarts(examId);
  60. } else {
  61. getExamStarts(examId);
  62. }
  63. getNetWork(examId, (e: any) => {
  64. if (!e.status) {
  65. callback({ cmd: 'disconnect_request', exam_id: examId, data: { message: '工作站未响应' } });
  66. }
  67. });
  68. });
  69. } else {
  70. //单WS单区
  71. let examId = parameter.examId;
  72. if (parameter.taskId) {
  73. getTaskStarts(examId);
  74. } else {
  75. getExamStarts(examId);
  76. }
  77. getNetWork(examId, (e: any) => {
  78. if (!e.status) {
  79. callback({ cmd: 'disconnect_request', exam_id: examId, data: { message: '工作站未响应' } });
  80. }
  81. });
  82. }
  83. });
  84. socket.on('msg2frontend', (e: any) => {
  85. callback(e);
  86. //实时状态
  87. if (e.cmd === 'exam_status') {
  88. let index = examStateList.findIndex((item: any) => {
  89. return item.examId == e.exam_id;
  90. });
  91. if (index != -1) {
  92. examStateList[index].examState = e.data;
  93. examStateList[index].beatNumber = examStateList[index].beatNumber + 1;
  94. }
  95. }
  96. //工作站状态
  97. if (e.cmd === 'init_result') {
  98. if (e.status == 666) {
  99. let data = {
  100. wk_id: e.data.wk_id,
  101. examId: e.exam_id
  102. };
  103. wkList.push(data);
  104. }
  105. }
  106. //测试违规
  107. if (e.cmd === 'warning_result') {
  108. }
  109. //后端播报语音
  110. if (e.cmd === 'return_audio_msg') {
  111. }
  112. //错误提示
  113. if (e.cmd === 'info_result') {
  114. }
  115. //错误提示
  116. if (e.cmd === 'error_result') {
  117. }
  118. //测试中违规提示
  119. if (e.cmd === 'warning_notify') {
  120. }
  121. //断线状态
  122. if (e.cmd === 'disconnect_request') {
  123. if (testList.length == 1) {
  124. getExit();
  125. //let examId = e?.exam_id || '';
  126. //getExit(examId);
  127. }
  128. }
  129. //状态变更
  130. if (e.cmd === 'set_exam_state') {
  131. let index = examStateList.findIndex((item: any) => {
  132. return item.examId == e.exam_id;
  133. });
  134. if (index != -1) {
  135. examStateList[index].examState = e.data;
  136. }
  137. if (e.data == 3) {
  138. //关闭遮罩层
  139. loading?.close();
  140. clearTimeout(loadingTime);
  141. }
  142. }
  143. //新建测试后返回信息,获取result_id
  144. if (e.cmd === 'open_one_test_ack') {
  145. }
  146. //人脸识别状态
  147. if (e.cmd === 'face_check_result') {
  148. }
  149. //测试结束结果
  150. if (e.cmd === 'oneresult') {
  151. }
  152. //结果生成完成(视频图片)
  153. if (e.cmd === 'static_urls_finished') {
  154. }
  155. //选择学生或测试结束后返回的数据
  156. if (e.cmd === 'result_info') {
  157. }
  158. });
  159. socket.on('disconnect', (e: any) => {
  160. callback(e);
  161. getExit();
  162. });
  163. };
  164. /**
  165. * 发送命令
  166. * @param type发送类型:v2版是单WS多人的项目
  167. * msgfrom_frontend:测试中命令交互,
  168. * get_exam_status:心跳,
  169. * exam_ends:结束测试,
  170. * fe_reconnect:重连,
  171. * task_starts:课程开启测试,
  172. * exam_starts:开启测试,
  173. * join_exam_room:加入房间,
  174. * @param data发送数据:
  175. * msgfrom_frontend发送cmd的参:
  176. * open_one_test:创建测试将由3转40,
  177. * start_face_recognition:开始人脸识别将由40转41,
  178. * stop_face_recognition:停止人脸识别将由41转43,
  179. * face_confirm_only:人脸确认即将测试,
  180. * start_one_test:开始测试将由43转42,
  181. * finish_one_test:正在测试中结束并下一次将由42转6转3,
  182. * close_one_test:中断任何阶段将由42、43、41、40转3,
  183. * suspend_face_recognition_channels:工作站识别的短跑,某道识别到了人就停止某道的识别,
  184. * resume_face_recognition_channels:工作站识别的短跑,重新开启某一道的识别,
  185. * next_test:,
  186. * result_info:,
  187. * @param callback回调函数
  188. */
  189. export const sendMessage = (type: string, data: any, callback?: () => void) => {
  190. if (socket.connected) {
  191. callback = callback || function () {};
  192. //版本2就拼接进去
  193. if (version == 'v2') {
  194. type = type + '_' + version;
  195. }
  196. socket.emit(type, JSON.stringify(data), callback);
  197. }
  198. };
  199. /**
  200. * 课程连接成功
  201. */
  202. const getTaskStarts = (data?: any) => {
  203. let examId = data ? data : parameter.examId;
  204. sendMessage(
  205. 'task_starts',
  206. {
  207. data: 'start_' + examId,
  208. class_id: parameter.classes,
  209. exam_type: parameter.standard,
  210. task_cate: parameter.taskCate,
  211. gesture: parameter.gesture,
  212. demo: parameter.demo,
  213. test_time: testTime
  214. },
  215. () => {}
  216. );
  217. };
  218. /**
  219. * 连接成功
  220. */
  221. const getExamStarts = (data?: any) => {
  222. let examId = data ? data : parameter.examId;
  223. sendMessage(
  224. 'exam_starts',
  225. {
  226. data: 'start_' + examId,
  227. class_id: parameter.classes,
  228. exam_type: parameter.standard,
  229. gesture: parameter.gesture,
  230. demo: parameter.demo,
  231. test_time: testTime
  232. },
  233. () => {}
  234. );
  235. };
  236. /**
  237. * 创建测试
  238. */
  239. export const openOneTest = (data?: any) => {
  240. return new Promise((resolve, reject) => {
  241. let examId = data ? data : parameter.examId;
  242. let index = examStateList.findIndex((item: any) => {
  243. return item.examId == examId;
  244. });
  245. sendMessage('msgfrom_frontend', {
  246. data: {
  247. cmd: 'open_one_test',
  248. exam_id: examId
  249. }
  250. });
  251. let timer1 = setInterval(() => {
  252. if (examStateList[index].examState == 40) {
  253. clearInterval(timer1);
  254. clearTimeout(timer2);
  255. resolve({ data: examStateList[index].examState });
  256. }
  257. }, 250);
  258. let timer2 = setTimeout(() => {
  259. if (examStateList[index].examState == 3) {
  260. clearInterval(timer1);
  261. clearTimeout(timer2);
  262. reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:open_one_test' } });
  263. }
  264. }, 30000);
  265. });
  266. };
  267. /**
  268. * 开始人脸识别
  269. */
  270. export const startFace = (data?: any) => {
  271. return new Promise((resolve, reject) => {
  272. let examId = data ? data : parameter.examId;
  273. let index = examStateList.findIndex((item: any) => {
  274. return item.examId == examId;
  275. });
  276. sendMessage('msgfrom_frontend', {
  277. data: {
  278. cmd: 'start_face_recognition',
  279. exam_id: examId
  280. }
  281. });
  282. let timer1 = setInterval(() => {
  283. if (examStateList[index].examState == 41) {
  284. clearInterval(timer1);
  285. clearTimeout(timer2);
  286. resolve({ data: examStateList[index].examState });
  287. }
  288. }, 250);
  289. let timer2 = setTimeout(() => {
  290. if (examStateList[index].examState == 40) {
  291. clearInterval(timer1);
  292. clearTimeout(timer2);
  293. reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:start_face_recognition' } });
  294. }
  295. }, 30000);
  296. });
  297. };
  298. /**
  299. * 停止人脸识别
  300. */
  301. export const stopFace = (data?: any) => {
  302. return new Promise((resolve, reject) => {
  303. let examId = data ? data : parameter.examId;
  304. let index = examStateList.findIndex((item: any) => {
  305. return item.examId == examId;
  306. });
  307. sendMessage('msgfrom_frontend', {
  308. data: {
  309. cmd: 'stop_face_recognition',
  310. exam_id: examId
  311. }
  312. });
  313. let timer1 = setInterval(() => {
  314. if (examStateList[index].examState == 43) {
  315. clearInterval(timer1);
  316. clearTimeout(timer2);
  317. resolve({ data: examStateList[index].examState });
  318. }
  319. }, 250);
  320. let timer2 = setTimeout(() => {
  321. if (examStateList[index].examState == 41) {
  322. clearInterval(timer1);
  323. clearTimeout(timer2);
  324. reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:stop_face_recognition' } });
  325. }
  326. }, 30000);
  327. });
  328. };
  329. /**
  330. * 确认并提交人脸
  331. */
  332. export const faceConfirmOnly = (data: any, callback?: any) => {
  333. let examId = data.exam_id ? data.exam_id : parameter.examId;
  334. let myData = null;
  335. if (Array.isArray(data)) {
  336. //数组类型
  337. myData = {
  338. cmd: 'face_confirm_only',
  339. data
  340. };
  341. } else {
  342. //对象类型
  343. myData = {
  344. cmd: 'face_confirm_only',
  345. exam_id: examId,
  346. result_id: data.result_id,
  347. student_id: data.student_id,
  348. gender: data.gender
  349. };
  350. }
  351. sendMessage(
  352. 'msgfrom_frontend',
  353. {
  354. data: myData
  355. },
  356. () => {
  357. callback();
  358. }
  359. );
  360. };
  361. /**
  362. * 开始测试
  363. */
  364. export const startOneTest = (data?: any, callback?: any) => {
  365. let examId = data ? data : parameter.examId;
  366. sendMessage(
  367. 'msgfrom_frontend',
  368. {
  369. data: {
  370. cmd: 'start_one_test',
  371. exam_id: examId
  372. }
  373. },
  374. () => {
  375. callback();
  376. }
  377. );
  378. };
  379. /**
  380. * 停止测试
  381. */
  382. export const finishOneTest = (data?: any) => {
  383. return new Promise((resolve, reject) => {
  384. let examId = data ? data : parameter.examId;
  385. let index = examStateList.findIndex((item: any) => {
  386. return item.examId == examId;
  387. });
  388. sendMessage('msgfrom_frontend', {
  389. data: {
  390. cmd: 'finish_one_test',
  391. exam_id: examId
  392. }
  393. });
  394. let timer1 = setInterval(() => {
  395. if (examStateList[index].examState == 3) {
  396. clearInterval(timer1);
  397. clearTimeout(timer2);
  398. resolve({ data: examStateList[index].examState });
  399. }
  400. }, 250);
  401. let timer2 = setTimeout(() => {
  402. if (examStateList[index].examState == 42) {
  403. clearInterval(timer1);
  404. clearTimeout(timer2);
  405. reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:finish_one_test' } });
  406. }
  407. }, 60000);
  408. });
  409. };
  410. /**
  411. * 关闭测试测试
  412. */
  413. export const closeOneTest = (data?: any) => {
  414. return new Promise((resolve, reject) => {
  415. let examId = data ? data : parameter.examId;
  416. let index = examStateList.findIndex((item: any) => {
  417. return item.examId == examId;
  418. });
  419. sendMessage('msgfrom_frontend', {
  420. data: {
  421. cmd: 'close_one_test',
  422. exam_id: examId
  423. }
  424. });
  425. let timer1 = setInterval(() => {
  426. if (examStateList[index].examState == 3) {
  427. clearInterval(timer1);
  428. clearTimeout(timer2);
  429. resolve({ data: examStateList[index].examState });
  430. }
  431. }, 250);
  432. let timer2 = setTimeout(() => {
  433. if (examStateList[index].examState != 3) {
  434. clearInterval(timer1);
  435. clearTimeout(timer2);
  436. reject({ cmd: 'disconnect_request', exam_id: examId, data: { message: '超时:close_one_test' } });
  437. }
  438. }, 30000);
  439. });
  440. };
  441. /**
  442. * 某道识别到了人就停止某道的识别
  443. */
  444. export const suspendFaceRecognitionChannels = (data: any) => {
  445. let track = data;
  446. sendMessage('msgfrom_frontend', {
  447. data: {
  448. cmd: 'suspend_face_recognition_channels',
  449. track: track
  450. }
  451. });
  452. };
  453. /**
  454. * 某道重新开始识别
  455. */
  456. export const resumeFaceRecognitionChannels = (data: any) => {
  457. let track = data;
  458. sendMessage('msgfrom_frontend', {
  459. data: {
  460. cmd: 'resume_face_recognition_channels',
  461. track: track
  462. }
  463. });
  464. };
  465. /**
  466. * 心跳
  467. */
  468. export const getNetWork = (data: any, callback?: any) => {
  469. timerManager[data] = setInterval(() => {
  470. let obj = wkList.find((item: any) => {
  471. return item.examId == data;
  472. });
  473. if (obj == undefined) {
  474. return false;
  475. }
  476. let wk_id = obj.wk_id;
  477. let examId = data ? data : parameter.examId;
  478. sendMessage(
  479. 'get_exam_status',
  480. {
  481. exam_id: examId,
  482. wk_id: wk_id,
  483. school_id: userInfo.school_id || null
  484. },
  485. () => {
  486. //如果心跳停止了就退出去
  487. let index = examStateList.findIndex((item: any) => {
  488. return item.examId == examId;
  489. });
  490. if (index == -1) {
  491. return false;
  492. }
  493. let beforBeatNumber = JSON.parse(JSON.stringify(examStateList[index].beatNumber));
  494. setTimeout(() => {
  495. //5秒后验证是否有变
  496. if (beforBeatNumber == examStateList[index].beatNumber) {
  497. //异常
  498. callback({ status: false });
  499. } else {
  500. //正常
  501. callback({ status: true });
  502. }
  503. }, 5000);
  504. }
  505. );
  506. }, beatTime);
  507. };
  508. /**
  509. * 关闭项目
  510. */
  511. export const examEnds = () => {
  512. getExit();
  513. };
  514. /**
  515. * 退出
  516. */
  517. const getExit = (data?: any) => {
  518. //关闭遮罩层
  519. loading?.close();
  520. //通知工作站关闭
  521. if (testList.length > 1 && !data) {
  522. //单WS多区
  523. examStateList.forEach((item: any) => {
  524. let examId = item.examId;
  525. sendMessage('exam_ends', {
  526. data: 'end_' + examId,
  527. class_id: parameter.classes
  528. });
  529. });
  530. //清除计时器
  531. getClearTimer();
  532. //如果正在连接就关闭
  533. if (socket?.connected) {
  534. socket?.close();
  535. }
  536. } else {
  537. //单WS单区
  538. let examId = data ? data : parameter.examId;
  539. sendMessage('exam_ends', {
  540. data: 'end_' + examId,
  541. class_id: parameter.classes
  542. });
  543. if (!data) {
  544. //清除计时器
  545. getClearTimer();
  546. //如果正在连接就关闭
  547. if (socket?.connected) {
  548. socket?.close();
  549. }
  550. }
  551. }
  552. };
  553. /**
  554. * 清空定时任务
  555. */
  556. const getClearTimer = () => {
  557. for (let key in timerManager) {
  558. if (timerManager.hasOwnProperty(key)) {
  559. clearInterval(timerManager[key]);
  560. timerManager[key] = null;
  561. }
  562. }
  563. };