package com.ssi.model; import com.alibaba.fastjson.JSONObject; import com.ssi.constant.URL; import com.ssi.constant.VehicleConstant; import com.ssi.constant.enums.Status; import com.ssi.entity.VmsTosOrders; import com.ssi.entity.dto.SwitchAutoParamDto; import com.ssi.entity.dto.TelecontrolParamDto; import com.ssi.response.SSIResponse; import com.ssi.utils.RestTemplateUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Map; /** * @author ZhangLiYao * @version 1.0 * @date 2020/7/23 10:23 */ @Component @Slf4j public class TelecontrolModel { @Value("${command-url}") private String commandUrl; @Value("${harbor.remote.vehicle_status}") private String remoteControlStatusKeyPrefix; @Value("${harbor.remote.overtime}") private String debugAppOvertimePrefix; @Value("${harbor.remote.overtimeTime}") private long overtimeTime; @Value("${harbor.remote.controlInfo}") private String controlInfo; @Value("${harbor.remote.socketOvertime}") private String socketOvertimePrefix; @Value("${harbor.remote.socketOvertimeTime}") private long socketOvertimeTime; @Value("${harbor.remote.throttleBrakeOperationValue}") private String throttleBrakeOperationValuePrefix; @Value("${harbor.remote.emergencyParking}") private String emergencyParkingKeyPrefix; @Value("${harbor.remote.autoPilot}") private String autoPilotKeyPrefix; @Value("${vehicle.latestData.redis.prefix}") private String vehicleInfo; @Value("${order.latestOrderKeyPrefix:harbor:command:status}") private String latestOrderKeyPrefix; @Autowired private RedisDataModel redisDataModel; public SSIResponse vehicleTelecontrol(TelecontrolParamDto telecontrolParamDto) { SSIResponse ssiResponse = null; try { String error = handleParam(telecontrolParamDto); if (StringUtils.isNotBlank(error)) { ssiResponse = SSIResponse.no(error); return ssiResponse; } if (!emergencyCheck(telecontrolParamDto)) { ssiResponse = SSIResponse.no("车辆紧停中,请先解除紧停再操作!"); return ssiResponse; } //校验退出接管时,车速和油门是否为0 if (this.checkThrottle(telecontrolParamDto)){ ssiResponse = SSIResponse.no("车速或油门不为0,退出接管失败!"); return ssiResponse; } //添加当前任务信息 this.addCurrentTaskInfo(telecontrolParamDto); this.addZeroedValue(telecontrolParamDto); String paramData = JSONObject.toJSONString(telecontrolParamDto); log.info(String.format("app遥控车辆指令请求发送:----%s", paramData)); String result = RestTemplateUtil.post(String.format("%s%s", commandUrl, URL.TELECONTROL_URL), paramData, null); log.info(String.format("app遥控车辆指令请求发送返回结果:----%s", result)); ssiResponse = handleResult(result,telecontrolParamDto); } catch (Exception e) { log.error("请求失败:", e); ssiResponse = SSIResponse.no("请求失败"); } finally { //退出接管失败持续下发 if (ssiResponse.getCode() == -1 && telecontrolParamDto.getControlType() == 1 && telecontrolParamDto.getOperationValue() == 2) { ssiResponse = continueCloseControl(telecontrolParamDto); } } this.handleOvertime(telecontrolParamDto, ssiResponse); this.setLastThrottleBrakeOperationValue(telecontrolParamDto, ssiResponse); return ssiResponse; } private void addCurrentTaskInfo(TelecontrolParamDto telecontrolParamDto) { Map json2Map = redisDataModel.getJson2Map(String.format("%s:%s", latestOrderKeyPrefix, telecontrolParamDto.getVin())); JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(json2Map)); VmsTosOrders vmsTosOrders = JSONObject.toJavaObject(jsonObject, VmsTosOrders.class); telecontrolParamDto.setTaskId(vmsTosOrders.getTaskNo().concat(",").concat(vmsTosOrders.getTaskNoMapping())); } //校验退出接管时,车速和油门是否为0 private boolean checkThrottle(TelecontrolParamDto teleDto) { if (teleDto.getControlType() != VehicleConstant.TELE_CONTROL_TYPE_TAKE_OVER || teleDto.getOperationValue() !=2){ return false; } String controlCache = redisDataModel.get(controlInfo + ":" + teleDto.getVin()); if (StringUtils.isBlank(controlCache)){ return false; } JSONObject controlJson = JSONObject.parseObject(controlCache); //获取油门值 if(controlJson.get("7") != null && controlJson.getInteger("7") > 0){ return true; } //获取车速 String vehicleCache = redisDataModel.get(vehicleInfo + ":" + teleDto.getVin()+"-harbor_D00A"); if (StringUtils.isBlank(vehicleCache)){ return false; } JSONObject vehicleJson = JSONObject.parseObject(vehicleCache); if (vehicleJson.get("speed") != null && vehicleJson.getDouble("speed") > 0){ return true; } return false; } /** * 处理回话超时 * @param telecontrolParamDto * @param ssiResponse */ public void handleOvertime(TelecontrolParamDto telecontrolParamDto, SSIResponse ssiResponse) { boolean isTakeOverCommand = TelecontrolParamDto.isTakeOverCommand(telecontrolParamDto); boolean isCancelTakeOver = TelecontrolParamDto.isCancelTakeOverCommand(telecontrolParamDto); String vin = telecontrolParamDto.getVin(); String appMac = telecontrolParamDto.getAppMac(); if (vin != null && appMac != null) { int code = ssiResponse.getCode(); if (isCancelTakeOver && (code == Status.SUCCESS.getCode())) { redisDataModel.del(debugAppOvertimePrefix + ":" + vin + ":" + appMac); } if ((!isCancelTakeOver && !isTakeOverCommand) || (isTakeOverCommand && (code == Status.SUCCESS.getCode()))) { redisDataModel.set(debugAppOvertimePrefix + ":" + vin + "&" + appMac, "1", overtimeTime); } } } /** * 油门到刹车 刹车到油门 要添加置0标识 * * @param telecontrolParamDto */ public void addZeroedValue(TelecontrolParamDto telecontrolParamDto) { Integer operationType = telecontrolParamDto.getOperationType(); // 7: 油门刹车操作 if (operationType == 7) { Integer zeroed = telecontrolParamDto.getZeroed(); //已经有置0标识值则不处理 if (zeroed == 0) { Integer currentOperationValue = telecontrolParamDto.getOperationValue(); int lastOperationValue = getLastThrottleBrakeOperationValue(telecontrolParamDto.getVin()); if (currentOperationValue > 0 && lastOperationValue < 0) { telecontrolParamDto.setZeroed(1); } if (currentOperationValue < 0 && lastOperationValue > 0) { telecontrolParamDto.setZeroed(-1); } } } } /** * 获取上一个油门刹车OperationValue的值 * * @param vin */ public int getLastThrottleBrakeOperationValue(String vin) { String val = redisDataModel.get(throttleBrakeOperationValuePrefix + ":" + vin); if (StringUtils.isNotBlank(val)) { return Integer.parseInt(val); } return 0; } /** * 设置最新的油门刹车OperationValue的值 * * @param telecontrolParamDto */ public void setLastThrottleBrakeOperationValue(TelecontrolParamDto telecontrolParamDto, SSIResponse ssiResponse) { Integer operationType = telecontrolParamDto.getOperationType(); boolean success = ssiResponse.getCode() == Status.SUCCESS.getCode(); // 7: 油门刹车操作 if (operationType == 7 && success) { String vin = telecontrolParamDto.getVin(); Integer operationValue = telecontrolParamDto.getOperationValue(); redisDataModel.set(throttleBrakeOperationValuePrefix + ":" + vin, String.valueOf(operationValue)); } } /** * 退出接管失败再重试三次 * @param telecontrolParamDto * @return */ private SSIResponse continueCloseControl(TelecontrolParamDto telecontrolParamDto) { int tryTimes = 3; int count = 0; SSIResponse ssiResponse = null; do { try { String paramData = JSONObject.toJSONString(telecontrolParamDto); String result = RestTemplateUtil.post(String.format("%s%s", commandUrl, URL.TELECONTROL_URL), paramData, null); ssiResponse = handleResult(result,telecontrolParamDto); count++; } catch (Exception e) { log.error("重复请求请求失败:", e); ssiResponse = SSIResponse.no("请求失败"); } } while (ssiResponse.getCode() == -1 && count < tryTimes); return ssiResponse; } private String handleParam(TelecontrolParamDto telecontrolParamDto) { if (telecontrolParamDto.getControlType() == 2 && telecontrolParamDto.getOperationType() == null) { return "常规操作,操作类型不能为空"; } return null; } private SSIResponse handleResult(String result,TelecontrolParamDto teleDto) { JSONObject jsonObject = JSONObject.parseObject(result); String code = jsonObject.getJSONObject("status").getString("code"); String details = jsonObject.getJSONObject("status").getString("details"); if (!"0000".equals(code)) { log.error(details); return SSIResponse.no(details); } if (StringUtils.isNotBlank(jsonObject.getString("data"))) { String ADCUStatus = jsonObject.getJSONObject("data").getString("ADCUStatus"); if ("1".equals(ADCUStatus)) { log.error("ADCU正在控车"); return SSIResponse.no("ADCU正在控车"); } } if (teleDto.getControlType() == VehicleConstant.TELE_CONTROL_TYPE_TAKE_OVER){ String info = redisDataModel.get(controlInfo + ":" + teleDto.getVin()); return SSIResponse.ok(info); }else { String info = redisDataModel.get(controlInfo + ":" + teleDto.getVin()); JSONObject operJson = null; if (StringUtils.isBlank(info)){ operJson = new JSONObject(); }else { operJson = JSONObject.parseObject(info); } if(teleDto.getOperationType() == 1 || teleDto.getOperationType() == 6){ operJson.put(String.valueOf(6),2); }else if(teleDto.getOperationType() != 7 || teleDto.getOperationType() < 0){ operJson.put(String.valueOf(teleDto.getOperationType()),teleDto.getOperationValue()); } redisDataModel.set(controlInfo + ":" + teleDto.getVin(),operJson.toString()); } /* String dataResult = jsonObject.getJSONObject("data").getString("result"); if (!"1".equals(dataResult) && !"17".equals(dataResult) && !"33".equals(dataResult) && !"49".equals(dataResult)) { log.error(String.format("指令执行失败")); return SSIResponse.no("指令执行失败"); }*/ return SSIResponse.ok(); } public Map getControlStatus(String vin) { String key = String.format("%s:%s", remoteControlStatusKeyPrefix, vin); return redisDataModel.getJson2Map(key); } public Map getEmergencyStatus(String vin) { String key = String.format("%s:%s", emergencyParkingKeyPrefix, vin); return redisDataModel.getJson2Map(key); } public Map getAutoPilotStatus(String vin) { String key = String.format("%s:%s", autoPilotKeyPrefix, vin); return redisDataModel.getJson2Map(key); } public SSIResponse breakVehicle(String vin) { TelecontrolParamDto paramDto = new TelecontrolParamDto(); paramDto.setVin(vin); paramDto.setControlType(2); paramDto.setOperationType(7); paramDto.setOperationValue(-75); paramDto.setZeroed(-1); return vehicleTelecontrol(paramDto); } public void setOrRefreshSocketOvertime(String sessionId, String vin) { redisDataModel.set(socketOvertimePrefix + ":" + sessionId + ":" + vin, vin, socketOvertimeTime); } public void deleteSocketOvertime(String sessionId, String vin) { redisDataModel.del(socketOvertimePrefix + ":" + sessionId + ":" + vin); } /** * 除取消接管操作外,紧停状态下其他操作不能操作 * @param telecontrolParamDto * @return */ public boolean emergencyCheck(TelecontrolParamDto telecontrolParamDto) { Integer controlType = telecontrolParamDto.getControlType(); Integer operationValue = telecontrolParamDto.getOperationValue(); //除取消接管操作外,紧停状态下其他操作不能操作 if (!(controlType == 1 && operationValue == 2)) { String vin = telecontrolParamDto.getVin(); String key = String.format("%s:%s", emergencyParkingKeyPrefix, vin); Map eMap = redisDataModel.getJson2Map(key); if (eMap != null) { Integer emergencyStatus = (Integer) eMap.get("emergencyStatus"); if (emergencyStatus == 1) { return false; } } } return true; } /** * 切换自动驾驶 * @return */ public SSIResponse switchAutopilot(SwitchAutoParamDto paramDto) { String result = RestTemplateUtil.post(String.format("%s%s", commandUrl, URL.SWITCHAUTO_URL), JSONObject.toJSONString(paramDto), null); return handleResult(result); } private SSIResponse handleResult(String result) { JSONObject jsonObject = JSONObject.parseObject(result); String code = jsonObject.getJSONObject("status").getString("code"); String details = jsonObject.getJSONObject("status").getString("details"); if (!"0000".equals(code)) { log.error(details); return SSIResponse.no(details); } return SSIResponse.ok(); } }