update 电单车协议

This commit is contained in:
Guoqs
2024-08-20 15:14:49 +08:00
parent 6aaece1862
commit 565f4790be
20 changed files with 39 additions and 28 deletions

View File

@@ -1,6 +1,6 @@
package com.jsowell.netty.decoder;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

View File

@@ -1,104 +0,0 @@
package com.jsowell.netty.domain.ebike;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.netty.domain.ebike.deviceupload.*;
import lombok.Getter;
import lombok.Setter;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@Getter
@Setter
public abstract class AbsEBikeMessage {
private String header; // 包头 (3字节)
private int length; // 长度 (2字节)
private int physicalId; // 物理ID (4字节)
private int messageId; // 消息ID (2字节)
private String command; // 命令 (1字节)
private Object payload; // 数据 (n字节)
private int checksum; // 校验 (2字节)
public AbsEBikeMessage(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum) {
this.header = header;
this.length = length;
this.physicalId = physicalId;
this.messageId = messageId;
this.command = command;
this.payload = payload;
this.checksum = checksum;
}
public abstract void parsePayload(byte[] dataBytes);
private static AbsEBikeMessage createMessageInstance(String command, String header, int length, int physicalId, int messageId, int checksum, byte[] dataBytes) {
switch (command) {
case "02":
return new EBikeMessageCmd02(header, length, physicalId, messageId, command, null, checksum, new CreditCardInfo(dataBytes));
case "03":
return new EBikeMessageCmd03(header, length, physicalId, messageId, command, null, checksum, new SettlementInfo(dataBytes));
case "04":
return new EBikeMessageCmd04(header, length, physicalId, messageId, command, null, checksum, new ConfirmOrder(dataBytes));
case "06":
return new EBikeMessageCmd06(header, length, physicalId, messageId, command, null, checksum, new PowerHeartbeat(dataBytes));
case "20":
return new EBikeMessageCmd20(header, length, physicalId, messageId, command, null, checksum, new DeviceRegister(dataBytes));
case "21":
return new EBikeMessageCmd21(header, length, physicalId, messageId, command, null, checksum, new DeviceHeartbeat(dataBytes));
case "22":
return new EBikeMessageCmd22(header, length, physicalId, messageId, command, null, checksum);
default:
throw new IllegalArgumentException("Unsupported command: " + command);
}
}
public static AbsEBikeMessage parseMessage(byte[] messageBytes) {
if (messageBytes == null || messageBytes.length < 14 || messageBytes.length > 256) {
throw new IllegalArgumentException("Invalid message bytes");
}
try {
// 读取包头
byte[] headerBytes = Arrays.copyOfRange(messageBytes, 0, 3);
String header = new String(headerBytes, StandardCharsets.UTF_8);
// 读取长度
byte[] lengthBytes = Arrays.copyOfRange(messageBytes, 3, 5);
int length = BytesUtil.bytesToIntLittle(lengthBytes);
// 验证长度
if (length != (messageBytes.length - 5)) {
throw new IllegalArgumentException("Invalid message length");
}
// 读取物理ID
byte[] physicalIdBytes = Arrays.copyOfRange(messageBytes, 5, 9);
int physicalId = BytesUtil.bytesToIntLittle(physicalIdBytes);
// 读取消息ID
byte[] messageIdBytes = Arrays.copyOfRange(messageBytes, 9, 11);
int messageId = BytesUtil.bytesToIntLittle(messageIdBytes);
// 读取命令
byte commandByte = messageBytes[11];
String command = BytesUtil.bcd2StrLittle(new byte[]{commandByte});
// 读取数据
byte[] dataBytes = Arrays.copyOfRange(messageBytes, 12, messageBytes.length - 2);
// 读取校验
byte[] checksumBytes = Arrays.copyOfRange(messageBytes, messageBytes.length - 2, messageBytes.length);
int checksum = BytesUtil.bytesToIntLittle(checksumBytes);
// 根据命令创建相应的子类实例
AbsEBikeMessage parsedMessage = createMessageInstance(command, header, length, physicalId, messageId, checksum, dataBytes);
parsedMessage.parsePayload(dataBytes);
return parsedMessage;
} catch (Exception e) {
throw new IllegalArgumentException("Error parsing message", e);
}
}
}

View File

@@ -1,42 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.common.util.BytesUtil;
import lombok.Data;
import java.util.Arrays;
@Data
public class ConfirmOrder {
/**
* 端口号
*/
private String portNumber;
/**
* 在线/离线启动
*/
private String startMode;
/**
* 卡片ID
*/
private String cardId;
/**
* 充电时长
*/
private String chargingTime;
/**
* 订单编号
*/
private String orderCode;
public ConfirmOrder(byte[] dataBytes) {
this.portNumber = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 0, 1)) + "";
this.startMode = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 1, 2)) + "";
this.cardId = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 2, 6)) + "";
this.chargingTime = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 6, 8)) + "";
this.orderCode = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, 8, 24));
}
}

View File

@@ -1,69 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.common.util.BytesUtil;
import lombok.Data;
import java.util.Arrays;
@Data
public class CreditCardInfo {
/**
* 卡片ID
*/
private String cardId;
/**
* 卡片类型
*/
private String cardType;
/**
* 端口号
*/
private String portNumber;
/**
* 余额卡内金额
*/
private String cardBalance;
/**
* 时间戳
*/
// private String timestamp;
/**
* 卡号2字节数
*/
private int card2Length;
/**
* 卡号2
*/
private String card2Code;
public CreditCardInfo(byte[] dataBytes) {
int startIndex = 0;
int length = 4;
this.cardId = BytesUtil.bcd2Str(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 1;
this.cardType = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 1;
this.portNumber = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 2;
this.cardBalance = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
// length = 4;
// this.timestamp = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
if (dataBytes.length > startIndex) {
length = 1;
card2Length = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length));
this.card2Code = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, card2Length)) + "";
}
}
}

View File

@@ -1,64 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.google.common.collect.Lists;
import com.jsowell.common.enums.ebike.PortStatusEnum;
import com.jsowell.common.util.BytesUtil;
import lombok.Getter;
import lombok.Setter;
import java.util.Arrays;
import java.util.List;
/**
* 此为心跳包间隔时间默认为3分钟方便服务器管理SocketIP
* 设备如2次收不到服务器应答则进入离线状态
*/
@Getter
@Setter
public class DeviceHeartbeat {
/**
* 电压:设备的当前电压(打包发送心跳包指令时的当前时间点的实时电压)
*/
private String voltage;
/**
* 端口数量:表示设备总共有多少个端口,和后面的“端口状态”配套
*/
private int portNumber;
/**
* 注4、各端口状态一个字节表示一个端口和“端口数量”匹配
* 如端口数量是16则“各端口状态”为16字节
* 每个字节表示的意思,
* 0=空闲1=充电中2=有充电器但未充电用户未启动充电3=有充电器但未充电(已充满电) 4=该路无法计量5=浮充6=存储器损坏,
* 7插座弹片卡住故障8接触不良或保险丝烧断故障9(算法-继电器粘连)0x0A=霍尔开关损坏(即插入检测传感器)。
* 0x0B=(预检-继电器坏或保险丝断0x0D=(预检-负载短路。0x0E=(过滤性预检-继电器粘连),0x0F=(刷卡芯片损坏故障)0x10=(检测电路故障)
*/
private List<String> portStatus;
/**
* 信号强度指分机与主机之间的无线信号强度如LORA信号。00则为有线组网或无信号强度功能
*/
private String rssi;
/**
* 当前环境温度表示当前设备内的温度可能和真正的当前环境温度有一定的误差如00则表示无此功能
*/
private String temperature;
public DeviceHeartbeat(byte[] dataBytes) {
this.voltage = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 0, 2)) + "";
this.portNumber = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 2, 3));
byte[] statusBytes = Arrays.copyOfRange(dataBytes, 3, this.portNumber);
List<String> statusList = Lists.newArrayList();
for (byte statusByte : statusBytes) {
int status = BytesUtil.bytesToIntLittle(new byte[]{statusByte});
statusList.add(PortStatusEnum.getDescriptionByValue(status));
}
this.portStatus = statusList;
this.rssi = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, dataBytes.length - 2, dataBytes.length - 1));
this.temperature = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, dataBytes.length - 1, dataBytes.length));
}
}

View File

@@ -1,49 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.common.util.BytesUtil;
import lombok.Data;
import java.util.Arrays;
@Data
public class DeviceRegister {
/**
* 固件版本如100则表示V1.00版本
*/
private String firmwareVersion;
/**
* 端口数量 表示设备总共有多少个端口
*/
private String portNumber;
/**
* 虚拟ID需要内部组网的设备的本地地址如485、LORA系列如不需组网的设备默认为00
*/
private String virtualId;
/**
* 设备类型: 见01指令中的设备类型表
*/
private String deviceType;
/**
* 工作模式第0位0=联网1=刷卡。第1位0=RN82091=BL0939。第2位0=无短路预检1=有短路预检。第3位0=光耦检测模式1=带灯模式。
*/
private String workMode;
/**
* 电源板版本号电源板的固件版本号如没有电源板的机型则为0
*/
private String powerBoardVersion;
public DeviceRegister(byte[] dataBytes) {
this.firmwareVersion = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 0, 2)) + "";
this.portNumber = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 2, 3)) + "";
this.virtualId = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 3, 4)) + "";
this.deviceType = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, 4, 5));
this.workMode = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, 5, 6));
this.powerBoardVersion = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, 6, 8));
}
}

View File

@@ -1,27 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
/**
* 刷卡操作02指令
* 特别注意上报的端口号如果不是FF为端口请求充电服务器需应答刷卡指令后如果服务器需要给当前端口启动充电的需要下发82指令来启动充电
* CreditCard
*/
public class EBikeMessageCmd02 extends AbsEBikeMessage {
private CreditCardInfo creditCardInfo;
public EBikeMessageCmd02(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum, CreditCardInfo creditCardInfo) {
super(header, length, physicalId, messageId, command, payload, checksum);
this.creditCardInfo = creditCardInfo;
}
@Override
public void parsePayload(byte[] dataBytes) {
this.creditCardInfo = new CreditCardInfo(dataBytes);
}
public CreditCardInfo getCreditCardInfo() {
return creditCardInfo;
}
}

View File

@@ -1,22 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
public class EBikeMessageCmd03 extends AbsEBikeMessage {
private SettlementInfo settlementInfo;
public EBikeMessageCmd03(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum, SettlementInfo settlementInfo) {
super(header, length, physicalId, messageId, command, payload, checksum);
this.settlementInfo = settlementInfo;
}
@Override
public void parsePayload(byte[] dataBytes) {
this.settlementInfo = new SettlementInfo(dataBytes);
}
public SettlementInfo getSettlementInfo() {
return settlementInfo;
}
}

View File

@@ -1,26 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
/**
* 充电端口订单确认04指令
* 这是老版本指令和06相对应有06指令时无此命令2019年6月份后生产的机型已无此指令
*/
public class EBikeMessageCmd04 extends AbsEBikeMessage {
private ConfirmOrder confirmOrder;
public EBikeMessageCmd04(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum, ConfirmOrder confirmOrder) {
super(header, length, physicalId, messageId, command, payload, checksum);
this.confirmOrder = confirmOrder;
}
@Override
public void parsePayload(byte[] dataBytes) {
this.confirmOrder = new ConfirmOrder(dataBytes);
}
public ConfirmOrder getSettlementInfo() {
return confirmOrder;
}
}

View File

@@ -1,27 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
/**
* 端口充电时功率心跳包06指令
* 这是新版本指令和04相对应有04指令时无此命令
* 此命令设备开始充电后82命令应答后每隔设5分钟发送1次无重发机制 充电结束后即停止发送
*/
public class EBikeMessageCmd06 extends AbsEBikeMessage {
private PowerHeartbeat powerHeartbeat;
public EBikeMessageCmd06(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum, PowerHeartbeat powerHeartbeat) {
super(header, length, physicalId, messageId, command, payload, checksum);
this.powerHeartbeat = powerHeartbeat;
}
@Override
public void parsePayload(byte[] dataBytes) {
this.powerHeartbeat = new PowerHeartbeat(dataBytes);
}
public PowerHeartbeat getPowerHeartbeat() {
return powerHeartbeat;
}
}

View File

@@ -1,25 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
/**
* 设备注册包20指令
*/
public class EBikeMessageCmd20 extends AbsEBikeMessage {
private DeviceRegister deviceRegister;
public EBikeMessageCmd20(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum, DeviceRegister deviceRegister) {
super(header, length, physicalId, messageId, command, payload, checksum);
this.deviceRegister = deviceRegister;
}
@Override
public void parsePayload(byte[] dataBytes) {
this.deviceRegister = new DeviceRegister(dataBytes);
}
public DeviceRegister getDeviceRegister() {
return deviceRegister;
}
}

View File

@@ -1,26 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
import lombok.Getter;
import lombok.Setter;
/**
* 设备注册包20指令
*/
@Getter
@Setter
public class EBikeMessageCmd21 extends AbsEBikeMessage {
private DeviceHeartbeat deviceHeartbeat;
public EBikeMessageCmd21(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum, DeviceHeartbeat deviceHeartbeat) {
super(header, length, physicalId, messageId, command, payload, checksum);
this.deviceHeartbeat = deviceHeartbeat;
}
@Override
public void parsePayload(byte[] dataBytes) {
this.deviceHeartbeat = new DeviceHeartbeat(dataBytes);
}
}

View File

@@ -1,25 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
import lombok.Getter;
import lombok.Setter;
/**
* 设备获取服务器时间22指令
* 此命令设备每次上电后就会发送直至服务器应答后就停止发送。如服务器无应答则每隔3分钟发送一次请求
* 每12小时从服务器获取一次时间如服务器不应答则每隔3分钟发送一次请求
*/
@Getter
@Setter
public class EBikeMessageCmd22 extends AbsEBikeMessage {
public EBikeMessageCmd22(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum) {
super(header, length, physicalId, messageId, command, payload, checksum);
}
@Override
public void parsePayload(byte[] dataBytes) {
}
}

View File

@@ -1,159 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.common.util.BytesUtil;
import lombok.Getter;
import lombok.Setter;
import java.util.Arrays;
/**
* 功率心跳包
*/
@Getter
@Setter
public class PowerHeartbeat {
/**
* 端口号当前充电的端口号。注00表示1号端口01表示2号端口
*/
private String port;
/**
* 各端口状态: 1=充电中, 2=已扫码暂未检测到功率等待插入充电器充电柜专有如单车桩出现此值为出错值请忽略3=有充电器但未充电已充满电5=浮充,其他值=出错值请忽略
*/
private String portStatus;
/**
* 充电时长:开始充电到当前为止,已经充电多长时间
*/
private String chargingTime;
/**
* 当前订单累计电量:当前端口的订单,开始充电到当前为止,已经消耗的电量
*/
private String totalUsedElectricity;
/**
* 在线/离线启动:=0表示启动时处于离线状态 =1表示启动时处于在线状态 3=验证码启动(仅支持带按键和屏幕的机型)
*/
private String startMode;
/**
* 实时功率:心跳包发送时的当前功率
*/
private String realTimePower;
/**
* 心跳包期间最大功率:心跳包期间出现过的最大功率
*/
private String maxPower;
/**
* 心跳包期间最小功率:心跳包期间出现过的最小功率
*/
private String minPower;
/**
* 心跳包期间平均功率:心跳包期间出现过的平均功率
*/
private String avgPower;
/**
* 订单编号:当前充电的订单编号
*/
private String orderCode;
/**
* 该时间段内消耗电量此数据需除以4800后才是真实的电量该字段属于调试使用服务器无需关心此字段
*/
// private String timePeriodElectricity;
/**
* 峰值功率:整个充电过程中出现过的最大功率,有些版本无此字段
*/
private String peakPower;
/**
* 设备的当前电压
*/
private String voltage;
/**
* 设备的当前电流,可以计量芯片采集也可以通过计算得出,0.001A为单位1000表示1A电流
*/
private String current;
/**
* 设备的当前环境温度,仅针对有此功能的机型
*/
private String ambientTemperature;
/**
* 端口温度,仅针对有此功能的机型
*/
private String portTemperature;
/**
* 上发指令当时的时间,有时候不准确,该字段属于调试使用,服务器无需关心此字段
*/
// private String timestamp;
/**
* 占位时长:(充电柜专用,其他设备忽略此字段)表示充满后占用设备的时长,单位为分钟
*/
private String occupancyTime;
public PowerHeartbeat(byte[] dataBytes) {
int startIndex = 0;
int length = 1;
this.port = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 1;
this.portStatus = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 2;
this.chargingTime = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 2;
this.totalUsedElectricity = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) * 0.01 + "";
length = 1;
this.startMode = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 2;
this.realTimePower = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) * 0.1 + "";
length = 2;
this.maxPower = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) * 0.1 + "";
length = 2;
this.minPower = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) * 0.1 + "";
length = 2;
this.avgPower = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) * 0.1 + "";
length = 16;
this.orderCode = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length));
length = 2;
this.peakPower = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 2;
this.voltage = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) * 0.1 + "";
length = 2;
this.current = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) * 0.0001 + "";
length = 1;
this.ambientTemperature = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 1;
this.portTemperature = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
// length = 4;
// this.timestamp = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
length = 2;
this.occupancyTime = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
}
}

View File

@@ -1,36 +0,0 @@
package com.jsowell.netty.domain.ebike.deviceupload;
import com.jsowell.common.util.BytesUtil;
import lombok.Data;
import java.util.Arrays;
@Data
public class SettlementInfo {
private String chargingDuration; // 充电时长, 单位:"秒"
private String maxPower; // 最大功率, 单位:"0.1W"
private String consumedEnergy; // 耗电量, 单位:"0.01度"
private String portNumber; // 端口号
private String startMode; // 在线/离线启动/验证码
private String cardNumberOrVerificationCode; // 卡号/验证码
private String stopReason; // 停止原因
private String orderNumber; // 订单编号
private String secondMaxPower; // 第二最大功率
// private String timestamp; // 时间戳 上发指令当时的时间,有时候不准确,该字段属于调试使用,服务器无需关心此字段
// private String placeholderDuration; // 占位时长 充电柜专用,其他设备忽略此字段 表示充满后占用设备的时长,单位为分钟
public SettlementInfo(byte[] dataBytes) {
this.chargingDuration = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 0, 2)) + "";
this.maxPower = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 2, 4)) + "";
this.consumedEnergy = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 4, 6)) + "";
this.portNumber = BytesUtil.bcd2StrLittle(new byte[]{dataBytes[6]});
this.startMode = BytesUtil.bcd2StrLittle(new byte[]{dataBytes[7]});
this.cardNumberOrVerificationCode = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, 8, 12));
this.stopReason = BytesUtil.bcd2StrLittle(new byte[]{dataBytes[12]});
this.orderNumber = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, 13, 29));
this.secondMaxPower = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 29, 31)) + "";
// this.timestamp = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 31, 35)) + "";
// this.placeholderDuration = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 35, 37)) + "";
}
}

View File

@@ -1,21 +0,0 @@
package com.jsowell.netty.domain.ebike.serversend;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
/**
* 查询设备联网状态81指令
* 注1、此命令应用于注册网络后通信过程中服务器主动查询设备状态
* 注2、此命令会触发设备发送“注册包”20、“设备心跳包”01和21指令上发时间会因设备不同而不同
*/
public class EBikeMessageCmd81 extends AbsEBikeMessage {
public EBikeMessageCmd81(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum) {
super(header, length, physicalId, messageId, command, payload, checksum);
}
@Override
public void parsePayload(byte[] dataBytes) {
}
}

View File

@@ -1,25 +0,0 @@
package com.jsowell.netty.domain.ebike.serversend;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
/**
* 服务器开始、停止充电操作82指令
*/
public class EBikeMessageCmd82 extends AbsEBikeMessage {
private SpecificData specificData;
public EBikeMessageCmd82(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum, SpecificData specificData) {
super(header, length, physicalId, messageId, command, payload, checksum);
this.specificData = specificData;
}
@Override
public void parsePayload(byte[] dataBytes) {
this.specificData = new SpecificData(dataBytes);
}
public SpecificData getSpecificData() {
return specificData;
}
}

View File

@@ -1,78 +0,0 @@
package com.jsowell.netty.domain.ebike.serversend;
import com.jsowell.common.util.BytesUtil;
import lombok.Getter;
import lombok.Setter;
import java.util.Arrays;
@Getter
@Setter
public class SpecificData {
private String rateMode; // 费率模式 (1字节)
private String balanceOrValidity; // 余额/有效期 (4字节)
private String portNumber; // 端口号 (1字节)
private String chargeCommand; // 充电命令 (1字节)
private String chargeDurationOrPower; // 充电时长/电量 (2字节)
private String orderNumber; // 订单编号 (16字节)
private String maxChargeDuration; // 最大充电时长 (2字节)
private String overloadPower; // 过载功率 (2字节)
private String qrCodeLight; // 二维码灯 (1字节)
private String longChargeMode; // 长充模式 (1字节)
private String extraFloatChargeTime; // 额外浮充时间 (2字节)
private String skipShortCircuitDetection; // 是否跳过短路检测 (1字节)
private String noUserPullOutCheck; // 不判断用户拔出 (1字节)
private String forceAutoStopWhenFull; // 强制带充满自停 (1字节)
private String fullChargePower; // 充满功率 (1字节)
private String maxFullChargePowerCheckTime; // 充满功率最长判断时间 (1字节)
public SpecificData(byte[] dataBytes) {
byte rateModeBytes = dataBytes[0];
this.rateMode = BytesUtil.bcd2StrLittle(new byte[]{rateModeBytes});
byte[] balanceOrValidityBytes = Arrays.copyOfRange(dataBytes, 1, 5);
this.balanceOrValidity = BytesUtil.bcd2StrLittle(balanceOrValidityBytes);
byte portNumberBytes = dataBytes[5];
this.portNumber = BytesUtil.bcd2StrLittle(new byte[]{portNumberBytes});
byte chargeCommandBytes = dataBytes[6];
this.chargeCommand = BytesUtil.bcd2StrLittle(new byte[]{chargeCommandBytes});
byte[] chargeDurationOrPowerBytes = Arrays.copyOfRange(dataBytes, 7, 9);
this.chargeDurationOrPower = BytesUtil.bcd2StrLittle(chargeDurationOrPowerBytes);
byte[] orderNumberBytes = Arrays.copyOfRange(dataBytes, 9, 25);
this.orderNumber = BytesUtil.bcd2StrLittle(orderNumberBytes);
byte[] maxChargeDurationBytes = Arrays.copyOfRange(dataBytes, 25, 27);
this.maxChargeDuration = BytesUtil.bcd2StrLittle(maxChargeDurationBytes);
byte[] overloadPowerBytes = Arrays.copyOfRange(dataBytes, 27, 29);
this.overloadPower = BytesUtil.bcd2StrLittle(overloadPowerBytes);
byte qrCodeLightBytes = dataBytes[29];
this.qrCodeLight = BytesUtil.bcd2StrLittle(new byte[]{qrCodeLightBytes});
byte longChargeModeBytes = dataBytes[30];
this.longChargeMode = BytesUtil.bcd2StrLittle(new byte[]{longChargeModeBytes});
byte[] extraFloatChargeTimeBytes = Arrays.copyOfRange(dataBytes, 31, 33);
this.extraFloatChargeTime = BytesUtil.bcd2StrLittle(extraFloatChargeTimeBytes);
byte skipShortCircuitDetectionBytes = dataBytes[33];
this.skipShortCircuitDetection = BytesUtil.bcd2StrLittle(new byte[]{skipShortCircuitDetectionBytes});
byte noUserPullOutCheckBytes = dataBytes[34];
this.noUserPullOutCheck = BytesUtil.bcd2StrLittle(new byte[]{noUserPullOutCheckBytes});
byte forceAutoStopWhenFullByte = dataBytes[35];
this.forceAutoStopWhenFull = BytesUtil.bcd2StrLittle(new byte[]{forceAutoStopWhenFullByte});
byte fullChargePowerBytes = dataBytes[36];
this.fullChargePower = BytesUtil.bcd2StrLittle(new byte[]{fullChargePowerBytes});
byte maxFullChargePowerCheckTimeBytes = dataBytes[37];
this.maxFullChargePowerCheckTime = BytesUtil.bcd2StrLittle(new byte[]{maxFullChargePowerCheckTimeBytes});
}
}

View File

@@ -1,7 +1,7 @@
package com.jsowell.netty.server.electricbicycles;
import com.alibaba.fastjson2.JSON;
import com.jsowell.netty.domain.ebike.AbsEBikeMessage;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import io.netty.channel.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;