1、增加缓存指令下发功能

This commit is contained in:
weidong 2024-04-08 10:57:14 +08:00
parent f92e2748b7
commit af3d394b39
12 changed files with 344 additions and 23 deletions

View File

@ -0,0 +1,34 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* GNSS收发统计消息每个工作周期结束的时候统计一次
*
* @author LiGang
*/
@Data
@TableName(value = "cachecmd")
public class DeviceCacheCmd {
// device type definition
public static final short TYPE_GNSS = 0;
public static final short TYPE_DTU = 1;
public static final short TYPE_MPU = 2;
public static final short TYPE_DEBUG = 3;
public static final short MAX_CMD_LEN = 350;
@TableId(value = "id", type = IdType.AUTO)
Long id;
LocalDateTime updatetime;
String deviceid;
Boolean syn;
Short type;
Integer msgtype;
String cmd;
}

View File

@ -0,0 +1,10 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DeviceCacheCmdMapper extends BaseMapper<DeviceCacheCmd> {
}

View File

@ -4,12 +4,15 @@ import com.imdroid.common.util.ThreadManager;
import com.imdroid.secapi.dto.GnssCalcData; import com.imdroid.secapi.dto.GnssCalcData;
import com.imdroid.sideslope.bd.*; import com.imdroid.sideslope.bd.*;
import com.imdroid.sideslope.message.D341LocationMessage; import com.imdroid.sideslope.message.D341LocationMessage;
import com.imdroid.sideslope.sal.Device;
import com.imdroid.sideslope.sal.DeviceService;
import com.imdroid.sideslope.service.WarningService; import com.imdroid.sideslope.service.WarningService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
@ -41,6 +44,9 @@ public class SingleLineGNSSCalcService implements GNSSDataCalcService {
@Autowired @Autowired
GNSSCalcFilterService gnssCalcFilterService; GNSSCalcFilterService gnssCalcFilterService;
@Resource(name = "local")
private DeviceService deviceService;
@Override @Override
public double[] calcSingle(D341LocationMessage message, boolean completeWhenIdle) { public double[] calcSingle(D341LocationMessage message, boolean completeWhenIdle) {
String deviceId = message.getId(); String deviceId = message.getId();
@ -92,6 +98,9 @@ public class SingleLineGNSSCalcService implements GNSSDataCalcService {
future = ThreadManager.getScheduledThreadPool().schedule(() -> { future = ThreadManager.getScheduledThreadPool().schedule(() -> {
try { try {
calCycleResult(deviceId, tenantId, date); calCycleResult(deviceId, tenantId, date);
// 清除统计
Device device = deviceService.findByDeviceId(deviceId);
if(device != null) device.clearStat();
} catch (Exception e) { } catch (Exception e) {
logger.error(e.toString()); logger.error(e.toString());
} }

View File

@ -1,15 +1,18 @@
package com.imdroid.sideslope.executor; package com.imdroid.sideslope.executor;
import com.imdroid.secapi.client.BeidouClient;
import com.imdroid.secapi.dto.GnssDevice; import com.imdroid.secapi.dto.GnssDevice;
import com.imdroid.sideslope.bd.Gga; import com.imdroid.sideslope.bd.Gga;
import com.imdroid.sideslope.message.D331RtcmMessage; import com.imdroid.sideslope.message.D331RtcmMessage;
import com.imdroid.sideslope.sal.Device; import com.imdroid.sideslope.sal.Device;
import com.imdroid.sideslope.sal.DeviceService; import com.imdroid.sideslope.sal.DeviceService;
import com.imdroid.sideslope.server.DeviceChannel;
import com.imdroid.sideslope.server.OnlineChannels; import com.imdroid.sideslope.server.OnlineChannels;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -26,6 +29,8 @@ public class D331RtcmMessageExecutor implements Executor<D331RtcmMessage, Void>
@Resource(name = "local") @Resource(name = "local")
private DeviceService deviceService; private DeviceService deviceService;
@Autowired
private BeidouClient beidouClient;
@Override @Override
public Void execute(D331RtcmMessage message) { public Void execute(D331RtcmMessage message) {
@ -73,6 +78,20 @@ public class D331RtcmMessageExecutor implements Executor<D331RtcmMessage, Void>
" quality: "+gga.getQuality());*/ " quality: "+gga.getQuality());*/
} }
// 收到第一个数据包如果控制通道没连接也通知上线
if(device1.getD3xxCount() == 1){
DeviceChannel channel = OnlineChannels.INSTANCE.getConfigChannel(device1.getDeviceId());
if(channel == null || !channel.isOnline()){
// 通知上线
try{
beidouClient.onDeviceActive(device1.getDeviceId(), device1.getTenantId());
}
catch (Exception e){
}
}
}
return null; return null;
} }

View File

@ -1,11 +1,14 @@
package com.imdroid.sideslope.executor; package com.imdroid.sideslope.executor;
import com.imdroid.secapi.client.BeidouClient;
import com.imdroid.sideslope.bd.Gga; import com.imdroid.sideslope.bd.Gga;
import com.imdroid.sideslope.calc.GNSSDataCalcService; import com.imdroid.sideslope.calc.GNSSDataCalcService;
import com.imdroid.sideslope.message.D341LocationMessage; import com.imdroid.sideslope.message.D341LocationMessage;
import com.imdroid.sideslope.sal.Device; import com.imdroid.sideslope.sal.Device;
import com.imdroid.sideslope.sal.DeviceService; import com.imdroid.sideslope.sal.DeviceService;
import com.imdroid.common.util.ThreadManager; import com.imdroid.common.util.ThreadManager;
import com.imdroid.sideslope.server.DeviceChannel;
import com.imdroid.sideslope.server.OnlineChannels;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -26,7 +29,8 @@ public class D341LocationMessageExecutor implements Executor<D341LocationMessage
private GNSSDataCalcService gnssCalcService; private GNSSDataCalcService gnssCalcService;
@Resource(name = "local") @Resource(name = "local")
private DeviceService deviceService; private DeviceService deviceService;
@Autowired
private BeidouClient beidouClient;
@Override @Override
public Void execute(D341LocationMessage message) { public Void execute(D341LocationMessage message) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
@ -47,6 +51,20 @@ public class D341LocationMessageExecutor implements Executor<D341LocationMessage
} }
} }
// 收到第一个数据包如果控制通道没连接也通知上线
if(device.getD341Count() == 1){
DeviceChannel channel = OnlineChannels.INSTANCE.getConfigChannel(device.getDeviceId());
if(channel == null || !channel.isOnline()){
// 通知上线
try{
beidouClient.onDeviceActive(device.getDeviceId(), device.getTenantId());
}
catch (Exception e){
}
}
}
ThreadManager.getFixedThreadPool().submit(() -> { ThreadManager.getFixedThreadPool().submit(() -> {
gnssCalcService.calcSingle(message,true); gnssCalcService.calcSingle(message,true);
}); });

View File

@ -86,7 +86,7 @@ public class Device {
} }
} }
void clearStat(){ public void clearStat(){
d3xxCount = 0; d3xxCount = 0;
d3xxbytes = 0; d3xxbytes = 0;
d341Count = 0; d341Count = 0;

View File

@ -1,5 +1,6 @@
package com.imdroid.beidou.controller; package com.imdroid.beidou.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imdroid.secapi.client.RtcmClient; import com.imdroid.secapi.client.RtcmClient;
import com.imdroid.secapi.dto.*; import com.imdroid.secapi.dto.*;
import com.imdroid.secapi.utils.HexUtil; import com.imdroid.secapi.utils.HexUtil;
@ -26,6 +27,8 @@ public class APIController extends BasicController{
GnssMsgMapper msgMapper; GnssMsgMapper msgMapper;
@Autowired @Autowired
GnssStatusMapper gnssStatusMapper; GnssStatusMapper gnssStatusMapper;
@Autowired
DeviceCacheCmdMapper cacheCmdMapper;
/****** config ack *******/ /****** config ack *******/
@PostMapping(value = "/api/config_ack") @PostMapping(value = "/api/config_ack")
@ -117,6 +120,19 @@ public class APIController extends BasicController{
} }
} }
// 检查有没有待发送的指令
QueryWrapper<DeviceCacheCmd> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deviceid",deviceId);
queryWrapper.eq("syn",false);
DeviceCacheCmd cacheCmd = cacheCmdMapper.selectOne(queryWrapper);
if(cacheCmd != null){
rtcmClient.config(deviceId, cacheCmd.getCmd());
cacheCmd.setSyn(true);
cacheCmdMapper.updateById(cacheCmd);
// 保存
saveMsg(deviceId, tenantId,cacheCmd.getMsgtype(), cacheCmd.getCmd(), true);
}
return null; return null;
} }

View File

@ -27,6 +27,8 @@ public class CmdLineController extends BasicController{
GnssMsgMapper msgMapper; GnssMsgMapper msgMapper;
@Autowired @Autowired
DeviceCmdMapper deviceCmdMapper; DeviceCmdMapper deviceCmdMapper;
@Autowired
DeviceCacheCmdMapper cacheCmdMapper;
/**** 推送页面 *****/ /**** 推送页面 *****/
@RequestMapping("/page/cmd_line") @RequestMapping("/page/cmd_line")
@ -43,6 +45,13 @@ public class CmdLineController extends BasicController{
return "/page/table/frequent_cmd"; return "/page/table/frequent_cmd";
} }
@RequestMapping("/page/table/q_cache_cmd")
public String queryCacheCmd(Model m, HttpSession session) {
initModel(m, session);
return "/page/table/q_cache_cmd";
}
/****** 发送指令 *******/ /****** 发送指令 *******/
@PostMapping(value = "/gnss/config_cmd") @PostMapping(value = "/gnss/config_cmd")
@ResponseBody @ResponseBody
@ -92,9 +101,48 @@ public class CmdLineController extends BasicController{
return HttpResult.success(txInfo); return HttpResult.success(txInfo);
} }
@PostMapping(value = "/gnss/cache_cmd")
@ResponseBody
public HttpResult cacheCmd(HttpSession session,
@RequestParam("tx_win") String cmd,
@RequestParam("device_id") String deviceId,
@RequestParam("cmd_type") int cmdType) {
String sendCmd = cmd.replaceAll(" +","");
short len = 0;
int msgType;
if(cmdType == 1){ // DTU,string format
msgType = 0xD31A;
len = (short) (sendCmd.length() + 5);
sendCmd = "D31A"+ HexUtil.Short2HexString(len)+
HexUtil.Int2HexString(Integer.parseInt(deviceId))+
"01"+HexUtil.String2HexString(sendCmd);
}
else{ //hex format
msgType = 0xD310+cmdType;
len = (short) (sendCmd.length()/2+4);
sendCmd = Integer.toHexString(msgType) + HexUtil.Short2HexString(len)+
HexUtil.Int2HexString(Integer.parseInt(deviceId))+sendCmd;
}
// 保存
if(sendCmd.length() >= DeviceCacheCmd.MAX_CMD_LEN) {
return HttpResult.fail("指令过长不能超过325字节");
}
DeviceCacheCmd cacheCmd = new DeviceCacheCmd();
cacheCmd.setDeviceid(deviceId);
cacheCmd.setUpdatetime(LocalDateTime.now());
cacheCmd.setType((short) cmdType);
cacheCmd.setSyn(false);
cacheCmd.setCmd(sendCmd);
cacheCmd.setMsgtype(msgType);
cacheCmdMapper.insert(cacheCmd);
return HttpResult.success("OK");
}
@RequestMapping("/gnss/cmd/list") @RequestMapping("/gnss/cmd/list")
@ResponseBody @ResponseBody
public JSONObject list(int page, int limit) { public JSONObject listCmd(int page, int limit) {
Page<DeviceCmd> pageable = new Page<>(page, limit); Page<DeviceCmd> pageable = new Page<>(page, limit);
IPage<DeviceCmd> cs = deviceCmdMapper.selectPage(pageable, null); IPage<DeviceCmd> cs = deviceCmdMapper.selectPage(pageable, null);
@ -106,9 +154,23 @@ public class CmdLineController extends BasicController{
return jsonObject; return jsonObject;
} }
@RequestMapping("/gnss/cache_cmd/list")
@ResponseBody
public JSONObject listCacheCmd(int page, int limit) {
Page<DeviceCacheCmd> pageable = new Page<>(page, limit);
IPage<DeviceCacheCmd> cs = cacheCmdMapper.selectPage(pageable, null);
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 0);
jsonObject.put("msg", "");
jsonObject.put("count", cs.getTotal());
jsonObject.put("data", cs.getRecords());
return jsonObject;
}
@PostMapping("/gnss/cmd/add") @PostMapping("/gnss/cmd/add")
@ResponseBody @ResponseBody
public String add(@RequestBody JSONObject object) throws Exception { public String addCmd(@RequestBody JSONObject object) throws Exception {
DeviceCmd deviceCmd = JSONObject.toJavaObject(object,DeviceCmd.class); DeviceCmd deviceCmd = JSONObject.toJavaObject(object,DeviceCmd.class);
int num = deviceCmdMapper.insert(deviceCmd); int num = deviceCmdMapper.insert(deviceCmd);
@ -121,10 +183,19 @@ public class CmdLineController extends BasicController{
@PostMapping("/gnss/cmd/delete") @PostMapping("/gnss/cmd/delete")
@ResponseBody @ResponseBody
public String delete(@RequestParam int del_id) throws Exception { public String deleteCmd(@RequestParam int del_id) throws Exception {
int num = deviceCmdMapper.deleteById(del_id); int num = deviceCmdMapper.deleteById(del_id);
if (num == 0) { if (num == 0) {
return HttpResult.failed(); return HttpResult.failed();
} else return HttpResult.ok(); } else return HttpResult.ok();
} }
@PostMapping("/gnss/cache_cmd/delete")
@ResponseBody
public String deleteCacheCmd(@RequestParam int del_id) throws Exception {
int num = cacheCmdMapper.deleteById(del_id);
if (num == 0) {
return HttpResult.failed();
} else return HttpResult.ok();
}
} }

View File

@ -281,3 +281,17 @@ CREATE TABLE IF NOT EXISTS `devicecmd` (
`content` varchar(800) NOT NULL, `content` varchar(800) NOT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/***
*/
CREATE TABLE IF NOT EXISTS `cachecmd` (
`id` bigint AUTO_INCREMENT,
`updatetime` datetime DEFAULT NULL,
`deviceid` varchar(20) NOT NULL,
`syn` bit(1) DEFAULT 0 COMMENT '是否已同步',
`type` smallint NOT NULL,
`msgtype` int default 0,
`cmd` varchar(350) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -37,7 +37,7 @@
<div class="layui-inline"> <div class="layui-inline">
<label class="layui-form-label">设备ID</label> <label class="layui-form-label">设备ID</label>
<div class="layui-input-inline"> <div class="layui-input-inline">
<input type="text" name="device_id" lay-verify="required|number" autocomplete="off" class="layui-input"> <input type="text" name="device_id" autocomplete="off" class="layui-input">
</div> </div>
</div> </div>
<div class="layui-inline"> <div class="layui-inline">
@ -54,10 +54,12 @@
</div> </div>
<div class="layui-inline"> <div class="layui-inline">
<div class="layui-input-block"> <div class="layui-input-block" style="float:right">
<button class="layui-btn" lay-submit="" lay-filter="send_btn">发送</button> <button class="layui-btn" lay-submit="" lay-filter="send_btn">立即发送</button>
<button class="layui-btn" lay-submit="" lay-filter="cache_btn">缓存发送</button>
<button class="layui-btn" lay-submit="" lay-filter="clear_btn">清屏</button> <button class="layui-btn" lay-submit="" lay-filter="clear_btn">清屏</button>
<button class="layui-btn" lay-submit="" lay-filter="frequent_btn">常用指令</button> <button class="layui-btn" lay-submit="" lay-filter="frequent_btn">常用指令</button>
<button class="layui-btn" lay-submit="" lay-filter="q_cache_btn">待发指令</button>
</div> </div>
</div> </div>
</div> </div>
@ -108,11 +110,42 @@
return false; return false;
}); });
form.on('submit(cache_btn)', function (data) {
$.ajax({
type:"POST",
url:"/gnss/cache_cmd",
data:data.field,
success: function (result) {
if(result.code == 0) layer.msg('指令已缓存,收到终端数据时下发');
else layer.alert(result.msg);
},
error: function () {
console.log("ajax error");
}
});
return false;
});
form.on('submit(clear_btn)', function (data) { form.on('submit(clear_btn)', function (data) {
rxWin.val(""); rxWin.val("");
return false; return false;
}); });
form.on('submit(q_cache_btn)', function (data) {
var index = layer.open({
title: '待发送指令',
type: 2,
shade: 0.2,
maxmin:true,
shadeClose: true,
offset: 'rb',
anim: 2,
area: ['50%', '100%'],
content: '../page/table/q_cache_cmd',
});
return false;
});
form.on('submit(frequent_btn)', function (data) { form.on('submit(frequent_btn)', function (data) {
var index = layer.open({ var index = layer.open({
title: '常用指令', title: '常用指令',

View File

@ -189,6 +189,7 @@
}); });
form.on('submit(initLocBtn)', function (data) { form.on('submit(initLocBtn)', function (data) {
layer.confirm('确定重新取初值?', function(index){
$.ajax({ $.ajax({
type:"POST", type:"POST",
url:"/gnss/device/init_loc", url:"/gnss/device/init_loc",
@ -204,6 +205,7 @@
console.log("ajax error"); console.log("ajax error");
} }
}); });
});
return false; return false;
}); });

View File

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>待发送指令</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="../../lib/layui-v2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../../css/public.css" media="all">
<style>
body {
background-color: #ffffff;
}
</style>
</head>
<body>
<div class="layui-form layuimini-form">
<table class="layui-hide" id="currentTableId" lay-filter="currentTableFilter"></table>
</div>
<script src="../../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
<script type="text/html" id="currentTableBar">
<a class="layui-btn layui-btn-xs layui-btn-danger data-count-delete" lay-event="delete">删除</a>
</script>
<script th:inline="none">
layui.use(['form', 'table'], function () {
var $ = layui.$,
form = layui.form,
table = layui.table;
var iframeIndex = parent.layer.getFrameIndex(window.name);
/**
* 初始化表单,要加上,不然刷新部分组件可能会不加载
*/
form.render();
table.render({
elem: '#currentTableId',
url: '/gnss/cache_cmd/list',
cols: [[
{field: 'deviceid', title: '设备编号'},
{field: 'updatetime', title: '更新时间', templet: "<div>{{layui.util.toDateString(d.updatetime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
{field: 'syn', title: '已发送',templet: "<div>{{d.syn==1?'是':'否'}}</div>"},
{field: 'type', title: '类型',templet: '#typeTrans'},
{field: 'cmd', title: '指令',width:'35%'},
{title: '操作', toolbar: '#currentTableBar', fixed: "right", minWidth: 60}
]],
limits: [10, 20, 50],
limit: 10,
page: true,
skin: 'line'
});
table.on('tool(currentTableFilter)', function (obj) {
var data = obj.data;
if (obj.event === 'delete') {
layer.confirm('确定删除该条指令?', function(index){
$.ajax({
type:"POST",
url:"/gnss/cache_cmd/delete",
data:{
'del_id':data.id
},
success: function (data) {
//data是cotroller相应处理函数的返回值
table.reload('currentTableId');
},
error: function () {
console.log("ajax error");
}
});
layer.close(index);
});
}
});
});
</script>
<script type="text/html" id="typeTrans">
{{# if(d.type == 0){ }}
<span>GNSS</span>
{{# } else if(d.type == 1){ }}
<span >DTU</span>
{{# } else if(d.type == 2){ }}
<span >MPU</span>
{{# } else { }}
<span >DEBUG</span>
{{# } }}
</script>
</body>
</html>