设备监控(未测试)

This commit is contained in:
磷叶 2024-12-26 10:52:30 +08:00
parent 949111d420
commit 184e651fb5
8 changed files with 142 additions and 70 deletions

View File

@ -18,6 +18,8 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 接收硬件参数
@ -34,10 +36,10 @@ public class ReceiveController {
private String token;
@Autowired
private IotService iotService;
private IotReceiveService iotReceiveService;
@Autowired
private IotReceiveService iotReceiveService;
private ScheduledExecutorService scheduledExecutorService;
/**
* 功能描述第三方平台数据接收<p>
@ -52,11 +54,11 @@ public class ReceiveController {
@PostMapping(value = "/receive")
@Anonymous
public ResponseEntity<String> receive(HttpServletRequest request){
String body = HttpUtils.getBody(request);
ObjBody obj = IotUtil.resolveBody(body, false);
if (obj != null){
// if (IotUtil.checkSignature(obj, token)){
// log.info("receive成功参数:" + body);
// 异步处理直接返回成功
scheduledExecutorService.schedule(() -> {
String body = HttpUtils.getBody(request);
ObjBody obj = IotUtil.resolveBody(body, false);
if (obj != null){
Object msg = obj.getMsg();
// 接收到msg
if (msg instanceof String) {
@ -64,12 +66,10 @@ public class ReceiveController {
} else {
iotReceiveService.handleReceive(JSON.parseObject(JSON.toJSONString(msg), ReceiveMsg.class));
}
// }else {
// log.error("receive签名错误:" + body);
// }
}else {
log.error("receive方法参数为空: body empty error");
}
}else {
log.error("receive方法参数为空: body empty error");
}
}, 0, TimeUnit.SECONDS);
return new ResponseEntity<>(HttpStatus.OK);
}

View File

@ -259,4 +259,17 @@ public class Device extends BaseEntity
@ApiModelProperty("电压系数")
private BigDecimal vxs;
@Excel(name = "用户手动操作类型", readConverterExp = "1=开启,2=关闭")
@ApiModelProperty("用户手动操作类型")
private String userOperaType;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "用户手动操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("用户手动操作时间")
private LocalDateTime userOperaTime;
@Excel(name = "用户手动操作时的总用电量")
@ApiModelProperty("用户手动操作时的总用电量")
private BigDecimal userOperaEle;
}

View File

@ -0,0 +1,21 @@
package com.ruoyi.ss.device.domain.enums;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
/**
* @author wjh
* 2024/12/26
*/
@Getter
@AllArgsConstructor
public enum DeviceUserOperaType {
OPEN("1", "开启"),
CLOSE("2", "关闭");
private final String type;
private final String msg;
}

View File

@ -69,6 +69,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
sd.expire_ele,
sd.last_init_reading,
sd.vxs,
sd.user_opera_type,
sd.user_opera_time,
sd.user_opera_ele,
sm.model_name as model,
sm.picture as picture,
sm.tags as model_tags,
@ -132,6 +135,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.version != null and query.version != ''"> and sd.version like concat('%', #{query.version}, '%')</if>
<if test="query.modelTag != null and query.modelTag != ''"> and find_in_set(#{query.modelTag}, sm.tags)</if>
<if test="query.excludeDeviceId != null"> and sd.device_id != #{query.excludeDeviceId}</if>
<if test="query.userOperaType != null and query.userOperaType != ''"> and user_opera_type = #{query.userOperaType}</if>
<if test="query.inStaff != null">
and sd.store_id
<if test="!query.inStaff">not</if>
@ -396,6 +400,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="expireEle != null">expire_ele,</if>
<if test="lastInitReading != null">last_init_reading,</if>
<if test="vxs != null">vxs,</if>
<if test="userOperaType != null">user_opera_type,</if>
<if test="userOperaTime != null">user_opera_time,</if>
<if test="userOperaEle != null">user_opera_ele,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="storeId != null">#{storeId},</if>
@ -451,6 +458,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="expireEle != null">#{expireEle},</if>
<if test="lastInitReading != null">#{lastInitReading},</if>
<if test="vxs != null">#{vxs},</if>
<if test="userOperaType != null">#{userOperaType},</if>
<if test="userOperaTime != null">#{userOperaTime},</if>
<if test="userOperaEle != null">#{userOperaEle},</if>
</trim>
</insert>
@ -537,6 +547,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="expireEle != null">expire_ele = #{expireEle},</if>
<if test="lastInitReading != null">last_init_reading = #{lastInitReading},</if>
<if test="vxs != null">vxs = #{vxs},</if>
<if test="userOperaType != null">user_opera_type = #{userOperaType},</if>
<if test="userOperaTime != null">user_opera_time = #{userOperaTime},</if>
<if test="userOperaEle != null">user_opera_ele = #{userOperaEle},</if>
</trim>
where device_id = #{deviceId}
</update>

View File

@ -21,10 +21,7 @@ import com.ruoyi.ss.device.domain.DeviceQuery;
import com.ruoyi.ss.device.domain.dto.DeviceBatchUpdateModelDTO;
import com.ruoyi.ss.device.domain.dto.DeviceRegisterDTO;
import com.ruoyi.ss.device.domain.dto.DeviceWifiDTO;
import com.ruoyi.ss.device.domain.enums.DeviceOnlineStatus;
import com.ruoyi.ss.device.domain.enums.DevicePowerStatus;
import com.ruoyi.ss.device.domain.enums.DeviceServiceMode;
import com.ruoyi.ss.device.domain.enums.DeviceStatus;
import com.ruoyi.ss.device.domain.enums.*;
import com.ruoyi.ss.device.domain.vo.DeviceMacSnVO;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.device.mapper.DeviceMapper;
@ -369,6 +366,7 @@ public class DeviceServiceImpl implements DeviceService
@Override
public int switchDevice(Long deviceId, boolean open, String reason) {
DeviceVO device = selectById(deviceId);
this.pullDeviceInfo(device, IotConstants.ONLINE_TYPE_GET);
return this.switchDevice(device, open ? DevicePowerStatus.ON : DevicePowerStatus.OFF, reason);
}
@ -813,10 +811,10 @@ public class DeviceServiceImpl implements DeviceService
// 校验
this.validatePreLogicDelete(deviceIds);
// 拉取最新数据
this.pullDeviceInfo(deviceIds, IotConstants.ONLINE_TYPE_GET);
// 查询设备列表
List<DeviceVO> deviceList = this.selectByIds(deviceIds);
// 拉取最新数据
this.pullDeviceInfoList(deviceList, IotConstants.ONLINE_TYPE_GET);
Integer result = transactionTemplate.execute(status -> {
// 关闭设备订单
@ -867,7 +865,7 @@ public class DeviceServiceImpl implements DeviceService
/**
* 切换设备通电状态/断电
*
* @param device 设备id
* @param device 设备id
* @param powerStatus 通电状态
* @param reason
*/
@ -876,18 +874,27 @@ public class DeviceServiceImpl implements DeviceService
ServiceUtil.assertion(device == null || device.getDeleted(), "设备不存在");
Integer result = transactionTemplate.execute(status -> {
// 修改数据库通断电状态
Device changePowerForm = new Device();
changePowerForm.setDeviceId(device.getDeviceId());
changePowerForm.setPowerStatus(powerStatus.getStatus());
int update = deviceMapper.updateSmDevice(changePowerForm);
// 修改数据库
Device data = new Device();
data.setDeviceId(device.getDeviceId());
data.setPowerStatus(powerStatus.getStatus());
data.setUserOperaTime(LocalDateTime.now());
data.setUserOperaEle(device.getTotalElectriQuantity());
if (DevicePowerStatus.ON.equals(powerStatus)) {
data.setUserOperaType(DeviceUserOperaType.OPEN.getType());
} else {
data.setUserOperaType(DeviceUserOperaType.CLOSE.getType());
}
int update = deviceMapper.updateSmDevice(data);
// 操作设备
if (update == 1) {
if (DevicePowerStatus.ON.equals(powerStatus)) {
iotService.open(device, reason);
boolean open = iotService.open(device, reason);
ServiceUtil.assertion(!open, "开启设备失败");
} else if (DevicePowerStatus.OFF.equals(powerStatus)) {
iotService.close(device, reason);
boolean close = iotService.close(device, reason);
ServiceUtil.assertion(!close, "关闭设备失败");
} else {
throw new ServiceException("不支持的操作");
}
@ -1308,7 +1315,9 @@ public class DeviceServiceImpl implements DeviceService
return iotService.setEle(device, device.getSurplusEleDb(), reason);
}
// 监控设备信息并对设备进行校准
/**
* 监控设备信息并对设备进行校准
*/
@Override
public void monitor(List<Long> deviceIds) {
DeviceQuery query = new DeviceQuery();
@ -1321,23 +1330,30 @@ public class DeviceServiceImpl implements DeviceService
for (DeviceVO device : deviceList) {
try {
scheduledExecutorService.schedule(() -> {
// 若物联网设备的时长或者电量小于当前设备数据库的值则执行一次同步开启
// 一般情况下物联网设备的值会大于或等于当前数据库的值
long deviceSeconds = device.getRemainTime() == null ? 0 : device.getRemainTime().longValue();
if (device.getSurplusSecondsDb() > deviceSeconds) {
this.syncTime(device.getDeviceId(), LocalDateTime.now(), "设备监控-同步时长");
this.syncTime(device.getDeviceId(), LocalDateTime.now(), "设备监控-同步ch时长");
}
if (device.getSurplusEleDb().compareTo(device.getSurplusEle()) > 0) {
this.syncEle(device.getDeviceId(), "设备监控-同步电量");
}
// TODO 若当前设备数据库的时长或者电量<=0且在这之后用户没有发open命令则执行一次同步关闭
if (device.getSurplusSecondsDb() <= 0) {
// TODO
// 若当前设备数据库的时长或者电量<=0且在这之后用户没有发open命令则执行一次同步关闭
boolean hasOpen = DeviceUserOperaType.OPEN.getType().equals(device.getUserOperaType()); // 是否开启过
LocalDateTime lastOperaTime = device.getUserOperaTime(); // 上次开启的时间
BigDecimal lastOperaEle = device.getUserOperaEle(); // 上次开启的总用电量
boolean hasOpenAfterExpireTime = hasOpen && lastOperaTime != null && lastOperaTime.isAfter(device.getExpireTime()); // 是否在时间结束后开启
if (device.getSurplusSecondsDb() <= 0 && !hasOpenAfterExpireTime) {
this.switchDevice(device, DevicePowerStatus.OFF, "设备监控-发现异常未关闭设备");
}
if (device.getSurplusEle().compareTo(device.getSurplusEleDb()) <= 0) {
// TODO
boolean hasOpenAfterExpireEle = hasOpen && lastOperaEle != null && lastOperaEle.compareTo(device.getExpireEle()) > 0; // 是否在结束电量后开启
if (device.getSurplusEle().compareTo(device.getSurplusEleDb()) <= 0 && !hasOpenAfterExpireEle) {
this.switchDevice(device, DevicePowerStatus.OFF, "设备监控-发现异常未关闭设备");
}
},0, TimeUnit.SECONDS);
@ -1345,9 +1361,6 @@ public class DeviceServiceImpl implements DeviceService
log.error("监控设备{}出错:{}", device.getDeviceNo(), e.getMessage());
}
}
}
/**

View File

@ -4,6 +4,7 @@ import com.ruoyi.common.constant.IotConstants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.iot.service.IotService;
import com.ruoyi.ss.device.domain.enums.DevicePowerStatus;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.device.service.DeviceService;
import com.ruoyi.ss.payBill.domain.PayBillVO;
@ -47,10 +48,9 @@ public class RechargeDepositAfterPay implements AfterPay {
ServiceUtil.assertion(bill == null, "订单不存在");
ServiceUtil.assertion(!TransactionBillStatus.UNPAID_DEPOSIT.getStatus().equals(bill.getStatus()), "订单状态非待支付押金");
deviceService.pullDeviceInfo(bill.getDeviceId(), IotConstants.ONLINE_TYPE_GET);
DeviceVO device = deviceService.selectById(bill.getDeviceId());
ServiceUtil.assertion(device == null, "设备不存在");
deviceService.pullDeviceInfo(device, IotConstants.ONLINE_TYPE_GET);
Integer result = transactionTemplate.execute(status -> {
// 修改订单信息
@ -60,8 +60,6 @@ public class RechargeDepositAfterPay implements AfterPay {
data.setStatus(TransactionBillStatus.SUCCESS_DEPOSIT.getStatus());
data.setDepositPayId(payBill.getPayId());
data.setPayTime(DateUtils.toDate(payBill.getPayTime()));
data.setDeviceRechargeStatus(TransactionBillDeviceRechargeStatus.SUCCESS.getStatus());
data.setOpenMsg("成功");
TransactionBillQuery query = new TransactionBillQuery();
query.setBillId(bill.getBillId());
query.setStatus(TransactionBillStatus.UNPAID_DEPOSIT.getStatus());
@ -72,28 +70,43 @@ public class RechargeDepositAfterPay implements AfterPay {
int received = transactionBillService.receiveMchShowMobileAmount(bill);
ServiceUtil.assertion(received != 1, "商户获取用户手机号扣款失败");
// 开启设备
this.openDevice(bill);
return update;
});
// 开启设备
this.openDevice(bill, device);
return result == null ? 0 : result;
}
/**
* 订单开启设备
*/
private int openDevice(TransactionBillVO bill) {
private int openDevice(TransactionBillVO bill, DeviceVO device) {
try {
iotService.open(bill, "分时段订单" + bill.getBillNo() + "开启");
return 1;
Integer result = transactionTemplate.execute(status -> {
// 修改订单信息
TransactionBill data = new TransactionBill();
data.setBillId(bill.getBillId());
data.setDeviceRechargeStatus(TransactionBillDeviceRechargeStatus.SUCCESS.getStatus());
data.setOpenMsg("成功");
int update = transactionBillService.updateSmTransactionBill(data);
ServiceUtil.assertion(update != 1, "修改订单信息失败,状态已经发生改变:%s", bill.getBillNo());
// 操作设备
int switchDevice = deviceService.switchDevice(device, DevicePowerStatus.ON, "分时段订单" + bill.getBillNo() + "开启");
ServiceUtil.assertion(switchDevice != 1, "操作设备%s失败", device.getDeviceNo());
return update;
});
return result == null ? 0 : result;
} catch (Exception e) {
log.error(e.getMessage());
TransactionBill data = new TransactionBill();
data.setBillId(bill.getBillId());
data.setDeviceRechargeStatus(TransactionBillDeviceRechargeStatus.FAIL.getStatus());
data.setOpenMsg(e.getMessage());
data.setBillId(bill.getBillId());
return transactionBillService.updateSmTransactionBill(data);
}
}

View File

@ -24,7 +24,7 @@ import com.ruoyi.ss.bonus.service.BonusService;
import com.ruoyi.ss.channelWithdraw.domain.ChannelWithdrawVO;
import com.ruoyi.ss.channelWithdraw.service.ChannelWithdrawService;
import com.ruoyi.dashboard.domain.vo.BillCountVo;
import com.ruoyi.ss.device.domain.vo.DeviceIotResultVO;
import com.ruoyi.ss.device.domain.enums.DevicePowerStatus;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.device.service.DeviceService;
import com.ruoyi.ss.payBill.domain.PayBillQuery;
@ -1049,7 +1049,8 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
} else if(SuitFeeType.TIMING_TIME.getType().equals(order.getSuitFeeType())){
deviceService.resetTime(device, false, reason);
}
iotService.close(device, reason);
int switchDevice = deviceService.switchDevice(device, DevicePowerStatus.OFF, reason);
ServiceUtil.assertion(switchDevice != 1, "操作设备失败");
}
return update;
@ -1919,28 +1920,23 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
DeviceVO device = deviceService.selectById(bill.getDeviceId());
ServiceUtil.assertion(device == null, "设备不存在");
ServiceUtil.assertion(StringUtils.isBlank(device.getMac()), "设备MAC为空请联系管理员处理");
ServiceUtil.assertion(StringUtils.isAllBlank(device.getMac(), device.getMac2()), "设备MAC为空请联系管理员处理");
deviceService.pullDeviceInfo(device, IotConstants.ONLINE_TYPE_GET);
// 开启
if (open) {
// 分时段订单直接open
if (SuitFeeType.timingList().contains(bill.getSuitFeeType())) {
return iotService.open(device, "订单" + bill.getBillNo() + "临时开启") ? 1 : 0;
} else {
// 计算设备剩余时长
LocalDateTime expireTime = device.getExpireTime();
if (expireTime == null) {
return 0;
}
Duration between = Duration.between(LocalDateTime.now(), expireTime);
if (between.getSeconds() > 0) {
CommandResponse res = iotService.setTime(device, between.getSeconds(), "订单" + bill.getBillNo() + "临时开启");
return res.isSuccess() ? 1 : 0;
}
return deviceService.switchDevice(device, DevicePowerStatus.ON, "分时段订单" + bill.getBillNo() + "临时开启");
}
// 其余订单同步时长
else {
CommandResponse res = deviceService.syncTime(device.getDeviceId(), LocalDateTime.now(), "时长订单" + bill.getBillNo() + "临时开启");
return res != null && res.isSuccess() ? 1 : 0;
}
} else {
return iotService.close(device, "订单" + bill.getBillNo() + "临时关闭") ? 1 : 0;
return deviceService.switchDevice(device, DevicePowerStatus.OFF, "订单" + bill.getBillNo() + "临时关闭");
}
return 0;
}
@Override

View File

@ -20,7 +20,6 @@ import com.ruoyi.ss.device.domain.DeviceQuery;
import com.ruoyi.ss.device.domain.dto.DeviceRegisterDTO;
import com.ruoyi.ss.device.domain.dto.DeviceWifiDTO;
import com.ruoyi.ss.device.domain.enums.DevicePowerStatus;
import com.ruoyi.ss.device.domain.vo.DeviceIotResultVO;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.device.service.DeviceAssembler;
import com.ruoyi.ss.device.service.DeviceConverter;
@ -130,15 +129,19 @@ public class AppDeviceController extends BaseController {
@ApiOperation("设备通断电")
@PutMapping("/{deviceId}/changePower")
public AjaxResult close(@PathVariable @ApiParam("设备id") Long deviceId, String status) {
DeviceVO device = smDeviceService.selectById(deviceId);
ServiceUtil.assertion(device == null, "设备不存在");
// 判断是否有权限更改
if (!deviceValidator.canOpera(deviceId, getUserId()) ) {
return error("您无权操作此设备");
}
DeviceVO device = smDeviceService.selectById(deviceId);
ServiceUtil.assertion(device == null, "设备不存在");
deviceAssembler.assembleAllowSwitch(device, getUserId());
ServiceUtil.assertion(!device.getAllowSwitch(), "您无权操作此设备");
// 获取最新数据
smDeviceService.pullDeviceInfo(device, IotConstants.ONLINE_TYPE_GET);
DevicePowerStatus powerStatus = DevicePowerStatus.parse(status);
return toAjax(smDeviceService.switchDevice(device, powerStatus, "商户开关设备"));
}