Browse Source

无人机

bihuisong 2 weeks ago
parent
commit
9288a8cf09
24 changed files with 3067 additions and 0 deletions
  1. 44 0
      monitor_ui/src/api/uav/uav.js
  2. 44 0
      monitor_ui/src/api/uav/uavdept/uavdept.js
  3. 396 0
      monitor_ui/src/views/uav/index.vue
  4. 272 0
      monitor_ui/src/views/uav/uavdept/monitoruavdept.vue
  5. 14 0
      pom.xml
  6. 4 0
      src/main/java/com/sooka/sponest/monitor/remoteapi/fallback/center/system/RemoteSystemBaseServiceFallbackFactory.java
  7. 2 0
      src/main/java/com/sooka/sponest/monitor/remoteapi/service/center/system/RemoteService.java
  8. 915 0
      src/main/java/com/sooka/sponest/monitor/uav/controller/CentermonitorTUavController.java
  9. 85 0
      src/main/java/com/sooka/sponest/monitor/uav/controller/CentermonitorTUavdeptController.java
  10. 218 0
      src/main/java/com/sooka/sponest/monitor/uav/domain/CentermonitorTUav.java
  11. 48 0
      src/main/java/com/sooka/sponest/monitor/uav/domain/CentermonitorTUavdept.java
  12. 50 0
      src/main/java/com/sooka/sponest/monitor/uav/domain/dto/CentermonitorTUavDTO.java
  13. 72 0
      src/main/java/com/sooka/sponest/monitor/uav/mapper/CentermonitorTUavMapper.java
  14. 65 0
      src/main/java/com/sooka/sponest/monitor/uav/mapper/CentermonitorTUavdeptMapper.java
  15. 69 0
      src/main/java/com/sooka/sponest/monitor/uav/service/ICentermonitorTUavService.java
  16. 61 0
      src/main/java/com/sooka/sponest/monitor/uav/service/ICentermonitorTUavdeptService.java
  17. 113 0
      src/main/java/com/sooka/sponest/monitor/uav/service/impl/CentermonitorTUavServiceImpl.java
  18. 91 0
      src/main/java/com/sooka/sponest/monitor/uav/service/impl/CentermonitorTUavdeptServiceImpl.java
  19. 51 0
      src/main/java/com/sooka/sponest/monitor/websocket/SemaphoreUtils.java
  20. 19 0
      src/main/java/com/sooka/sponest/monitor/websocket/WebSocketConfig.java
  21. 91 0
      src/main/java/com/sooka/sponest/monitor/websocket/WebSocketServer.java
  22. 121 0
      src/main/java/com/sooka/sponest/monitor/websocket/WebSocketUsers.java
  23. 132 0
      src/main/resources/mapper/monitor/uav/CentermonitorTUavMapper.xml
  24. 90 0
      src/main/resources/mapper/monitor/uav/CentermonitorTUavdeptMapper.xml

+ 44 - 0
monitor_ui/src/api/uav/uav.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询无人机列表
+export function listUav(query) {
+  return request({
+    url: '/center-monitor/uav/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询无人机详细
+export function getUav(id) {
+  return request({
+    url: '/center-monitor/uav/' + id,
+    method: 'get'
+  })
+}
+
+// 新增无人机
+export function addUav(data) {
+  return request({
+    url: '/center-monitor/uav',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改无人机
+export function updateUav(data) {
+  return request({
+    url: '/center-monitor/uav',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除无人机
+export function delUav(id) {
+  return request({
+    url: '/center-monitor/uav/' + id,
+    method: 'delete'
+  })
+}

+ 44 - 0
monitor_ui/src/api/uav/uavdept/uavdept.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询无人机部门关联列表
+export function listUavdept(query) {
+  return request({
+    url: '/center-monitor/uavdept/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询无人机部门关联详细
+export function getUavdept(id) {
+  return request({
+    url: '/center-monitor/uavdept/' + id,
+    method: 'get'
+  })
+}
+
+// 新增无人机部门关联
+export function addUavdept(data) {
+  return request({
+    url: '/center-monitor/uavdept',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改无人机部门关联
+export function updateUavdept(data) {
+  return request({
+    url: '/center-monitor/uavdept',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除无人机部门关联
+export function delUavdept(id) {
+  return request({
+    url: '/center-monitor/uavdept/' + id,
+    method: 'delete'
+  })
+}

+ 396 - 0
monitor_ui/src/views/uav/index.vue

@@ -0,0 +1,396 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="88px">
+      <el-form-item label="基站名称" prop="baseStationName">
+        <el-input
+          v-model="queryParams.baseStationName"
+          placeholder="请输入基站名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="无人机名称" prop="uavName">
+        <el-input
+          v-model="queryParams.uavName"
+          placeholder="请输入无人机名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+        >修改
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+        >删除
+        </el-button>
+      </el-col>
+      <!--      <el-col :span="1.5">-->
+      <!--        <el-button-->
+      <!--          type="warning"-->
+      <!--          plain-->
+      <!--          icon="el-icon-download"-->
+      <!--          size="mini"-->
+      <!--          @click="handleExport"-->
+      <!--        >导出-->
+      <!--        </el-button>-->
+      <!--      </el-col>-->
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="uavList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column label="序号" align="center" type="index"/>
+      <el-table-column label="基站名称" align="center" prop="baseStationName"/>
+      <el-table-column label="机库ID" align="center" prop="baseStationId"/>
+      <el-table-column label="机库SN" align="center" prop="baseStationSn"/>
+      <el-table-column label="经度" align="center" prop="longitude"/>
+      <el-table-column label="纬度" align="center" prop="latitude"/>
+      <el-table-column label="海拔高度" align="center" prop="height"/>
+      <el-table-column label="无人机名称" align="center" prop="uavName"/>
+      <el-table-column label="无人机ID" align="center" prop="uavId"/>
+      <el-table-column label="无人机SN" align="center" prop="uavSn"/>
+      <el-table-column label="操作" align="center" width="250" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >修改
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+          >删除
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-s-management"
+            @click="handleUavDept(scope.row)"
+          >部门管理
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改无人机对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
+      <el-form ref="form" :inline="true" :model="form" :rules="rules" label-width="140px">
+        <el-form-item label="基站名称" prop="baseStationName">
+          <el-input v-model="form.baseStationName" placeholder="请输入基站名称"/>
+        </el-form-item>
+        <el-form-item label="机库ID" prop="baseStationId">
+          <el-input v-model="form.baseStationId" placeholder="请输入机库ID"/>
+        </el-form-item>
+        <el-form-item label="机库SN" prop="baseStationSn">
+          <el-input v-model="form.baseStationSn" placeholder="请输入机库SN"/>
+        </el-form-item>
+        <el-form-item label="经度" prop="longitude">
+          <el-input v-model="form.longitude" placeholder="请输入经度"/>
+        </el-form-item>
+        <el-form-item label="纬度" prop="latitude">
+          <el-input v-model="form.latitude" placeholder="请输入纬度"/>
+        </el-form-item>
+        <el-form-item label="海拔高度" prop="height">
+          <el-input v-model="form.height" placeholder="请输入海拔高度"/>
+        </el-form-item>
+        <el-form-item label="无人机名称" prop="uavName">
+          <el-input v-model="form.uavName" placeholder="请输入无人机名称"/>
+        </el-form-item>
+        <el-form-item label="无人机ID" prop="uavId">
+          <el-input v-model="form.uavId" placeholder="请输入无人机ID"/>
+        </el-form-item>
+        <el-form-item label="无人机SN" prop="uavSn">
+          <el-input v-model="form.uavSn" placeholder="请输入无人机SN"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 部门管理弹框 -->
+    <el-dialog title="部门管理列表" :visible.sync="showUavDept" @close="closeDialog">
+      <monitoruavdept v-if="showUavDept" ref="monitorDept" :uavId="uavIdValue" :uavName="uavNameValue"
+                      style="width: 100%;height: 100%;"
+      ></monitoruavdept>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {listUav, getUav, delUav, addUav, updateUav} from "@/api/uav/uav";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import {treeselect} from "@/api/system/dept";
+import {checkLat, checkLon, checkPositiveDecimal} from '@/api/system/rules'
+import monitoruavdept from "@/views/uav/uavdept/monitoruavdept.vue";
+
+export default {
+  name: "Uav",
+  components: {monitoruavdept, Treeselect},
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 无人机表格数据
+      uavList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        baseStationName: null,
+        baseStationId: null,
+        baseStationSn: null,
+        longitude: null,
+        latitude: null,
+        height: null,
+        uavName: null,
+        uavId: null,
+        uavSn: null,
+        deptId: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        baseStationName: [
+          {required: true, message: "基站名称不能为空", trigger: "blur"},
+        ],
+        baseStationId: [
+          {required: true, message: "机库ID不能为空", trigger: "blur"},
+        ],
+        baseStationSn: [
+          {required: true, message: "机库SN不能为空", trigger: "blur"},
+        ],
+        uavName: [
+          {required: true, message: "无人机名称不能为空", trigger: "blur"},
+        ],
+        uavId: [
+          {required: true, message: "无人机ID不能为空", trigger: "blur"},
+        ],
+        uavSn: [
+          {required: true, message: "无人机SN不能为空", trigger: "change"},
+        ],
+        longitude: [
+          {required: true, message: "经度不能为空", trigger: "change"},
+          {validator: checkLon, trigger: 'blur'}
+        ],
+        latitude: [
+          {required: true, message: "纬度不能为空", trigger: "change"},
+          {validator: checkLat, trigger: 'blur'}
+        ],
+        height: [
+          {required: true, message: '海拔高度不能为空', trigger: 'blur'},
+          {validator: checkPositiveDecimal, trigger: 'blur'}
+        ],
+      },
+      // 表格树数据
+      deptList: [],
+      // 部门树选项
+      deptOptions: [],
+      // 过滤掉已禁用部门树选项
+      enabledDeptOptions: undefined,
+      showUavDept: false,
+      uavIdValue: null,
+      uavNameValue: null,
+    };
+  },
+  created() {
+    this.getList();
+    this.getDeptTree()
+  },
+  methods: {
+    handleUavDept(row) {
+      const id = row.id
+      const uavName = row.uavName
+      this.uavIdValue = id
+      this.uavNameValue = uavName
+      this.showUavDept = true
+      this.getDeptTree()
+    },
+    closeDialog() {
+      this.showUavDept = false
+    },
+    /** 查询无人机列表 */
+    getList() {
+      this.loading = true;
+      listUav(this.queryParams).then(response => {
+        this.uavList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 查询部门下拉树结构 */
+    getDeptTree() {
+      treeselect().then(response => {
+        this.deptOptions = response.data;
+        this.enabledDeptOptions = this.filterDisabledDept(JSON.parse(JSON.stringify(response.data)));
+      });
+    },
+    // 过滤禁用的部门
+    filterDisabledDept(deptList) {
+      return deptList.filter(dept => {
+        if (dept.disabled) {
+          return false;
+        }
+        if (dept.children && dept.children.length) {
+          dept.children = this.filterDisabledDept(dept.children);
+        }
+        return true;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        baseStationName: null,
+        baseStationId: null,
+        baseStationSn: null,
+        longitude: null,
+        latitude: null,
+        height: null,
+        uavName: null,
+        uavId: null,
+        uavSn: null,
+        deptId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加无人机";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getUav(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改无人机";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateUav(this.form).then(response => {
+              this.$modal.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addUav(this.form).then(response => {
+              this.$modal.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$modal.confirm('是否确认删除无人机编号为"' + ids + '"的数据项?').then(function () {
+        return delUav(ids);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('system/uav/export', {
+        ...this.queryParams
+      }, `uav_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>

+ 272 - 0
monitor_ui/src/views/uav/uavdept/monitoruavdept.vue

@@ -0,0 +1,272 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+        >修改
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+        >删除
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+        >导出
+        </el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="uavdeptList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column label="部门名称" align="center" prop="deptName"/>
+      <el-table-column label="添加时间" align="center" prop="createTime"/>
+      <el-table-column label="修改时间" align="center" prop="updateTime"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >修改
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+          >删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改AI对应部门对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="部门名称" prop="deptId">
+          <treeselect :options="deptOptions" v-model="form.deptId" :show-count="true" placeholder="请选择部门名称"
+                      @select="hx"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  listUavdept,
+  getUavdept,
+  delUavdept,
+  addUavdept,
+  updateUavdept
+} from "@/api/uav/uavdept/uavdept";
+import {treeselect} from "@/api/system/dept";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+  name: "Uavdept",
+  components: {
+    Treeselect
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // AI对应部门表格数据
+      uavdeptList: [],
+      // 部门树选项
+      deptOptions: undefined,
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        uavId: null,
+        uavName: null,
+        deptId: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        deptId: [
+          {required: true, message: "部门名称不能为空", trigger: "blur"}
+        ],
+      },
+    };
+  },
+  props: {
+    uavId: null,
+    uavName: null,
+  },
+  created() {
+    this.getList();
+    this.getTreeselect();
+  },
+  methods: {
+    /** 查询AI对应部门列表 */
+    getList() {
+      this.loading = true;
+      this.queryParams.uavId = this.uavId;
+      listUavdept(this.queryParams).then(response => {
+        this.uavdeptList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 查询部门下拉树结构 */
+    getTreeselect() {
+      treeselect().then(response => {
+        this.deptOptions = response.data;
+      });
+    },
+    //部门ID、部门名称赋值
+    hx(node) {
+      this.form.deptId = node.id;
+      this.form.deptName = node.label
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        uavId: null,
+        deptId: null,
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.form.uavId = this.uavId;
+      this.form.uavName = this.uavName
+      this.open = true;
+      this.title = "添加无人机对应部门";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getUavdept(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改无人机对应部门";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateUavdept(this.form).then(response => {
+              this.$modal.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addUavdept(this.form).then(response => {
+              this.$modal.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$modal.confirm('是否确认删除数据项?').then(function () {
+        return delUavdept(ids);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('center-monitor/aidevicedept/export', {
+        ...this.queryParams
+      }, `aidevicedept_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>

+ 14 - 0
pom.xml

@@ -114,6 +114,20 @@
             <artifactId>isoparser</artifactId>
             <version>1.1.22</version>
         </dependency>
+
+
+        <!-- SpringBoot Websocket -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+
+        <!--hutool-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.2</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 4 - 0
src/main/java/com/sooka/sponest/monitor/remoteapi/fallback/center/system/RemoteSystemBaseServiceFallbackFactory.java

@@ -61,6 +61,10 @@ public class RemoteSystemBaseServiceFallbackFactory implements FallbackFactory<R
                 return null;
             }
 
+            public R<String> uavSignUrl() {
+                return null;
+            }
+
             /**
              * 获取参数配置_文件地址
              */

+ 2 - 0
src/main/java/com/sooka/sponest/monitor/remoteapi/service/center/system/RemoteService.java

@@ -60,4 +60,6 @@ public interface RemoteService {
     @GetMapping(value = "/dept/selectChildrenCountyOrVillagesDeptListByDeptId")
     R<List<SysDept>> selectChildrenCountyOrVillagesDeptListByDeptId(@RequestParam(value = "deptId") Long deptId);
 
+    @GetMapping(value = "/config/selectConfigKey/uavSignUrl")
+    R<String> uavSignUrl();
 }

+ 915 - 0
src/main/java/com/sooka/sponest/monitor/uav/controller/CentermonitorTUavController.java

@@ -0,0 +1,915 @@
+package com.sooka.sponest.monitor.uav.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.sooka.sponest.monitor.remoteapi.service.center.system.RemoteService;
+import com.sooka.sponest.monitor.uav.domain.CentermonitorTUav;
+import com.sooka.sponest.monitor.uav.domain.dto.CentermonitorTUavDTO;
+import com.sooka.sponest.monitor.uav.service.ICentermonitorTUavService;
+import com.sooka.sponest.monitor.websocket.WebSocketUsers;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.net.ssl.SSLContext;
+import javax.servlet.http.HttpServletResponse;
+import javax.websocket.*;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 无人机Controller
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+@RestController
+@RequestMapping("/uav")
+public class CentermonitorTUavController extends BaseController {
+
+    @Autowired
+    private ICentermonitorTUavService centermonitorTUavService;
+
+    @Resource
+    private RemoteService remoteService;
+
+    private static final Logger log = LoggerFactory.getLogger(CentermonitorTUavController.class);
+
+    @Value("${sooka.uav_server.username}")
+    private String username;
+
+    @Value("${sooka.uav_server.password}")
+    private String password;
+
+    private static final String REQUEST_URL = "https://47.93.50.30/etkqq/login";
+
+    /**
+     * 2.登录鉴权
+     */
+    @GetMapping("/getToken")
+    public String getToken() {
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            HttpPost httpPost = new HttpPost(REQUEST_URL);
+            // 使用 JSON 处理库构建请求体
+            String requestBody = "{\"username\": \"" + username + "\", \"password\": \"" + password + "\"}";
+            StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    return EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
+                } else {
+                    log.error("响应实体为空");
+                    return "响应实体为空";
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return "请求失败,状态码: " + statusCode;
+            }
+        } catch (Exception e) {
+            return "请求发生异常: " + e.getMessage();
+        }
+    }
+
+    @GetMapping("/getSign")
+    public AjaxResult getSign() {
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            R<String> requestResult = remoteService.uavSignUrl();
+            if (requestResult == null || requestResult.getData() == null) {
+                log.error("获取的请求 URL 为空");
+                return AjaxResult.error("获取的请求 URL 为空");
+            }
+            String requestUrl = requestResult.getData();
+            // 使用 HttpGet 进行 GET 请求
+            HttpGet httpGet = new HttpGet(requestUrl + "/etkqq/loginToken");
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpGet);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    String returnUrl = requestUrl + "/tower/pub/zgtt?sign=" + json.get("data").toString();
+                    return AjaxResult.success("操作成功", returnUrl);
+                } else {
+                    log.error("响应实体为空");
+                    return AjaxResult.error("响应实体为空");
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.error("请求失败");
+    }
+
+    /**
+     * 无人机组织树
+     */
+    @GetMapping("/getCopterTree")
+    public AjaxResult getCopterTree() {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            R<String> requestResult = remoteService.uavSignUrl();
+            if (requestResult == null || requestResult.getData() == null) {
+                log.error("获取的请求 URL 为空");
+                return AjaxResult.error("获取的请求 URL 为空");
+            }
+            String requestUrl = requestResult.getData();
+            // 使用 HttpGet 进行 GET 请求
+            HttpGet httpGet = new HttpGet(requestUrl + "/etkqq/copterMetaData/getCopterTree");
+            httpGet.setHeader("Authorization", jsonObject.get("token").toString());
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpGet);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString(), json.get("result"));
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return AjaxResult.error("请求失败,状态码: " + statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 无人机详细信息
+     */
+    @GetMapping("/getCopterBasicData/{copterId}")
+    public AjaxResult getCopterBasicData(@PathVariable("copterId") String copterId) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            R<String> requestResult = remoteService.uavSignUrl();
+            if (requestResult == null || requestResult.getData() == null) {
+                log.error("获取的请求 URL 为空");
+                return AjaxResult.error("获取的请求 URL 为空");
+            }
+            String requestUrl = requestResult.getData();
+            // 使用 HttpGet 进行 GET 请求
+            HttpGet httpGet = new HttpGet(requestUrl + "/etkqq/copterMetaData/getCopterBasicData?copterId=" + copterId);
+            httpGet.setHeader("Authorization", jsonObject.get("token").toString());
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpGet);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString(), json.get("result"));
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 机库组织树
+     */
+    @GetMapping("/getOrgTree")
+    public AjaxResult getOrgTree() {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            R<String> requestResult = remoteService.uavSignUrl();
+            if (requestResult == null || requestResult.getData() == null) {
+                log.error("获取的请求 URL 为空");
+                return AjaxResult.error("获取的请求 URL 为空");
+            }
+            String requestUrl = requestResult.getData();
+            // 使用 HttpGet 进行 GET 请求
+            HttpGet httpGet = new HttpGet(requestUrl + "/etkqq/security/getOrgTree");
+            httpGet.setHeader("Authorization", jsonObject.get("token").toString());
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpGet);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString(), json.get("result"));
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 机库详细信息
+     */
+    @GetMapping("/getBoxBasicData/{boxId}")
+    public AjaxResult getBoxBasicData(@PathVariable("boxId") String boxId) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            R<String> requestResult = remoteService.uavSignUrl();
+            if (requestResult == null || requestResult.getData() == null) {
+                log.error("获取的请求 URL 为空");
+                return AjaxResult.error("获取的请求 URL 为空");
+            }
+            String requestUrl = requestResult.getData();
+            // 使用 HttpGet 进行 GET 请求
+            HttpGet httpGet = new HttpGet(requestUrl + "/etkqq/boxMetaData/getBoxBasicData?boxId=" + boxId);
+            httpGet.setHeader("Authorization", jsonObject.get("token").toString());
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpGet);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString(), json.get("result"));
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 无人机直播视频
+     */
+    @PostMapping("/openLive")
+    public AjaxResult openLive(@RequestBody Map<String, Object> map) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        R<String> requestResult = remoteService.uavSignUrl();
+        if (requestResult == null || requestResult.getData() == null) {
+            log.error("获取的请求 URL 为空");
+            return AjaxResult.error("获取的请求 URL 为空");
+        }
+        String requestUrl = requestResult.getData();
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            HttpPost httpPost = new HttpPost(requestUrl + "/etkqq/box/openLive");
+            // 使用 JSON 处理库构建请求体
+            String requestBody = "{" +
+                    "\"copterId\": \"" + map.get("copterId") + "\", " +
+                    "\"boxId\": \"" + map.get("boxId") + "\", " +
+                    "\"copterSn\": \"" + map.get("copterSn") + "\", " +
+                    "\"boxSn\": \"" + map.get("boxSn") + "\" ," +
+                    "\"type\": \"" + map.get("type") + "\"}";
+            StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+            httpPost.setHeader("Authorization", jsonObject.get("token").toString());
+
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString(), json.get("result"));
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return AjaxResult.error("请求失败,状态码: " + statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 无人机控制指令
+     */
+    @PostMapping("/realControl")
+    public AjaxResult realControl(@RequestBody Map<String, Object> map) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        R<String> requestResult = remoteService.uavSignUrl();
+        if (requestResult == null || requestResult.getData() == null) {
+            log.error("获取的请求 URL 为空");
+            return AjaxResult.error("获取的请求 URL 为空");
+        }
+        String requestUrl = requestResult.getData();
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            HttpPost httpPost = new HttpPost(requestUrl + "/etkqq/box/realControl");
+            // 使用 JSON 处理库构建请求体
+            LinkedHashMap<String, Object> point = (LinkedHashMap<String, Object>) map.get("point");
+            String requestBody = "{" +
+                    "\"boxId\": \"" + map.get("boxId") + "\", " +
+                    "\"copterId\": \"" + map.get("copterId") + "\", " +
+                    "\"boxSn\": \"" + map.get("boxSn") + "\" ," +
+                    "\"code\": \"" + map.get("code") + "\", " +
+                    "\"speed\": \"" + map.get("speed") + "\", " +
+                    "\"angle\": \"" + map.get("angle") + "\", " +
+                    "\"mode\": \"" + map.get("mode") + "\", " +
+                    "\"point\": {" +
+                    "\"latitude\": \"" + point.get("latitude") + "\", " +
+                    "\"longitude\": \"" + point.get("longitude") + "\", " +
+                    "\"altitude\": \"" + point.get("altitude") + "\"" +
+                    "}" +
+                    "}";
+            StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+            httpPost.setHeader("Authorization", jsonObject.get("token").toString());
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString());
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return AjaxResult.error("请求失败,状态码: " + statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 无人机实时轨迹
+     */
+    @PostMapping("/currentWayline")
+    public AjaxResult currentWayline(@RequestBody Map<String, Object> map) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        R<String> requestResult = remoteService.uavSignUrl();
+        if (requestResult == null || requestResult.getData() == null) {
+            log.error("获取的请求 URL 为空");
+            return AjaxResult.error("获取的请求 URL 为空");
+        }
+        String requestUrl = requestResult.getData();
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            HttpPost httpPost = new HttpPost(requestUrl + "/etkqq/task/currentWayline");
+            // 使用 JSON 处理库构建请求体
+            String requestBody = "{\"copterSn\": \"" + map.get("copterSn") + "\"}";
+            StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+            httpPost.setHeader("Authorization", jsonObject.get("token").toString());
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString(), json.get("result"));
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return AjaxResult.error("请求失败,状态码: " + statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 无人机历史轨迹
+     */
+    @PostMapping("/historyWayline")
+    public AjaxResult historyWayline(@RequestBody Map<String, Object> map) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        R<String> requestResult = remoteService.uavSignUrl();
+        if (requestResult == null || requestResult.getData() == null) {
+            log.error("获取的请求 URL 为空");
+            return AjaxResult.error("获取的请求 URL 为空");
+        }
+        String requestUrl = requestResult.getData();
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            HttpPost httpPost = new HttpPost(requestUrl + "/etkqq/task/historyWayline");
+            // 使用 JSON 处理库构建请求体
+            String requestBody = "{\"copterSn\": \"" + map.get("copterSn") + "\"}";
+            StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+            httpPost.setHeader("Authorization", jsonObject.get("token").toString());
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString(), json.get("result"));
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return AjaxResult.error("请求失败,状态码: " + statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 喊话器喊话
+     */
+    @PostMapping("/drcSpeakerTtsPlayStart")
+    public AjaxResult drcSpeakerTtsPlayStart(@RequestBody Map<String, Object> map) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        R<String> requestResult = remoteService.uavSignUrl();
+        if (requestResult == null || requestResult.getData() == null) {
+            log.error("获取的请求 URL 为空");
+            return AjaxResult.error("获取的请求 URL 为空");
+        }
+        String requestUrl = requestResult.getData();
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            HttpPost httpPost = new HttpPost(requestUrl + "/etkqq/box/drcSpeakerTtsPlayStart");
+            // 使用 JSON 处理库构建请求体
+            String requestBody = "{" +
+                    "\"boxSn\": \"" + map.get("boxSn") + "\" ," +
+                    "\"copterSn\": \"" + map.get("copterSn") + "\", " +
+                    "\"name\": \"" + map.get("name") + "\", " +
+                    "\"text\": \"" + map.get("text") + "\"}";
+            StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+            httpPost.setHeader("Authorization", jsonObject.get("token").toString());
+
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString());
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return AjaxResult.error("请求失败,状态码: " + statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 开始执行计划
+     */
+    @GetMapping("/startPlan")
+    public AjaxResult startPlan(@RequestParam(value = "boxSn") String boxSn, @RequestParam(value = "planId") String planId) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            R<String> requestResult = remoteService.uavSignUrl();
+            if (requestResult == null || requestResult.getData() == null) {
+                log.error("获取的请求 URL 为空");
+                return AjaxResult.error("获取的请求 URL 为空");
+            }
+            String requestUrl = requestResult.getData();
+            // 使用 HttpGet 进行 GET 请求
+            HttpGet httpGet = new HttpGet(requestUrl + "/etkqq/boxMetaData/getBoxBasicData?boxSn=" + boxSn + "&planId=" + planId);
+            httpGet.setHeader("Authorization", jsonObject.get("token").toString());
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpGet);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString());
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 快速执飞
+     */
+    @PostMapping("/fastFlying")
+    public AjaxResult fastFlying(@RequestBody Map<String, Object> map) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        R<String> requestResult = remoteService.uavSignUrl();
+        if (requestResult == null || requestResult.getData() == null) {
+            log.error("获取的请求 URL 为空");
+            return AjaxResult.error("获取的请求 URL 为空");
+        }
+        String requestUrl = requestResult.getData();
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            HttpPost httpPost = new HttpPost(requestUrl + "/etkqq/plan/fastFlying");
+            // 使用 JSON 处理库构建请求体
+            String requestBody = "{" +
+                    "\"boxSn\": \"" + map.get("boxSn") + "\" ," +
+                    "\"boxId\": \"" + map.get("boxId") + "\", " +
+                    "\"copterId\": \"" + map.get("copterId") + "\", " +
+                    "\"copterSn\": \"" + map.get("copterSn") + "\", " +
+                    "\"longitude\": \"" + map.get("longitude") + "\", " +
+                    "\"latitude\": \"" + map.get("latitude") + "\", " +
+                    "\"flySpeed\": \"" + map.get("flySpeed") + "\", " +
+                    "\"boxPosition\": \"" + map.get("boxPosition") + "\", " +
+                    "\"expand2\": \"" + map.get("expand2") + "\"}";
+            StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+            httpPost.setHeader("Authorization", jsonObject.get("token").toString());
+
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString());
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return AjaxResult.error("请求失败,状态码: " + statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 规划航线
+     */
+    //todo 待完善 且调用接口返回500 根据增删改查修改返回类型
+    @PostMapping("/operationTask")
+    public AjaxResult operationTask(@RequestBody CentermonitorTUavDTO dto) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        R<String> requestResult = remoteService.uavSignUrl();
+        if (requestResult == null || requestResult.getData() == null) {
+            log.error("获取的请求 URL 为空");
+            return AjaxResult.error("获取的请求 URL 为空");
+        }
+        LinkedHashMap<String, Object> task = new LinkedHashMap<>();
+        task.put("taskType", dto.getTaskType());
+        task.put("taskName", dto.getTaskName());
+        task.put("heightMode", dto.getHeightMode());
+        task.put("relativeHeight", dto.getRelativeHeight());
+        task.put("globalHeight", dto.getGlobalHeight());
+        task.put("returnAltitude", dto.getReturnAltitude());
+        task.put("completeAction", dto.getCompleteAction());
+        task.put("flySpeed", dto.getFlySpeed());
+        task.put("lostSignal", dto.getLostSignal());
+        task.put("flyCourse", dto.getFlyCourse());
+        task.put("flySeconds", dto.getFlySeconds());
+        task.put("waypointNum", dto.getWaypointNum());
+        task.put("boxPosition", dto.getBoxPosition());
+        task.put("resolution", dto.getResolution());
+        task.put("headingOverlap", dto.getHeadingOverlap());
+        task.put("sideOverlap", dto.getSideOverlap());
+        task.put("heading", dto.getHeading());
+        task.put("wayLineYaw", dto.getWayLineYaw());
+        task.put("cameraId", dto.getCameraId());
+        task.put("wayPointList", dto.getWayPointList());
+        task.put("boxId", dto.getBoxId());
+        task.put("type", dto.getType());
+        String requestUrl = requestResult.getData();
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            HttpPost httpPost = new HttpPost(requestUrl + "/etkqq/task/operationTask");
+            // 使用 JSON 处理库构建请求体
+            String requestBody = JSONObject.toJSONString(task);
+            StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+            httpPost.setHeader("Authorization", jsonObject.get("token").toString());
+
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString());
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return AjaxResult.error("请求失败,状态码: " + statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        //todo websocket
+        WebSocketUsers.sendMessageToUsersByText(JSON.toJSONString(AjaxResult.success("10002",new ArrayList<>())));
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 规划计划
+     */
+    //todo 待完善 且调用接口返回500 根据增删改查修改返回类型
+    @PostMapping("/operationPlan")
+    public AjaxResult operationPlan(@RequestBody Map<String, Object> map) {
+        String token = getToken();
+        JSONObject jsonObject = JSONObject.parseObject(token);
+        if (token == null) {
+            return AjaxResult.error("获取 token 失败");
+        }
+        R<String> requestResult = remoteService.uavSignUrl();
+        if (requestResult == null || requestResult.getData() == null) {
+            log.error("获取的请求 URL 为空");
+            return AjaxResult.error("获取的请求 URL 为空");
+        }
+        String requestUrl = requestResult.getData();
+        try (CloseableHttpClient httpClient = createCustomHttpClient()) {
+            HttpPost httpPost = new HttpPost(requestUrl + "/etkqq/plan/operationPlan");
+            // 使用 JSON 处理库构建请求体
+            String requestBody = "{" +
+                    "\"caption\": \"" + map.get("caption") + "\" ," +
+                    "\"planType\": \"" + map.get("planType") + "\", " +
+                    "\"businessType\": \"" + map.get("businessType") + "\", " +
+                    "\"isAi\": \"" + map.get("isAi") + "\", " +
+                    "\"aiMode\": \"" + map.get("aiMode") + "\", " +
+                    "\"isHover\": \"" + map.get("isHover") + "\", " +
+                    "\"taskId\": \"" + map.get("taskId") + "\", " +
+                    "\"type\": \"" + map.get("type") + "\", " +
+                    "\"boxId\": \"" + map.get("boxId") + "\"}";
+            StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+            httpPost.setHeader("Authorization", jsonObject.get("token").toString());
+
+            // 发送请求
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200) {
+                HttpEntity responseEntity = response.getEntity();
+                if (responseEntity != null) {
+                    JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
+                    return AjaxResult.success(json.get("retMsg").toString());
+                }
+            } else {
+                log.error("请求失败,状态码: {}", statusCode);
+                return AjaxResult.error("请求失败,状态码: " + statusCode);
+            }
+        } catch (Exception e) {
+            log.error("发生未知异常", e);
+        }
+        return AjaxResult.success("操作成功,暂无数据");
+    }
+
+    /**
+     * 无人机机库实时数据
+     */
+    //todo 未测试接口连通性
+    @GetMapping("/getHangarRealTimeData")
+    public String getHangarRealTimeData(@RequestParam(value = "copterSn") String copterSn,@RequestParam(value = "boxSn") String boxSn) {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final StringBuilder response = new StringBuilder();
+        try {
+            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
+            String uri = "wss://47.93.50.30/etkqq/api/websocket/100";
+
+            Session session = container.connectToServer(new Endpoint() {
+                @Override
+                public void onOpen(Session session, EndpointConfig config) {
+                    String message = String.format("{\"copterSn\": \"%s\", \"boxSn\": \"%s\"}",
+                            copterSn, boxSn);
+                    session.addMessageHandler(new MessageHandler.Whole<String>() {
+                        @Override
+                        public void onMessage(String msg) {
+                            response.append(msg);
+                            latch.countDown();
+                        }
+                    });
+                    session.getAsyncRemote().sendText(message);
+                }
+            }, ClientEndpointConfig.Builder.create().build(), new URI(uri));
+            latch.await(10, TimeUnit.SECONDS);
+            session.close();
+            return response.toString();
+        } catch (Exception e) {
+            return "Error: " + e.getMessage();
+        }
+    }
+
+    /**
+     * 机库日志
+     */
+    //todo 未测试接口连通性
+    @GetMapping("/getHangarLog")
+    public String getHangarLog(@RequestParam(value = "copterSn") String copterSn,@RequestParam(value = "boxSn") String boxSn) {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final StringBuilder response = new StringBuilder();
+        try {
+            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
+            String uri = "wss://47.93.50.30/etkqq/api/websocket/102";
+
+            Session session = container.connectToServer(new Endpoint() {
+                @Override
+                public void onOpen(Session session, EndpointConfig config) {
+                    String message = String.format("{\"copterSn\": \"%s\", \"boxSn\": \"%s\"}",
+                            copterSn, boxSn);
+                    session.addMessageHandler(new MessageHandler.Whole<String>() {
+                        @Override
+                        public void onMessage(String msg) {
+                            response.append(msg);
+                            latch.countDown();
+                        }
+                    });
+                    session.getAsyncRemote().sendText(message);
+                }
+            }, ClientEndpointConfig.Builder.create().build(), new URI(uri));
+            latch.await(10, TimeUnit.SECONDS);
+            session.close();
+            return response.toString();
+        } catch (Exception e) {
+            return "Error: " + e.getMessage();
+        }
+    }
+
+    /**
+     * 工厂方法:创建自定义配置的 HttpClient
+     *
+     * @return 配置好的 CloseableHttpClient 实例
+     * @throws Exception 如果配置过程中发生错误
+     */
+    private CloseableHttpClient createCustomHttpClient() throws Exception {
+        // 设置请求超时配置
+        RequestConfig requestConfig = RequestConfig.custom()
+                .setConnectTimeout(5000) // 连接超时:5秒
+                .setSocketTimeout(10000) // 读取超时:10秒
+                .setConnectionRequestTimeout(3000) // 请求连接超时:3秒
+                .build();
+
+        // 配置 SSL 上下文(可根据需求启用或禁用 SSL 验证)
+        SSLContext sslContext = SSLContextBuilder.create()
+                .loadTrustMaterial((chain, authType) -> true) // 忽略证书验证(仅用于测试环境)
+                .build();
+
+        return HttpClients.custom()
+                .setDefaultRequestConfig(requestConfig)
+                .setSSLContext(sslContext)
+                .setSSLHostnameVerifier(new NoopHostnameVerifier()) // 忽略主机名验证(仅用于测试环境)
+                .build();
+    }
+
+    /**
+     * 查询无人机列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo list(CentermonitorTUav centermonitorTUav) {
+        startPage();
+        List<CentermonitorTUav> list = centermonitorTUavService.selectCentermonitorTUavList(centermonitorTUav);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出无人机列表
+     */
+    @Log(title = "无人机", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, CentermonitorTUav centermonitorTUav) {
+        List<CentermonitorTUav> list = centermonitorTUavService.selectCentermonitorTUavList(centermonitorTUav);
+        ExcelUtil<CentermonitorTUav> util = new ExcelUtil<CentermonitorTUav>(CentermonitorTUav.class);
+        util.exportExcel(response, list, "无人机数据");
+    }
+
+    /**
+     * 获取无人机详细信息
+     */
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return AjaxResult.success(centermonitorTUavService.selectCentermonitorTUavById(id));
+    }
+
+    /**
+     * 新增无人机
+     */
+    @Log(title = "无人机", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CentermonitorTUav centermonitorTUav) {
+        return toAjax(centermonitorTUavService.insertCentermonitorTUav(centermonitorTUav));
+    }
+
+    /**
+     * 修改无人机
+     */
+    @Log(title = "无人机", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CentermonitorTUav centermonitorTUav) {
+        return toAjax(centermonitorTUavService.updateCentermonitorTUav(centermonitorTUav));
+    }
+
+    /**
+     * 删除无人机
+     */
+    @Log(title = "无人机", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(centermonitorTUavService.deleteCentermonitorTUavByIds(ids));
+    }
+
+    /**
+     * 根据部门id查询无人机列表
+     *
+     * @return
+     */
+    @GetMapping("/selectCentermonitorTUavByDeptId")
+    public AjaxResult selectCentermonitorTUavByDeptId() {
+        return AjaxResult.success(centermonitorTUavService.selectCentermonitorTUavByDeptId());
+    }
+
+    /**
+     * 根据uavId获取无人机及所属部门集合
+     */
+    @GetMapping("/selectUavByUavId/{uavId}")
+    public AjaxResult selectUavByUavId(@PathVariable("uavId") Long uavId) {
+        return AjaxResult.success(centermonitorTUavService.selectUavByUavId(uavId));
+    }
+
+}

+ 85 - 0
src/main/java/com/sooka/sponest/monitor/uav/controller/CentermonitorTUavdeptController.java

@@ -0,0 +1,85 @@
+package com.sooka.sponest.monitor.uav.controller;
+
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.annotation.RequiresPermissions;
+import com.sooka.sponest.monitor.uav.domain.CentermonitorTUavdept;
+import com.sooka.sponest.monitor.uav.service.ICentermonitorTUavdeptService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 无人机部门关联Controller
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+@RestController
+@RequestMapping("/uavdept")
+public class CentermonitorTUavdeptController extends BaseController {
+    @Autowired
+    private ICentermonitorTUavdeptService centermonitorTUavdeptService;
+
+    /**
+     * 查询无人机部门关联列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo list(CentermonitorTUavdept centermonitorTUavdept) {
+        startPage();
+        List<CentermonitorTUavdept> list = centermonitorTUavdeptService.selectCentermonitorTUavdeptList(centermonitorTUavdept);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出无人机部门关联列表
+     */
+    @Log(title = "无人机部门关联", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, CentermonitorTUavdept centermonitorTUavdept) {
+        List<CentermonitorTUavdept> list = centermonitorTUavdeptService.selectCentermonitorTUavdeptList(centermonitorTUavdept);
+        ExcelUtil<CentermonitorTUavdept> util = new ExcelUtil<CentermonitorTUavdept>(CentermonitorTUavdept.class);
+        util.exportExcel(response, list, "无人机部门关联数据");
+    }
+
+    /**
+     * 获取无人机部门关联详细信息
+     */
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return AjaxResult.success(centermonitorTUavdeptService.selectCentermonitorTUavdeptById(id));
+    }
+
+    /**
+     * 新增无人机部门关联
+     */
+    @Log(title = "无人机部门关联", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CentermonitorTUavdept centermonitorTUavdept) {
+        return toAjax(centermonitorTUavdeptService.insertCentermonitorTUavdept(centermonitorTUavdept));
+    }
+
+    /**
+     * 修改无人机部门关联
+     */
+    @Log(title = "无人机部门关联", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CentermonitorTUavdept centermonitorTUavdept) {
+        return toAjax(centermonitorTUavdeptService.updateCentermonitorTUavdept(centermonitorTUavdept));
+    }
+
+    /**
+     * 删除无人机部门关联
+     */
+    @Log(title = "无人机部门关联", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(centermonitorTUavdeptService.deleteCentermonitorTUavdeptByIds(ids));
+    }
+}

+ 218 - 0
src/main/java/com/sooka/sponest/monitor/uav/domain/CentermonitorTUav.java

@@ -0,0 +1,218 @@
+package com.sooka.sponest.monitor.uav.domain;
+
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.util.List;
+
+/**
+ * 无人机对象 centermonitor_t_uav
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+public class CentermonitorTUav extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    private Long id;
+
+    /**
+     * 基站名称
+     */
+    @Excel(name = "基站名称")
+    private String baseStationName;
+
+    /**
+     * 机库ID
+     */
+    @Excel(name = "机库ID")
+    private String baseStationId;
+
+    /**
+     * 机库SN
+     */
+    @Excel(name = "机库SN")
+    private String baseStationSn;
+
+    /**
+     * 经度
+     */
+    @Excel(name = "经度")
+    private String longitude;
+
+    /**
+     * 维度
+     */
+    @Excel(name = "维度")
+    private String latitude;
+
+    /**
+     * 高度
+     */
+    @Excel(name = "高度")
+    private String height;
+
+    /**
+     * 无人机名称
+     */
+    @Excel(name = "无人机名称")
+    private String uavName;
+
+    /**
+     * 无人机ID
+     */
+    @Excel(name = "无人机ID")
+    private String uavId;
+
+    /**
+     * 无人机SN
+     */
+    @Excel(name = "无人机SN")
+    private String uavSn;
+
+    /**
+     * 部门id
+     */
+    @Excel(name = "部门id")
+    private String deptId;
+
+    /**
+     * 部门名称
+     */
+    @Excel(name = "部门名称")
+    private String deptName;
+
+    /**
+     * 部门集合
+     */
+    @Excel(name = "部门集合")
+    private List<CentermonitorTUavdept> deptList;
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setBaseStationName(String baseStationName) {
+        this.baseStationName = baseStationName;
+    }
+
+    public String getBaseStationName() {
+        return baseStationName;
+    }
+
+    public void setBaseStationId(String baseStationId) {
+        this.baseStationId = baseStationId;
+    }
+
+    public String getBaseStationId() {
+        return baseStationId;
+    }
+
+    public void setBaseStationSn(String baseStationSn) {
+        this.baseStationSn = baseStationSn;
+    }
+
+    public String getBaseStationSn() {
+        return baseStationSn;
+    }
+
+    public void setLongitude(String longitude) {
+        this.longitude = longitude;
+    }
+
+    public String getLongitude() {
+        return longitude;
+    }
+
+    public void setLatitude(String latitude) {
+        this.latitude = latitude;
+    }
+
+    public String getLatitude() {
+        return latitude;
+    }
+
+    public void setHeight(String height) {
+        this.height = height;
+    }
+
+    public String getHeight() {
+        return height;
+    }
+
+    public void setUavName(String uavName) {
+        this.uavName = uavName;
+    }
+
+    public String getUavName() {
+        return uavName;
+    }
+
+    public void setUavId(String uavId) {
+        this.uavId = uavId;
+    }
+
+    public String getUavId() {
+        return uavId;
+    }
+
+    public void setUavSn(String uavSn) {
+        this.uavSn = uavSn;
+    }
+
+    public String getUavSn() {
+        return uavSn;
+    }
+
+    public void setDeptId(String deptId) {
+        this.deptId = deptId;
+    }
+
+    public String getDeptId() {
+        return deptId;
+    }
+
+    public String getDeptName() {
+        return deptName;
+    }
+
+    public void setDeptName(String deptName) {
+        this.deptName = deptName;
+    }
+
+    public List<CentermonitorTUavdept> getDeptList() {
+        return deptList;
+    }
+
+    public void setDeptList(List<CentermonitorTUavdept> deptList) {
+        this.deptList = deptList;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("id", getId())
+                .append("baseStationName", getBaseStationName())
+                .append("baseStationId", getBaseStationId())
+                .append("baseStationSn", getBaseStationSn())
+                .append("longitude", getLongitude())
+                .append("latitude", getLatitude())
+                .append("height", getHeight())
+                .append("uavName", getUavName())
+                .append("uavId", getUavId())
+                .append("uavSn", getUavSn())
+                .append("deptId", getDeptId())
+                .append("deptName", getDeptName())
+                .append("deptList", getDeptList())
+                .toString();
+    }
+}

+ 48 - 0
src/main/java/com/sooka/sponest/monitor/uav/domain/CentermonitorTUavdept.java

@@ -0,0 +1,48 @@
+package com.sooka.sponest.monitor.uav.domain;
+
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 无人机部门关联对象 centermonitor_t_uavdept
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+@Data
+public class CentermonitorTUavdept extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    private Long id;
+
+    /**
+     * 无人机id
+     */
+    @Excel(name = "无人机id")
+    private Long uavId;
+
+    /**
+     * 无人机名称
+     */
+    @Excel(name = "无人机名称")
+    private String uavName;
+
+    /**
+     * 所属部门id
+     */
+    @Excel(name = "所属部门id")
+    private Long deptId;
+
+    /**
+     * 所属部门名称
+     */
+    @Excel(name = "所属部门名称")
+    private String deptName;
+
+}

+ 50 - 0
src/main/java/com/sooka/sponest/monitor/uav/domain/dto/CentermonitorTUavDTO.java

@@ -0,0 +1,50 @@
+package com.sooka.sponest.monitor.uav.domain.dto;
+
+import com.ruoyi.common.core.web.domain.BaseEntity;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class CentermonitorTUavDTO {
+    private Integer taskType;
+    private String taskName;
+    private Integer heightMode;
+    private Integer relativeHeight;
+    private Integer globalHeight;
+    private Integer returnAltitude;
+    private Integer completeAction;
+    private Integer flySpeed;
+    private Integer lostSignal;
+    private Integer flyCourse;
+    private Integer flySeconds;
+    private Integer waypointNum;
+    private String boxPosition;
+    private Double resolution;
+    private Integer headingOverlap;
+    private Integer sideOverlap;
+    private Integer heading;
+    private Integer wayLineYaw;
+    private Integer cameraId;
+    private List<WayPoint> wayPointList;
+    private Integer boxId;
+    private String type;
+
+    @Data
+    public static class WayPoint {
+        private Integer number;
+        private String latitude;
+        private String longitude;
+        private Double relativeHeight;
+        private Double altitude;
+        private String expand2;
+        private List<Action> actions;
+    }
+
+    @Data
+    public static class Action {
+        private Integer number;
+        private Integer actionType;
+        private Integer actionParam;
+    }
+}

+ 72 - 0
src/main/java/com/sooka/sponest/monitor/uav/mapper/CentermonitorTUavMapper.java

@@ -0,0 +1,72 @@
+package com.sooka.sponest.monitor.uav.mapper;
+
+import com.sooka.sponest.monitor.uav.domain.CentermonitorTUav;
+import com.sooka.sponest.monitor.uav.domain.CentermonitorTUavdept;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 无人机Mapper接口
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+public interface CentermonitorTUavMapper {
+    /**
+     * 查询无人机
+     *
+     * @param id 无人机主键
+     * @return 无人机
+     */
+    public CentermonitorTUav selectCentermonitorTUavById(Long id);
+
+    /**
+     * 查询无人机列表
+     *
+     * @param centermonitorTUav 无人机
+     * @return 无人机集合
+     */
+    public List<CentermonitorTUav> selectCentermonitorTUavList(CentermonitorTUav centermonitorTUav);
+
+    /**
+     * 新增无人机
+     *
+     * @param centermonitorTUav 无人机
+     * @return 结果
+     */
+    public int insertCentermonitorTUav(CentermonitorTUav centermonitorTUav);
+
+    /**
+     * 修改无人机
+     *
+     * @param centermonitorTUav 无人机
+     * @return 结果
+     */
+    public int updateCentermonitorTUav(CentermonitorTUav centermonitorTUav);
+
+    /**
+     * 删除无人机
+     *
+     * @param id 无人机主键
+     * @return 结果
+     */
+    public int deleteCentermonitorTUavById(Long id);
+
+    /**
+     * 批量删除无人机
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteCentermonitorTUavByIds(Long[] ids);
+
+    /**
+     * 根据部门id查询无人机列表
+     * @param deptId
+     * @return
+     */
+    List<CentermonitorTUav> selectCentermonitorTUavByDeptId(@Param("deptId") String deptId);
+
+    CentermonitorTUav selectUavByUavId(@Param("uavId") Long uavId);
+}

+ 65 - 0
src/main/java/com/sooka/sponest/monitor/uav/mapper/CentermonitorTUavdeptMapper.java

@@ -0,0 +1,65 @@
+package com.sooka.sponest.monitor.uav.mapper;
+
+import com.sooka.sponest.monitor.uav.domain.CentermonitorTUavdept;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 无人机部门关联Mapper接口
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+public interface CentermonitorTUavdeptMapper {
+    /**
+     * 查询无人机部门关联
+     *
+     * @param id 无人机部门关联主键
+     * @return 无人机部门关联
+     */
+    public CentermonitorTUavdept selectCentermonitorTUavdeptById(Long id);
+
+    /**
+     * 查询无人机部门关联列表
+     *
+     * @param centermonitorTUavdept 无人机部门关联
+     * @return 无人机部门关联集合
+     */
+    public List<CentermonitorTUavdept> selectCentermonitorTUavdeptList(CentermonitorTUavdept centermonitorTUavdept);
+
+    /**
+     * 新增无人机部门关联
+     *
+     * @param centermonitorTUavdept 无人机部门关联
+     * @return 结果
+     */
+    public int insertCentermonitorTUavdept(CentermonitorTUavdept centermonitorTUavdept);
+
+    /**
+     * 修改无人机部门关联
+     *
+     * @param centermonitorTUavdept 无人机部门关联
+     * @return 结果
+     */
+    public int updateCentermonitorTUavdept(CentermonitorTUavdept centermonitorTUavdept);
+
+    /**
+     * 删除无人机部门关联
+     *
+     * @param id 无人机部门关联主键
+     * @return 结果
+     */
+    public int deleteCentermonitorTUavdeptById(Long id);
+
+    /**
+     * 批量删除无人机部门关联
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteCentermonitorTUavdeptByIds(Long[] ids);
+
+    public List<CentermonitorTUavdept> selectUavdeptByUavId(@Param("uavId") Long uavId);
+
+}

+ 69 - 0
src/main/java/com/sooka/sponest/monitor/uav/service/ICentermonitorTUavService.java

@@ -0,0 +1,69 @@
+package com.sooka.sponest.monitor.uav.service;
+
+import com.sooka.sponest.monitor.uav.domain.CentermonitorTUav;
+
+import java.util.List;
+
+/**
+ * 无人机Service接口
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+public interface ICentermonitorTUavService {
+    /**
+     * 查询无人机
+     *
+     * @param id 无人机主键
+     * @return 无人机
+     */
+    public CentermonitorTUav selectCentermonitorTUavById(Long id);
+
+    /**
+     * 查询无人机列表
+     *
+     * @param centermonitorTUav 无人机
+     * @return 无人机集合
+     */
+    public List<CentermonitorTUav> selectCentermonitorTUavList(CentermonitorTUav centermonitorTUav);
+
+    /**
+     * 新增无人机
+     *
+     * @param centermonitorTUav 无人机
+     * @return 结果
+     */
+    public int insertCentermonitorTUav(CentermonitorTUav centermonitorTUav);
+
+    /**
+     * 修改无人机
+     *
+     * @param centermonitorTUav 无人机
+     * @return 结果
+     */
+    public int updateCentermonitorTUav(CentermonitorTUav centermonitorTUav);
+
+    /**
+     * 批量删除无人机
+     *
+     * @param ids 需要删除的无人机主键集合
+     * @return 结果
+     */
+    public int deleteCentermonitorTUavByIds(Long[] ids);
+
+    /**
+     * 删除无人机信息
+     *
+     * @param id 无人机主键
+     * @return 结果
+     */
+    public int deleteCentermonitorTUavById(Long id);
+
+    /**
+     * 根据部门id查询无人机列表
+     * @return
+     */
+    List<CentermonitorTUav> selectCentermonitorTUavByDeptId();
+
+    CentermonitorTUav selectUavByUavId(Long uavId);
+}

+ 61 - 0
src/main/java/com/sooka/sponest/monitor/uav/service/ICentermonitorTUavdeptService.java

@@ -0,0 +1,61 @@
+package com.sooka.sponest.monitor.uav.service;
+
+import com.sooka.sponest.monitor.uav.domain.CentermonitorTUavdept;
+
+import java.util.List;
+
+/**
+ * 无人机部门关联Service接口
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+public interface ICentermonitorTUavdeptService {
+    /**
+     * 查询无人机部门关联
+     *
+     * @param id 无人机部门关联主键
+     * @return 无人机部门关联
+     */
+    public CentermonitorTUavdept selectCentermonitorTUavdeptById(Long id);
+
+    /**
+     * 查询无人机部门关联列表
+     *
+     * @param centermonitorTUavdept 无人机部门关联
+     * @return 无人机部门关联集合
+     */
+    public List<CentermonitorTUavdept> selectCentermonitorTUavdeptList(CentermonitorTUavdept centermonitorTUavdept);
+
+    /**
+     * 新增无人机部门关联
+     *
+     * @param centermonitorTUavdept 无人机部门关联
+     * @return 结果
+     */
+    public int insertCentermonitorTUavdept(CentermonitorTUavdept centermonitorTUavdept);
+
+    /**
+     * 修改无人机部门关联
+     *
+     * @param centermonitorTUavdept 无人机部门关联
+     * @return 结果
+     */
+    public int updateCentermonitorTUavdept(CentermonitorTUavdept centermonitorTUavdept);
+
+    /**
+     * 批量删除无人机部门关联
+     *
+     * @param ids 需要删除的无人机部门关联主键集合
+     * @return 结果
+     */
+    public int deleteCentermonitorTUavdeptByIds(Long[] ids);
+
+    /**
+     * 删除无人机部门关联信息
+     *
+     * @param id 无人机部门关联主键
+     * @return 结果
+     */
+    public int deleteCentermonitorTUavdeptById(Long id);
+}

+ 113 - 0
src/main/java/com/sooka/sponest/monitor/uav/service/impl/CentermonitorTUavServiceImpl.java

@@ -0,0 +1,113 @@
+package com.sooka.sponest.monitor.uav.service.impl;
+
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.sooka.sponest.monitor.uav.domain.CentermonitorTUav;
+import com.sooka.sponest.monitor.uav.mapper.CentermonitorTUavMapper;
+import com.sooka.sponest.monitor.uav.mapper.CentermonitorTUavdeptMapper;
+import com.sooka.sponest.monitor.uav.service.ICentermonitorTUavService;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+
+/**
+ * 无人机Service业务层处理
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+@Service
+public class CentermonitorTUavServiceImpl implements ICentermonitorTUavService {
+    @Autowired
+    private CentermonitorTUavMapper centermonitorTUavMapper;
+    @Autowired
+    private CentermonitorTUavdeptMapper centermonitorTUavdeptMapper;
+
+    /**
+     * 查询无人机
+     *
+     * @param id 无人机主键
+     * @return 无人机
+     */
+    @Override
+    public CentermonitorTUav selectCentermonitorTUavById(Long id) {
+        return centermonitorTUavMapper.selectCentermonitorTUavById(id);
+    }
+
+    /**
+     * 查询无人机列表
+     *
+     * @param centermonitorTUav 无人机
+     * @return 无人机
+     */
+    @Override
+    public List<CentermonitorTUav> selectCentermonitorTUavList(CentermonitorTUav centermonitorTUav) {
+        return centermonitorTUavMapper.selectCentermonitorTUavList(centermonitorTUav);
+    }
+
+    /**
+     * 新增无人机
+     *
+     * @param centermonitorTUav 无人机
+     * @return 结果
+     */
+    @Override
+    public int insertCentermonitorTUav(CentermonitorTUav centermonitorTUav) {
+        return centermonitorTUavMapper.insertCentermonitorTUav(centermonitorTUav);
+    }
+
+    /**
+     * 修改无人机
+     *
+     * @param centermonitorTUav 无人机
+     * @return 结果
+     */
+    @Override
+    public int updateCentermonitorTUav(CentermonitorTUav centermonitorTUav) {
+        return centermonitorTUavMapper.updateCentermonitorTUav(centermonitorTUav);
+    }
+
+    /**
+     * 批量删除无人机
+     *
+     * @param ids 需要删除的无人机主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCentermonitorTUavByIds(Long[] ids) {
+        return centermonitorTUavMapper.deleteCentermonitorTUavByIds(ids);
+    }
+
+    /**
+     * 删除无人机信息
+     *
+     * @param id 无人机主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCentermonitorTUavById(Long id) {
+        return centermonitorTUavMapper.deleteCentermonitorTUavById(id);
+    }
+
+    /**
+     * 根据部门id查询无人机列表
+     *
+     * @return
+     */
+    @Override
+    public List<CentermonitorTUav> selectCentermonitorTUavByDeptId() {
+        Long deptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();
+        return centermonitorTUavMapper.selectCentermonitorTUavByDeptId(String.valueOf(deptId));
+    }
+
+    @Override
+    public CentermonitorTUav selectUavByUavId(Long uavId) {
+        CentermonitorTUav centermonitorTUav = centermonitorTUavMapper.selectUavByUavId(uavId);
+        if (ObjectUtils.isNotEmpty(centermonitorTUav)) {
+            centermonitorTUav.setDeptList(centermonitorTUavdeptMapper.selectUavdeptByUavId(centermonitorTUav.getId()));
+        }
+        return centermonitorTUav;
+    }
+}

+ 91 - 0
src/main/java/com/sooka/sponest/monitor/uav/service/impl/CentermonitorTUavdeptServiceImpl.java

@@ -0,0 +1,91 @@
+package com.sooka.sponest.monitor.uav.service.impl;
+
+import com.sooka.sponest.monitor.uav.domain.CentermonitorTUavdept;
+import com.sooka.sponest.monitor.uav.mapper.CentermonitorTUavdeptMapper;
+import com.sooka.sponest.monitor.uav.service.ICentermonitorTUavdeptService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * 无人机部门关联Service业务层处理
+ *
+ * @author ruoyi
+ * @date 2025-03-19
+ */
+@Service
+public class CentermonitorTUavdeptServiceImpl implements ICentermonitorTUavdeptService {
+    @Autowired
+    private CentermonitorTUavdeptMapper centermonitorTUavdeptMapper;
+
+    /**
+     * 查询无人机部门关联
+     *
+     * @param id 无人机部门关联主键
+     * @return 无人机部门关联
+     */
+    @Override
+    public CentermonitorTUavdept selectCentermonitorTUavdeptById(Long id) {
+        return centermonitorTUavdeptMapper.selectCentermonitorTUavdeptById(id);
+    }
+
+    /**
+     * 查询无人机部门关联列表
+     *
+     * @param centermonitorTUavdept 无人机部门关联
+     * @return 无人机部门关联
+     */
+    @Override
+    public List<CentermonitorTUavdept> selectCentermonitorTUavdeptList(CentermonitorTUavdept centermonitorTUavdept) {
+        return centermonitorTUavdeptMapper.selectCentermonitorTUavdeptList(centermonitorTUavdept);
+    }
+
+    /**
+     * 新增无人机部门关联
+     *
+     * @param centermonitorTUavdept 无人机部门关联
+     * @return 结果
+     */
+    @Override
+    public int insertCentermonitorTUavdept(CentermonitorTUavdept centermonitorTUavdept) {
+        centermonitorTUavdept.setCreateTime(new Date());
+        return centermonitorTUavdeptMapper.insertCentermonitorTUavdept(centermonitorTUavdept);
+    }
+
+    /**
+     * 修改无人机部门关联
+     *
+     * @param centermonitorTUavdept 无人机部门关联
+     * @return 结果
+     */
+    @Override
+    public int updateCentermonitorTUavdept(CentermonitorTUavdept centermonitorTUavdept) {
+        centermonitorTUavdept.setUpdateTime(new Date());
+        return centermonitorTUavdeptMapper.updateCentermonitorTUavdept(centermonitorTUavdept);
+    }
+
+    /**
+     * 批量删除无人机部门关联
+     *
+     * @param ids 需要删除的无人机部门关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCentermonitorTUavdeptByIds(Long[] ids) {
+        return centermonitorTUavdeptMapper.deleteCentermonitorTUavdeptByIds(ids);
+    }
+
+    /**
+     * 删除无人机部门关联信息
+     *
+     * @param id 无人机部门关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCentermonitorTUavdeptById(Long id) {
+        return centermonitorTUavdeptMapper.deleteCentermonitorTUavdeptById(id);
+    }
+}

+ 51 - 0
src/main/java/com/sooka/sponest/monitor/websocket/SemaphoreUtils.java

@@ -0,0 +1,51 @@
+package com.sooka.sponest.monitor.websocket;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Semaphore;
+
+/**
+ * 信号量相关处理
+ *
+ * @author ruoyi
+ */
+public class SemaphoreUtils {
+
+    /**
+     * SemaphoreUtils 日志控制器
+     */
+    private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class);
+
+    /**
+     * 获取信号量
+     *
+     * @param semaphore
+     * @return
+     */
+    public static boolean tryAcquire(Semaphore semaphore) {
+        boolean flag = false;
+
+        try {
+            flag = semaphore.tryAcquire();
+        } catch (Exception e) {
+            LOGGER.error("获取信号量异常", e);
+        }
+
+        return flag;
+    }
+
+    /**
+     * 释放信号量
+     *
+     * @param semaphore
+     */
+    public static void release(Semaphore semaphore) {
+
+        try {
+            semaphore.release();
+        } catch (Exception e) {
+            LOGGER.error("释放信号量异常", e);
+        }
+    }
+}

+ 19 - 0
src/main/java/com/sooka/sponest/monitor/websocket/WebSocketConfig.java

@@ -0,0 +1,19 @@
+package com.sooka.sponest.monitor.websocket;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+
+/**
+ * websocket 配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+public class WebSocketConfig {
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+}

+ 91 - 0
src/main/java/com/sooka/sponest/monitor/websocket/WebSocketServer.java

@@ -0,0 +1,91 @@
+package com.sooka.sponest.monitor.websocket;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.websocket.*;
+import javax.websocket.server.ServerEndpoint;
+import java.util.concurrent.Semaphore;
+
+/**
+ * websocket 消息处理
+ *
+ * @author ruoyi
+ */
+@Component
+@ServerEndpoint("/ws")
+public class WebSocketServer {
+    /**
+     * WebSocketServer 日志控制器
+     */
+    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);
+
+    /**
+     * 默认最多允许同时在线人数100
+     */
+    public static int socketMaxOnlineCount = 100;
+
+    private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount);
+
+    /**
+     * 连接建立成功调用的方法
+     */
+    @OnOpen
+    public void onOpen(Session session) throws Exception {
+        boolean semaphoreFlag = false;
+        // 尝试获取信号量
+        semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore);
+        if (!semaphoreFlag) {
+            // 未获取到信号量
+            LOGGER.error("\n 当前在线人数超过限制数: {}", socketMaxOnlineCount);
+            WebSocketUsers.sendMessageToUserByText(session, "当前在线人数超过限制数:" + socketMaxOnlineCount);
+            session.close();
+        } else {
+            // 添加用户
+            WebSocketUsers.put(session.getId(), session);
+            LOGGER.info("\n 建立连接 : {}", session);
+            LOGGER.info("\n 当前人数 : {}", WebSocketUsers.getUsers().size());
+            WebSocketUsers.sendMessageToUserByText(session, "连接成功");
+        }
+    }
+
+    /**
+     * 连接关闭时处理
+     */
+    @OnClose
+    public void onClose(Session session) {
+        LOGGER.info("\n 关闭连接 : {}", session);
+        // 移除用户
+        WebSocketUsers.remove(session.getId());
+        // 获取到信号量则需释放
+        SemaphoreUtils.release(socketSemaphore);
+    }
+
+    /**
+     * 抛出异常时处理
+     */
+    @OnError
+    public void onError(Session session, Throwable exception) throws Exception {
+        if (session.isOpen()) {
+            // 关闭连接
+            session.close();
+        }
+        String sessionId = session.getId();
+        LOGGER.info("\n 连接异常 : {}", sessionId);
+        LOGGER.info("\n 异常信息 : {}", exception);
+        // 移出用户
+        WebSocketUsers.remove(sessionId);
+        // 获取到信号量则需释放
+        SemaphoreUtils.release(socketSemaphore);
+    }
+
+    /**
+     * 服务器接收到客户端消息时调用的方法
+     */
+    @OnMessage
+    public void onMessage(String message, Session session) {
+        String msg = message.replace("你", "我").replace("吗", "");
+        WebSocketUsers.sendMessageToUserByText(session, msg);
+    }
+}

+ 121 - 0
src/main/java/com/sooka/sponest/monitor/websocket/WebSocketUsers.java

@@ -0,0 +1,121 @@
+package com.sooka.sponest.monitor.websocket;
+
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.websocket.Session;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * websocket 客户端用户集
+ *
+ * @author ruoyi
+ */
+@Slf4j
+public class WebSocketUsers {
+    /**
+     * WebSocketUsers 日志控制器
+     */
+    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class);
+
+    /**
+     * 用户集
+     */
+    private static Map<String, Session> USERS = new ConcurrentHashMap<String, Session>();
+
+    /**
+     * 存储用户
+     *
+     * @param key     唯一键
+     * @param session 用户信息
+     */
+    public static void put(String key, Session session) {
+        USERS.put(key, session);
+    }
+
+    /**
+     * 移除用户
+     *
+     * @param session 用户信息
+     * @return 移除结果
+     */
+    public static boolean remove(Session session) {
+        String key = null;
+        boolean flag = USERS.containsValue(session);
+        if (flag) {
+            Set<Map.Entry<String, Session>> entries = USERS.entrySet();
+            for (Map.Entry<String, Session> entry : entries) {
+                Session value = entry.getValue();
+                if (value.equals(session)) {
+                    key = entry.getKey();
+                    break;
+                }
+            }
+        } else {
+            return true;
+        }
+        return remove(key);
+    }
+
+    /**
+     * 移出用户
+     *
+     * @param key 键
+     */
+    public static boolean remove(String key) {
+        LOGGER.info("\n 正在移出用户 - {}", key);
+        Session remove = USERS.remove(key);
+        if (remove != null) {
+            boolean containsValue = USERS.containsValue(remove);
+            LOGGER.info("\n 移出结果 - {}", containsValue ? "失败" : "成功");
+            return containsValue;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * 获取在线用户列表
+     *
+     * @return 返回用户集合
+     */
+    public static Map<String, Session> getUsers() {
+        return USERS;
+    }
+
+    /**
+     * 群发消息文本消息
+     *
+     * @param message 消息内容
+     */
+    public static void sendMessageToUsersByText(String message) {
+        Collection<Session> values = USERS.values();
+        for (Session value : values) {
+            sendMessageToUserByText(value, message);
+            log.info("message:{}", message);
+        }
+    }
+
+    /**
+     * 发送文本消息
+     *
+     * @param userName 自己的用户名
+     * @param message  消息内容
+     */
+    public static void sendMessageToUserByText(Session session, String message) {
+        if (session != null) {
+            try {
+                session.getBasicRemote().sendText(message);
+            } catch (IOException e) {
+                LOGGER.error("\n[发送消息异常]", e);
+            }
+        } else {
+            LOGGER.info("\n[你已离线]");
+        }
+    }
+}

+ 132 - 0
src/main/resources/mapper/monitor/uav/CentermonitorTUavMapper.xml

@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.sooka.sponest.monitor.uav.mapper.CentermonitorTUavMapper">
+
+    <resultMap type="CentermonitorTUav" id="CentermonitorTUavResult">
+        <result property="id" column="id"/>
+        <result property="baseStationName" column="base_station_name"/>
+        <result property="baseStationId" column="base_station_id"/>
+        <result property="baseStationSn" column="base_station_sn"/>
+        <result property="longitude" column="longitude"/>
+        <result property="latitude" column="latitude"/>
+        <result property="height" column="height"/>
+        <result property="uavName" column="uav_name"/>
+        <result property="uavId" column="uav_id"/>
+        <result property="uavSn" column="uav_sn"/>
+        <result property="deptId" column="dept_id"/>
+        <result property="deptName" column="dept_name"/>
+    </resultMap>
+
+    <sql id="selectCentermonitorTUavVo">
+        select id,
+               base_station_name,
+               base_station_id,
+               base_station_sn,
+               longitude,
+               latitude,
+               height,
+               uav_name,
+               uav_id,
+               uav_sn
+        from centermonitor_t_uav
+    </sql>
+
+    <select id="selectCentermonitorTUavList" parameterType="CentermonitorTUav" resultMap="CentermonitorTUavResult">
+        <include refid="selectCentermonitorTUavVo"/>
+        <where>
+            <if test="baseStationName != null  and baseStationName != ''">and base_station_name like concat('%',
+                #{baseStationName}, '%')
+            </if>
+            <if test="baseStationId != null  and baseStationId != ''">and base_station_id = #{baseStationId}</if>
+            <if test="baseStationSn != null  and baseStationSn != ''">and base_station_sn = #{baseStationSn}</if>
+            <if test="longitude != null  and longitude != ''">and longitude = #{longitude}</if>
+            <if test="latitude != null  and latitude != ''">and latitude = #{latitude}</if>
+            <if test="height != null  and height != ''">and height = #{height}</if>
+            <if test="uavName != null  and uavName != ''">and uav_name like concat('%', #{uavName}, '%')</if>
+            <if test="uavId != null  and uavId != ''">and uav_id = #{uavId}</if>
+            <if test="uavSn != null  and uavSn != ''">and uav_sn = #{uavSn}</if>
+        </where>
+    </select>
+
+    <select id="selectCentermonitorTUavById" parameterType="Long" resultMap="CentermonitorTUavResult">
+        <include refid="selectCentermonitorTUavVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertCentermonitorTUav" parameterType="CentermonitorTUav" useGeneratedKeys="true" keyProperty="id">
+        insert into centermonitor_t_uav
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="baseStationName != null">base_station_name,</if>
+            <if test="baseStationId != null">base_station_id,</if>
+            <if test="baseStationSn != null">base_station_sn,</if>
+            <if test="longitude != null">longitude,</if>
+            <if test="latitude != null">latitude,</if>
+            <if test="height != null">height,</if>
+            <if test="uavName != null">uav_name,</if>
+            <if test="uavId != null">uav_id,</if>
+            <if test="uavSn != null">uav_sn,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="baseStationName != null">#{baseStationName},</if>
+            <if test="baseStationId != null">#{baseStationId},</if>
+            <if test="baseStationSn != null">#{baseStationSn},</if>
+            <if test="longitude != null">#{longitude},</if>
+            <if test="latitude != null">#{latitude},</if>
+            <if test="height != null">#{height},</if>
+            <if test="uavName != null">#{uavName},</if>
+            <if test="uavId != null">#{uavId},</if>
+            <if test="uavSn != null">#{uavSn},</if>
+        </trim>
+    </insert>
+
+    <update id="updateCentermonitorTUav" parameterType="CentermonitorTUav">
+        update centermonitor_t_uav
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="baseStationName != null">base_station_name = #{baseStationName},</if>
+            <if test="baseStationId != null">base_station_id = #{baseStationId},</if>
+            <if test="baseStationSn != null">base_station_sn = #{baseStationSn},</if>
+            <if test="longitude != null">longitude = #{longitude},</if>
+            <if test="latitude != null">latitude = #{latitude},</if>
+            <if test="height != null">height = #{height},</if>
+            <if test="uavName != null">uav_name = #{uavName},</if>
+            <if test="uavId != null">uav_id = #{uavId},</if>
+            <if test="uavSn != null">uav_sn = #{uavSn},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCentermonitorTUavById" parameterType="Long">
+        delete
+        from centermonitor_t_uav
+        where id = #{id}
+    </delete>
+
+    <delete id="deleteCentermonitorTUavByIds" parameterType="String">
+        delete from centermonitor_t_uav where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="selectCentermonitorTUavByDeptId" parameterType="CentermonitorTUav" resultMap="CentermonitorTUavResult">
+        SELECT
+            ctu.*
+        FROM
+            centermonitor_t_uav ctu
+                LEFT JOIN centermonitor_t_uavdept ctud ON ctud.uav_id = ctu.id
+        WHERE
+            ctud.dept_id = #{deptId}
+    </select>
+
+    <select id="selectUavByUavId" parameterType="Long" resultMap="CentermonitorTUavResult">
+        SELECT ctu.*,
+               GROUP_CONCAT(ctud.dept_id) dept_id,
+               GROUP_CONCAT(ctud.dept_name) dept_name
+        FROM centermonitor_t_uav ctu
+                 LEFT JOIN centermonitor_t_uavdept ctud ON ctud.uav_id = ctu.id
+        where ctu.uav_id = #{uavId}
+        group by ctu.uav_id
+    </select>
+</mapper>

+ 90 - 0
src/main/resources/mapper/monitor/uav/CentermonitorTUavdeptMapper.xml

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.sooka.sponest.monitor.uav.mapper.CentermonitorTUavdeptMapper">
+    
+    <resultMap type="CentermonitorTUavdept" id="CentermonitorTUavdeptResult">
+        <result property="id"    column="id"    />
+        <result property="uavId"    column="uav_id"    />
+        <result property="uavName"    column="uav_name"    />
+        <result property="deptId"    column="dept_id"    />
+        <result property="deptName"    column="dept_name"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectCentermonitorTUavdeptVo">
+        select id, uav_id,uav_name, dept_id,dept_name, create_by, create_time, update_by,update_time from centermonitor_t_uavdept
+    </sql>
+
+    <select id="selectCentermonitorTUavdeptList" parameterType="CentermonitorTUavdept" resultMap="CentermonitorTUavdeptResult">
+        <include refid="selectCentermonitorTUavdeptVo"/>
+        <where>
+            <if test="uavId != null ">and uav_id = #{uavId}</if>
+            <if test="deptId != null ">and dept_id = #{deptId}</if>
+        </where>
+    </select>
+    
+    <select id="selectCentermonitorTUavdeptById" parameterType="Long" resultMap="CentermonitorTUavdeptResult">
+        <include refid="selectCentermonitorTUavdeptVo"/>
+        where id = #{id}
+    </select>
+        
+    <insert id="insertCentermonitorTUavdept" parameterType="CentermonitorTUavdept" useGeneratedKeys="true" keyProperty="id">
+        insert into centermonitor_t_uavdept
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="uavId != null">uav_id,</if>
+            <if test="uavName != null">uav_name,</if>
+            <if test="deptId != null">dept_id,</if>
+            <if test="deptName != null">dept_name,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="uavId != null">#{uavId},</if>
+            <if test="uavName != null">#{uavName},</if>
+            <if test="deptId != null">#{deptId},</if>
+            <if test="deptName != null">#{deptName},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCentermonitorTUavdept" parameterType="CentermonitorTUavdept">
+        update centermonitor_t_uavdept
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="uavId != null">uav_id = #{uavId},</if>
+            <if test="uavName != null">uav_name = #{uavName},</if>
+            <if test="deptId != null">dept_id = #{deptId},</if>
+            <if test="deptName != null">dept_name = #{deptName},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCentermonitorTUavdeptById" parameterType="Long">
+        delete from centermonitor_t_uavdept where id = #{id}
+    </delete>
+
+    <delete id="deleteCentermonitorTUavdeptByIds" parameterType="String">
+        delete from centermonitor_t_uavdept where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="selectUavdeptByUavId" parameterType="Long" resultMap="CentermonitorTUavdeptResult">
+        <include refid="selectCentermonitorTUavdeptVo"/>
+        where uav_id = #{uavId}
+    </select>
+</mapper>