309 lines
9.0 KiB
Java
309 lines
9.0 KiB
Java
package com.muyu.vehicle;
|
||
|
||
import com.alibaba.fastjson2.JSONObject;
|
||
import com.muyu.common.SystemConstant;
|
||
import com.muyu.common.pool.ScheduledThreadPool;
|
||
import com.muyu.domain.Vehicle;
|
||
import com.muyu.domain.model.PositionModel;
|
||
import com.muyu.utils.CalculateCheckDigit;
|
||
import com.muyu.utils.ConversionUtil;
|
||
import com.muyu.utils.VehicleUtils;
|
||
import com.muyu.vehicle.model.VehicleData;
|
||
import com.muyu.vehicle.model.properties.MqttProperties;
|
||
import com.muyu.vehicle.thread.VehicleThread;
|
||
import lombok.AllArgsConstructor;
|
||
import lombok.Builder;
|
||
import lombok.Data;
|
||
import lombok.NoArgsConstructor;
|
||
import lombok.extern.log4j.Log4j2;
|
||
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
|
||
|
||
import java.math.BigDecimal;
|
||
import java.math.RoundingMode;
|
||
import java.util.List;
|
||
import java.util.Objects;
|
||
import java.util.concurrent.LinkedBlockingQueue;
|
||
import java.util.concurrent.ScheduledFuture;
|
||
|
||
import static com.muyu.common.SystemConstant.*;
|
||
|
||
/**
|
||
* @author Saisai.Liu
|
||
* @version 1.0
|
||
* @description 车辆实例
|
||
* @date 2023/11/16
|
||
*/
|
||
@Data
|
||
@Log4j2
|
||
@Builder
|
||
@NoArgsConstructor
|
||
@AllArgsConstructor
|
||
public class VehicleInstance {
|
||
|
||
/**
|
||
* 路线轨迹编码
|
||
*/
|
||
private String positionCode;
|
||
|
||
/**
|
||
* 路径队列
|
||
*/
|
||
private LinkedBlockingQueue<PositionModel> positionQueue = new LinkedBlockingQueue<>();
|
||
/**
|
||
* 车辆
|
||
*/
|
||
private Vehicle vehicle;
|
||
/**
|
||
* 实例数据
|
||
*/
|
||
private VehicleData vehicleData;
|
||
/**
|
||
* 上一个定位点
|
||
*/
|
||
private PositionModel lastPosition;
|
||
/**
|
||
* 车辆工作线程
|
||
*/
|
||
private VehicleThread vehicleThread;
|
||
/**
|
||
* 消息状态
|
||
*/
|
||
private String msgCode;
|
||
/**
|
||
* 线程提交回调
|
||
*/
|
||
private ScheduledFuture<?> scheduledFuture;
|
||
|
||
/**
|
||
* 链接上报
|
||
*/
|
||
private MqttClient client = null;
|
||
|
||
|
||
/**
|
||
* Mqtt配置
|
||
*/
|
||
private MqttProperties mqttProperties;
|
||
|
||
|
||
/***
|
||
* 获取当前车辆VIN
|
||
* @return VIN
|
||
*/
|
||
public String getVin() {
|
||
return this.vehicle.getVin();
|
||
}
|
||
|
||
/**
|
||
* 发送消息
|
||
* @param msg 消息
|
||
*/
|
||
public void sendMsg(String msg) {
|
||
//得到16进制报文
|
||
String sHex = ConversionUtil.strToSixteen(msg);
|
||
//计算校验和
|
||
String makeCheck = CalculateCheckDigit.makeCheck(sHex);
|
||
msg = MSG_START + sHex + makeCheck + " " + MSG_END;
|
||
// 创建消息并设置 QoS
|
||
MqttMessage message = new MqttMessage(msg.getBytes());
|
||
message.setQos(this.mqttProperties.getQos());
|
||
// 发布消息
|
||
try {
|
||
client.publish(this.mqttProperties.getTopic(), message);
|
||
} catch (MqttException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 初始化客户端
|
||
*/
|
||
public void initClient () {
|
||
try {
|
||
client = new MqttClient(mqttProperties.getBroker(), mqttProperties.getClientId(), new MemoryPersistence());
|
||
// 连接参数
|
||
MqttConnectOptions options = new MqttConnectOptions();
|
||
// 设置用户名和密码
|
||
if (Objects.nonNull(mqttProperties.getUsername()) && Objects.nonNull(mqttProperties.getPassword())) {
|
||
options.setUserName(mqttProperties.getUsername());
|
||
options.setPassword(mqttProperties.getPassword().toCharArray());
|
||
}
|
||
|
||
options.setConnectionTimeout(1);
|
||
options.setKeepAliveInterval(20);
|
||
// 连接
|
||
client.connect(options);
|
||
log.debug("车辆:[{}] 客户端初始化成功连接配置:{}", getVin(),
|
||
JSONObject.toJSONString(this.mqttProperties));
|
||
} catch (MqttException e) {
|
||
log.error("车辆:[{}] 客户端初始化异常", getVin(), e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 是否连接在线
|
||
* @return 在线返回true,不在线为false
|
||
*/
|
||
public boolean isOnline () {
|
||
if (this.client == null){
|
||
return false;
|
||
}
|
||
return this.client.isConnected();
|
||
}
|
||
|
||
/**
|
||
* 是否建立车辆模拟线程
|
||
* @return 建立返回true不建立返回false
|
||
*/
|
||
public boolean isSend(){
|
||
return this.vehicleThread != null;
|
||
}
|
||
|
||
/**
|
||
* 关闭连接
|
||
*/
|
||
public void closeClient(){
|
||
if (this.client != null){
|
||
try {
|
||
// 断开连接
|
||
this.client.disconnect();
|
||
// 关闭连接
|
||
this.client.close();
|
||
log.debug("车辆:[{}] 客户端下线成功", getVin());
|
||
} catch (MqttException e) {
|
||
log.error("车辆:[{}] 客户端关闭异常:[{}]",getVin(), e.getMessage(), e);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 初始化车辆路线
|
||
* @param positionModelList 路线集合
|
||
*/
|
||
public void settingPosition(List<PositionModel> positionModelList){
|
||
positionQueue.clear();
|
||
positionModelList.forEach(positionQueue::offer);
|
||
log.info("车辆:{} 设置路径成功", this.getVin());
|
||
}
|
||
|
||
/**
|
||
* 初始化线程
|
||
*/
|
||
public void initVehicleThread() {
|
||
if (this.positionCode == null){
|
||
throw new RuntimeException("车辆["+getVin()+"]为选中路径");
|
||
}
|
||
VehicleThread vehicleThread = new VehicleThread();
|
||
vehicleThread.setVehicleInstance(this);
|
||
this.setVehicleThread(vehicleThread);
|
||
ScheduledFuture<?> scheduledFuture = ScheduledThreadPool.submit(vehicleThread);
|
||
this.setScheduledFuture(scheduledFuture);
|
||
log.info("初始化车辆上报模拟线程开始:[{}]", this.getVin());
|
||
}
|
||
|
||
/**
|
||
* 开始上报线程
|
||
*/
|
||
public void startSend() {
|
||
this.msgCode = "上报";
|
||
if (this.vehicleThread != null){
|
||
this.vehicleThread.resume();
|
||
}
|
||
log.info("车辆[{}],开始上报", this.getVin());
|
||
}
|
||
|
||
/**
|
||
* 暂停上报线程
|
||
*/
|
||
public void pauseSend() {
|
||
this.msgCode = "暂停";
|
||
if (this.vehicleThread != null) {
|
||
this.vehicleThread.pause();
|
||
}
|
||
log.info("车辆[{}],暂停上报", this.getVin());
|
||
}
|
||
|
||
/**
|
||
* 结束发送
|
||
*/
|
||
public void stopSend() {
|
||
this.msgCode = "停止";
|
||
if (this.vehicleThread != null){
|
||
this.vehicleThread.stop();
|
||
}
|
||
log.info("车辆[{}],停止上报", this.getVin());
|
||
}
|
||
|
||
/**
|
||
* 取消执行
|
||
*/
|
||
public void cancelExecution() {
|
||
scheduledFuture.cancel(true);
|
||
this.vehicleThread = null;
|
||
this.scheduledFuture = null;
|
||
}
|
||
|
||
/**
|
||
* 模拟车辆数据
|
||
*/
|
||
public String imitateData() {
|
||
String gear = this.vehicleData.getGear();
|
||
if (!"D".equals(gear)){
|
||
log.info("车辆不是动车档位,不进行模拟数据");
|
||
return null;
|
||
}
|
||
// 获取上一次定位点
|
||
PositionModel lastPositionModel = this.lastPosition == null ? positionQueue.poll() : this.lastPosition;
|
||
// 获取当前定位点
|
||
PositionModel currentPositionModel = positionQueue.poll();
|
||
if (currentPositionModel == null) {
|
||
return "表示当前定位点已经跑完,需要其他操作";
|
||
}
|
||
// 两点之间的距离
|
||
BigDecimal distance = VehicleUtils.distance(lastPositionModel, currentPositionModel);
|
||
// 车辆总里程 相加
|
||
vehicleData.setMileage(vehicleData.getMileage().add(distance));
|
||
// 定位点填写
|
||
vehicleData.setLongitude(currentPositionModel.getLongitude());
|
||
vehicleData.setLatitude(currentPositionModel.getLatitude());
|
||
// 当前电量减少
|
||
// 电池浮动
|
||
BigDecimal batteryFloat = VehicleUtils.batteryFloat();
|
||
// 百公里占比
|
||
BigDecimal hundredKMScale = distance.divide(SystemConstant.hundredKilometers).setScale(3, RoundingMode.HALF_UP);
|
||
// 使用电量
|
||
BigDecimal powerUsage = powerConsumption.multiply(hundredKMScale)
|
||
.multiply(batteryFloat)
|
||
.setScale(2, RoundingMode.HALF_UP);
|
||
// 剩余电量
|
||
vehicleData.setRemainingBattery(vehicleData.getRemainingBattery().subtract(powerUsage));
|
||
// 百公里消耗量
|
||
vehicleData.setFuelConsumptionRate(
|
||
powerConsumption.multiply(batteryFloat).divide(new BigDecimal(1000)).setScale(2, RoundingMode.HALF_UP).toString()
|
||
);
|
||
// 计算总速度
|
||
vehicleData.setSpeed(
|
||
distance.divide(new BigDecimal(2))
|
||
.multiply(new BigDecimal("3600"))
|
||
.setScale(2, RoundingMode.HALF_UP).toString()
|
||
);
|
||
vehicleData.imitateBase();
|
||
vehicleData.imitateMotor();
|
||
vehicleData.imitateBatteryPack();
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 更改车辆档位
|
||
* @param gear
|
||
*/
|
||
public void setGear (String gear) {
|
||
this.vehicleData.setGear(gear);
|
||
}
|
||
|
||
}
|