增加移动距离超过某一门限后触发变周期的功能

This commit is contained in:
weidong 2023-12-13 09:28:32 +08:00
parent 5a23385a82
commit 231a672181
16 changed files with 123 additions and 71 deletions

View File

@ -24,6 +24,7 @@ public class FwdRecord {
Integer tenantid;
String deviceid;
String project_id;
Short devicenum;
LocalDateTime starttime;
LocalDateTime endtime;
Short state;

View File

@ -11,7 +11,7 @@ public class GnssGroupCalc {
Float xy_threshold;
Float z_threshold;
Boolean auto_filter;
Integer filter_max_hour;
Float shock;
Integer filter_min_hour;
Float auto_threshold;
Integer device_num;
}

View File

@ -38,7 +38,7 @@ public class MultiLineGNSSCalcService {
if(msgTime!=null) logger.info("proc D342: "+msgTime+" D341 num: "+d341Count);
// 如果序号为0则创建一条转发记录表
if(d342Message.getSeq() == 0){
if(d342Message.getSeq() == 0 && d342Message.getProjectId()!=null){
createFwdReord(d342Message);
}
@ -47,7 +47,7 @@ public class MultiLineGNSSCalcService {
// 计算上轮结果
calcService.calSingleDone(deviceId, d342Message.getTenantId(),lastDate);
// 重算最近的
dataPersistService.updateRb562(deviceId,lastDate);
lastDate = dataPersistService.updateRb562(deviceId,lastDate);
// 记录转发表更新为upload done
FwdRecord fwdRecord = fwdRecordMap.get(deviceId);
if(fwdRecord != null){
@ -57,7 +57,7 @@ public class MultiLineGNSSCalcService {
fwdRecordMap.remove(deviceId);
}
}
else if(msgTime!=null && msgTime.isAfter(lastDate.plusMinutes(1))){
else if(msgTime!=null && msgTime.isAfter(lastDate.plusMinutes(2))){
// 计算上轮结果
calcService.calSingleDone(deviceId, d342Message.getTenantId(),lastDate);
}
@ -81,6 +81,7 @@ public class MultiLineGNSSCalcService {
fwdRecord.setProject_id(d342Message.getProjectId());
fwdRecord.setState(FwdRecord.STATE_UPLOADING);
fwdRecord.setStarttime(d342Message.getOriginalTime());
fwdRecord.setDevicenum((short) 1);
fwdRecordMap.put(deviceId, fwdRecord);
}
}

View File

@ -4,7 +4,6 @@ import com.imdroid.sideslope.message.D331RtcmMessage;
import com.imdroid.sideslope.sal.Device;
import com.imdroid.sideslope.sal.DeviceService;
import com.imdroid.sideslope.server.OnlineChannels;
import com.imdroid.sideslope.util.ThreadManager;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.slf4j.Logger;
@ -50,9 +49,6 @@ public class D331RtcmMessageExecutor implements Executor<D331RtcmMessage, Void>
deviceChannel.writeAndFlush(buf);
});
}
ThreadManager.getFixedThreadPool().execute(() -> {
deviceService.updateLatestDataTime(id, message.getCreateTime());
});
return null;
}

View File

@ -17,12 +17,11 @@ import javax.annotation.Resource;
*/
@Component
public class D3F0SelfCheckMessageExecutor implements Executor<D3F0SelfCheckMessage, Void> {
@Autowired
private DataPersistService dataPersistService;
@Autowired
private BeidouClient beidouClient;
// 由于消息数据大从缓存查询设备信息效率更高
@Resource(name = "local")
private DeviceService deviceService;
@ -37,7 +36,7 @@ public class D3F0SelfCheckMessageExecutor implements Executor<D3F0SelfCheckMessa
dataPersistService.saveDeviceState(message);
});
// 通知beidou服务
// 通知beidou服务设备上线
beidouClient.onDeviceActive(message.getId());
// 存储最新设备状态信息到数据库中
return null;

View File

@ -27,7 +27,6 @@ public class D3F2StopIndicationMessageExecutor implements Executor<D3F2StopIndic
@Autowired
private BeidouClient beidouClient;
@Override
public Void execute(D3F2StopIndicationMessage message) {
// 补齐tenantId
@ -40,12 +39,7 @@ public class D3F2StopIndicationMessageExecutor implements Executor<D3F2StopIndic
dataPersistService.saveDeviceTrxStat(message);
});
// 更新设备的最新数据时间
ThreadManager.getFixedThreadPool().submit(() -> {
deviceService.updateLatestDataTime(message.getId(), message.getCreateTime());
});
// 通知beidou服务
// 通知beidou服务设备休眠
beidouClient.onDeviceStop(message.getId());
return null;

View File

@ -113,6 +113,14 @@ public class ForwardGnssTask {
// 按项目打包推送
for (Map.Entry<String, List<GnssCalcData>> entry: projects.entrySet()){
SendToXFZ(entry.getKey(), entry.getValue());
// 记录推送
FwdRecord fwdRecord = new FwdRecord();
fwdRecord.setProject_id(entry.getKey());
fwdRecord.setTenantid(1);
fwdRecord.setDevicenum((short) entry.getValue().size());
fwdRecord.setStarttime(nowTime);
fwdRecord.setState(FwdRecord.STATE_FWD_DONE);
fwdRecordsMapper.insert(fwdRecord);
}
}

View File

@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@ -44,6 +43,7 @@ public class DbDeviceServiceImpl implements DeviceService {
device.setParentId(gnssDevice.getParentid());
device.setName(gnssDevice.getName());
device.setProjectId(gnssDevice.getProject_id());
device.setCalcGroupId(gnssDevice.getCalc_group_id());
return device;
}
@ -60,6 +60,7 @@ public class DbDeviceServiceImpl implements DeviceService {
device.setParentId(gnssDevice.getParentid());
device.setName(gnssDevice.getName());
device.setProjectId(gnssDevice.getProject_id());
device.setCalcGroupId(gnssDevice.getCalc_group_id());
deviceList.add(device);
}
return deviceList;
@ -77,8 +78,4 @@ public class DbDeviceServiceImpl implements DeviceService {
return true;
}
@Override
public void updateLatestDataTime(String deviceId, LocalDateTime latestDataTime) {
}
}

View File

@ -31,4 +31,6 @@ public class Device {
private Integer deviceType;
private Integer calcGroupId;
}

View File

@ -2,7 +2,6 @@ package com.imdroid.sideslope.sal;
import com.imdroid.secapi.dto.GnssCalcData;
import java.time.LocalDateTime;
import java.util.List;
/**
@ -17,5 +16,4 @@ public interface DeviceService {
boolean postLocationRecord(GnssCalcData locationRecord, boolean isExceed);
void updateLatestDataTime(String deviceId, LocalDateTime latestDataTime);
}

View File

@ -6,7 +6,6 @@ import com.imdroid.secapi.dto.GnssCalcData;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -54,9 +53,4 @@ public class LocalDeviceServiceImpl implements DeviceService {
public boolean postLocationRecord(GnssCalcData locationRecord, boolean isExceed) {
return delegate.postLocationRecord(locationRecord, isExceed);
}
@Override
public void updateLatestDataTime(String deviceId, LocalDateTime latestDataTime) {
delegate.updateLatestDataTime(deviceId, latestDataTime);
}
}

View File

@ -13,5 +13,5 @@ import java.time.LocalDateTime;
public interface GNSSDeviceLocationRecordService {
void save(GnssCalcData locationRecord, boolean isExceed) throws Exception;
void saveRawData(D341LocationMessage message);
void updateRb562(String deviceId, LocalDateTime afterTime);
LocalDateTime updateRb562(String deviceId, LocalDateTime afterTime);
}

View File

@ -5,13 +5,15 @@ import com.imdroid.secapi.dto.*;
import com.imdroid.sideslope.bd.Tilt;
import com.imdroid.sideslope.bd.UBXUtil;
import com.imdroid.sideslope.message.D341LocationMessage;
import com.imdroid.sideslope.sal.Device;
import com.imdroid.sideslope.sal.DeviceService;
import com.imdroid.sideslope.util.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.Duration;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@ -31,22 +33,23 @@ public class GNSSDeviceLocationRecordServiceImpl implements GNSSDeviceLocationRe
@Autowired
private GnssRawDataMapper gnssRawDataMapper;
@Autowired
private GnssDeviceMapper gnssDeviceRepository;
@Resource(name = "local")
private DeviceService gnssDeviceRepository;
@Autowired
private GnssGroupCalcMapper groupCalcMapper;
public static final int FILTER_SHORT_CYCLE_HOUR = 4;
public static final int FILTER_LONG_CYCLE_HOUR = 25;
public static final int FILTER_MIN_CYCLE_HOUR = 2;
public static final int FILTER_DEFAULT_CYCLE_HOUR = 8;
static final int FILTER_MIN_RECORD_NUM = 10;
static final float XY_THRESHOLD = 30; //水平异常点30mm
static final float Z_THRESHOLD = 30; //高程异常点30mm
static final float AUTO_THRESHOLD = 50; //触发自适应滤波的门限50mm
static class FilterSetting{
static class VaryFilterCycle{
public LocalDateTime startTime;
public int filterCycleHour;
}
final private ConcurrentHashMap<String, FilterSetting> shortCycleDevices = new ConcurrentHashMap<>();
final private ConcurrentHashMap<String, VaryFilterCycle> autoCycleDevices = new ConcurrentHashMap<>();
final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
protected Class<GnssCalcData> getClazz() {
@ -55,24 +58,76 @@ public class GNSSDeviceLocationRecordServiceImpl implements GNSSDeviceLocationRe
@Override
public void save(GnssCalcData locationRecord, boolean isExceed) {
GnssDevice gnssDevice = gnssDeviceRepository.queryByDeviceId(locationRecord.getDeviceid());
String deviceId = locationRecord.getDeviceid();
Device gnssDevice = gnssDeviceRepository.findByDeviceId(deviceId);
if(gnssDevice == null) return;
//补充解算记录的设备信息
locationRecord.setTenantid(gnssDevice.getTenantid());
locationRecord.setTenantid(gnssDevice.getTenantId());
locationRecord.setEnabled(true);
// 获取平滑参数
GnssGroupCalc groupCalc = getCalcParams(gnssDevice.getCalc_group_id());
GnssGroupCalc groupCalc = getCalcParams(gnssDevice.getCalcGroupId());
// 计算平滑周期
int filterCycle = groupCalc.getFilter_hour();
VaryFilterCycle varyCycle = autoCycleDevices.get(deviceId);
if(varyCycle!=null){
filterCycle = varyCycle.filterCycleHour;
}
// 平滑处理
int filterCycle = groupCalc.getFilter_hour();
if(groupCalc.getAuto_filter()) {
filterCycle = calcFilterCycle(locationRecord.getDeviceid(), isExceed, groupCalc);
}
calcFilterLocation(locationRecord, filterCycle,
groupCalc.getXy_threshold(), groupCalc.getZ_threshold());
repository.insert(locationRecord);
// 更新平滑周期
if(groupCalc.getAuto_filter()) {
refreshFilterCycle(deviceId, groupCalc, locationRecord);
}
}
void refreshFilterCycle(String deviceId, GnssGroupCalc groupCalc, GnssCalcData curCalcData){
LocalDateTime now = LocalDateTime.now();
VaryFilterCycle varyCycle = autoCycleDevices.get(deviceId);
// 如果当前处于变周期阶段则根据时间调整周期
if(varyCycle != null){
if(varyCycle.filterCycleHour >= groupCalc.getFilter_hour()){
//变周期结束
autoCycleDevices.remove(deviceId);
}
else {
if (now.isAfter(varyCycle.startTime.plusHours(1))) {
varyCycle.filterCycleHour++;
varyCycle.startTime = now;
logger.info(deviceId + ": refresh filter cycle "+varyCycle.filterCycleHour);
}
}
}
// 否则判断是否将进入变周期
else{
// 比较当前均值和FilterMinHour前的均值之差是否超过门限
LocalDateTime cmpTime = now.minusHours(groupCalc.getFilter_min_hour());
QueryWrapper<GnssCalcData> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deviceid", deviceId);
queryWrapper.ge("createtime",cmpTime.format(dateFormatter));
queryWrapper.last("limit 1");
GnssCalcData gnssCalcData = repository.selectOne(queryWrapper);
if(gnssCalcData != null && gnssCalcData.getEnabled()){
if(gnssCalcData.getRb562e()!=null && gnssCalcData.getRb562d()!=null){
double gapE = Math.abs(gnssCalcData.getRb562e() - curCalcData.getRb562e());
double gapN = Math.abs(gnssCalcData.getRb562n() - curCalcData.getRb562n());
if(gapE>=groupCalc.getAuto_threshold() || gapN>=groupCalc.getAuto_threshold()){
VaryFilterCycle varyCycle1 = new VaryFilterCycle();
varyCycle1.startTime = now;
varyCycle1.filterCycleHour = groupCalc.getFilter_min_hour();
autoCycleDevices.put(deviceId, varyCycle1);
logger.info(deviceId + ": filter cycle change to "+varyCycle1.filterCycleHour);
}
}
}
}
}
GnssGroupCalc getCalcParams(int calcGroupId){
@ -80,10 +135,11 @@ public class GNSSDeviceLocationRecordServiceImpl implements GNSSDeviceLocationRe
if(calcParam == null){
calcParam = new GnssGroupCalc();
calcParam.setAuto_filter(false);
calcParam.setFilter_hour(FILTER_SHORT_CYCLE_HOUR);
calcParam.setFilter_max_hour(FILTER_LONG_CYCLE_HOUR);
calcParam.setFilter_hour(FILTER_DEFAULT_CYCLE_HOUR);
calcParam.setFilter_min_hour(FILTER_MIN_CYCLE_HOUR);
calcParam.setXy_threshold(XY_THRESHOLD);
calcParam.setZ_threshold(Z_THRESHOLD);
calcParam.setAuto_threshold(AUTO_THRESHOLD);
}
return calcParam;
}
@ -171,7 +227,7 @@ public class GNSSDeviceLocationRecordServiceImpl implements GNSSDeviceLocationRe
* 2固定平滑窗口大小始终固定
* 3自适应当isExceed为true时平滑窗口为SHORT_CYCLE过了SHORT_CYCLE后逐渐增长到LONG_CYCLE
*/
int calcFilterCycle(String deviceId, boolean isExceed, GnssGroupCalc groupCalc){
/* int calcFilterCycle(String deviceId, boolean isExceed, GnssGroupCalc groupCalc){
int filterCycle = groupCalc.getFilter_max_hour();
LocalDateTime now = LocalDateTime.now();
if (isExceed) {
@ -199,13 +255,13 @@ public class GNSSDeviceLocationRecordServiceImpl implements GNSSDeviceLocationRe
}
return filterCycle;
}
*/
@Override
public void updateRb562(String deviceId, LocalDateTime afterTime){
public LocalDateTime updateRb562(String deviceId, LocalDateTime afterTime){
// 获取平滑参数
GnssDevice gnssDevice = gnssDeviceRepository.queryByDeviceId(deviceId);
if(gnssDevice == null) return;
GnssGroupCalc groupCalc = getCalcParams(gnssDevice.getCalc_group_id());
Device gnssDevice = gnssDeviceRepository.findByDeviceId(deviceId);
if(gnssDevice == null) return afterTime;
GnssGroupCalc groupCalc = getCalcParams(gnssDevice.getCalcGroupId());
// 平滑处理
LocalDateTime beforTime = afterTime.plusHours(groupCalc.getFilter_hour());
@ -213,6 +269,9 @@ public class GNSSDeviceLocationRecordServiceImpl implements GNSSDeviceLocationRe
query.eq("deviceid", deviceId);
query.le("createtime", beforTime.format(dateFormatter));
query.ge("createtime", afterTime.format(dateFormatter));
query.orderByAsc("createtime");
LocalDateTime lastTime = afterTime;
List<GnssCalcData> calcDataListToUpdate = repository.selectList(query);
for(GnssCalcData calcData:calcDataListToUpdate){
@ -220,8 +279,10 @@ public class GNSSDeviceLocationRecordServiceImpl implements GNSSDeviceLocationRe
calcFilterLocation(calcData, groupCalc.getFilter_hour(),
groupCalc.getXy_threshold(), groupCalc.getZ_threshold());
repository.updateById(calcData);
lastTime = calcData.getCreatetime();
logger.info(deviceId + " update rb562");
}
}
return lastTime;
}
}

View File

@ -71,8 +71,8 @@ CREATE TABLE IF NOT EXISTS `gnssgroupcalc` (
`xy_threshold` float DEFAULT NULL COMMENT '坏点水平门限',
`z_threshold` float DEFAULT NULL COMMENT '坏点垂直门限',
`auto_filter` bit(1) DEFAULT 0,
`filter_max_hour` int DEFAULT NULL COMMENT '平滑窗口',
`shock` float DEFAULT NULL,
`filter_min_hour` int DEFAULT NULL COMMENT '平滑窗口',
`auto_threshold` float DEFAULT NULL,
`device_num` int DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@ -242,8 +242,9 @@ CREATE TABLE IF NOT EXISTS `warningcfg` (
CREATE TABLE IF NOT EXISTS `fwdrecords` (
`id` bigint AUTO_INCREMENT,
`tenantid` int NOT NULL,
`deviceid` varchar(20) NOT NULL,
`project_id` varchar(64) DEFAULT NULL COMMENT '项目id',
`devicenum` smallint NOT NULL,
`deviceid` varchar(20) NOT NULL,
`starttime` datetime DEFAULT NULL,
`endtime` datetime DEFAULT NULL,
`state` smallint DEFAULT 0,

View File

@ -147,12 +147,12 @@
toolbar: '#toolbarTable',
cols: [[
{field: 'id', title: '组号', sort: true},
{field: 'filter_hour', title: '平滑窗口(小时)'},
{field: 'filter_hour', title: '滤波周期(小时)'},
{field: 'xy_threshold', title: '水平异常门限(mm)'},
{field: 'z_threshold', title: '垂直异常门限(mm)'},
{field: 'auto_filter', title: '自适应平滑', templet: '#autoFilterTrans'},
{field: 'filter_max_hour', title: '最大平滑窗口'},
{field: 'shock', title: 'shock'},
{field: 'auto_filter', title: '自适应滤波', templet: '#autoFilterTrans'},
{field: 'filter_min_hour', title: '最小滤波周期'},
{field: 'auto_threshold', title: '触发门限(mm)'},
{field: 'device_num', title: '关联设备数'},
{title: '操作', toolbar: '#currentTableBar', align: "center"}
]],
@ -207,8 +207,8 @@
layero.find('#xy_threshold').val(data.xy_threshold);
layero.find('#z_threshold').val(data.z_threshold);
layero.find('#auto_filter').val(data.auto_filter);
layero.find('#filter_max_hour').val(data.filter_max_hour);
layero.find('#shock').val(data.shock);
layero.find('#filter_min_hour').val(data.filter_min_hour);
layero.find('#auto_threshold').val(data.auto_threshold);
}
});
$(window).on("resize", function () {

View File

@ -8,7 +8,7 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">平滑窗口(小时)</label>
<label class="layui-form-label required">滤波周期(小时)</label>
<div class="layui-input-block">
<input type="number" name="filter_hour" id="filter_hour" lay-verify="required" lay-reqtext="不能为空" class="layui-input">
</div>
@ -26,7 +26,7 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">自适应平滑</label>
<label class="layui-form-label">自适应滤波</label>
<div class="layui-input-inline">
<select name="auto_filter" id="auto_filter" lay-filter="type1">
<option value="false">禁用</option>
@ -35,15 +35,15 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">大平滑窗口(小时)</label>
<label class="layui-form-label">小滤波周期(小时)</label>
<div class="layui-input-block">
<input type="number" name="filter_max_hour" id="filter_max_hour" class="layui-input">
<input type="number" name="filter_min_hour" id="filter_min_hour" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">shock</label>
<label class="layui-form-label">触发门限(mm)</label>
<div class="layui-input-block">
<input type="number" name="shock" id="shock" class="layui-input">
<input type="number" name="auto_threshold" id="auto_threshold" class="layui-input">
</div>
</div>