<template>
  <div>
    <el-form ref="serialForm" :model="form" :disabled="disabled" :rules="rules" label-width="120px">
      <el-form-item label="磅秤设备" prop="serial">
        <div v-if="!isSupported" class="error">
          您的浏览器不支持 Web Serial API，请使用 Chrome 89+ 或 Edge 89+
        </div>
        <!-- <div v-if="error != null " class="error">
            {{ error.message }}
        </div> -->
        <div v-else>
          <span v-if="isConnected" class="connected">
            ✅ 已连接: {{ serialDevice }}
            <el-button
              class="ml20"
              type="danger"
              size="small"
              @click="disconnect"

              >断开</el-button
            >
          </span>
          <span v-if="isConnecting" class="connected">
            正在连接...: {{ serialDevice }}

          </span>
          <span v-if="!isConnected && !isConnecting" class="disconnected">
            ❌ 未连接串口设备
            <el-button class="ml20" type="primary" size="small" @click="connect" :disabled="!isSupported"
              >连接</el-button
            >
          </span>
        </div>
      </el-form-item>
      <el-form-item label="称显重量(kg)" prop="receivedData">
        <div class="serialInput-block">
          <span>{{ receivedData }}</span>
        <el-input
          v-model="form.receivedData"
          placeholder=""
          :disabled="isSupported && isConnected"
          size="large"
          class="large-input"
          @blur="submitWeight"
           style="width: 240px;"
        />
        <el-button
          class="ml20"
          type="primary"
          v-if="isConnected && isReading"
          @click="stopReader"
          size="medium"
          icon="el-icon-check"
        ></el-button>
        <el-button
          class="ml20"
          type="primary"
          v-if="isConnected && !isReading"
          size="medium"
          @click="startReader"
          icon="el-icon-refresh"
        ></el-button>
        </div>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
export default {
  name: "WeightSerial",
  props: {
    inputVal: {
      type: Number,
      default: 0,
    },
    type: {
      type: Number,
      default: 2,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  watch: {
    inputVal: {
      handler(val) {
        console.log(val);
        this.form.receivedData = val;
      },
      deep: true,
      immediate: true,
    },
    form: {
      handler(val) {
        console.log(val);
        this.$emit("confirm", val.receivedData);
      },
      deep: true,
      immediate: true,
    },
  },
  data() {
    var validateNumber = (str) => {
      const regex =
        /^(?!0+\.00$)(?!.*\.\d{3,})(?:0?\.[1-9]\d?|0?\.0[1-9]|[1-9]\d{0,4}(?:\.\d{1,2})?)$/;
      const num = parseFloat(str);
      return regex.test(str) && num >= 0.01 && num <= 99999.99;
    };
    var validateNumber1 = (str) => {
      const regex =
        /^(?!0+\.00$)(?!.*\.\d{3,})(?:0?\.[1-9]\d?|0?\.0[1-9]|[1-9]\d{0,2}(?:\.\d{1,2})?)$/;
      const num = parseFloat(str);
      return regex.test(str) && num >= 0.01 && num <= 999.99;
    };
    var valiWeight = (rule, value, callback) => {
      //包含小数的数字
      var msg = "范围(0.01~99999.99),两位小数";
      if (value && !validateNumber(value)) {
        callback(new Error(msg));
      } else {
        callback();
      }
    };
    var valiWeight1 = (rule, value, callback) => {
      //包含小数的数字
      var msg = "范围(0.01~999.99),两位小数";
      if (value && !validateNumber1(value)) {
        callback(new Error(msg));
      } else {
        callback();
      }
    };
    return {
      serialDevice: null,
      isSupported: "serial" in navigator,
      serialPort: null,
     // isConnected: false,
      connectionError: null,
      form: {
        receivedData: null,
      },

      reader: null,
     // isReading: false, //是否正在读取数据
      baudRate: 9600,
      buffer: [], //接收到的数据片段
      rules: {
        receivedData: [
          {
            required: true,
            message: "称显重量不能为空",
            trigger: "blur",
          },
          { validator: this.type==1?valiWeight1:valiWeight, trigger: "blur" },
        ],
      },
    };
  },
  computed: {
    connectionStatus() {
      return this.$store.getters['serial/connectionStatus']
    },
    isConnected() {
      return this.$store.getters['serial/isConnected']
    },
    isConnecting() {
      return this.$store.getters['serial/isConnecting']
    },
    isReading() {
      return this.$store.getters['serial/isReading']
    },
    portInfo() {
      return this.$store.getters['serial/portInfo']
    },
    error() {
      return this.$store.state.serial.error
    },
    statusText() {
      const statusMap = {
        disconnected: '未连接',
        connecting: '连接中...',
        connected: '已连接',
        error: '连接错误'
      }
      return statusMap[this.connectionStatus]
    },
    receivedData() {
      return this.updateInputData(this.$store.state.serial.receivedData);
    }
  },
  mounted() {
    if (this.isSupported && !this.isConnected && !this.isConnecting) {
      this.connect();
    }
  },
  beforeDestroy() {
    console.log("beforeDestroy")
   // this.disconnect();
  },
  methods: {
    //被父组件调用校验重量是否输入
    async validateForm() {
      return new Promise((resolve) => {
        this.$refs.serialForm.validate((valid) => {
          resolve(valid); // 返回验证结果
        });
      });
    },
    submitWeight() {
      if (!(this.isSupported && this.isConnected)) {
        //手动输入的情况
        this.$emit("confirm", this.form.receivedData);
      }
    },
    async connect() {
      /*
      try {
        // 是否支持串口通信
        console.log(navigator);
        if (!("serial" in navigator)) {
          alert("当前浏览器不支持串口通信，请更换浏览器！");
          return;
        }
        this.serialPort = await navigator.serial.requestPort(); // 弹出系统串口列表对话框，选择一个串口进行连接
        console.log("serialPort:", this.serialPort);
        // 打开串口
        await this.serialPort.open({ baudRate: parseInt(this.baudRate) });
        // 获取串口设备信息
        let portInfo = await this.serialPort.getInfo();
        console.log("portInfo:", portInfo);
        this.serialDevice = portInfo.usbProductId || "";
        this.isConnected = true;
        this.connectionError = null;
        this.$emit("connected", this.port);
        // 开始监听数据
        this.listenToData();
      } catch (e) {
        console.log(e); // The prompt has been dismissed without selecting a device.
      }
        */
      try {
        await this.$store.dispatch('serial/connect')
      } catch (error) {
        console.error('连接失败:', error)
      }
    },
    async disconnect() {
      try {
        await this.$store.dispatch('serial/disconnect')
      } catch (error) {
        console.error('断开连接失败:', error)
      }
      this.$emit("disconnecting");

    },
    //停止读取数据
    async stopReader() {
      /*
      if (this.serialPort === null || !this.serialPort.writable) {
        console.log("Not opened.");
        return;
      }

      if (this.isReading) {
        this.isReading = false;
        this.reader?.cancel();
        console.log("停止读取");
      }*/
      try {
        await this.$store.dispatch('serial/stopReading').then(()=>{
          console.log('停止读取完成')
          //this.$emit("confirm", this.form.receivedData);
        })
      } catch (error) {
        console.error('停止读取失败:', error)
      }

    },
    //重新读取
    async startReader() {
      this.buffer = [];
      //this.listenToData();
      try {
        await this.$store.dispatch('serial/startReading')
      } catch (error) {
        console.error('重新读取失败:', error)
      }
    },
    async listenToData() {
      console.log(this.serialPort);
      this.isReading = true;
      while (this.serialPort && this.serialPort.readable && this.isReading) {
        try {
          this.reader = this.serialPort.readable.getReader();

          while (true) {
            const { value, done } = await this.reader.read();
            if (done) {
              break;
            }
            this.updateInputData(value);
          }
        } catch (error) {
          console.error("读取数据错误:", error);
          this.$emit("error", error);
        } finally {
          if (this.reader) {
            this.reader.releaseLock();
          }
          // 如果仍然处于读取状态但reader结束了，尝试重新开始
          if (this.isReading && this.serialPort && this.serialPort.readable) {
            setTimeout(() => this.listenToData(), 0);
          }
        }
      }
    },
    // 更新接收到的数据
    updateInputData(data) {
      let array = new Uint8Array(data); // event.data.buffer就是接收到的包数据了
      // 将接收到的数据累积到 buffer 中
      this.buffer.push(...array);

      // 检查是否累积到完整的帧数据（12 字节）
      while (this.buffer.length >= 12) {
        // 查找帧头(0x02)
        const frameStartIndex = this.buffer.indexOf(0x02);
        if (frameStartIndex === -1) {
          // 没有找到帧头，清空无效数据
          this.buffer = [];
          break;
        }

        // 移除帧头之前的所有数据
        if (frameStartIndex > 0) {
          this.buffer = this.buffer.slice(frameStartIndex);
          continue;
        }

        // 检查是否已接收完整帧(12字节)
        if (this.buffer.length < 12) {
          break;
        }

        const frame = this.buffer.slice(0, 12);

        // 验证帧尾(0x03)
        if (frame[11] !== 0x03) {
          // 帧尾不匹配，丢弃第一个字节(可能是损坏的帧头)
          this.buffer = this.buffer.slice(1);
          continue;
        }
        this.buffer = this.buffer.slice(12); // 移除已提取的数据
        // 解析帧数据
        this.parseData(frame);
      }
    },
    // 解析数据
    parseData(frame) {
      try {
        const weight = this.parseFrame(frame);
        this.form.receivedData = weight;
      } catch (e) {
        console.log(`解析错误 - ${e.message}\n`);
      }
    },
    // 解析单帧数据
    parseFrame(frame) {
      if (frame.length !== 12) {
        throw new Error("帧长度必须为 12 字节");
      }

      // 检查帧头和帧尾
      if (frame[0] !== 0x02 || frame[11] !== 0x03) {
        throw new Error("无效的帧头或帧尾");
      }

      // 提取符号位
      const sign = String.fromCharCode(frame[1]);
      if (sign !== "+" && sign !== "-") {
        throw new Error("无效的符号位");
      }

      // 提取称量数据（字节 3-8）
      const weightData = frame.slice(2, 8);
      const weightStr = String.fromCharCode(...weightData); // 将字节数据转换为 ASCII 字符串

      // 提取小数点位数（字节 9）
      const decimalPlaces = frame[8] - 48; // 将 ASCII 码转换为数字
      if (decimalPlaces < 0 || decimalPlaces > 4) {
        throw new Error("小数点位数错误");
      }

      // 分割整数部分和小数部分
      const len = weightStr.length - decimalPlaces;
      if (len <= 0) {
        throw new Error("称量数据格式错误");
      }
      const integerPart = weightStr.substring(0, len);
      const decimalPart = weightStr.substring(len);
      const weight = parseFloat(`${sign}${integerPart}.${decimalPart}`);

      // 提取校验和（字节 10-11）
      const checksumHigh = frame[9]; // 校验和高位
      const checksumLow = frame[10]; // 校验和低位

      // 计算校验和（字节 2-9 的异或值）
      let calculatedChecksum = 0;
      for (let i = 1; i < 9; i++) {
        calculatedChecksum ^= frame[i];
      }

      // 拆分校验和
      const calculatedChecksumHigh = (calculatedChecksum >> 4) & 0x0f;
      const calculatedChecksumLow = calculatedChecksum & 0x0f;

      // 转换为 ASCII 码
      const expectedChecksumHigh =
        calculatedChecksumHigh < 10
          ? calculatedChecksumHigh + 48
          : calculatedChecksumHigh + 55;
      const expectedChecksumLow =
        calculatedChecksumLow < 10
          ? calculatedChecksumLow + 48
          : calculatedChecksumLow + 55;

      // 校验和验证
      if (
        checksumHigh !== expectedChecksumHigh ||
        checksumLow !== expectedChecksumLow
      ) {
        throw new Error("校验和错误");
      }

      return weight;
    },
  },
};
</script>
<style>
/* 样式保持不变 */
.large-input{
  font-size: 42px;
  width: 300px;
}
.large-input>.el-input__inner{
  height: 80px;
  line-height: 80px;
}
.serialInput-block{
  display: flex;
  align-items: center;
}
.serial-port {
  font-family: Arial, sans-serif;
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.error {
  color: red;
  font-weight: bold;
}

.connection-status {
  margin: 10px 0;
  padding: 8px;
  border-radius: 4px;
  font-weight: bold;
}

.connected {
  color: #4caf50;
}

.disconnected {
  color: #f44336;
}
</style>
