doum
2026-03-05 e77e096fbfcc1bf525b9ea3382a26abe111c957d
更改头部
已添加2个文件
已删除3个文件
已修改12个文件
822 ■■■■■ 文件已修改
admin/public/index.html 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/workbench/index.js 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/main.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/admissionStatistics.vue 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/carStatistics.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/dangerStatic.vue 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/deviceDianbiao.vue 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/screenBoard.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/login.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/vue.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/whyc_admin/static/css/app.e13cc487.css 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/whyc_admin/static/js/app.0d1f0eb3.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/whyc_admin/static/js/app.0d1f0eb3.js.map 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/device_service/pom.xml 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/device_service/src/main/java/com/doumee/tcp/WaterElectricityUtil.java 507 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_admin/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/Device.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/public/index.html
@@ -11,6 +11,5 @@
    <noscript>
      <strong>We're sorry but æ™ºæ…§å›­åŒºå®‰æ¶ˆä¸€ä½“化系统 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
admin/src/api/workbench/index.js
@@ -30,7 +30,5 @@
}
// pc工作台
export function getWorkbenchData (data) {
  return request.get('/visitsAdmin/cloudService/business/staging/pCWorkPlatformData', {
    params: {...data  }
  })
  return request.get('/visitsAdmin/cloudService/business/staging/pCWorkPlatformData', data)
}
admin/src/main.js
@@ -29,7 +29,6 @@
Vue.use(directives)
Vue.use(filters)
Vue.use(plugins)
new Vue({
  data: {
    loading: false
admin/src/views/business/admissionStatistics.vue
@@ -133,9 +133,9 @@
      }
    },
    mounted() {
      var now = new Date();
      var year = now.getFullYear();
      var month = now.getMonth() + 1 > 9 ? now.getMonth() + 1 : `0${now.getMonth() + 1}`;
    var now = new Date()
    var year = now.getFullYear()
    var month = now.getMonth() + 1 > 9 ? now.getMonth() + 1 : `0${now.getMonth() + 1}`
      this.value = `${year}-${month}`
      this.date1 = `${year}-${month}`
@@ -152,13 +152,13 @@
      },
      changeBB() {
        if (this.radio === 'month') {
          let now = new Date();
          let year = now.getFullYear();
          let month = now.getMonth() + 1 > 9 ? now.getMonth() + 1 : `0${now.getMonth() + 1}`;
        const now = new Date()
        const year = now.getFullYear()
        const month = now.getMonth() + 1 > 9 ? now.getMonth() + 1 : `0${now.getMonth() + 1}`
          this.date1 = `${year}-${month}`
        } else {
          let now = new Date();
          let year = now.getFullYear();
        const now = new Date()
        const year = now.getFullYear()
          this.date1 = `${year}`
        }
        this.getReportLists()
@@ -174,12 +174,12 @@
            }
              this.column = res[0]
              this.list = res.slice(1).map(row => {
                const obj = {};
            const obj = {}
                this.column.forEach((header, index) => {
                  obj[header] = row[index];
                });
                return obj;
              });
              obj[header] = row[index]
            })
            return obj
          })
            // if (res.length === 0) {
            //   this.column = []
            //   this.list = []
@@ -192,13 +192,13 @@
      },
      changeDateType() {
        if (this.dateType === 'month') {
          let now = new Date();
          let year = now.getFullYear();
          let month = now.getMonth() + 1 > 9 ? now.getMonth() + 1 : `0${now.getMonth() + 1}`;
        const now = new Date()
        const year = now.getFullYear()
        const month = now.getMonth() + 1 > 9 ? now.getMonth() + 1 : `0${now.getMonth() + 1}`
          this.value = `${year}-${month}`
        } else {
          let now = new Date();
          let year = now.getFullYear();
        const now = new Date()
        const year = now.getFullYear()
          this.value = `${year}`
        }
        this.getRataLists()
@@ -234,10 +234,10 @@
        const myChart = echarts.init(document.querySelector('.echart1'))
        let names = this.info.cumulativeDataList.map(item => item.name)
        let datas = this.info.cumulativeDataList.map(item => item.total)
      const names = this.info.cumulativeDataList.map(item => item.name)
      const datas = this.info.cumulativeDataList.map(item => item.total)
        let option = {
      const option = {
          grid: {
            left: '0%',
            right: '5%',
@@ -263,7 +263,7 @@
            axisLabel: {
              formatter: function (value) {
                // å››èˆäº”入到最近的整数
                return Math.round(value);
              return Math.round(value)
              }
            }
          },
@@ -342,16 +342,16 @@
        console.log(dom)
        const myChart = echarts.init(dom)
        let data = this.listZB.map(item => {
      const data = this.listZB.map(item => {
          return {
            value: item.total,
            name: item.name
          }
        })
        let option = {
      const option = {
          tooltip: {
            trigger: 'item',
          trigger: 'item'
          },
          legend: {
            show: false,
@@ -366,11 +366,11 @@
              labelLine: {
                normal: {
                  length: 30,
                  length2: 70,
                length2: 70
                }
              },
              label: {
                formatter: "{a|{b}} {d}%",
              formatter: '{a|{b}} {d}%',
                rich: {
                  a: {
                    color: '#333333',
@@ -392,7 +392,7 @@
      initDept3() {
        const myChart = echarts.init(document.querySelector('#echart3'))
        let option = {
      const option = {
          grid: {
            left: '5%',
            right: '10%',
@@ -501,7 +501,7 @@
        .main_table {
            display: flex;
            align-items: start;
            align-items: flex-start;
            justify-content: space-between;
            margin-top: 10px;
            .main_table_list {
@@ -662,7 +662,6 @@
                padding: 20px;
                box-sizing: border-box;
                /*border-right: 12px solid #f7f7f7;*/
                .echart1 {
                    width: 100%;
admin/src/views/business/carStatistics.vue
@@ -531,7 +531,7 @@
        .main_table {
            display: flex;
            align-items: start;
            align-items: flex-start;
            justify-content: space-between;
            margin-top: 10px;
            .main_table_list {
admin/src/views/business/dangerStatic.vue
@@ -77,13 +77,13 @@
</template>
<script>
import QueryForm from '@/components/common/QueryForm'
// import QueryForm from '@/components/common/QueryForm'
import * as echarts from 'echarts'
import { hiddenDangerDataPost } from '@/api/business/hiddenDanger'
import dayjs from 'dayjs'
export default {
  components: {
    QueryForm,
    // QueryForm,
  },
  data() {
    return {
@@ -101,7 +101,7 @@
            options: [
              { label: '待处理', value: '0' },
              { label: '已处理', value: '1' },
              { label: '已退回', value: '2' },
              { label: '已退回', value: '2' }
            ]
          },
          {
@@ -163,10 +163,8 @@
            this.initDept()
            this.initDept3()
          })
        }
      })
    },
    changeForm(e) {
      this.getData()
@@ -177,8 +175,8 @@
      this.typeList.forEach(i => {
        total += i.total
      })
      let colors = ['#d75a44', '#e39f4d', '#f0d05f', '#7ac7f6', '#4469ee', '#698af0','#86a2f1','#a1b4f6','#a0b5f5', '#9fb5f4', '#b6c7f7', '#c8d5f8']
      let option = {
      const colors = ['#d75a44', '#e39f4d', '#f0d05f', '#7ac7f6', '#4469ee', '#698af0', '#86a2f1', '#a1b4f6', '#a0b5f5', '#9fb5f4', '#b6c7f7', '#c8d5f8']
      const option = {
        grid: {
          left: '0%',
          right: '0%',
@@ -193,8 +191,8 @@
            left: '45%',
            textStyle: {
              color: '#666666',
              fontSize: 13,
            },
              fontSize: 13
            }
          }, {
            text: total,
            top: '50%',
@@ -202,8 +200,8 @@
            textStyle: {
              color: '#080404',
              fontSize: 16,
              fontWeight: 'bold',
            },
              fontWeight: 'bold'
            }
          }],
        legend: {
          left: 'center',
@@ -226,7 +224,7 @@
            type: 'pie',
            radius: ['34%', '50%'],
            label: {
              formatter: "{a|{b}}\n\n{c} | {d}%",
              formatter: '{a|{b}}\n\n{c} | {d}%',
              rich: {
                a: {
                  color: '#333333',
@@ -264,7 +262,7 @@
    initDept() {
      const myChart = echarts.init(document.querySelector('.echart2'))
      let option = {
      const option = {
        grid: {
          left: '10%',
          right: '10%',
@@ -286,7 +284,7 @@
          name: '隐患数',
          minInterval: 1,
          axisLine: {
            show: true,
            show: true
          }
        },
        series: [
@@ -305,7 +303,7 @@
              show: true,      // æ˜¾ç¤ºæ ‡ç­¾
              position: 'top', // è®©æ ‡ç­¾æ˜¾ç¤ºåœ¨æŸ±å­é¡¶éƒ¨
              color: '#666666',   // æ–‡å­—颜色
              fontSize: 14,    // æ–‡å­—大小
              fontSize: 14 // æ–‡å­—大小
            }
          }
        ]
@@ -320,10 +318,10 @@
      const myChart = echarts.init(document.querySelector('#echart3'))
      let names = this.yearList.map(item => item.name)
      let datas = this.yearList.map(item => item.total)
      const names = this.yearList.map(item => item.name)
      const datas = this.yearList.map(item => item.total)
      let option = {
      const option = {
        tooltip: {
          trigger: 'axis',
          axisPointer: {
@@ -349,7 +347,7 @@
          axisLabel: {
            formatter: function (value) {
              // å››èˆäº”入到最近的整数
              return Math.round(value);
              return Math.round(value)
            }
          }
        },
@@ -478,7 +476,7 @@
  .main_table {
    display: flex;
    align-items: start;
    align-items: flex-start;
    justify-content: space-between;
    .main_table_echart {
      width: 64%;
@@ -554,7 +552,7 @@
  .main_content {
    display: flex;
    align-items: start;
    align-items: flex-start;
    justify-content: space-between;
    height: 500px;
    margin-bottom: 10px;
admin/src/views/business/deviceDianbiao.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,186 @@
<template>
    <TableLayout :permissions="['business:device:query']">
        <!-- æœç´¢è¡¨å• -->
        <el-form ref="searchForm"  slot="search-form" :model="searchForm" label-width="100px" inline>
            <el-form-item title="名称" prop="name">
                <el-input v-model="searchForm.name" placeholder="请输入名称" @keypress.enter.native="search"></el-input>
            </el-form-item>
            <el-form-item title="标识符" prop="no">
                <el-input v-model="searchForm.no" placeholder="请输入设备标识符" @keypress.enter.native="search"></el-input>
            </el-form-item>
            <el-form-item title="设备号" prop="doorNo">
                <el-input v-model="searchForm.doorNo" placeholder="请输入设备号" @keypress.enter.native="search"></el-input>
            </el-form-item>
            <section>
                <el-button type="primary" @click="search">搜索</el-button>
                <el-button @click="reset">重置</el-button>
            </section>
        </el-form>
        <!-- è¡¨æ ¼å’Œåˆ†é¡µ -->
        <template v-slot:table-wrap>
          <ul class="toolbar" v-permissions="['business:device:create', 'business:device:delete']">
            <li><el-button type="primary" @click="$refs.operaDeviceWindow.open('新建电表')" icon="el-icon-plus" v-permissions="['business:device:create']">新建</el-button></li>
            <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['business:device:delete']">删除</el-button></li>
          </ul>
            <el-table
                :height="tableHeightNew"
                v-loading="isWorking.search"
                :data="tableData.list"
                stripe >
              <el-table-column type="selection" width="55"></el-table-column>
              <el-table-column prop="name" label="名称" fixed min-width="150" align="center"></el-table-column>
              <el-table-column prop="no" label="电表地址域" min-width="100" align="center" ></el-table-column>
              <el-table-column prop="doorNo" label="设备号"  min-width="120" align="center" show-overflow-tooltip></el-table-column>
              <el-table-column prop="channelNo" label="开关序号" align="center" min-width="100"></el-table-column>
              <el-table-column prop="manufature" label="厂商" align="center" min-width="100" show-overflow-tooltip></el-table-column>
              <el-table-column prop="regionPathName" align="center"  min-width="100" label="所在位置"></el-table-column>
              <el-table-column prop="ip" label="IP"  min-width="150" align="center" show-overflow-tooltip ></el-table-column>
              <el-table-column prop="port" label="端口" align="center"  ></el-table-column>
              <el-table-column prop="isUsed" label="是否使用">
                <template slot-scope="{row}">
                  <el-switch @change="changeUsed($event, row)" v-model="row.isUsed" active-color="#13ce66"
                             inactive-color="#ff4949" :active-value="0" :inactive-value="1">
                  </el-switch>
                </template>
              </el-table-column>
              <el-table-column prop="editDate" label="最近更新时间" align="center" min-width="150"></el-table-column>
              <el-table-column
                  label="操作"
                  align="center"
                  min-width="280"
                  fixed="right"
              >
                  <template slot-scope="{row}">
                    <el-button type="text" @click="$refs.operaDeviceWindow.open('编辑电表', row)" icon="el-icon-edit" v-permissions="['business:device:update']">编辑</el-button>
                    <el-button type="text" @click="$refs.operaDeviceDataWindow.open('查看电表数据', row)" icon="el-icon-view" v-permissions="['business:device:update']">数据</el-button>
                    <el-button type="text" @click="send(row,1)" icon="el-icon-circle-check"  v-permissions="['business:device:update']">开闸</el-button>
                    <el-button type="text" class="red" @click="send(row,0)" icon="el-icon-circle-close"  v-permissions="['business:device:update']">关闸</el-button>
                    <el-button type="text" class="red" @click="deleteById(row)" icon="el-icon-delete" v-permissions="['business:device:delete']">删除</el-button>
                  </template>
                </el-table-column>
            </el-table>
            <pagination
                @size-change="handleSizeChange"
                @current-change="handlePageChange"
                :pagination="tableData.pagination"  >
            </pagination>
        </template>
      <el-dialog
          :visible.sync="visibleSend"
          style="z-index: 100000"
          append-to-body
          width="50%"
          height="50%"
          :title="'控制电表-【'+ form.name+'】'"
      >
        <el-form :model="form" ref="form"  :rules="rules">
          <el-form-item label="执行操作:"  >
            <b class="green" v-if="form.status ===1">开闸</b>
            <b class="red" v-else>关闸</b>
          </el-form-item>
          <el-form-item label="开关序号" prop="channelNo">
            <el-input v-model="form.channelNo" type="text" placeholder="请输入开关序号 ï¼Œå¤šä¸ªç”¨è‹±æ–‡é€—号隔开,如 1,2,3" v-trim/>
          </el-form-item>
          <p class="tip-warn" style="width: 100%;"><i class="el-icon-warning"></i>设备原始开关序号信息:{{form.channelNo1}},控制多个开关,请用英文逗号隔开,如 1,2;</p>
        </el-form>
        <template  v-slot:footer  >
          <el-button @click="sendAction()" type="primary" v-if="form.status === 1" :loading="isWorkSending">确认开闸</el-button>
          <el-button @click="sendAction()" type="danger" v-if="form.status !== 1" :loading="isWorkSending">确认关闸</el-button>
          <el-button @click="sendClose()">返回</el-button>
        </template>
      </el-dialog>
        <!-- æ–°å»º/修改 -->
    <OperaDeviceDuanluqiWindow ref="operaDeviceWindow" @success="handlePageChange"/>
    <OperaDeviceDataListWindow ref="operaDeviceDataWindow" @success="handlePageChange"/>
    </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaDeviceDataListWindow from '@/components/business/OperaDeviceDataListWindow'
import OperaDeviceDuanluqiWindow from '@/components/business/OperaDeviceDuanluqiWindow'
export default {
  name: 'DeviceDuanluqi',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaDeviceDuanluqiWindow ,OperaDeviceDataListWindow},
  data () {
    return {
      // æœç´¢
      searchForm: {
        doorNo: '',
        no: '',
        name: '',
        type: 6
      },
      isWorkSending: false,
      form: {
        id: '',
        name: '',
        channelNo: '',
        channelNo1: '',
        status: null
      },
      visibleSend: false,
      options: [],
      rules: {
        channelNo: [{ required: true, message: '请输入需要操作的开关序号', trigger: 'blur' }],
      }
    }
  },
  created () {
    this.config({
      module: '设备信息表',
      api: '/business/device',
      'field.id': 'id',
      'field.main': 'id'
    })
    this.search()
  },
  methods: {
    changeUsed (e, row) {
      this.api.updateUsedById({
        id: row.id,
        isUsed: e
      })
    },
    showPassward (row) {
      if (!row.showPwd) {
        this.$set(row, 'showPwd', true)
      } else {
        this.$set(row, 'showPwd', false)
      }
    },
    sendAction () {
      if (!this.form.channelNo) {
        return
      }
      this.$dialog.actionConfirm('确认进行电表【' + (this.form.status === 1 ? '开闸' : '关闸') + '】操作吗?', '操作确认提醒')
        .then(() => {
          this.isWorkSending = true
          this.api.duanluqiCmd(this.form)
            .then(res => {
              this.$tip.apiSuccess(res || '请求成功')
              this.sendClose()
            })
            .catch(e => {
            })
            .finally(() => {
              this.isWorkSending = false
            })
        })
        .catch(() => {})
    },
    send (row, type) {
      this.visibleSend = true
      this.form = { id: row.id, name: row.name, channelNo: row.channelNo, status: type ,channelNo1:row.channelNo}
    },
    sendClose () {
      this.visibleSend = false
      this.isWorkSending = false
      this.form = { id: '', name: '', channelNo: '', status: '',channelNo1:'' }
    }
  }
}
</script>
admin/src/views/business/screenBoard.vue
@@ -141,7 +141,7 @@
        .main_table {
            display: flex;
            align-items: start;
            align-items: flex-start;
            justify-content: space-between;
            margin-top: 10px;
            .main_table_list {
admin/src/views/login.vue
@@ -104,6 +104,7 @@
    ...mapState(['sysConfig'])
  },
  mounted () {
    alert(1)
    this.username = localStorage.getItem('username') || ''
    this.password = localStorage.getItem('password') || ''
    this.phone = localStorage.getItem('phone') || ''
admin/vue.config.js
@@ -12,7 +12,7 @@
  lintOnSave: false,
  devServer: {
    host: '0.0.0.0',
    port: 10012,
    port: 10085,
    proxy: {
      [process.env.VUE_APP_API_PREFIX]: {
        target: process.env.VUE_APP_API_URL,
admin/whyc_admin/static/css/app.e13cc487.css
ÎļþÒÑɾ³ý
admin/whyc_admin/static/js/app.0d1f0eb3.js
ÎļþÒÑɾ³ý
admin/whyc_admin/static/js/app.0d1f0eb3.js.map
ÎļþÒÑɾ³ý
server/visits/device_service/pom.xml
@@ -9,13 +9,13 @@
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>
    <artifactId>device_service</artifactId>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <fastjson.version>1.2.70</fastjson.version>
    </properties>
    <dependencies>
@@ -28,5 +28,10 @@
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
    </dependencies>
</project>
server/visits/device_service/src/main/java/com/doumee/tcp/WaterElectricityUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,507 @@
package com.doumee.tcp;
import com.alibaba.fastjson.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
public class WaterElectricityUtil {
    private static double parseBcdToDouble(byte[] bcdBytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bcdBytes) {
            sb.append(String.format("%02X", b));
        }
        try {
            return Double.parseDouble(sb.toString());
        } catch (NumberFormatException e) {
            return 0.0;
        }
    }
    private static byte[] reverseAddress(String address) {
        byte[] result = new byte[6];
        for (int i = 0; i < 6; i++) {
            if (i * 2 + 1 < address.length()) {
                String hex = address.substring(i * 2, Math.min((i + 1) * 2, address.length()));
                result[i] = (byte) Integer.parseInt(hex, 16);
            }
        }
        return result;
    }
    private static byte calculateChecksum(byte[] data, int offset, int length) {
        int sum = 0;
        for (int i = offset; i < offset + length; i++) {
            sum += data[i] & 0xFF;
        }
        return (byte) (sum & 0xFF);
    }
    private static byte[] hexStringToByteArray(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }
    public static byte calcCS(byte[] data) {
        int sum = 0;
        for (byte b : data) {
            sum += b & 0xFF;
        }
        return (byte) (sum & 0xFF);
    }
    public static byte[] getRequestParam(int feCount, byte[] address, byte control, byte[] data) throws IOException {
        ///FEFEFE    68  999999999999    68  01  02  65  F3C1  16
//        byte b = (byte) 0xFE;
//        byte[] msg = {(byte) 0xFE,0x68};
        ByteArrayOutputStream frame = new ByteArrayOutputStream();
        for (int i = 0; i < feCount; i++) {
            frame.write(0xFE);
        }
        // 2. å¸§èµ·å§‹ç¬¦
        frame.write(0x68);
        // 3. åœ°å€åŸŸ
        frame.write(address);
        // 4. å†æ¬¡å¸§èµ·å§‹ç¬¦
        frame.write(0x68);
        // 5. æŽ§åˆ¶ç 
        frame.write(control);
        // 6. æ•°æ®é•¿åº¦
        frame.write(data.length);
        // 7. æ•°æ®åŸŸ
        frame.write(data);
        // 8. è®¡ç®— CS(从第一个 68 å¼€å§‹ï¼‰
        byte[] csData = frame.toByteArray();
        int start = feCount; // ç¬¬ä¸€ä¸ª 68 çš„位置
        byte cs = calcCS(Arrays.copyOfRange(csData, start, csData.length));
        frame.write(cs);
        // 9. ç»“束符
        frame.write(0x16);
        return frame.toByteArray();
    }
    private static byte[] readDeviceData(String ip, int port, byte[] data) {
        Socket socket = null;
        try {
            socket = new Socket(ip, port);
            socket.setSoTimeout(5000);
            java.io.OutputStream out = socket.getOutputStream();
            java.io.InputStream in = socket.getInputStream();
            out.write(data);
            out.flush();
            // è¯»å–响应
            byte[] buffer = new byte[2048];
            int bytesRead = in.read(buffer);
            byte[] response = Arrays.copyOf(buffer, bytesRead);
            // è§£æžå“åº”数据
            return response;
        } catch (Exception e) {
//            e.printStackTrace();
            throw new RuntimeException("Failed to read from device", e);
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                }
            }
        }
    }
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = String.format("%02X", b & 0xFF);
            hexString.append(hex);
        }
        return hexString.toString();
    }
    /**
     * å°†12位十进制地址转换为6字节BCD小端序地址
     *
     * @param decimalAddress 12位十进制地址字符串
     * @return 6字节的BCD地址(小端序)
     */
    private static byte[] convertToBCDAddress(String decimalAddress) {
        // 1. éªŒè¯è¾“å…¥
        if (decimalAddress == null || decimalAddress.length() != 12) {
            throw new IllegalArgumentException("地址必须是12位十进制数");
        }
        if (!decimalAddress.matches("\\d{12}")) {
            throw new IllegalArgumentException("地址必须全部是数字");
        }
        // 2. å‡†å¤‡ç»“果数组(6字节)
        byte[] result = new byte[6];
        // 3. ä»Žå³å‘左每2位一组处理(小端序)
        for (int i = 0; i < 6; i++) {
            // è®¡ç®—在字符串中的位置(从右向左)
            int strIndex = 10 - (i * 2);  // å› ä¸ºè¦å–两位,所以是10,8,6,4,2,0
            String twoDigits = decimalAddress.substring(strIndex, strIndex + 2);
            // å°†ä¸¤ä½åè¿›åˆ¶æ•°è½¬æ¢ä¸ºBCD字节
            // ä¾‹å¦‚:"25" -> 0x25
            result[i] = (byte) Integer.parseInt(twoDigits, 16);
        }
        // æ³¨æ„ï¼šä¸Šé¢çš„循环顺序已经是小端序,result[0]存的是最低两位
        return result;
    }
    private static String subByte(String value, byte sub) {
        byte b = (byte) Integer.parseInt(value, 16);
        int result = (b & 0xFF) - (sub & 0xFF);
        // ç¡®ä¿ç»“果在0-255范围内(处理负数)
        if (result < 0) {
            result += 256;
        }
        String hexResult = String.format("%02X", result & 0xFF);
        return hexResult;
    }
    private static String[] parseSub33Reverse(String msg, int n) {
        //33333333  3333
        String[] nArr = new String[n];
        byte _33 = 0x33;
        for (int i = 0; i < n; i++) {
            int index = i * 2;
            String twoDigits = msg.substring(index, index + 2);
            String hexResult = subByte(twoDigits, _33);
            // åå‘存储:nArr[n - i - 1] å®žçŽ°åè½¬
            nArr[n - i - 1] = hexResult;
        }
        return nArr;
    }
    public static Map<String, Object> water(String ip, int port, String address) throws IOException {
        byte[] address_buf = convertToBCDAddress(address);
        byte control = 0x01;
        byte[] data = {0x43, (byte) 0xC3};
        byte[] bufReq = getRequestParam(3, address_buf, control, data);
        byte[] resp = readDeviceData(ip, port, bufReq);
        String hex = bytesToHex(resp);
        //FEFEFE6899254652010068810843C3333433333333E916
//        System.out.println(hex);
        String msg = hex.substring(30, 30 + 8 + 4);
        String[] nArr = parseSub33Reverse(msg, 4);
        Double total = strArrNum(nArr);
        Map<String, Object> r = new HashMap<>();
        r.put("total", total);
        msg = hex.substring(40, 40 + 2);
        byte sub = 0x33;
        String hexResult = subByte(msg, sub);
        String v = hexToBinary(hexResult);
        /**
         * Y0.B0 存储器状态 (1:故障,0:正常);
         * Y0.B1 阀门状态 (1:故障,0:正常);
         * Y0.B2 信号状态 (1:故障,0:正常);
         * Y0.B3 电池状态 (1:故障,0:正常);
         * Y0.B4 保留;
         * Y0.B5 保留;
         * Y0.B6 水表通讯状态( 1:故障,0:正常);
         * Y0.B7 阀门开关状态 (1: åˆ,0:开);
         * æ³¨æ„ï¼šçŠ¶æ€ä½æ— æ—¶ä¸º 0(正常)
         * çº¢è‰²ï¼šæ— è®°å¿†ç›´è¯»è¡¨çš„状态
         */
        r.put("status", v);
        return r;
    }
    private static void electricityTotal(String ip, int port, byte[] addressBuf, Map<String, Object> map) throws IOException {
        byte control = 0x11;
        byte[] data = {0x33, 0x33, 0x33, 0x33};
        byte[] bufReq = getRequestParam(4, addressBuf, control, data);
        byte[] respBuf = readDeviceData(ip, port, bufReq);
        String resp = bytesToHex(respBuf);
        String msg = resp.substring(28 + 8, 28 + 8 + 8);
        String[] nArr = parseSub33Reverse(msg, 4);
        Double total = strArrNum(nArr);
        map.put("total", total);
    }
    private static void electricityStatus(String ip, int port, byte[] addressBuf, Map<String, Object> map) throws IOException {
        byte control = 0x11;
        byte[] data = {0x36, 0x38, 0x33, 0x37};
        byte[] bufReq = getRequestParam(4, addressBuf, control, data);
        byte[] respBuf = readDeviceData(ip, port, bufReq);
        String resp = bytesToHex(respBuf);
        String msg = resp.substring(36, 36 + 4);
        System.out.println(resp);
        String[] nArr = parseSub33Reverse(msg, 2);
        String status = hexToBinary1(nArr);
//        String resp = "FEFEFEFE   68  615121010000    68  91  06  36383337    3333    7916";
        map.put("status", status);
    }
    private static void electricityTime(String ip, int port, byte[] addressBuf, Map<String, Object> map) throws IOException {
        byte control = 0x11;
        byte[] data = {0x3F, 0x34, 0x33, 0x37};
        byte[] bufReq = getRequestParam(4, addressBuf, control, data);
        byte[] respBuf = readDeviceData(ip, port, bufReq);
        String resp = bytesToHex(respBuf);
        String msg = resp.substring(36, 36 + 14);
//        System.out.println(msg);
        String[] nArr = parseSub33Reverse(msg, 7);
        String ts = hexToBinary1(nArr);
        String time = "20"+ts.substring(0,6)+ts.substring(8);
        Date date = getDateByStr(time);
//        String resp = "FEFEFEFE   68  615121010000    68  91  06  36383337    3333    7916";
        map.put("currentTime", formatData(date));
    }
    public static Date getDateByStr(String date)  {
        TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
        if(date!=null ){
            int i = date.indexOf("+");
            if(i >0){
                date = date.substring(0,i);
            }
        }
        DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        df.setTimeZone(tz);
        Date dates = null;
        try {
            dates = df.parse(date);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dates;
    }
    public static String formatData(Date date)  {
        DateFormat df = new SimpleDateFormat("yyyyå¹´MM月dd日HH时mm分ss秒");
        try {
            return  df.format(date);
        } catch (Exception e) {
        }
        return null;
    }
    public static Map<String, Object> electricity(String ip, int port, String address) throws IOException {
        Map<String, Object> r = new HashMap<>();
        byte[] addressBuf = convertToBCDAddress(address);
        electricityTotal(ip, port, addressBuf, r);
        electricityStatus(ip, port, addressBuf, r);
        electricityTime(ip, port, addressBuf, r);
        return r;
    }
    private static String hexToBinary(String hex) {
        StringBuilder sb = new StringBuilder();
        for (char c : hex.toCharArray()) {
            int val = Integer.parseInt(String.valueOf(c), 16);
            sb.append(String.format("%4s", Integer.toBinaryString(val)).replace(' ', '0'));
        }
        return sb.toString();
    }
    private static String hexToBinary(String[] hexArr) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < hexArr.length; i++) {
            sb.append(hexToBinary(hexArr[i]));
        }
        return sb.toString();
    }
    private static String hexToBinary1(String[] hexArr) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < hexArr.length; i++) {
            sb.append(hexArr[i]);
        }
        return sb.toString();
    }
    private static String addHex(String hex1, String hex2) {
        int num1 = Integer.parseInt(hex1.replace("0x", ""), 16);
        int num2 = Integer.parseInt(hex2.replace("0x", ""), 16);
        int sum = num1 + num2;
        return "0x" + Integer.toHexString(sum).toUpperCase();
    }
    private static void testWater() throws IOException {
        //"00000152462599"; 000152462599
        //FEFEFE    68  999999999999    68  01(C)  02(L)  65(DI0) F3(DI1)  C1(CS校验码)  16(结束符)
        //FEFEFE6899254652010068010243C33016
        //FEFEFE6899254652010068810843C3333333333333E816
//        FEFEFE6899254652010068810843C3333333333333E816
        String ip = "192.168.1.78";
        int port = 1030;
//        DeviceData d = readDeviceData(ip, port, "00000152462599");
//        System.out.println(d);
//        String address = "000152462599";
        String address = "000152462599";
        //00000152462599
        byte[] address_buf = convertToBCDAddress(address);
//        byte[] address_buf = hexStringToByteArray(address);
//        System.out.println(buf);
        /**
         *
         * FEFEFE   68  000152462599  68010243C33016
         * FEFEFE   68  992546520100  68010243C33016
         */
        byte control = 0x01;
        byte[] data = {0x43, (byte) 0xC3};
        byte[] datas = getRequestParam(3, address_buf, control, data);
        System.out.println(bytesToHex(datas));
        System.out.println("FEFEFE6899254652010068010243C33016");
//         datas = hexStringToByteArray("FEFEFE6899254652010068010243C33016");
        byte[] resp = readDeviceData(ip, port, datas);
        String r = bytesToHex(resp);
        System.out.println(r);
        /**
         *
         * FEFEFE   68  992546520100    68  01  02  43C3    30  16
         * FEFEFE   68  992546520100    68  81  08  43C3    33333333    3333    E8  16
         */
//        String r = "FEFEFE6899254652010068810843C3333333333333E816";
        String msg = r.substring(30, 30 + 8 + 4);
        System.out.println(msg);
        String[] nArr = parseSub33Reverse(msg, 4);
        System.out.println(strArrNum(nArr));
//        byte d10 = 0x10;
//        byte add = 0x33;
//        String hex = String.format("%02X", (d10 + add) & 0xFF);
//        System.out.println(hex);
//        r = addHex("0x10", "0x33");
//        System.out.println(r);
//
//        r = addHex("0x90", "0x33");
//        System.out.println(r);
        msg = r.substring(40, 40 + 2);
        System.out.println(msg);
        byte sub = 0x33;
        String hexResult = subByte(msg, sub);
        String v = hexToBinary(hexResult);
        System.out.println(v);
    }
    /**
     * æœ€åŽä¸€ä½æ˜¯å°æ•°ç‚¹
     *
     * @param nArr
     * @return
     */
    private static Double strArrNum(String[] nArr) {
        if (nArr == null || nArr.length == 0) {
            return 0.0;
        }
        // å°†æ‰€æœ‰éƒ¨åˆ†æ‹¼æŽ¥èµ·æ¥
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < nArr.length; i++) {
            sb.append(nArr[i]);
        }
        // åœ¨é€‚当位置插入小数点
        String combined = sb.toString();
        int totalLength = combined.length();
        int decimalLength = nArr[nArr.length - 1].length();
        // æ’入小数点
        String numberStr = combined.substring(0, totalLength - decimalLength) + "." + combined.substring(totalLength - decimalLength);
        return Double.parseDouble(numberStr);
    }
    public static void electricityTest() throws IOException {
        //000001215161
//        FEFEFEFE  68  615121010000    68  11  04  33333333    8516
//        FEFEFEFE6861512101000068910833333333A93333334B16
//        FEFEFEFE6861512101000068910833333333AC3333334E16
        String ip = "192.168.1.78";
        int port = 1030;
        String address = "000001215161";
        byte[] addressBuf = convertToBCDAddress(address);
//        byte control = 0x11;
//        byte[] data = {0x33, 0x33, 0x33, 0x33};
////
//        byte[] bufReq = getRequestParam(4, addressBuf, control, data);
////        String req = bytesToHex(bufReq);
//////        String param = "FEFEFE68615121010000681104333333338516";
////        System.out.println(req);
//////        System.out.println(param);
////        byte[] buf = hexStringToByteArray(param);
//        byte[] respBuf = readDeviceData(ip, port, bufReq);
//        String hex = bytesToHex(respBuf);
////        System.out.println(hex);
//        String resp = "FEFEFEFE6861512101000068910833333333AC3333334E16";
////                     FEFEFEFE68615121010000   68  91  08 33333333 A9  333333    4B16
//
//        String msg = resp.substring(28, 28 + 8);
//        System.out.println(msg);
//
//        String[] nArr = parseSub33Reverse(msg, 4);
//        System.out.println(strArrNum(nArr));
//        msg = resp.substring(28 + 8, 28 + 8 + 8);
//        System.out.println(msg);
//
//        nArr = parseSub33Reverse(msg, 4);
////        parseSub33Reverse
//        System.out.println(strArrNum(nArr));
//        String msgStatus = "FEFEFEFE 68 379707010000 68 11 04 36383337 9316";
//        String msgStatus = "FEFEFEFE68379707010000681104363833379316";
//        byte control = 0x11;
//        byte[] data = {0x36, 0x38, 0x33, 0x37};
//        byte[] bufReq = getRequestParam(4, addressBuf, control, data);
//        String req = bytesToHex(bufReq);
//        System.out.println(req);
//        System.out.println(msgStatus);
//        byte[] respBuf = readDeviceData(ip, port, bufReq);
//        String resp = bytesToHex(respBuf);
//        System.out.println(resp);
//        FEFEFEFE  68  615121010000  68  91  06  36383337  3333  79  16
//        FEFEFEFE  68  615121010000    68  91  06  36383337    3333    79  16
        String resp = "FEFEFEFE686151210100006891063638333733337916";
        String msg = resp.substring(36, 36 + 4);
        System.out.println(msg);
        String[] nArr = parseSub33Reverse(msg, 2);
//        System.out.println(strArrNum(nArr));
        String v = hexToBinary(nArr);
        v="0000000000010000";
        System.out.println(v);
        System.out.println(v.charAt(11));
    }
    public static void main(String[] args) throws IOException {
//        testWater();
//        electricityTest();
        water("192.168.1.78",1030,"000152462599");
        Map<String, Object> map = electricity("192.168.1.78", 1030, "000001215161");
        System.out.println(JSONObject.toJSONString(map));
//        FEFEFE6899254652010068810 843C3333433333333   E9 16
//        FEFEFE6899254652010068810 84  3C3333433333333E916
//        Map<String, Object> map1 = water("192.168.1.78", 1030, "000152462599");
//        System.out.println(JSONObject.toJSONString(map1));
    }
}
server/visits/dmvisit_admin/src/main/resources/bootstrap.yml
@@ -1,6 +1,6 @@
spring:
  profiles:
    active: pro
    active: dev
  application:
    name: visitsAdmin
    # å®‰å…¨é…ç½®
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/Device.java
@@ -73,8 +73,8 @@
    @ExcelColumn(name="排序码")
    private Integer sortnum;
    @ApiModelProperty(value = "类型 0门禁 1车库 2LED 3广播点 4广播设备 5断路器空开", example = "1")
    @ExcelColumn(name="类型 0门禁 1车库 2LED 3广播点 4广播设备 5断路器空开")
    @ApiModelProperty(value = "类型 0门禁 1车库 2LED 3广播点 4广播设备 5断路器空开 6海康电表", example = "1")
    @ExcelColumn(name="类型 0门禁 1车库 2LED 3广播点 4广播设备 5断路器空开 6海康电表")
    private Integer type;
    @ApiModelProperty(value = "是否园区出入口 0不是 1是", example = "1")
    @ExcelColumn(name="是否园区出入口 0不是 1是")