zhanghaijun 2 éve%!(EXTRA string=óta)
szülő
commit
4dd62a1877

+ 12 - 0
train-server/src/main/java/top/haijunit/train/controller/DeviceController.java

@@ -1,5 +1,6 @@
 package top.haijunit.train.controller;
 
+import cn.hutool.core.util.ArrayUtil;
 import lombok.RequiredArgsConstructor;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -8,9 +9,11 @@ import top.haijunit.common.utils.JsonUtils;
 import top.haijunit.train.device.domain.CommandEnum;
 import top.haijunit.train.device.domain.SituationVo;
 import top.haijunit.train.device.entity.DeviceEntity;
+import top.haijunit.train.device.repository.DeviceRepository;
 import top.haijunit.train.device.service.DeviceService;
 import top.haijunit.train.utils.TrainWebSocketUtils;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -24,6 +27,7 @@ import java.util.List;
 public class DeviceController {
 
     private final DeviceService deviceService;
+    private final DeviceRepository deviceRepository;
 
     @GetMapping("/all")
     public ResultData<List<DeviceEntity>> device() {
@@ -62,4 +66,12 @@ public class DeviceController {
         TrainWebSocketUtils.sendMessage(CommandEnum.SITUATION_WATER_CONTROL, new SituationVo.WaterVo(isOpen, strength));
         return ResultData.success("水灾控制");
     }
+
+    @GetMapping("/situation/devices")
+    public ResultData<List<DeviceEntity>> getSituationDevice() {
+        List<CommandEnum> list = new ArrayList<>(2);
+        list.add(CommandEnum.SITUATION_CLIENT_INFO);
+        list.add(CommandEnum.SITUATION_WATER_LEVEL);
+        return ResultData.success(deviceRepository.findByCommands(list));
+    }
 }

+ 5 - 0
train-server/src/main/java/top/haijunit/train/device/repository/DeviceRepository.java

@@ -1,6 +1,7 @@
 package top.haijunit.train.device.repository;
 
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
 import top.haijunit.train.device.domain.CommandEnum;
 import top.haijunit.train.device.entity.DeviceEntity;
@@ -18,4 +19,8 @@ public interface DeviceRepository extends JpaRepository<DeviceEntity, Long> {
     DeviceEntity findByCommandAndCode(CommandEnum command, String code);
 
     List<DeviceEntity> findByCommand(CommandEnum command);
+
+
+    @Query("SELECT e FROM db_device e WHERE e.command IN (:commands)")
+    List<DeviceEntity> findByCommands(List<CommandEnum> commands);
 }

+ 34 - 0
train-server/src/test/java/top/haijunit/train/test/LicenseCreateTest.java

@@ -0,0 +1,34 @@
+package top.haijunit.train.test;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import top.haijunit.train.utils.LicenseUtil;
+
+/**
+ * @author zhanghaijun
+ * @date 2024/3/1 22:27
+ * @description 生成授权
+ */
+@Slf4j
+public class LicenseCreateTest {
+
+    @DisplayName("生成授权")
+    @ParameterizedTest
+    @CsvSource({"b6f35e941ae79841494fe2e3e7f961a9+876,2024-06-01"})
+    void generateLicense(String machineStr, String dateStr) {
+        if (machineStr.contains(LicenseUtil.MARK)) {
+            String machineCode = StrUtil.subBefore(machineStr, LicenseUtil.MARK, false);
+            String code = StrUtil.subAfter(machineStr, LicenseUtil.MARK, false);
+            Assertions.assertEquals(code, LicenseUtil.generateCheckKey(machineCode));
+            machineStr = machineCode;
+        }
+        String string = machineStr + LicenseUtil.SEPARATOR + DateUtil.parse(dateStr).getTime();
+        String licenseStr = LicenseUtil.licenseEncrypt(string);
+        log.info(">>>>>>授权码: {}", licenseStr);
+    }
+}

+ 2 - 2
train-server/src/test/java/top/haijunit/train/test/LicenseTest.java

@@ -49,8 +49,8 @@ public class LicenseTest {
 
     @DisplayName("生成授权")
     @ParameterizedTest
-    @CsvSource({"'',2024-03-01", "b6f35e941ae79841494fe2e3e7f961a9+876,2024-01-01", "b6f35e941ae79841494fe2e3e7f961a9+876,2024-12-31"})
-    // @CsvSource({"7d9e6e98007f58466a75da0a0e963bea+159,2073-12-31"})
+    @CsvSource({"'',2024-06-01", "b6f35e941ae79841494fe2e3e7f961a9+876,2024-06-01"})
+        // @CsvSource({"7d9e6e98007f58466a75da0a0e963bea+159,2073-12-31"})
     void generateLicense(String machineStr, String dateStr) {
         if (machineStr.contains(LicenseUtil.MARK)) {
             String machineCode = StrUtil.subBefore(machineStr, LicenseUtil.MARK, false);

+ 11 - 6
train-web/src/api/device.ts

@@ -1,21 +1,26 @@
 import request from "@/api/http";
-import { AxiosPromise } from 'axios';
-import { DeviceItem } from '@/types/device'
+import {AxiosPromise} from 'axios';
+import {DeviceItem} from '@/types/device'
 
 export function deviceAll(): AxiosPromise<Array<DeviceItem>> {
-  return request({ url: '/device/all', method: 'get' })
+  return request({url: '/device/all', method: 'get'})
 }
 
 export function addDevice(data: DeviceItem): AxiosPromise<any> {
-  return request({ url: '/device/add', method: 'post', data })
+  return request({url: '/device/add', method: 'post', data})
+}
+
+// 态势的数据
+export function situationDevice(): AxiosPromise<any> {
+  return request({url: '/device/situation/devices', method: 'get'})
 }
 
 // 客流进站速度控制
 export function situationClientControl(speed: number): AxiosPromise<any> {
-  return request({ url: '/device/situation/client/' + speed, method: 'get' })
+  return request({url: '/device/situation/client/' + speed, method: 'get'})
 }
 
 // 水灾强度控制
 export function situationWaterControl(isOpen: boolean, strength: number): AxiosPromise<any> {
-  return request({ url: '/device/situation/water/' + isOpen + '/' + strength, method: 'get' })
+  return request({url: '/device/situation/water/' + isOpen + '/' + strength, method: 'get'})
 }

+ 1 - 1
train-web/src/layout/index.vue

@@ -12,7 +12,7 @@
         <router-view v-slot="{ Component, route }">
           <transition mode="out-in">
             <div style="width: 100%; height: 100%; overflow: hidden">
-              <keep-alive>
+              <keep-alive exclude="Situation">
                 <component v-if="!route.meta.link" :is="Component" :key="route.path"/>
               </keep-alive>
             </div>

+ 135 - 28
train-web/src/pages/situation/index.vue

@@ -1,25 +1,58 @@
 <template>
   <device :img-url="imageUrl" :devices="[]">
     <div class="situation-box">
-
-    </div>
-    <div>
-      <el-button type="primary" @click="waterControl">一键控制</el-button>
+      <el-row :gutter="20">
+        <el-col :span="24" style="text-align: center">{{ '客流态势' }}</el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="6">{{ '态势级别' }}</el-col>
+        <el-col :span="18">
+          <div :class="{[waterLevel]:true,'level-box':true}" v-if="clientLevel=='low'">低级别:影响较小</div>
+          <div :class="{[waterLevel]:true,'level-box':true}" v-else-if="clientLevel=='medium'">中级别:影响适中</div>
+          <div :class="{[waterLevel]:true,'level-box':true}" v-else-if="clientLevel=='high'">高级别:影响较大</div>
+          <div :class="{[waterLevel]:true,'level-box':true}" v-else-if="clientLevel=='critical'">紧急级别:影响非常严重</div>
+          <div :class="{[waterLevel]:true,'level-box':true}" v-else></div>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="6">{{ '客流速度' }}</el-col>
+        <el-col :span="18">
+          <el-slider v-model="clientSpeed" @change="clientControl"/>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="24" style="text-align: center">{{ '水灾态势' }}</el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="6">{{ '态势级别' }}</el-col>
+        <el-col :span="18">
+          <div :class="{[waterLevel]:true,'level-box':true}" v-if="waterLevel=='low'">低级别:影响较小</div>
+          <div :class="{[waterLevel]:true,'level-box':true}" v-else-if="waterLevel=='medium'">中级别:影响适中</div>
+          <div :class="{[waterLevel]:true,'level-box':true}" v-else-if="waterLevel=='high'">高级别:影响较大</div>
+          <div :class="{[waterLevel]:true,'level-box':true}" v-else-if="waterLevel=='critical'">紧急级别:影响非常严重</div>
+          <div :class="{[waterLevel]:true,'level-box':true}" v-else></div>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="6">{{ '水灾控制' }}</el-col>
+        <el-col :span="18">
+          <el-slider v-model="waterSpeed" :format-tooltip="(val:number)=>val / 100" @change="waterControl"/>
+        </el-col>
+      </el-row>
     </div>
-    <template v-for="item in dataList">
-      <el-popover placement="top-start" :title="item.name" :width="80" trigger="hover" :visible=true>
+    <template v-for="item in deviceData">
+      <el-popover placement="top-start" :title="item.postion" :width="80" :visible=true>
         <template #reference>
-          <div class="point-box" :style="{ top: item.top, left: item.left }">{{ item.name }}</div>
+          <div class="point-box" :style="{ top: item.top, left: item.left }">{{ item.postion }}</div>
         </template>
         <div>
-          <el-slider v-model="value4" :format-tooltip="formatTooltip" />
-          <div>
+          <div v-if="item.command == 20501">
             <span style="text-align: right;display: inline-block;min-width: 30px;margin-right: 6px;">{{ '大客流: ' }}</span>
-            <span>{{ item.people }}</span><span>{{ '人/m2' }}</span>
+            <span>{{ item.value }}</span><span>{{ '人/m2' }}</span>
           </div>
-          <div>
+          <div v-if="item.command == 20601">
             <span style="text-align: right;display: inline-block;min-width: 30px;margin-right: 6px;">{{ '水位: ' }}</span>
-            <span>{{ item.water }}</span><span>{{ 'm' }}</span>
+            <span>{{ item.value }}</span><span>{{ 'm' }}</span>
           </div>
         </div>
       </el-popover>
@@ -28,34 +61,108 @@
 </template>
 <script lang="ts" setup>
 import imageUrl from '@/assets/images/backage-situation.png'
-import { ElPopover } from 'element-plus';
-import { situationWaterControl } from '@/api/device.ts'
-
-const dataList = [
-  { name: '站厅', top: '18vh', left: '110vh', people: 10, water: 0.21 },
-  { name: '站台', top: '62vh', left: '110vh', people: 12, water: 1.01 }
-];
-const value4 = ref(0);
-const formatTooltip = (val: number) => {
-  return val / 100
-}
+import {ElPopover} from 'element-plus';
+import {situationClientControl, situationDevice, situationWaterControl} from '@/api/device.ts'
 
-const waterControl = function () {
-  situationWaterControl(true, 0.5).then(res => {
-    console.log(res);
+// 客流进站速度
+const clientSpeed = ref(0);
+const clientLevel = ref<'low' | 'medium' | 'high' | 'critical'>('low');
+// 水流速度
+const waterSpeed = ref(0);
+const waterLevel = ref<'low' | 'medium' | 'high' | 'critical'>('low');
+
+// 态势数据
+const deviceData = ref<{ command: number, postion: string, value: number, top: string, left: string }[]>([]);
+// 定时器
+let intervalId: number | undefined = undefined;
+onMounted(function () {
+  intervalId = setInterval(getDevice, 1000);
+})
+onBeforeUnmount(() => {
+  if (intervalId) {
+    clearInterval(intervalId);
+  }
+});
+const getDevice = function () {
+  situationDevice().then(({data}) => {
+    let resultArray = [];
+    for (let i = 0; i < data.length; i++) {
+      const device: any = data[i];
+      // console.log("---", device.message);
+      resultArray = [...resultArray, ...((JSON.parse(device.message) || []).map((item) => {
+        console.log("item: ", item);
+        switch (item.postion.trim()) {
+          case "站厅":
+            return {...item, command: device.command, postion: '站厅', top: '18vh', left: '110vh'}
+          case "站台":
+            return {...item, command: device.command, postion: '站台', top: '62vh', left: '110vh'}
+          case "站厅A端":
+            return {...item, command: device.command, postion: '站厅A端', top: '18vh', left: '10vh'}
+          default:
+            return undefined;
+        }
+      })).filter((item) => item)]
+    }
+    console.log(JSON.stringify(resultArray));
+    deviceData.value = resultArray;
+  });
+};
+const clientControl = (value: number) => {
+  situationClientControl(value).then(() => {
+    ElNotification({title: '系统通知', message: '客流控制成功', type: 'success'})
+  });
+}
+const waterControl = function (value: number) {
+  situationWaterControl(value != 0, value / 100).then(() => {
+    ElNotification({title: '系统通知', message: value != 0 ? '水灾控制成功' : '关闭水灾成功', type: 'success'})
   });
 };
 
 </script>
-<style  lang="scss" scoped>
+<style lang="scss" scoped>
 .point-box {
-  width: 6vh;
+  min-width: 6vh;
   height: 3vh;
   text-align: center;
+  padding: 2px 3px;
   background-color: blue;
   border-radius: 1vh;
   color: white;
   position: absolute;
   cursor: pointer;
 }
+
+.situation-box {
+  position: absolute;
+  bottom: 12vh;
+  height: 22vh;
+  width: 60vh;
+  border: 1px solid #031a67;
+  border-radius: 2vh;
+  padding: 3vh;
+}
+
+.level-box {
+  text-align: center;
+  border-radius: 1vh;
+  color: #ffffff;
+
+  &.low {
+    background-color: #3498db;
+  }
+
+  &.medium {
+    background-color: #f39c12;
+  }
+
+  &.high {
+    background-color: #e74c3c;
+  }
+
+  &.critical {
+    background-color: #c0392b;
+  }
+}
+
+
 </style>

+ 2 - 1
train-web/src/router/index.ts

@@ -22,7 +22,8 @@ const routes = [
       }, {
         path: "/situation",
         name: "场景态势智能体仿真",
-        component: () => import("@/pages/situation/index.vue")
+        component: () => import("@/pages/situation/index.vue"),
+        meta: { noCache: true }
       }
     ],
   }, {

+ 2 - 0
train-web/src/types/auto-imports.d.ts

@@ -309,6 +309,7 @@ import { UnwrapRef } from 'vue'
 declare module 'vue' {
   interface ComponentCustomProperties {
     readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
+    readonly ElNotification: UnwrapRef<typeof import('element-plus/es')['ElNotification']>
     readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
     readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
     readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
@@ -604,6 +605,7 @@ declare module 'vue' {
 declare module '@vue/runtime-core' {
   interface ComponentCustomProperties {
     readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
+    readonly ElNotification: UnwrapRef<typeof import('element-plus/es')['ElNotification']>
     readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
     readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
     readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>

+ 2 - 0
train-web/src/types/components.d.ts

@@ -10,6 +10,7 @@ declare module 'vue' {
     Device: typeof import('./../components/Device.vue')['default']
     ElAside: typeof import('element-plus/es')['ElAside']
     ElButton: typeof import('element-plus/es')['ElButton']
+    ElCol: typeof import('element-plus/es')['ElCol']
     ElContainer: typeof import('element-plus/es')['ElContainer']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
@@ -21,6 +22,7 @@ declare module 'vue' {
     ElOption: typeof import('element-plus/es')['ElOption']
     ElRadio: typeof import('element-plus/es')['ElRadio']
     ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
+    ElRow: typeof import('element-plus/es')['ElRow']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSlider: typeof import('element-plus/es')['ElSlider']
     ElTable: typeof import('element-plus/es')['ElTable']