设备对接 接口优化

master
guochaojie 4 months ago
parent bf5205b029
commit 342cc33f65

@ -8,25 +8,26 @@ import cc.yunxi.domain.po.*;
import cc.yunxi.domain.vo.device.*;
import cc.yunxi.domain.vo.file.FileUploadRespVO;
import cc.yunxi.service.*;
import cc.yunxi.service.impl.CommonService;
import cc.yunxi.utils.DeviceStatusManager;
import cc.yunxi.utils.RedisTool;
import cc.yunxi.utils.UserContext;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* <p>
@ -63,13 +64,68 @@ public class DeviceController {
private IClientService clientService;
@Resource
private ICommonService commonService;
@Resource
private RedisTool redisTool;
@Resource
private IFileService fileService;
@ApiOperation("小程序获取设备配置")
@PostMapping("/getDeviceConfig")
public CommonResult<Map<String, Object>> getDeviceConfig(@RequestBody DeviceVO deviceVO) {
RecycleDevice device = deviceService.getByDeviceCode(deviceVO.getDeviceCode());
if (device == null) {
return CommonResult.error(404, "设备不存在");
}
if (device.getStatus() != 1) {
return CommonResult.error(404, "当前设备已暂停服务");
}
RecycleDeviceConfig config = configService.getByCompanyId(device.getBeLongCompanyId());
if (config == null) {
return CommonResult.error(404, "设备未配置配置项!!");
}
List<RecycleBucket> buckets = bucketService.getByDeviceCode(deviceVO.getDeviceCode());
if (buckets.size() == 0) {
return CommonResult.error(404, "当前设备没有桶配置清单!!");
}
Map<String, Object> result = new HashMap<>();
result.put("deviceCode", device.getDeviceCode());//设备代码
result.put("deviceName", deviceVO.getDeviceCode());//设备名称
result.put("userStatus", device.getUseStatus());//设备状态
List<Object> list = new ArrayList<>();
for (RecycleBucket bucket : buckets) {
Map<String, Object> _bucket = new HashMap<>();
_bucket.put("bucketCode", bucket.getBucketCode());//桶编码
_bucket.put("doorNum", bucket.getDoorNum());//门号
_bucket.put("showOrd", bucket.getShowOrd());//显示顺序
_bucket.put("productCode", bucket.getProductCode());//
_bucket.put("productSubCode", bucket.getProductSubCode());//
_bucket.put("isShow", bucket.getIsShow());//是否显示
_bucket.put("showName", bucket.getShowName());//显示名称
BigDecimal price = calculatePrice(bucket, config);//综合计算价格
_bucket.put("price", price);//价格
_bucket.put("currentWeight", bucket.getCurrentWeight());//当前重量
BigDecimal warningWeight = config.getWarningWeight();
if (null != warningWeight && warningWeight.compareTo(BigDecimal.ZERO) > 0) {
_bucket.put("warningWeight", warningWeight);//报警重量
}
BigDecimal weight = bucket.getWarningWeight();
if (null != weight && weight.compareTo(BigDecimal.ZERO) > 0) {
_bucket.put("warningWeight", weight);//报警重量
}
_bucket.put("closeDelay", bucket.getCloseDelay());//关门时延
_bucket.put("bagNo", bucket.getBagNo());//垃圾袋编号
list.add(_bucket);
}
result.put("bucketList", list);
return CommonResult.success(result);
}
//获取设备配置
@PostMapping("/conf")
@ApiModelProperty("获取设备配置")
@ApiOperation("机柜获取设备配置")
public CommonResult<DeviceRespVO> conf(@RequestBody DeviceVO deviceVO) {
CommonResult<DeviceRespVO> result = new CommonResult<>();
DeviceRespVO respVO = new DeviceRespVO();
@ -114,9 +170,9 @@ public class DeviceController {
_bucket.setBucketCode(bucket.getBucketCode());
_bucket.setDoorNum(bucket.getDoorNum());
_bucket.setOrdNum(bucket.getShowOrd());
_bucket.setCode(bucket.getProductCode());
_bucket.setSubCode(bucket.getProductSubCode());
_bucket.setShowText(bucket.getShowName());
_bucket.setProductCode(bucket.getProductCode());
_bucket.setProductSubCode(bucket.getProductSubCode());
_bucket.setShowName(bucket.getShowName());
_bucket.setPrice(price);
_bucket.setSingleWeightLimit(BigDecimal.ZERO);
_bucket.setTotalWeightLimit(bucket.getWarningWeight());
@ -222,7 +278,7 @@ public class DeviceController {
//手机号登录
@PostMapping("/customerLogin")
@ApiModelProperty("投递员手机号登录")
@ApiOperation("投递员手机号登录")
public CommonResult<LoginRespVO> customerLogin(@RequestBody LoginReqVO loginReqVO) {
CommonResult<LoginRespVO> result = new CommonResult<>();
Client client = clientService.getLastestClientByPhone(loginReqVO.getPhone());
@ -246,7 +302,7 @@ public class DeviceController {
}
@PostMapping("/customerLogin")
@ApiModelProperty("清运员手机号登录")
@ApiOperation("清运员手机号登录")
public CommonResult<LoginRespVO> cleanerLogin(@RequestBody LoginReqVO loginReqVO) {
Recycler recycler = recyclerService.getRecyclerByPhoneNumber(loginReqVO.getPhone());
@ -278,7 +334,7 @@ public class DeviceController {
//投递重量上传
@PostMapping("/delivery")
@ApiModelProperty("投递订单上传")
@ApiOperation("投递订单上传")
public CommonResult<String> delivery(@RequestBody DeliveryOrderVO orderVO) {
// UserDTO user = UserContext.getUser();
RecycleDevice device = deviceService.getByDeviceCode(orderVO.getDeviceCode());
@ -333,7 +389,7 @@ public class DeviceController {
//清运重量上传
@PostMapping("/clean")
@ApiModelProperty("清运订单上传")
@ApiOperation("清运订单上传")
public CommonResult<String> clean(@RequestBody CleanOrderVO orderVO) {
UserDTO user = UserContext.getUser();
RecycleDevice device = deviceService.getByDeviceCode(orderVO.getDeviceCode());
@ -388,7 +444,7 @@ public class DeviceController {
//设备事件上报
@PostMapping("/event")
@ApiModelProperty("设备事件上报")
@ApiOperation("设备事件上报")
public CommonResult<String> event(@RequestBody DeviceEventVO eventVO) {
RecycleDevice device = deviceService.getByDeviceCode(eventVO.getDeviceCode());
@ -412,32 +468,25 @@ public class DeviceController {
//设备心跳上报
@PostMapping("/heartbeat")
@ApiModelProperty("设备心跳上报")
public CommonResult<String> heartbeat(@RequestParam String deviceCode, HttpServletRequest request) {
String remoteAddr = request.getRemoteAddr();
DeviceStatus des = new DeviceStatus();
des.setDeviceCode(deviceCode);
des.setIp(remoteAddr);
des.setHeartBeat(LocalDateTime.now());
@ApiOperation("设备心跳上报")
public CommonResult<String> heartbeat(@RequestBody DeviceStatus des) {
deviceStatusManager.putDeviceStatus(des);
log.info("设备心跳上报成功,设备编码:{}", deviceCode);
return CommonResult.success(deviceCode);
return CommonResult.success(des.getDeviceCode());
}
@ApiModelProperty("设备命令下发")
@ApiOperation("设备命令下发")
@PostMapping("/command")
public CommonResult<String> command(@RequestBody CommandVO commandVO) {
String deviceCode = commandVO.getDeviceCode();
UserDTO user = UserContext.getUser();
public CommonResult<String> command(@RequestBody CommandVO cmd) {
String deviceCode = cmd.getDeviceCode();
DeviceStatus status = deviceStatusManager.getDeviceStatus(deviceCode);
// 判断设备是否在线
Boolean online = status.getOnline();
if (!online) return CommonResult.error(400, "设备不在线");
String ip = status.getIp();
// 下发指令
String res = HttpUtil.post("http://" + ip + "/command", JSONUtil.toJsonStr(commandVO), 1000);
String ip = status.getIp();
Integer port = status.getPort();
String res = HttpUtil.post("http://" + ip + ":" + port + "/command", JSONUtil.toJsonStr(cmd), 1000);
// 记录指令下发事件
CommonResult _result = JSONUtil.toBean(res, CommonResult.class);
RecycleDeviceEvent event = new RecycleDeviceEvent();
@ -454,6 +503,7 @@ public class DeviceController {
} else {
event.setEventResult("0");
}
UserDTO user = UserContext.getUser();
event.setCreateUserId(user.getId());
event.setCreateTime(new Date());
event.setCompanyId(user.getCompanyId());
@ -463,7 +513,7 @@ public class DeviceController {
return _result;
}
@ApiModelProperty("获取设备状态")
@ApiOperation("获取设备状态")
@PostMapping("/status")
public CommonResult<String> command(@RequestBody DeviceVO deviceVO) {
String deviceCode = deviceVO.getDeviceCode();
@ -476,15 +526,66 @@ public class DeviceController {
// 判断设备是否在线
Boolean online = status.getOnline();
if (!online) return CommonResult.error(400, "设备不在线");
String post = HttpUtil.post("http://" + status.getIp() + "/command", JSONUtil.toJsonStr(cmd), 1000);
String ip = status.getIp();
Integer port = status.getPort();
String post = HttpUtil.post("http://" + ip + ":" + port + "/command", JSONUtil.toJsonStr(cmd), 1000);
JSONObject json = JSONUtil.parseObj(post);
Integer code = json.getInt("code");
if (code == 200 || code == 201) {
String data = json.getStr("data");
if (StrUtil.isEmpty(data)) {
DeviceStatus deviceStatus = JSONUtil.toBean(data, DeviceStatus.class);
deviceStatusManager.putDeviceStatus(deviceStatus);
return CommonResult.success("状态已更新", "success");
} else {
return CommonResult.error(400, "获取状态失败");
}
} else {
return CommonResult.error(400, json.getStr("msg"));
}
return CommonResult.success("状态已更新", "success");
}
@ApiOperation("接收实时重量")
@PostMapping("/weight")
public CommonResult<String> weight(@RequestBody WeightVO weight) {
DeviceStatus deviceStatus = deviceStatusManager.getDeviceStatus(weight.getDeviceCode());
List<BucketStatus> bucketList = deviceStatus.getBucketList();
boolean flag = false;
for (BucketStatus bucketStatus : bucketList) {
if (bucketStatus.getBucketCode().equals(weight.getBucketCode()))
flag = true;
}
if (!flag) {
CommonResult.error(400, "设备编码与桶编码不匹配");
}
redisTool.setValue(weight.getBucketCode(), JSONUtil.toJsonStr(weight), 1000 * 60l);//一分钟过期
return CommonResult.success("success");
}
@ApiOperation("获取实时重量")
@PostMapping("/getWeight")
public CommonResult<WeightVO> getWeight(@RequestBody DeviceBucketVO deviceBucketVO) {
// DeviceStatus deviceStatus = deviceStatusManager.getDeviceStatus(deviceBucketVO.getDeviceCode());
// List<BucketStatus> bucketList = deviceStatus.getBucketList();
// boolean flag = false;
// for (BucketStatus bucketStatus : bucketList) {
// if (bucketStatus.getBucketCode().equals(deviceBucketVO.getBucketCode()))
// flag = true;
// }
// if (!flag) {
// CommonResult.error(400, "设备编码与桶编码不匹配");
// }
long keyExpire = redisTool.getKeyExpire(deviceBucketVO.getBucketCode(), TimeUnit.SECONDS);
if (keyExpire == -1) {
return CommonResult.error(400, "获取实时重量失败");
}
Object value = redisTool.getValue(deviceBucketVO.getBucketCode());
if (value != null) {
WeightVO weightVO = JSONUtil.toBean(value.toString(), WeightVO.class);
return CommonResult.success(weightVO);
} else {
return CommonResult.error(400, "获取重量失败");
}
}
}

@ -0,0 +1,28 @@
package cc.yunxi.domain.vo.device;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@Data
@ApiModel("桶状态")
public class BucketStatus {
@ApiModelProperty(value = "桶编码",required = true)
@NotBlank(message = "桶编码不能为空")
private String bucketCode;
@ApiModelProperty(value = "桶编码",required = true)
@NotNull(message = "桶编码不能为空")
private Integer doorNum;
@ApiModelProperty(value = "投递门状态",required = true)
private Boolean deliveryDoor;
@ApiModelProperty(value = "清运门状态",required = true)
private Boolean cleanDoor;
@ApiModelProperty(value = "是否满",required = true)
private Boolean isFull;
@ApiModelProperty(value = "重量",required = true)
private BigDecimal weight;
}

@ -0,0 +1,14 @@
package cc.yunxi.domain.vo.device;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("设备桶")
public class DeviceBucketVO {
@ApiModelProperty("设备编码")
private String deviceCode;
@ApiModelProperty("桶编码")
private String bucketCode;
}

@ -55,12 +55,12 @@ public class DeviceRespVO {
private Integer ordNum;
@ApiModelProperty(value = "回收大类", required = true, example = "HS001")
@NotBlank(message = "回收大类不能为空")
private String code;
private String productCode;
@ApiModelProperty(value = "回收子类", example = "HS001001")
private String subCode;
private String productSubCode;
@ApiModelProperty(value = "显示文字", required = true, example = "纸张&废塑料")
@NotBlank(message = "显示文字不能为空")
private String showText;
private String showName;
@ApiModelProperty(value = "满溢报警", required = true, example = "1")
@NotNull(message = "满溢报警不能为空")
private Integer fullAlarm;

@ -4,19 +4,27 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
@ApiModel("设备状态")
public class DeviceStatus {
@ApiModelProperty("设备编码")
private String deviceCode;//设备编码
@ApiModelProperty("设备ip地址")
private String ip;//IP地址
@ApiModelProperty("最后心跳时间")
private LocalDateTime heartBeat;//心跳时间
@ApiModelProperty("ip是否变化")
private Boolean ipChanged;//ip是否变化
@ApiModelProperty("设备是否在线")
private Boolean online;//设备是否在线
@ApiModelProperty(value = "设备编码",required = true)
@NotBlank(message = "设备编码不能为空")
private String deviceCode;
@ApiModelProperty(value = "是否在线")
private Boolean online;
@ApiModelProperty(value = "设备ip地址",required = true)
@NotNull(message = "设备ip地址不能为空")
private String ip;
@ApiModelProperty(value = "设备端口",required = true)
@NotNull(message = "设备端口不能为空")
private Integer port;
@ApiModelProperty(value = "烟雾报警", required = true, example = "true")
private Boolean smogAlarm;
@ApiModelProperty(value = "桶状态", required = true)
private List<BucketStatus> bucketList;
}

@ -0,0 +1,20 @@
package cc.yunxi.domain.vo.device;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
@ApiModel("实时重量")
public class WeightVO {
@ApiModelProperty(value = "设备编码",required = true)
private String deviceCode;
@ApiModelProperty(value = "桶编码",required = true)
private String bucketCode;
@ApiModelProperty(value = "重量",required = true)
private BigDecimal weight;
@ApiModelProperty(value = "称重完成",required = true)
private boolean finish;
}

@ -1,51 +1,60 @@
package cc.yunxi.utils;
import cc.yunxi.domain.vo.device.DeviceStatus;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class DeviceStatusManager {
private static final ConcurrentHashMap<String, DeviceStatus> DEVICE_STATUS_MAP = new ConcurrentHashMap<>();
@Resource
private RedisTool redisTool;
public DeviceStatus getDeviceStatus(String deviceCode) {
return DEVICE_STATUS_MAP.get(deviceCode);
public void putDeviceStatus(DeviceStatus des) {
String deviceCode = des.getDeviceCode();
Object value = redisTool.getValue(deviceCode);
long keyExpire = redisTool.getKeyExpire(deviceCode, TimeUnit.SECONDS);
log.info("设备编码:{} 缓存过期时间:{}", deviceCode, keyExpire);
if (null == value) {
log.info("设备编码:{} 不存在,放入redis", deviceCode);
} else {
DeviceStatus status = JSONUtil.toBean(value.toString(), DeviceStatus.class);
boolean changed = isDeviceStatusChanged(status, des);
if (changed) {
//todo 更新数据库
}
public ConcurrentHashMap<String, DeviceStatus> getAllDeviceStatus() {
return DEVICE_STATUS_MAP;
}
redisTool.setValue(deviceCode, JSONUtil.toJsonStr(des), 60 * 1000L);
log.info("设备状态信息:{} 状态信息已存在,更新缓存", deviceCode);
public void putDeviceStatus(DeviceStatus deviceStatus) {
DEVICE_STATUS_MAP.put(deviceStatus.getDeviceCode(), deviceStatus);
}
public void removeDeviceStatus(String deviceCode) {
DEVICE_STATUS_MAP.remove(deviceCode);
public DeviceStatus getDeviceStatus(String deviceCode) {
Object value = redisTool.getValue(deviceCode);
if (null != value) {
return JSONUtil.toBean(value.toString(), DeviceStatus.class);
}
//状态是否改变
private boolean isChanged(DeviceStatus deviceStatus) {
DeviceStatus oldStatus = DEVICE_STATUS_MAP.get(deviceStatus.getDeviceCode());
if (oldStatus == null) {
return true;
} else {
if (!oldStatus.getIp().equals(deviceStatus.getIp())) {
log.info("设备编码:{} 的IP地址发生变化old:{} , new:{} ", deviceStatus.getDeviceCode(), oldStatus.getIp(), deviceStatus.getIp());
return true;
return null;
}
private boolean isDeviceStatusChanged(DeviceStatus old, DeviceStatus now) {
boolean flag = false;
if (old.getIp() != now.getIp()) {
flag = true;
}
return false;
if (old.getSmogAlarm() != now.getSmogAlarm()) {
flag = true;
}
return flag;
}
}

@ -26,30 +26,6 @@ public class DeviceStatusMonitor {
@Scheduled(fixedDelay = 1000 * 90)//每1.5个周期执行一次 必定有一次心跳 无则视为掉线
public void monitorDeviceStatus() {
ConcurrentHashMap<String, DeviceStatus> allDeviceStatus = statusManager.getAllDeviceStatus();
if (allDeviceStatus.size() > 0) {
log.info("设备状态监控开始");
//需要更新状态的设备
List<DeviceStatus> list = new ArrayList<>();
// 遍历设备状态
for (DeviceStatus deviceStatus : allDeviceStatus.values()) {
// 判断设备状态是否超时
if (System.currentTimeMillis() - deviceStatus.getHeartBeat().toEpochSecond(ZoneOffset.of("+8")) > TIME_OUT) {
// 设备状态超时,修改设备状态为离线
deviceStatus.setOnline(false);
//todo 更新设备在线状态
log.info("设备 {} 已离线", deviceStatus.getDeviceCode());
}
if (deviceStatus.getIpChanged()) {
//todo 更新设备通信地址
deviceStatus.setIpChanged(false);
log.info("设备ip地址发生变化,新的地址:{},已更新至数据库", deviceStatus.getIp());
}
}
// todo 更新设备状态
log.info("设备状态监控结束");
} else {
log.error("没有设备在线!!!");
}
}
}

Loading…
Cancel
Save