/*
 * 文 件 名:  RealtimeDataFrame
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  实时监测数据帧
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/12/22
 */
package com.ruoyi.ems.charging.model.req;

import com.ruoyi.ems.charging.model.BaseFrame;
import com.ruoyi.ems.charging.protocol.ProtocolConstants;
import com.ruoyi.ems.charging.utils.ByteUtils;
import io.netty.buffer.ByteBuf;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 实时监测数据帧 (0x13)
 * 上送充电枪实时数据，周期上送时：待机5分钟、充电5秒
 * 这是能耗平台最核心的数据帧，包含充电功率、电量等关键信息
 *
 * @author lvwenbin
 * @version [版本号, 2025/12/22]
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class RealtimeDataFrame extends BaseFrame {
    
    /**
     * 交易流水号 (BCD码 16字节)
     */
    private String transactionNo;
    
    /**
     * 桩编号 (BCD码 7字节)
     */
    private String pileCode;
    
    /**
     * 枪号 (BCD码 1字节)
     */
    private String gunNo;
    
    /**
     * 状态 (BIN码 1字节)
     * 0x00：离线, 0x01：故障, 0x02：空闲, 0x03：充电
     * 0x04：空闲-脉冲静置, 0x05：充电中-脉冲检测
     */
    private byte status;
    
    /**
     * 枪是否归位 (BIN码 1字节)
     * 0x00：否, 0x01：是, 0x02：未知
     */
    private byte gunReturned;
    
    /**
     * 是否插枪 (BIN码 1字节)
     * 0x00：否, 0x01：是
     */
    private byte gunConnected;
    
    /**
     * 输出电压 (BIN码 2字节)
     * 精确到小数点后一位，待机置零
     */
    private int outputVoltage;
    
    /**
     * 输出电流 (BIN码 2字节)
     * 精确到小数点后一位，待机置零
     */
    private int outputCurrent;
    
    /**
     * 枪线温度 (BIN码 1字节)
     * 整形，偏移量-50，待机置零
     */
    private int gunTemperature;
    
    /**
     * 枪线编码 (BIN码 8字节)
     * 没有置零
     */
    private byte[] gunLineCode;
    
    /**
     * SOC (BIN码 1字节)
     * 待机置零，交流桩置零
     */
    private int soc;
    
    /**
     * 电池组最高温度 (BIN码 1字节)
     * 整形，偏移量-50，待机置零，交流桩置零
     */
    private int batteryMaxTemp;
    
    /**
     * 累计充电时间 (BIN码 2字节)
     * 单位：min，待机置零
     */
    private int chargingTime;
    
    /**
     * 剩余时间 (BIN码 2字节)
     * 单位：min，待机置零，交流桩置零
     */
    private int remainingTime;
    
    /**
     * 充电度数 (BIN码 4字节)
     * 精确到小数点后四位，待机置零
     */
    private long chargingEnergy;
    
    /**
     * 计损充电度数 (BIN码 4字节)
     * 精确到小数点后四位，待机置零
     * 未设置计损比例时等于充电度数
     */
    private long lossCorrectedEnergy;
    
    /**
     * 已充金额 (BIN码 4字节)
     * 精确到小数点后四位，待机置零
     * (电费+服务费)*计损充电度数
     */
    private long chargedAmount;
    
    /**
     * 硬件故障 (BIN码 2字节)
     * Bit位表示（0否1是）
     */
    private int hardwareFault;
    
    @Override
    public byte getFrameType() {
        return ProtocolConstants.FRAME_TYPE_REALTIME_DATA;
    }
    
    @Override
    protected void encodeBody(ByteBuf buf) {
        ByteUtils.writeBcd(buf, transactionNo, 16);
        ByteUtils.writeBcd(buf, pileCode, 7);
        ByteUtils.writeBcd(buf, gunNo, 1);
        buf.writeByte(status);
        buf.writeByte(gunReturned);
        buf.writeByte(gunConnected);
        buf.writeShortLE(outputVoltage);
        buf.writeShortLE(outputCurrent);
        buf.writeByte(gunTemperature);
        if (gunLineCode != null && gunLineCode.length == 8) {
            buf.writeBytes(gunLineCode);
        } else {
            buf.writeBytes(new byte[8]);
        }
        buf.writeByte(soc);
        buf.writeByte(batteryMaxTemp);
        buf.writeShortLE(chargingTime);
        buf.writeShortLE(remainingTime);
        buf.writeIntLE((int) chargingEnergy);
        buf.writeIntLE((int) lossCorrectedEnergy);
        buf.writeIntLE((int) chargedAmount);
        buf.writeShortLE(hardwareFault);
    }
    
    @Override
    protected void decodeBody(ByteBuf buf) {
        this.transactionNo = ByteUtils.readBcd(buf, 16);
        this.pileCode = ByteUtils.readBcd(buf, 7);
        this.gunNo = ByteUtils.readBcd(buf, 1);
        this.status = buf.readByte();
        this.gunReturned = buf.readByte();
        this.gunConnected = buf.readByte();
        this.outputVoltage = buf.readShortLE() & 0xFFFF;
        this.outputCurrent = buf.readShortLE() & 0xFFFF;
        this.gunTemperature = buf.readByte() & 0xFF;
        this.gunLineCode = new byte[8];
        buf.readBytes(this.gunLineCode);
        this.soc = buf.readByte() & 0xFF;
        this.batteryMaxTemp = buf.readByte() & 0xFF;
        this.chargingTime = buf.readShortLE() & 0xFFFF;
        this.remainingTime = buf.readShortLE() & 0xFFFF;
        this.chargingEnergy = buf.readIntLE() & 0xFFFFFFFFL;
        this.lossCorrectedEnergy = buf.readIntLE() & 0xFFFFFFFFL;
        this.chargedAmount = buf.readIntLE() & 0xFFFFFFFFL;
        this.hardwareFault = buf.readShortLE() & 0xFFFF;
    }
    
    // ==================== 数据转换方法 ====================
    
    /**
     * 获取输出电压(V)
     */
    public double getOutputVoltageValue() {
        return ByteUtils.protocolToVoltage(outputVoltage);
    }
    
    /**
     * 获取输出电流(A)
     */
    public double getOutputCurrentValue() {
        return ByteUtils.protocolToCurrent(outputCurrent);
    }
    
    /**
     * 获取输出功率(kW)
     */
    public double getOutputPower() {
        return getOutputVoltageValue() * getOutputCurrentValue() / 1000.0;
    }
    
    /**
     * 获取枪线温度(℃)
     */
    public int getGunTemperatureValue() {
        return ByteUtils.protocolToTemperature(gunTemperature);
    }
    
    /**
     * 获取电池组最高温度(℃)
     */
    public int getBatteryMaxTempValue() {
        return ByteUtils.protocolToTemperature(batteryMaxTemp);
    }
    
    /**
     * 获取充电度数(kWh)
     */
    public double getChargingEnergyValue() {
        return ByteUtils.protocolToEnergy(chargingEnergy);
    }
    
    /**
     * 获取计损充电度数(kWh)
     */
    public double getLossCorrectedEnergyValue() {
        return ByteUtils.protocolToEnergy(lossCorrectedEnergy);
    }
    
    /**
     * 获取已充金额(元)
     */
    public double getChargedAmountValue() {
        return ByteUtils.protocolToAmount(chargedAmount);
    }
    
    /**
     * 获取完整枪号
     */
    public String getFullGunNo() {
        return pileCode + gunNo;
    }
    
    /**
     * 获取状态描述
     */
    public String getStatusEnum() {
        switch (status) {
            case ProtocolConstants.GUN_STATUS_OFFLINE:
                return "0"; // 离线
            case ProtocolConstants.GUN_STATUS_FAULT:
                return "1"; // 故障
            case ProtocolConstants.GUN_STATUS_IDLE:
                return "2"; // 空闲
            case ProtocolConstants.GUN_STATUS_CHARGING:
                return "3"; // 充电中
            case ProtocolConstants.GUN_STATUS_IDLE_PULSE:
                return "4"; // 空闲-脉冲静置
            case ProtocolConstants.GUN_STATUS_CHARGING_PULSE:
                return "5"; // 充电中-脉冲检测
            default:
                return "-1"; // 未知
        }
    }
    
    /**
     * 是否正在充电
     */
    public boolean isCharging() {
        return status == ProtocolConstants.GUN_STATUS_CHARGING 
            || status == ProtocolConstants.GUN_STATUS_CHARGING_PULSE;
    }
    
    /**
     * 是否空闲
     */
    public boolean isIdle() {
        return status == ProtocolConstants.GUN_STATUS_IDLE 
            || status == ProtocolConstants.GUN_STATUS_IDLE_PULSE;
    }
    
    /**
     * 是否插枪
     */
    public boolean isGunConnected() {
        return gunConnected == ProtocolConstants.GUN_CONNECTED;
    }
    
    /**
     * 解析硬件故障位
     */
    public String getHardwareFaultDesc() {
        if (hardwareFault == 0) {
            return "无故障";
        }
        StringBuilder sb = new StringBuilder();
        String[] faultNames = {
            "急停按钮动作故障", "无可用整流模块", "出风口温度过高", "交流防雷故障",
            "交直流模块DC20通信中断", "绝缘检测模块FC08通信中断", "电度表通信中断", "读卡器通信中断",
            "RC10通信中断", "风扇调速板故障", "直流熔断器故障", "高压接触器故障", "门打开"
        };
        for (int i = 0; i < faultNames.length && i < 16; i++) {
            if ((hardwareFault & (1 << i)) != 0) {
                if (sb.length() > 0) {
                    sb.append(",");
                }
                sb.append(faultNames[i]);
            }
        }
        return sb.toString();
    }
    
    @Override
    public String toString() {
        return String.format(
            "RealtimeDataFrame[transNo=%s, pile=%s, gun=%s, status=%s, V=%.1f, A=%.1f, P=%.2fkW, energy=%.4fkWh, soc=%d%%, time=%dmin]",
            transactionNo, pileCode, gunNo, status,
            getOutputVoltageValue(), getOutputCurrentValue(), getOutputPower(),
            getChargingEnergyValue(), soc, chargingTime);
    }
}
