diff --git a/car-business-common/pom.xml b/car-business-common/pom.xml index 8842d31..073eeee 100644 --- a/car-business-common/pom.xml +++ b/car-business-common/pom.xml @@ -44,6 +44,25 @@ god-common-swagger + + + com.god + car-base-remote + 3.6.3 + + + + com.baomidou + mybatis-plus-boot-starter + 3.4.2 + + + + com.baomidou + mybatis-plus-boot-starter + 3.4.2 + + diff --git a/car-business-common/src/main/java/com/god/business/common/domain/BreakDown.java b/car-business-common/src/main/java/com/god/business/common/domain/BreakDown.java new file mode 100644 index 0000000..7576280 --- /dev/null +++ b/car-business-common/src/main/java/com/god/business/common/domain/BreakDown.java @@ -0,0 +1,100 @@ +package com.god.business.common.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.god.base.common.domain.Fence; +import com.god.business.common.domain.request.ReqAddBreakDown; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** + * 故障记录 + * + * @author LouZhiSHuo + * @Date 2023/11/29 14:40 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class BreakDown { + + + /** + * 故障记录主建 + */ + private Integer id; + + /** + * 车辆VIN + */ + @NotEmpty(message = "车辆VIN不能为空") + private String carVin; + + /** + * 故障码Id + */ + @NotNull(message = "故障主建不能为空") + private Integer breakdownId; + + + /** + * 故障状态 + */ + private Integer status; + + + /** + * 故障开始时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss") + @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8") + private Date createTime; + + /** + * 故障结束时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss") + @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8") + private Date endTime; + + + /** + * 创建人 + */ + private String createBy; + + /** + * 修改人 + */ + private String updateBy; + + /** + * 更新时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss") + @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8") + private Date updateTime; + + + + public static BreakDown insertBreakDown(ReqAddBreakDown reqAddBreakDown){ + return BreakDown.builder() + .carVin(reqAddBreakDown.getCarVin()) + .breakdownId(reqAddBreakDown.getBreakdownId()) + .status(reqAddBreakDown.getStatus()) + .createTime(reqAddBreakDown.getCreateTime()) + .endTime(reqAddBreakDown.getEndTime()) + .createBy(reqAddBreakDown.getCreateBy()) + .updateBy(reqAddBreakDown.getUpdateBy()) + .updateTime(reqAddBreakDown.getUpdateTime()) + .build(); + } + +} diff --git a/car-business-common/src/main/java/com/god/business/common/domain/request/ReqAddBreakDown.java b/car-business-common/src/main/java/com/god/business/common/domain/request/ReqAddBreakDown.java new file mode 100644 index 0000000..7a19ff3 --- /dev/null +++ b/car-business-common/src/main/java/com/god/business/common/domain/request/ReqAddBreakDown.java @@ -0,0 +1,81 @@ +package com.god.business.common.domain.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 故障添加参数 + * + * @author LouZhiSHuo + * @Date 2023/11/29 14:48 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ReqAddBreakDown { + + + /** + * 车辆VIN + */ + private String carVin; + + /** + * 故障码Id + */ + private Integer breakdownId; + + /** + * 故障状态 + */ + private Integer status; + + /** + * 故障开始时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss") + @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8") + private Date createTime; + + /** + * 故障结束时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss") + @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8") + private Date endTime; + + /** + * 创建人 + */ + private String createBy; + + /** + * 修改人 + */ + private String updateBy; + + /** + * 修改时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd HH-mm-ss") + @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss",timezone = "GMT+8") + private Date updateTime; + + + + + + + + + + + +} diff --git a/car-business-server/src/main/java/com/god/base/server/consumer/KafkaSub.java b/car-business-server/src/main/java/com/god/base/server/consumer/KafkaSub.java new file mode 100644 index 0000000..9ef1b36 --- /dev/null +++ b/car-business-server/src/main/java/com/god/base/server/consumer/KafkaSub.java @@ -0,0 +1,16 @@ +package com.god.base.server.consumer; + +import com.god.common.redis.service.RedisService; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +/** + * 拉取车辆信息 + * + * @author LouZhiSHuo + * @Date 2023/11/27 13:36 + **/ +public class KafkaSub { + +} diff --git a/car-business-server/src/main/java/com/god/base/server/controller/AlarmController.java b/car-business-server/src/main/java/com/god/base/server/controller/AlarmController.java new file mode 100644 index 0000000..2135c08 --- /dev/null +++ b/car-business-server/src/main/java/com/god/base/server/controller/AlarmController.java @@ -0,0 +1,64 @@ +package com.god.base.server.controller; + +import com.god.base.remote.RemoteCarService; +import com.god.base.server.service.AlarmService; +import com.god.business.common.domain.BreakDown; +import com.god.business.common.domain.request.ReqAddBreakDown; +import com.god.common.core.domain.Result; +import com.god.common.log.annotation.Log; +import com.god.common.log.enums.BusinessType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * 预警控制层 + * + * @author LouZhiSHuo + * @Date 2023/11/27 10:50 + **/ +@Controller +public class AlarmController { + + @Autowired + private AlarmService alarmService; + + + /** + * 围栏报警 + * @param carVinId + * @return + */ + @GetMapping("/startAlarm") + @Log(title = "围栏报警业务") + public Result rangeWarning(@RequestParam("carVinId") String carVinId) { + //围栏报警业务实现 + alarmService.startAlarm(carVinId); + return Result.success(); + } + + + /** + * 添加故障记录 + * @param reqAddBreakDown + * @return + */ + @PostMapping("/insertBreakDownLogs") + @Log(title = "添加车辆预警记录",businessType = BusinessType.INSERT) + public Result insertBreakDownLogs(@RequestBody ReqAddBreakDown reqAddBreakDown) { + //添加车辆预警记录 + alarmService.save(BreakDown.insertBreakDown(reqAddBreakDown)); + + return Result.success(); + } + + + + + + + +} diff --git a/car-business-server/src/main/java/com/god/base/server/mapper/AlarmMapper.java b/car-business-server/src/main/java/com/god/base/server/mapper/AlarmMapper.java new file mode 100644 index 0000000..a8c049f --- /dev/null +++ b/car-business-server/src/main/java/com/god/base/server/mapper/AlarmMapper.java @@ -0,0 +1,14 @@ +package com.god.base.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.god.business.common.domain.BreakDown; + +/** + * 预计故障mapper + * + * @author LouZhiSHuo + * @Date 2023/11/29 15:02 + **/ +public interface AlarmMapper extends BaseMapper { + +} diff --git a/car-business-server/src/main/java/com/god/base/server/service/AlarmService.java b/car-business-server/src/main/java/com/god/base/server/service/AlarmService.java new file mode 100644 index 0000000..d410612 --- /dev/null +++ b/car-business-server/src/main/java/com/god/base/server/service/AlarmService.java @@ -0,0 +1,17 @@ +package com.god.base.server.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.god.business.common.domain.BreakDown; + +/** + * @author LouZhiSHuo + * @Date 2023/11/27 20:40 + **/ +public interface AlarmService extends IService{ + + /** + * 围栏报警业务实现 + * @param carVinId + */ + void startAlarm(String carVinId); +} diff --git a/car-business-server/src/main/java/com/god/base/server/service/impl/AlarmServiceImpl.java b/car-business-server/src/main/java/com/god/base/server/service/impl/AlarmServiceImpl.java new file mode 100644 index 0000000..c0d07c0 --- /dev/null +++ b/car-business-server/src/main/java/com/god/base/server/service/impl/AlarmServiceImpl.java @@ -0,0 +1,134 @@ +package com.god.base.server.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.god.base.common.domain.Fence; +import com.god.base.remote.RemoteCarService; +import com.god.base.server.common.domain.Car; +import com.god.base.server.mapper.AlarmMapper; +import com.god.base.server.service.AlarmService; +import com.god.base.server.util.FenceAlgorithm; +import com.god.business.common.domain.BreakDown; + +import com.god.common.core.domain.Result; + +import lombok.extern.log4j.Log4j2; + +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.awt.geom.Point2D; +import java.util.*; + +/** + * @author LouZhiSHuo + * @Date 2023/11/27 20:40 + **/ +@Service +@Log4j2 +public class AlarmServiceImpl extends ServiceImpl implements AlarmService { + + @Resource + private RemoteCarService remoteCarService; + + @Resource + private FenceAlgorithm fenceAlgorithm; + + + + + + + /** + * 围栏报警业务实现 + * @param carVinId + */ + @Override + public void startAlarm(String carVinId) { + //调用 根据车辆VIN查询车辆信息 carController/list + Result carResult = remoteCarService.list(carVinId); + //车辆信息 + Car car = carResult.getData(); + //根据车辆信息查询该车辆绑定的围栏Id + long fenceId = car.getFenceId(); + //获取到围栏Id,根据围栏Id查询围栏 信息 FenceController/fenceById + //Redis中的围栏坐标信息格式: " 经度,维度; " + Result fenceResult = remoteCarService.fenceById((int) fenceId); + //围栏信息 + Fence fence = fenceResult.getData(); + //获取该围栏行驶状态 + Integer driveStatus = fence.getDriveStatus();// 1-驶入 2-驶出 + //获取围栏坐标串 + String fenceLocation = fence.getFenceLocation(); //116.357183,39.926512; 116.394348,39.909989; 116.34757,39.90617; + String[] split = fenceLocation.split(";"); + + List polygon = new ArrayList<>(); + + for (int i = 0; i < split.length; i++) { + String[] splits = split[i].split(","); + + Point2D.Double aDouble = new Point2D.Double(); + + for (int j = 0; j < splits.length; j++) { + if (j == 0){ + aDouble.x = Double.parseDouble(splits[j]); + } else { + aDouble.y = Double.parseDouble(splits[j]); + } + } + polygon.add(aDouble); + } + + //消费消息的消息 + + //算法计算 + //假设当前经纬度 + Point2D.Double car_one = new Point2D.Double(39.9125, 116.3917); + boolean ptInPoly = fenceAlgorithm.computeFence(car_one, polygon); + + if (driveStatus == 1){ + //驶入 + //围栏区域外 -正常 进入后报警 + if (ptInPoly) { + //报警 + //存储消息到redis + + } else { + log.info("当前车辆:{},正常行驶中~~~~~~",carVinId); + } + + } else { + //驶出 + //围栏区域内 -正常 驶出报警 + + if (!ptInPoly) { + //报警 + } else { + log.info("当前车辆:{},正常行驶中~~~~~~",carVinId); + } + + } + //判断围栏状态 + //1-驶入 + //判断坐标是否在范围 外围:不做操作 , 坐标进入围栏内: 发送消息给围栏内的用户(使用RabbitMq) + //2-驶出 + //判断坐标是否在范围 外围:发送消息给围栏内的用户(使用RabbitMq) , 坐标在围栏内:不做操作 + } + + + /** + * 添加故障记录 + * @param entity + * @return + */ + @Override + public boolean save(BreakDown entity) { + entity.setCreateTime(new Date()); + + boolean save = super.save(entity); + if (!save) { + throw new RuntimeException("故障记录添加失败"); + } + return true; + } +} \ No newline at end of file diff --git a/car-business-server/src/main/java/com/god/base/server/util/FenceAlgorithm.java b/car-business-server/src/main/java/com/god/base/server/util/FenceAlgorithm.java new file mode 100644 index 0000000..e79e1ff --- /dev/null +++ b/car-business-server/src/main/java/com/god/base/server/util/FenceAlgorithm.java @@ -0,0 +1,124 @@ +package com.god.base.server.util; + +import org.springframework.stereotype.Component; + +import java.awt.geom.Point2D; +import java.util.List; + +/** + * 围栏算法 + * + * @author LouZhiSHuo + * @Date 2023/11/28 19:12 + **/ +@Component +public class FenceAlgorithm { + + + /** + * 围栏区域内计算 + * @param point + * @param pts + * @return + */ + public boolean computeFence(Point2D.Double point, List pts){ + //围栏顶点坐标数 + int fenceSize = pts.size(); + + //如果点位在多边形的顶点或边上,也算在多边形内部,返回true + boolean boundDrVertex = true; + + //射线与多边形相交次数 + int intersectCount = 0; + + ///浮点型计时候的容差 + double precision = 2e-10; + + //相邻两点位置 + Point2D.Double fenceOne,fenceTwo; + + //确认当前点 + Point2D.Double p = point; + + //起点 + fenceOne = pts.get(0); + + for (int i =0;i Math.max(fenceOne.x,fenceTwo.x)) { + //无交点 + fenceOne = fenceTwo; + //继续判断 + continue; + } + + if (p.x > Math.min(fenceOne.x,fenceTwo.x) && p.x < Math.max(fenceOne.x,fenceTwo.x)) { + //当前点在线段于X轴上的投影内 + if (p.y <= Math.max(fenceOne.y,fenceTwo.y)) { + //当前点的y坐标小于线段对y轴投影的最大值 + if (fenceOne.x == fenceTwo.x && p.y >= Math.min(fenceOne.y,fenceTwo.y)) { + //当前点垂直在x轴边上 + return boundDrVertex; + } + + if (fenceOne.y == fenceTwo.y) { + //平行于X轴 + if (fenceOne.y == p.y) { + //位于当前围栏的边上 + return boundDrVertex; + } else { + //反之相交次数+1 + ++intersectCount; + } + + } else { + //非水平情况 + double v = (p.x - fenceOne.x) * (fenceTwo.y - fenceOne.y) / (fenceTwo.x - fenceOne.x) + fenceOne.y; + //围栏某条边上 + if (Math.abs(p.y - v) < precision) { + return boundDrVertex; + } + + if (p.y < v) { + //相交次数+1 + ++intersectCount; + } + } + } + } else { + //当前带你不在线段投影到x轴的区间中 + if (p.x == fenceTwo.x && p.y <= fenceTwo.y) { + //位于x坐标对应的平行于y轴上的线上的低于终点y的坐标 + Point2D.Double temp = pts.get((i + 1) % fenceSize); + if (p.x >= Math.min(fenceOne.x , temp.x) && p.x <= Math.max(fenceOne.x,temp.x)) { + //若当前点的x坐标位于fenceOne 和 temp 组成的线段关于x轴的投影中 + //则记为该点的射线只穿过端点一次 + //那么相交+1 + ++intersectCount; + } else { + //若当前坐标的x不包含 fenceOne 和 temp 组成的线段关于x轴的投影中 , 则点射线通过的两条线段组成了一个弯折的部分, + //那么记为该点的射线穿过了端点两次 + intersectCount += 2; + } + } + } + fenceOne = fenceTwo; + } + if (intersectCount % 2 == 0) { + //偶数在围栏外面 + return false; + } else { + //奇数在围栏内 + return true; + } + } +}