package com.tievd.jyz.handler.alg.common;
|
|
import cn.hutool.core.collection.ConcurrentHashSet;
|
import cn.hutool.core.util.ObjectUtil;
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONObject;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.tievd.cube.modules.system.entity.SysDepart;
|
import com.tievd.jyz.cache.CarPlaceCache;
|
import com.tievd.jyz.cache.DepartCache;
|
import com.tievd.jyz.cache.DeviceCache;
|
import com.tievd.jyz.cache.EventCodeRelOilCache;
|
import com.tievd.jyz.constants.SystemConstant;
|
import com.tievd.jyz.dto.CarScoreCacheDTO;
|
import com.tievd.jyz.entity.*;
|
import com.tievd.jyz.handler.alg.AlgHandleInterface;
|
import com.tievd.jyz.mapper.*;
|
import com.tievd.jyz.mqtt.command.MqttCommandReceiver;
|
import com.tievd.jyz.mqtt.dto.EventInfoDTO;
|
import com.tievd.jyz.service.ICarInfoService;
|
import com.tievd.jyz.util.StringCampareUtil;
|
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.scheduling.annotation.Scheduled;
|
import org.springframework.stereotype.Component;
|
|
import java.sql.Timestamp;
|
import java.time.LocalDateTime;
|
import java.util.*;
|
import java.util.concurrent.ConcurrentHashMap;
|
|
/**
|
* 车辆识别处理类
|
* @author timi
|
*/
|
@Slf4j
|
@Component
|
public class CarIdentificationAlgHandler implements AlgHandleInterface {
|
|
/** 车辆识别标识头 */
|
private final String[] ALG_TAG = new String[]{"C10001"};
|
|
@Autowired
|
private OilEventMapper oilEventMapper;
|
@Autowired
|
private CameraMapper cameraMapper;
|
@Autowired
|
private OilRecordMapper oilRecordMapper;
|
@Autowired
|
private ICarInfoService carInfoService;
|
@Autowired
|
private SysCarModelMapper sysCarModelMapper;
|
@Autowired
|
private OilPositionMapper oilPositionMapper;
|
@Autowired
|
private MqttCommandReceiver mqttCommandReceiver;
|
@Value("${init.sendCommandTopic:/ty/aibox/command/}")
|
private String sendCommandTopic;
|
|
@Value("${init.carInOutTimeout:1800000}")
|
private Long carInOutTimeout;
|
@Value("${init.carOilTimeThreshold:60000}")
|
private Long carOilTimeThreshold;
|
|
/** 轻量级解决并发请求导致数据重复问题 */
|
private final Set<String> platNumberSet = new ConcurrentHashSet<>();
|
/** 车辆在当前事务中,第一次出现在加油区的时间,platNumber_orgCode:time */
|
private final Map<String,Long> firstOilTimeMap = new ConcurrentHashMap<>();
|
|
private final List<CarScoreCacheDTO> carCache = new ArrayList<>();
|
|
double handleScore = 0.9;
|
|
double discardScore = 0.6;
|
|
@Override
|
public String[] getAlgTag(){
|
return ALG_TAG;
|
}
|
|
/**
|
* 处理入口
|
* @param eventInfoDTO
|
* @param sn
|
* @param time
|
*/
|
@Override
|
public void handle(EventInfoDTO<JSONObject> eventInfoDTO, String sn,String time) {
|
//当前事件唯一标识
|
String eventCode = eventInfoDTO.getEventCode();
|
//检测算法编码
|
String algCode = eventInfoDTO.getAlgCode();
|
//终端编码
|
String cameraCode = eventInfoDTO.getCameraCode();
|
//扩展属性
|
JSONObject extend = new JSONObject();
|
Object extendObj = eventInfoDTO.getExtend();
|
if(extendObj instanceof String){
|
extend = JSONObject.parseObject(extendObj.toString());
|
}else if(extendObj instanceof JSONObject){
|
extend = (JSONObject)extendObj;
|
}
|
log.info("车辆识别事件 eventCode:{},extend:{}",eventCode,extendObj);
|
Device device = DeviceCache.getDeviceBySn(sn);
|
if(ObjectUtil.isNull(device)){
|
log.error("网关设备不存在,sn:{}",sn);
|
return;
|
}
|
JSONArray carArr = extend.getJSONArray("cars");
|
if(carArr.size() == 0){
|
return;
|
}
|
long timeLong = System.currentTimeMillis();
|
if (StringUtils.isBlank(time)) {
|
timeLong = Long.valueOf(timeLong);
|
}
|
vehicleIdentificationHandle(eventCode,cameraCode,carArr,device, timeLong);
|
}
|
|
/**
|
* 车辆识别处理
|
*/
|
private void vehicleIdentificationHandle(String eventCode, String cameraCode, JSONArray carArr, Device device, long time){
|
for(Object obj : carArr){
|
JSONObject infoObj = (JSONObject) obj;
|
//车辆识别
|
//车牌号
|
String plateNumber = infoObj.getString("number");
|
//过滤置信度低的数据--------------------
|
double score = infoObj.getDoubleValue("score");
|
if (score < discardScore) {
|
log.info("检测到存在车牌号置信度较低,丢弃 {}====>{}", plateNumber, score);
|
continue;
|
}
|
if(StringUtils.isEmpty(plateNumber) || plateNumber.length() < 7){
|
continue;
|
}
|
//加油位
|
String oilPosition = infoObj.getString("oilPosition");
|
if (StringUtils.isNotBlank(oilPosition)) {
|
List<OilPosition> positions = oilPositionMapper.selectList(new QueryWrapper<>(new OilPosition().setPositionCode(oilPosition)));
|
oilPosition = positions.size() > 0 ? positions.get(0).getOilPosition() : oilPosition;
|
}
|
String carType = infoObj.getString("type");
|
String carReqStr = new StringBuilder().append(plateNumber).append("_").append(device.getOrgCode()).toString();
|
if(platNumberSet.contains(carReqStr)){
|
//存在并发请求,同加油站、同车辆请求还未处理完,跳过
|
log.info("存在并发请求,同加油站、同车辆请求还未处理完,跳过,carReqStr:{}",carReqStr);
|
return;
|
}
|
synchronized (this){
|
//轻量级解决并发请求导致数据重复问题
|
if(platNumberSet.contains(carReqStr)){
|
//存在并发请求,同加油站、同车辆请求还未处理完,跳过
|
log.info("存在并发请求,同加油站、同车辆请求还未处理完,跳过,carReqStr:{}",carReqStr);
|
return;
|
}
|
platNumberSet.add(carReqStr);
|
}
|
try {
|
SysCarModel sysCarModel = sysCarModelMapper.selectOne(new QueryWrapper<SysCarModel>().eq("model_code", carType));
|
LambdaQueryWrapper<OilRecord> wrapper = new LambdaQueryWrapper();
|
wrapper.eq(OilRecord::getLicenseNum, plateNumber)
|
.eq(OilRecord::getOrgCode, device.getOrgCode())
|
.isNull(OilRecord::getEndTime);
|
OilRecord oilRecord = oilRecordMapper.selectOne(wrapper);
|
//查看是否有相似车牌号数据,有则使其进入更新流程---------------------
|
if (ObjectUtil.isNull(oilRecord)) {
|
wrapper.clear();
|
wrapper.eq(OilRecord::getOrgCode, device.getOrgCode())
|
.eq(OilRecord::getDeviceId, device.getId())
|
.isNull(OilRecord::getEndTime);
|
List<OilRecord> recordList = oilRecordMapper.selectList(wrapper);
|
if (recordList.size() > 0) {
|
for (OilRecord record : recordList) {
|
if (StringCampareUtil.scoreLCS(plateNumber, record.getLicenseNum()) > discardScore) {
|
log.info("检测到存在相似车牌号记录{}====>{}", plateNumber, record.getLicenseNum());
|
oilRecord = record;
|
plateNumber = record.getLicenseNum();
|
break;
|
}
|
}
|
}
|
}
|
//-------------------------------------------------------------------
|
Camera camera = cameraMapper.selectOne(new QueryWrapper<Camera>().eq("code", cameraCode).eq("device_id", device.getId()));
|
if (ObjectUtil.isNull(oilRecord)) {
|
//如果新记录为出口摄像头拍到,则不新增记录
|
if (SystemConstant.INSTALL_ADDRESS_OUT.equals(camera.getInstallAddress().intValue())) {
|
platNumberSet.remove(carReqStr);
|
continue;
|
}
|
//新增记录,默认为停靠
|
if (score < handleScore) {
|
//若置信度小于标准,则加入池中等待比较
|
log.info("检测到存在车牌号置信度中等 {}====>{}", plateNumber, score);
|
platNumberSet.remove(carReqStr);
|
joinWaitPool(eventCode, cameraCode, infoObj, device, time, plateNumber, score);
|
continue;
|
}
|
oilRecord = oilRecordGen(cameraCode, plateNumber, oilPosition, device, time);
|
oilRecord.setOilPosition(oilPosition);
|
oilRecord.setModelCode(sysCarModel.getModelCode());
|
oilRecordMapper.insert(oilRecord);
|
//请求图片
|
EventCodeRelOilCache.put(eventCode, oilRecord.getId());
|
} else {
|
//更新进出记录
|
if (ObjectUtil.isNull(camera)) {
|
log.error("抓拍摄像头获取失败,丢弃本次数据");
|
return;
|
}
|
if (SystemConstant.INSTALL_ADDRESS_OUT.equals(camera.getInstallAddress().intValue())) {
|
//为出口抓拍到时,更新离开时间、停靠时长和停车是否规范。
|
oilRecordEndTimeUpdate(oilRecord);
|
EventCodeRelOilCache.put(eventCode, oilRecord.getId());
|
} else if (SystemConstant.INSTALL_ADDRESS_FUEL_OIL.equals(camera.getInstallAddress().intValue())) {
|
// 为加油位抓拍到时,则更新加油位和加油量
|
OilRecord tmpOilRecord = new OilRecord();
|
tmpOilRecord.setId(oilRecord.getId());
|
if (StringUtils.isNotBlank(oilPosition)) {
|
tmpOilRecord.setOilPosition(oilPosition);
|
}
|
if(SystemConstant.BEHAVIOR_TYPE_TMP.equals(oilRecord.getBehavior().intValue())){
|
Long firstOilTime = firstOilTimeMap.get(carReqStr);
|
if(ObjectUtil.isNull(firstOilTime)){
|
firstOilTimeMap.put(carReqStr,System.currentTimeMillis());
|
}else{
|
if(System.currentTimeMillis() - firstOilTime > carOilTimeThreshold){
|
//需求为加油位连续抓拍1分钟,则为加油行为
|
tmpOilRecord.setBehavior(SystemConstant.BEHAVIOR_TYPE_OIL.byteValue());
|
//FIXME 先随便取一个默认容量
|
tmpOilRecord.setOilVolume(50);
|
if (ObjectUtil.isNotNull(sysCarModel)) {
|
tmpOilRecord.setOilVolume(sysCarModel.getOilCapacity());
|
}
|
}
|
}
|
}
|
oilRecordMapper.updateById(tmpOilRecord);
|
}
|
}
|
//建立车辆档案信息
|
CarInfo carInfo = new CarInfo();
|
carInfo.setLicenseNum(plateNumber);
|
carInfo.setLicensePlace(CarPlaceCache.getPlace(plateNumber.substring(0, 2)));
|
if (oilRecord != null) {
|
carInfo.setImgPath(oilRecord.getImgPath());
|
carInfo.setCreateTime(oilRecord.getStartTime());
|
}
|
if (sysCarModel != null) {
|
carInfo.setModelId(sysCarModel.getModelCode()).setModelName(sysCarModel.getModelName());
|
}
|
carInfoService.saveOrUpdate(carInfo);
|
}finally {
|
platNumberSet.remove(carReqStr);
|
}
|
}
|
//默认自动上报图片视频
|
// if(EventCodeRelOilCache.count(eventCode) > 0){
|
// //需要请求图片
|
// log.info("请求图片,eventCode:{}",eventCode);
|
// EventResourceDTO eventResourceDTO = new EventResourceDTO(SystemConstant.IMG_SOURCE_TYPE, eventCode, cameraCode);
|
// EventResourceCommand eventResourceCommand = new EventResourceCommand(mqttCommandReceiver, eventResourceDTO, sendCommandTopic, device.getSn());
|
// eventResourceCommand.init();
|
// if (eventResourceCommand.execute() == SystemConstant.DEAL_FAIL) {
|
// log.error("事件图片指令发送失败");
|
// }
|
// }
|
}
|
|
|
/**
|
* 置信度中等的信息加入比较等待池,1分钟后重新提取进行处理
|
*/
|
private void joinWaitPool(String eventCode, String cameraCode, JSONObject car, Device device, long time, String plateNumber, double score) {
|
CarScoreCacheDTO scoreCacheDTO = new CarScoreCacheDTO();
|
scoreCacheDTO.setEventCode(eventCode)
|
.setCameraCode(cameraCode)
|
.setCarInfo(car)
|
.setLienseNum(plateNumber)
|
.setTime(time)
|
.setDeviceSn(device.getSn())
|
.setScore(score);
|
carCache.add(scoreCacheDTO);
|
}
|
|
/**
|
* 对比等待池中的车牌相似情况,并分组,取出每组评分最高的车牌,并将置信度置为0.9,重新加入处理流程
|
*/
|
@Scheduled(fixedDelay = 60000L)
|
public void waitHandle(){
|
if (carCache.size() == 0) return;
|
log.info("开始处理缓存车牌数据==============》");
|
List<CarScoreCacheDTO> carCache0 = new ArrayList<>();
|
synchronized (carCache) {
|
carCache0.addAll(carCache);
|
carCache.clear();
|
}
|
carCache0.sort(Comparator.comparingDouble(CarScoreCacheDTO::getScore));
|
//分组数据
|
List<List<CarScoreCacheDTO>> handleData = new ArrayList<>();
|
Set idxSet = new HashSet();
|
for (int i = 0; i < carCache0.size(); i++) {
|
if (idxSet.contains(i)) {
|
continue;
|
}
|
List<CarScoreCacheDTO> carGroup = new ArrayList<>();
|
CarScoreCacheDTO carInfo = carCache0.get(i);
|
carGroup.add(carInfo);
|
String lienseNum0 = carInfo.getLienseNum();
|
String cameraCode0 = carInfo.getCameraCode();
|
for (int j = i + 1; j < carCache0.size(); j++) {
|
if (idxSet.contains(j)) {
|
continue;
|
}
|
String lienseNum = carCache0.get(j).getLienseNum();
|
String cameraCode = carCache0.get(j).getCameraCode();
|
if (!cameraCode.equals(cameraCode0)) continue;
|
if (lienseNum0.equals(lienseNum)) {
|
carInfo.setCount(carInfo.getCount() + 1);
|
idxSet.add(j);
|
continue;
|
}
|
//获取相似度,相似则加入同一组
|
double similarScore = StringCampareUtil.scoreLCS(lienseNum0, lienseNum);
|
if (similarScore > discardScore) {
|
carGroup.add(carCache0.get(j));
|
idxSet.add(j);
|
}
|
}
|
handleData.add(carGroup);
|
}
|
carCache0.clear();
|
for (List<CarScoreCacheDTO> groupData : handleData) {
|
groupData.sort((o1, o2) -> o1.getScore() * o1.getCount() >= o2.getScore() * o2.getCount() ? 1 : -1);
|
carCache0.add(groupData.get(0));
|
}
|
joinHandle(carCache0);
|
log.info("处理完成,已筛选出车牌数据==============》{}", JSON.toJSONString(carCache0));
|
}
|
|
public void joinHandle(List<CarScoreCacheDTO> carCacheList) {
|
for (CarScoreCacheDTO car : carCacheList) {
|
JSONArray carArr = new JSONArray(){{
|
add(car.getCarInfo());
|
}};
|
Device device = DeviceCache.getDeviceBySn(car.getDeviceSn());
|
log.info("=====> 低置信度数据重新加入流程", car.getCarInfo().toJSONString());
|
car.getCarInfo().put("score", handleScore);
|
vehicleIdentificationHandle(car.getEventCode(), car.getCameraCode(), carArr, device, car.getTime());
|
}
|
}
|
|
|
|
/**
|
* 车辆记录对象生成
|
* @param cameraCode
|
* @param plateNumber
|
* @param oilPosition
|
* @param device
|
* @param time
|
* @return
|
*/
|
private OilRecord oilRecordGen(String cameraCode, String plateNumber, String oilPosition, Device device, long time){
|
OilRecord oilRecord = new OilRecord();
|
oilRecord.setCameraCode(cameraCode);
|
oilRecord.setCreateTime(new Date());
|
oilRecord.setRecordDay(LocalDateTime.now().format(SystemConstant.DATE_DAY_FORMATTER));
|
oilRecord.setDeviceId(device.getId());
|
//默认停靠
|
oilRecord.setBehavior(SystemConstant.BEHAVIOR_TYPE_TMP.byteValue());
|
oilRecord.setLicenseNum(plateNumber);
|
oilRecord.setOilPosition(oilPosition);
|
oilRecord.setOrgCode(device.getOrgCode());
|
SysDepart sysDepart = DepartCache.getDepartByOrgCode(device.getOrgCode());
|
if(ObjectUtil.isNotNull(sysDepart)){
|
oilRecord.setOrgName(sysDepart.getDepartName());
|
}
|
oilRecord.setStartTime(new Timestamp(time));
|
return oilRecord;
|
}
|
|
|
/**
|
* 定时更新车辆进出状态
|
* 超时未检测到车辆离开时,自动设置为离开状态
|
*/
|
@Scheduled(fixedDelay = 60000L)
|
public void scheduled(){
|
List<OilRecord> oilRecords = oilRecordMapper.selectList(new QueryWrapper<OilRecord>().isNull("end_time"));
|
for(OilRecord oilRecord:oilRecords){
|
if((System.currentTimeMillis() - oilRecord.getStartTime().getTime()) > carInOutTimeout){
|
//超时,自动设置为离开状态
|
log.info("车辆超时未离场,自动设置为离开状态,机构:{},车牌号:{},进场时间:{}",oilRecord.getOrgName(),oilRecord.getLicenseNum(),oilRecord.getStartTime());
|
oilRecordEndTimeUpdate(oilRecord);
|
}
|
}
|
}
|
|
/**
|
* 更新车辆出场信息
|
* @param oilRecord
|
*/
|
private void oilRecordEndTimeUpdate(OilRecord oilRecord){
|
OilRecord tmpOilRecord = new OilRecord();
|
tmpOilRecord.setId(oilRecord.getId());
|
tmpOilRecord.setStandard(SystemConstant.STANDARD_OK.byteValue());
|
Long eventCount = oilEventMapper.selectCount(new QueryWrapper<OilEvent>().eq("license_num",oilRecord.getLicenseNum()).eq("org_code",oilRecord.getOrgCode()).between("create_time",oilRecord.getCreateTime(),new Date()));
|
if(ObjectUtil.isNotNull(eventCount) && eventCount > 0){
|
//存在相关告警则认为为存在违规
|
tmpOilRecord.setStandard(SystemConstant.STANDARD_ERROR.byteValue());
|
}
|
tmpOilRecord.setEndTime(new Timestamp(System.currentTimeMillis()));
|
int spandTime = (int) (tmpOilRecord.getEndTime().getTime() - oilRecord.getStartTime().getTime())/1000/60;
|
tmpOilRecord.setSpandTime(spandTime);
|
oilRecordMapper.updateById(tmpOilRecord);
|
String carReqStr = new StringBuilder().append(oilRecord.getLicenseNum()).append("_").append(oilRecord.getOrgCode()).toString();
|
firstOilTimeMap.remove(carReqStr);
|
}
|
}
|