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 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 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); } }