ws.ts 14 KB

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