Browse Source

日常开发

林旭祥 3 weeks ago
parent
commit
56f52f8bfc
3 changed files with 287 additions and 4 deletions
  1. 1 0
      src/router/index.ts
  2. 16 4
      src/views/game/index.vue
  3. 270 0
      src/views/game/test.vue

+ 1 - 0
src/router/index.ts

@@ -31,6 +31,7 @@ const router = createRouter({
         { path: '/analysis/detail', component: () => import('@/views/analysis/detail.vue') },
         { path: '/sunshineRun', component: () => import('@/views/sunshineRun/index.vue') },
         { path: '/game', component: () => import('@/views/game/index.vue') },
+        { path: '/game/test', component: () => import('@/views/game/test.vue') },
         { path: '/score', component: () => import('@/views/score/index.vue') },
       ]
     }

+ 16 - 4
src/views/game/index.vue

@@ -123,6 +123,9 @@ const getInit = async () => {
           return ['bodyposecontroller', 'game_basketball', 'game_football', 'game_fruit'].includes(item.exam_name)
         })
         projectList.value = list;
+        if (import.meta.env.APP_ENV != 'pro') {
+          projectList.value.push({ exam_name_cn: "单个17点位(区号在主表)", exam_name: "test" })
+        }
       } else {
         proxy?.$modal.msgError(`设备信息缺失,请重新登录绑定设备号后使用`);
       }
@@ -144,6 +147,9 @@ const getInit = async () => {
       }
     }
     if (e?.type == 'bodyposecontroller_result') {
+      if (!currentGame.value) {
+        return false;
+      }
       let obj = projectList.value.find((item: any) => {
         return item.exam_name == currentGame.value
       })
@@ -190,14 +196,20 @@ const getInit = async () => {
 };
 
 //打开窗口
-const getJump = (data: any) => {
-  console.log("222", data)
+const getJump = (data: any) => {  console.log("222", data)
+  if(data.exam_name == 'test'){
+    router.push({ path: '/game/test' });
+  }else{
+
   currentGame.value = data.exam_name;
 
   data.area_test_id.split(",").forEach((item: any) => {
     checkBodypose(item);
   })
 
+  }
+
+
 };
 
 /**
@@ -399,7 +411,7 @@ $waiPadding: 6.51rem;
     width: 100%;
 
     .item {
-      width: 50%;
+      flex: 1;
       display: flex;
 
     }
@@ -410,7 +422,7 @@ $waiPadding: 6.51rem;
 .close {
   z-index: 9999;
   position: fixed;
-  bottom: 30px;
+  bottom: 35px;
   left: 50%;
   margin-left: -1.6rem;
 }

+ 270 - 0
src/views/game/test.vue

@@ -0,0 +1,270 @@
+<template>
+  <div>
+    <canvas ref="canvasRef" :width="clientObj.width" :height="clientObj.height"></canvas>
+  </div>
+</template>
+
+<script setup name="HumanBody" lang="ts">
+import { initSpeech, speckText, playMusic, controlMusic, speckCancel, chineseNumber } from '@/utils/speech';
+import { useWebSocket } from '@/utils/bodyposeWs';
+const { proxy } = getCurrentInstance() as any;
+const router = useRouter();
+const { bodyposeWs, startDevice, checkBodypose, openBodypose, terminateBodypose, suspendBodypose, resumeBodypose, getBodyposeState, closeWS } = useWebSocket();
+const canvasRef = ref(null);
+
+const data = reactive<any>({
+  bodyposeData: {},//姿态信息
+  bodyposeState: false,//姿态识别窗口状态
+  parameter: {},//参数
+  deviceInfo: {},//设备信息
+  againNum: 0,//再次启动次数
+  againTimer: null,//定时状态
+  wsState: false,//WS状态
+  clientObj: {},//浏览器对象
+  boxes: [],//四个点坐标
+  proportion: null,//人框和屏幕比例
+});
+
+const { bodyposeData, bodyposeState, parameter, deviceInfo, againNum, againTimer, wsState, clientObj, boxes, proportion } = toRefs(data);
+
+/**
+ * 初始化
+ */
+const getInit = async () => {
+  console.log("触发姿态识别")
+  let deviceid = localStorage.getItem('deviceid') || '';
+  if (!deviceid) {
+    proxy?.$modal.msgError(`请重新登录绑定设备号后使用`);
+    return false;
+  }
+  bodyposeState.value = true;
+  if (wsState.value) {
+    proxy?.$modal.msgWarning(`操作过快,请稍后重试`);
+    setTimeout(() => {
+      bodyposeState.value = false;
+    }, 1000)
+    return false;
+  }
+  speckText("正在姿态识别");
+  proxy?.$modal.msgWarning(`正在姿态识别`);
+  bodyposeWs((e: any) => {
+    console.log("bodyposeWS", e)
+    if (e?.wksid) {
+      wsState.value = true;
+      //获取设备信息
+      startDevice({ deviceid: deviceid });
+      console.log("获取设备信息")
+    }
+    if (e?.type == 'fe_device_init_result') {
+      //接收设备信息并发送请求
+      if (e?.device_info) {
+        deviceInfo.value = e.device_info;
+        getCheckBodypose();
+        console.log("返回设备信息,检查是否支持姿态识别")
+      } else {
+        proxy?.$modal.msgError(`设备信息缺失,请重新登录绑定设备号后使用`);
+      }
+    }
+    if (e?.cmd == 'check_bodyposecontroller_available') {
+      let handcontroller_id = deviceInfo.value.handcontroller_id;
+      if (e?.code == 0) {
+        //查看姿态识别状态,如果不处于关闭就先关闭再重新启动(可能会APP退出然后工作站还在运行的可能性)
+        getBodyposeState(handcontroller_id);
+        againNum.value = 0;
+        againTimer.value = null;
+        clearTimeout(againTimer.value);
+        console.log("查看姿态识别状态")
+      } else {
+        //尝试多次查询姿态识别状态
+        if (againNum.value <= 2) {
+          againTimer.value = setTimeout(() => {
+            getCheckBodypose();
+          }, 500)
+          againNum.value++;
+        } else {
+          let msg = "";
+          if (e.code == 102402) {
+            msg = `多次连接失败请重试,姿态识别模块被占用`;
+          } else {
+            msg = `多次连接失败请重试,姿态识别模块不可用,code:${e.code}`;
+          }
+          proxy?.$modal.msgWarning(msg);
+          againNum.value = 0;
+          againTimer.value = null;
+          clearTimeout(againTimer.value);
+          getCloseBodypose();//直接关闭 
+        }
+
+      }
+    }
+    if (e?.cmd == 'get_bodyposecontroller_state') {
+      let handcontroller_id = deviceInfo.value.handcontroller_id;
+      //state说明: 0:关闭,3:空闲,36:工作中
+      if ([3, 36].includes(e.state)) {
+        getCloseBodypose();
+        proxy?.$modal.msgWarning(`请重新姿态识别`);
+      } else {
+        openBodypose(handcontroller_id);
+      }
+    }
+    if (e?.type == 'bodyposecontroller_result') {
+      let arr = e.data.result.keypoints;
+      let result = [];
+      for (let i = 0; i < arr.length; i += 3) {
+        result.push(arr.slice(i, i + 2));
+      }
+      console.log("result", result)
+      bodyposeData.value = result;
+      if (boxes.value.length == 0) {
+        speckText("识别成功");
+        proxy?.$modal.msgWarning(`识别成功`);
+        let arr = e.data.result.boxes;
+        boxes.value = [{ x: arr[0], y: arr[3] }, { x: arr[0], y: arr[1] }, { x: arr[2], y: arr[1] }, { x: arr[2], y: arr[3] }]
+        proportion.value = (clientObj.value.height / (arr[3] - arr[1])).toFixed(2);
+      }
+      //绘制图案
+      getCanvas();
+    }
+    if (e?.cmd == 'terminate_bodyposecontroller') {
+      if (e?.code == 0) {
+        closeWS();
+        bodyposeState.value = false;
+      }
+    }
+    if (e?.type == 'disconnect') {
+      wsState.value = false;
+    }
+  });
+};
+
+
+/**
+ * 查询姿态识别状态
+*/
+const getCheckBodypose = () => {
+  let handcontroller_id = deviceInfo.value.handcontroller_id;
+  //检查是否支持姿态识别
+  checkBodypose(handcontroller_id);
+};
+
+/**
+ * 关闭姿态识别
+*/
+const getCloseBodypose = () => {
+  let handcontroller_id = deviceInfo.value.handcontroller_id;
+  terminateBodypose(handcontroller_id);
+  bodyposeState.value = false;
+  speckCancel(); //停止播报
+  setTimeout(() => {
+    if (wsState.value) {
+      closeWS();
+    }
+  }, 3000)
+};
+
+/**
+ * 绘图
+*/
+const getCanvas = () => {
+  const canvas: any = canvasRef.value;
+  const ctx = canvas.getContext('2d');
+  // 清空整个画布
+  ctx.clearRect(0, 0, canvas.width, canvas.height);
+  // 保存当前状态 
+  ctx.save();
+  function calculateOffset(a: any, b: any) {
+    return {
+      x: b.x - a.x,
+      y: b.y - a.y
+    };
+  }
+  const pointA = { x: clientObj.value.width / 2, y: clientObj.value.height / 2 };
+  const pointB = { x: (boxes.value[2].x + boxes.value[0].x) / 2, y: (boxes.value[3].y + boxes.value[1].y) / 2 };
+  const offset = calculateOffset(pointA, pointB);
+  ctx.translate(-offset.x, -offset.y);
+  // console.log("Canvas分辨率", clientObj.value);
+  // console.log("人体图片四点坐标", boxes.value)
+  // console.log("Canvas中心", pointA);
+  // console.log("人体中心", pointB);
+  // console.log("offset", offset)
+  // console.log("proportion.value",proportion.value)
+  const originalPoints = bodyposeData.value;
+  // 计算缩放后坐标
+  const postData = originalPoints.map((point: any) => {
+    const newX = (point[0] - pointB.x) * proportion.value + pointB.x;
+    const newY = (point[1] - pointB.y) * proportion.value + pointB.y;
+    return [newX, newY];
+  });
+  // console.log("原始坐标:", originalPoints);
+  // console.log("缩放后坐标:", postData);
+
+  //绘制头部
+  const point1 = { x: postData[4][0], y: postData[4][1] };
+  const point2 = { x: postData[3][0], y: postData[3][1] };
+  // 计算椭圆参数
+  const centerX = (point1.x + point2.x) / 2; // 椭圆中心X
+  const centerY = (point1.y + point2.y) / 2; // 椭圆中心Y
+  const distance = Math.sqrt(
+    Math.pow(point2.x - point1.x, 2) +
+    Math.pow(point2.y - point1.y, 2)
+  ); // 两个焦点之间的距离
+  const radiusX = distance * 0.5; // 水平半径(可调整)
+  const radiusY = distance * 0.6; // 垂直半径(可调整)
+  // 1. 绘制填充椭圆
+  ctx.beginPath();
+  ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);
+  ctx.fillStyle = 'red'; // 填充颜色
+  ctx.fill(); // 填充
+  // 2. 绘制边框
+  ctx.strokeStyle = 'red';
+  ctx.lineWidth = 5;
+  ctx.stroke();
+  // 绘制每个点
+  postData.forEach((point: any, index) => {
+    //眼睛鼻子不显示
+    if ([0, 1, 2].includes(index)) {
+      const [x, y] = point;
+      ctx.beginPath();
+      ctx.arc(x, y, 5, 0, Math.PI * 2); // 绘制半径为5的圆点
+      ctx.fillStyle = 'red';
+      ctx.fill();
+      ctx.lineWidth = 1;
+      ctx.stroke();
+    }
+  });
+  // 根据点关系连线
+  const arr = [[10, 8], [8, 6], [6, 5], [5, 7], [7, 9], [6, 12], [5, 11], [12, 11], [12, 14], [14, 16], [11, 13], [13, 15]]
+  arr.forEach((point: any) => {
+    let index1 = point[0];
+    let index2 = point[1];
+    //连线
+    const dian1 = { x: postData[index1][0], y: postData[index1][1] };
+    const dian2 = { x: postData[index2][0], y: postData[index2][1] };
+    // 绘制连线
+    ctx.beginPath();
+    ctx.moveTo(dian1.x, dian1.y); // 起点
+    ctx.lineTo(dian2.x, dian2.y); // 终点
+    ctx.strokeStyle = 'red'; // 线条颜色
+    ctx.lineWidth = 3; // 线条宽度
+    ctx.stroke(); // 描边
+  });
+  ctx.restore(); // 恢复状态
+};
+
+onBeforeMount(() => {
+  clientObj.value = {
+    width: document.documentElement.clientWidth,
+    height: document.documentElement.clientHeight,
+  }
+  getInit();
+});
+
+onMounted(() => {
+})
+
+onBeforeUnmount(() => {
+  closeWS();
+});
+</script>
+
+<style lang="scss" scoped></style>