Compare commits

..

10 Commits

Author SHA1 Message Date
3aa4e2a0fb feat: 新增 RTKRCV 自动配置 2025-11-13 00:44:07 +08:00
weidong
8ca33f908a 1、地图显示离线和24小时离线和无效解的位置 2025-11-12 09:07:25 +08:00
weidong
b7453a9331 1、手机端增加经纬度显示 2025-11-11 16:14:08 +08:00
weidong
387364029a 1、将稳定的时间最大设为12小时 2025-11-04 09:32:35 +08:00
weidong
d0ae7671d7 1、增加最近24小时离线统计 2025-10-29 09:29:30 +08:00
weidong
77cc7707ee 1、优化配置应答处理 2025-10-28 16:50:22 +08:00
weidong
3cd8277586 1、倾角计推送的bug 2025-10-28 12:35:45 +08:00
weidong
f2ef68a9ae 1、修改d331解析角度的bug 2025-10-28 10:06:15 +08:00
weidong
ab0bd9943c 1、d3f2其它信息显示上个周期角度最大最小值
2、振动用第2个传感器数据
2025-10-28 09:19:43 +08:00
weidong
b04ec862b2 1、优化推送 2025-10-23 16:23:10 +08:00
51 changed files with 2498 additions and 196 deletions

View File

@ -47,4 +47,15 @@ public interface BeidouClient {
@PostMapping("/upgrade_complete")
String onUpgradeComplete();
@PostMapping("/rtkrcv_ack")
String onRtkrcvAck(@RequestParam(name = "deviceId") String deviceId,
@RequestParam(name = "data") String data);
@PostMapping("/rtk_gngga")
String onRtkGngga(@RequestParam(name = "deviceId") String deviceId,
@RequestParam(name = "lat") Double lat,
@RequestParam(name = "lon") Double lon,
@RequestParam(name = "alt") Double alt,
@RequestParam(name = "geo") Double geoidSeparation);
}

View File

@ -0,0 +1,19 @@
package com.imdroid.secapi.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name="RtkrcvClient", url = "http://localhost:9904/gnss")
public interface RtkrcvClient {
@PostMapping("/rtk/start")
String start(@RequestParam("device_id") String deviceId,
@RequestParam(value = "clear_log", required = false) boolean clearLog);
@PostMapping("/rtk/stop")
String stop(@RequestParam("device_id") String deviceId);
@GetMapping("/rtk/log_exists")
Boolean logExists(@RequestParam("device_id") String deviceId);
}

View File

@ -3,7 +3,6 @@ package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.imdroid.common.util.ByteUtil;
import com.imdroid.common.util.HexUtil;
import lombok.Data;
@ -103,10 +102,9 @@ public class GnssDevice {
return cmdList;
}
public boolean clearChangeFlag(String ack){
public boolean clearChangeFlag(byte[] ackBytes){
boolean result = false;
if(change_flag!=0) {
byte[] ackBytes = ByteUtil.hexStringTobyte(ack);
if(ackBytes[ackBytes.length-1] == 1) {
if (ackBytes[8] == 0x55) {
change_flag &= ~PARA_MASK_HAS_BATTERY;

View File

@ -16,6 +16,9 @@ public class GnssStatusJoin {
@ExcelProperty("租户id")
Integer tenantid;
@ExcelProperty("租户名")
String tenantname;
@ExcelProperty("更新日期时间")
LocalDateTime updatetime;

View File

@ -22,23 +22,23 @@ public interface GnssStatusMapper extends MPJBaseMapper<GnssStatus> {
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid from gnssstatus s ,gnssdevices d where s.deviceid=d.deviceid and s.state <> 0"})
List<GnssStatusJoin> queryOnline();
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid from gnssstatus s ,gnssdevices d where " +
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid,d.tenantname from gnssstatus s ,gnssdevices d where " +
"s.deviceid=d.deviceid and d.tenantid<>0 and d.opmode=0"})
List<GnssStatusJoin> queryDeployed();
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid from gnssstatus s ,gnssdevices d where " +
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid,d.tenantname from gnssstatus s ,gnssdevices d where " +
"s.deviceid=d.deviceid and d.tenantid = #{tenantid} and d.opmode=0"})
List<GnssStatusJoin> queryDeployedByTenant(int tenantid);
//多个入参要用Param注解
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid from gnssstatus s ,gnssdevices d where " +
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid,d.tenantname from gnssstatus s ,gnssdevices d where " +
"s.deviceid=d.deviceid and d.tenantid = #{tenantid} and d.opmode=0 and d.project_id = #{projectName}"})
List<GnssStatusJoin> queryDeployedByProject(@Param("tenantid") int tenantid, @Param("projectName") String projectName);
// 需要关联设备类型
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid from gnssstatus s ,gnssdevices d where s.deviceid=d.deviceid and s.deviceid = #{deviceId}"})
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid,d.tenantname from gnssstatus s ,gnssdevices d where s.deviceid=d.deviceid and s.deviceid = #{deviceId}"})
GnssStatusJoin queryByDeviceId(String deviceId);
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid from gnssstatus s ,gnssdevices d where s.deviceid=d.deviceid"})
@Select({"select s.*, d.devicetype, d.model, d.group_id, d.name, d.project_id,d.iccid,d.tenantname from gnssstatus s ,gnssdevices d where s.deviceid=d.deviceid"})
GnssStatusJoin queryAll();
}

View File

@ -0,0 +1,16 @@
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;
@Data
@TableName(value = "rtkgroup")
public class RtkGroup {
@TableId(value = "id", type = IdType.AUTO)
Integer id;
String name;
String inpstr2_path;
String inpstr3_path;
}

View File

@ -0,0 +1,7 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface RtkGroupMapper extends BaseMapper<RtkGroup> {
}

View File

@ -0,0 +1,21 @@
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;
@Data
@TableName(value = "rtkprofile")
public class RtkProfile {
@TableId(value = "id", type = IdType.AUTO)
Integer id;
Integer rtkgroup_id;
String device_id;
String pos1_posmode;
String inpstr1_type;
String inpstr1_path;
String outstr2_path;
String out_height;
}

View File

@ -0,0 +1,7 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface RtkProfileMapper extends BaseMapper<RtkProfile> {
}

View File

@ -0,0 +1,19 @@
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;
@Data
@TableName(value = "rtksolution")
public class RtkSolution {
@TableId(value = "id", type = IdType.AUTO)
Integer id;
String device_id;
Double latitude;
Double longitude;
Double altitude;
Short status;
}

View File

@ -0,0 +1,7 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface RtkSolutionMapper extends BaseMapper<RtkSolution> {
}

View File

@ -15,10 +15,7 @@ import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class Forwarder {
@ -96,15 +93,10 @@ public class Forwarder {
/***
* 推送指定企业设备ID时间的GNSS记录查找指定时间前cycle分钟的有效数据推送
* @param sendTime要推送的记录的时间
* @param resendRecord重推送信息
*/
private boolean forwardGnssRecords(LocalDateTime sendTime, ResendRecord resendRecord) {
String endTime = sendTime.format(formatter);
String beginTime = sendTime.minusMinutes(fwdCycleMinutes).format(formatter);
// 查找属于指定推送组的设备列表
List<GnssDeviceJoin> gnssDeviceList;
private List<GnssDeviceJoin> getFwdDeviceList(ResendRecord resendRecord){
MPJQueryWrapper jquery = null;
if(resendRecord!=null && StringUtils.hasText(resendRecord.getDeviceid())){
@ -118,8 +110,9 @@ public class Forwarder {
.leftJoin("gnssstatus d on t.deviceid = d.deviceid")
.and(warpper->warpper.eq("fwd_group_id", fwdGroupId)
.or()
.eq("fwd_group_id2", fwdGroupId)).
eq("t.deviceid", resendRecord.getDeviceid());
.eq("fwd_group_id2", fwdGroupId))
.eq("t.deviceid", resendRecord.getDeviceid())
.eq("opmode",GnssDevice.OP_MODE_USE);
}
else if(resendRecord!=null && resendRecord.getProjectid()!=null){
jquery = new MPJQueryWrapper<GnssDevice> ()
@ -135,7 +128,8 @@ public class Forwarder {
.eq("fwd_group_id2", fwdGroupId))
.and(warpper->warpper.eq("project_id",resendRecord.getProjectid())
.or()
.eq("project2_id",resendRecord.getProjectid()));
.eq("project2_id",resendRecord.getProjectid()))
.eq("opmode",GnssDevice.OP_MODE_USE);
}
else{
jquery = new MPJQueryWrapper<GnssDevice> ()
@ -148,29 +142,58 @@ public class Forwarder {
.leftJoin("gnssstatus d on t.deviceid = d.deviceid")
.and(warpper->warpper.eq("fwd_group_id", fwdGroupId)
.or()
.eq("fwd_group_id2", fwdGroupId));
.eq("fwd_group_id2", fwdGroupId))
.eq("opmode",GnssDevice.OP_MODE_USE);
}
gnssDeviceList = deviceMapper.selectJoinList(GnssDeviceJoin.class, jquery);
logger.debug("candidate fwd devices {}", gnssDeviceList.size());
return deviceMapper.selectJoinList(GnssDeviceJoin.class, jquery);
}
// 查询最近半小时的GNSS记录
HashMap<String, GnssCalcData> getFwdGnssData(LocalDateTime sendTime,List<GnssDeviceJoin> gnssDeviceList, ResendRecord resendRecord){
String endTime = sendTime.format(formatter);
String beginTime = sendTime.minusMinutes(fwdCycleMinutes).format(formatter);
// 直接以设备列表作为查询条件查询语句很大一个单位可能有几千台设备
// 用设备列表关联的所有的tenantId来查询然后再筛选效率更高但可能包含了非推送的设备列表
HashSet<Integer> tenantIdSet = new HashSet<>();
for(GnssDeviceJoin d:gnssDeviceList){
tenantIdSet.add(d.getTenantid());
}
logger.debug("candidate fwd tenants {}", tenantIdSet.size());
// 查询最近一个推送周期的GNSS记录
QueryWrapper<GnssCalcData> gnssQueryWrapper = new QueryWrapper<>();
gnssQueryWrapper.ge("createtime",beginTime);
gnssQueryWrapper.le("createtime",endTime);
gnssQueryWrapper.orderByDesc("createtime");
gnssQueryWrapper.eq("enabled",true);
gnssQueryWrapper.eq("stabled",true);
gnssQueryWrapper.isNotNull("rpose");
if(resendRecord != null && StringUtils.hasText(resendRecord.getDeviceid())){
gnssQueryWrapper.eq("deviceid", resendRecord.getDeviceid());
}
else {
gnssQueryWrapper.and(wrapper -> {
int i=0;
for (Integer tenantId : tenantIdSet) {
if(i==0) wrapper.eq("tenantid", tenantId);
else wrapper.or().eq("tenantid", tenantId);
i++;
}
});
}
gnssQueryWrapper.orderByDesc("createtime");
List<GnssCalcData> locationRecords = gnssDataMapper.selectList(gnssQueryWrapper);
logger.debug("candidate fwd records {}", locationRecords.size());
if(locationRecords.size() == 0) return false;
List<GnssCalcData> gnssDataList = gnssDataMapper.selectList(gnssQueryWrapper);
// 同一台设备只保留最近的记录
HashMap<String, GnssCalcData> gnssDataMap = new HashMap<>();
for(GnssCalcData data:gnssDataList){
gnssDataMap.put(data.getDeviceid(),data);
}
return gnssDataMap;
}
// 构造按项目id分类的GNSS记录
ConcurrentHashMap<String, List<GnssCalcData>> projects = new ConcurrentHashMap<>();
HashMap<String, List<GnssCalcData>> createFwdDataByProject(
List<GnssDeviceJoin> gnssDeviceList,HashMap<String, GnssCalcData> gnssDataMap,ResendRecord resendRecord){
HashMap<String, List<GnssCalcData>> projects = new HashMap<>();
for(GnssDeviceJoin device:gnssDeviceList){
if(device.getOpmode() != GnssDevice.OP_MODE_USE) continue;
String projectId = device.getProject_id();
@ -191,43 +214,57 @@ public class Forwarder {
recordsToSend = new ArrayList<>();
projects.put(projectId,recordsToSend);
}
for(GnssCalcData record:locationRecords){
if(record.getDeviceid().equals(device.getDeviceid())) {
// 替换成推送用的名字和数值
if (device.getIpose() != null &&
device.getIposn() != null &&
device.getIposd() != null) {
record.setRpose(record.getRpose() - device.getIpose());
record.setRposn(record.getRposn() - device.getIposn());
record.setRposd(record.getRposd() - device.getIposd());
}
if (fwdNameType == FWD_DEVICE_ALIAS_NAME || (device.getName()!=null&&device.getName().contains("替换"))) {
if (StringUtils.hasText(device.getFwddeviceid())) {
record.setDeviceid(device.getFwddeviceid());
}
}
else if(fwdNameType == FWD_DEVICE_NAME){
String devName = "";
if(StringUtils.hasText(device.getProject_id())) devName = devName+device.getProject_id()+"-";
if(StringUtils.hasText(device.getSector())) devName = devName+device.getSector()+"-";
if(StringUtils.hasText(device.getName())) devName = devName+device.getName();
if(StringUtils.hasText(devName)) {
record.setDeviceid(devName);
}
}
// 用r9250来传设备经纬度
record.setR9250e(device.getLongitude());
record.setR9250n(device.getLatitude());
// 用aux来传xyz
record.setAuxe(device.getRoll());
record.setAuxn(device.getPitch());
record.setAuxd(device.getYaw());
recordsToSend.add(record);
break;
GnssCalcData record=gnssDataMap.get(device.getDeviceid());
if(record!=null) {
// 替换成推送用的名字和数值
if (device.getIpose() != null &&
device.getIposn() != null &&
device.getIposd() != null) {
record.setRpose(record.getRpose() - device.getIpose());
record.setRposn(record.getRposn() - device.getIposn());
record.setRposd(record.getRposd() - device.getIposd());
}
if (fwdNameType == FWD_DEVICE_ALIAS_NAME || (device.getName()!=null&&device.getName().contains("替换"))) {
if (StringUtils.hasText(device.getFwddeviceid())) {
record.setDeviceid(device.getFwddeviceid());
}
}
else if(fwdNameType == FWD_DEVICE_NAME){
String devName = "";
if(StringUtils.hasText(device.getProject_id())) devName = devName+device.getProject_id()+"-";
if(StringUtils.hasText(device.getSector())) devName = devName+device.getSector()+"-";
if(StringUtils.hasText(device.getName())) devName = devName+device.getName();
if(StringUtils.hasText(devName)) {
record.setDeviceid(devName);
}
}
// 用r9250来传设备经纬度
record.setR9250e(device.getLongitude());
record.setR9250n(device.getLatitude());
// 用aux来传xyz
record.setAuxe(device.getRoll());
record.setAuxn(device.getPitch());
record.setAuxd(device.getYaw());
recordsToSend.add(record);
}
}
return projects;
}
private boolean forwardGnssRecords(LocalDateTime sendTime, ResendRecord resendRecord) {
// 查找属于指定推送组fwdGroupId的设备列表补推条件可能包含项目号或设备号
List<GnssDeviceJoin> gnssDeviceList = getFwdDeviceList(resendRecord);
logger.debug("candidate fwd devices {}", gnssDeviceList.size());
if(gnssDeviceList.size() == 0) return false;
//查找要推送的数据
HashMap<String, GnssCalcData> gnssDataMap = getFwdGnssData(sendTime, gnssDeviceList, resendRecord);
logger.debug("candidate fwd records {}", gnssDataMap.size());
if(gnssDataMap.size() == 0) return false;
// 构造按项目id分类的GNSS记录
HashMap<String, List<GnssCalcData>> projects =
createFwdDataByProject(gnssDeviceList,gnssDataMap,resendRecord);
// 按项目打包推送
totalSendNum = 0;

View File

@ -31,6 +31,11 @@ public class Gga {
*/
private double altitude=0;
/**
* 大地水准面相对椭球面的高度
*/
private double geoidSeparation = 0;
/**
* GPS状态0初始化1单点定位2码差分3无效PPS4固定解5浮点解6正在估算7人工输入固定值8模拟模式9WAAS差分
*/
@ -94,8 +99,8 @@ public class Gga {
}
}
if(ggaStr != null){
String[] params = ggaStr.split(",",11);
if(params.length == 11){
String[] params = ggaStr.split(",",14);
if(params.length >= 12){
Gga gga = new Gga();
try {
gga.setLatitude(transGGAPos(Double.parseDouble(params[2])));
@ -103,6 +108,7 @@ public class Gga {
gga.setAltitude(Double.parseDouble(params[9]));
gga.setQuality(Integer.parseInt(params[6]));
gga.setSatellitesInUsed(Integer.parseInt(params[7]));
gga.setGeoidSeparation(Double.parseDouble(params[11]));
return gga;
}
catch (Exception e){

View File

@ -169,7 +169,9 @@ public class GNSSCalcFilterService {
newRecord.setAuxn(NumberUtils.scaleTwo(msumN / minCount));
newRecord.setAuxd(NumberUtils.scaleTwo(msumD / minCount));
//滤波窗口里的时间跨度超过滤波时间的2/3才认为稳定
newRecord.setStabled(lastRecordTime.isBefore(newRecordTime.minusHours(filterCycleHour * 1 / 2)));
int stabledHours=filterCycleHour/2;
if(stabledHours>12) stabledHours=12;
newRecord.setStabled(lastRecordTime.isBefore(newRecordTime.minusHours(stabledHours)));
return true;
}
}
@ -249,7 +251,9 @@ public class GNSSCalcFilterService {
newRecord.setAuxn(NumberUtils.scaleTwo(msumN / minCount));
newRecord.setAuxd(NumberUtils.scaleTwo(msumD / minCount));
//滤波窗口里的时间跨度超过滤波时间的2/3才认为稳定
newRecord.setStabled(lastRecordTime.isBefore(newRecordTime.minusHours(filterCycleHour * 1 / 2)));
int stabledHours=filterCycleHour/2;
if(stabledHours>12) stabledHours=12;
newRecord.setStabled(lastRecordTime.isBefore(newRecordTime.minusHours(stabledHours)));
return true;
}
}

View File

@ -7,6 +7,7 @@ import com.imdroid.secapi.dto.GnssDevice;
import com.imdroid.common.util.ByteUtil;
import com.imdroid.sideslope.bd.Gga;
import com.imdroid.sideslope.bd.Rtcm1005;
import com.imdroid.sideslope.bd.Tilt;
import com.imdroid.sideslope.calc.GNSSDataCalcService;
import com.imdroid.sideslope.message.D331RtcmMessage;
import com.imdroid.sideslope.ntrip.UdpNtripServer;
@ -117,6 +118,11 @@ public class D331RtcmMessageExecutor implements Executor<D331RtcmMessage, Void>
deviceBs.setLongitude(gga.getLongitude());
deviceBs.setAltitude(gga.getAltitude());
}
// update angles
Tilt tilt = message.getTilt();
if(tilt!=null){
deviceBs.updateMinMaxAngle(tilt.getRoll(),tilt.getPitch());
}
// 添加NTRIP处理

View File

@ -5,6 +5,7 @@ import com.imdroid.common.util.ThreadManager;
import com.imdroid.secapi.client.BeidouClient;
import com.imdroid.secapi.dto.GnssDevice;
import com.imdroid.sideslope.bd.Gga;
import com.imdroid.sideslope.bd.Tilt;
import com.imdroid.sideslope.calc.GNSSDataCalcService;
import com.imdroid.sideslope.message.D341LocationMessage;
import com.imdroid.sideslope.service.Device;
@ -78,6 +79,10 @@ public class D341LocationMessageExecutor implements Executor<D341LocationMessage
double[] pos = message.getB562_loc();
device.updateCalcQuality((int) pos[3]);
}
Tilt tilt = message.getTilt();
if(tilt!=null){
device.updateMinMaxAngle(tilt.getRoll(),tilt.getPitch());
}
Gga gga = message.getGga();
if(gga != null) {

View File

@ -60,6 +60,14 @@ public class D3F2StopIndicationMessageExecutor implements Executor<D3F2StopIndic
message.setOtherInfo(message.getOtherInfo() + ",valid calc:" + device.getLastValidCalcDataTime());
}
}
if(device.getMinAngleX()!=null){
message.setOtherInfo(String.format("%s,303(%.2f,%.2f,%.2f,%.2f)",
message.getOtherInfo(),
device.getMinAngleX(),
device.getMaxAngleX(),
device.getMinAngleY(),
device.getMaxAngleY()));
}
//更新统计
device.updateRx(message.getHeader(), message.getLen(),1);
GnssTrxMsg gnssTrxMsg = message.getTrxMsg();

View File

@ -20,21 +20,22 @@ public class D331RtcmMessage extends BaseMessage {
@Override
public void decodeBody(ByteBuf src) {
// get操作不会移动指针这样可以确保整个全转发出去
int pos = 0;
this.packetNum = 1;
this.len = src.readableBytes();//this.len & 0x7FF;//有可能两个d331粘包
this.header = src.getUnsignedShort(0); // flag
int onePacketLen = src.getUnsignedShort(2); // length:11 bits
this.header = src.getUnsignedShort(pos); pos+=2;// flag
int onePacketLen = src.getUnsignedShort(pos); pos+=2;// length:11 bits
this.seq = onePacketLen >> 11;
onePacketLen = onePacketLen&0x7FF;
this.id = String.valueOf(src.getUnsignedInt(4)); //id
int pos = onePacketLen + 4;
while(pos < this.len){
if(src.getUnsignedShort(pos) == 0xd331){
onePacketLen = src.getUnsignedShort(pos+2)&0x7FF;
pos = pos+4+onePacketLen;
packetNum ++;
this.id = String.valueOf(src.getUnsignedInt(pos)); pos+=4;//id
if(onePacketLen + 4<this.len) {
while (pos < this.len) {
if (src.getUnsignedShort(pos) == 0xd331) {
onePacketLen = src.getUnsignedShort(pos + 2) & 0x7FF;
pos = pos + 4 + onePacketLen;
packetNum++;
} else pos++;
}
else pos++;
}
/*if (src.readableBytes() - 4 < this.len) {
String msg = String.format("id[%s],长度字段值[%s]效益包的消息体长度[%s]", id, this.len, src.readableBytes() - 4);

View File

@ -0,0 +1,78 @@
package com.imdroid.sideslope.rtkrcv;
import com.imdroid.secapi.client.BeidouClient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class RtkOutServer {
private final String deviceId;
private final int port;
private final BeidouClient beidouClient;
private volatile boolean running = false;
private ServerSocket serverSocket;
private java.io.BufferedWriter logWriter;
public RtkOutServer(String deviceId, int port, BeidouClient beidouClient){
this.deviceId = deviceId;
this.port = port;
this.beidouClient = beidouClient;
}
public void start() throws IOException {
if(running) return;
running = true;
serverSocket = new ServerSocket(port);
try {
java.nio.file.Path dir = java.nio.file.Paths.get("/opt/rtk/"+deviceId);
java.nio.file.Files.createDirectories(dir);
java.nio.file.Path log = dir.resolve("rtkrcv.log");
logWriter = java.nio.file.Files.newBufferedWriter(log, java.nio.charset.StandardCharsets.UTF_8,
java.nio.file.StandardOpenOption.CREATE, java.nio.file.StandardOpenOption.APPEND);
} catch (Exception ignore) {}
Thread t = new Thread(this::acceptLoop, "rtk-out-server-"+deviceId+":"+port);
t.setDaemon(true);
t.start();
}
private void acceptLoop(){
while(running){
try {
Socket sock = serverSocket.accept();
handleClient(sock);
} catch (IOException e) {
if(running) { /* ignore transient errors */ }
}
}
}
private void handleClient(Socket sock){
Thread t = new Thread(() -> {
try (BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8))){
String line;
while((line = br.readLine()) != null){
try{ beidouClient.onRtkrcvAck(deviceId, line); } catch (Exception ignore) {}
try{
if(logWriter!=null){ synchronized (this){ logWriter.write(line); logWriter.write("\n"); logWriter.flush(); } }
}catch(Exception ignore){}
}
} catch (IOException ignored) {
} finally {
try { sock.close(); } catch (Exception ignore) {}
}
}, "rtk-out-client-"+deviceId);
t.setDaemon(true);
t.start();
}
public void close(){
running = false;
try { if(serverSocket!=null) serverSocket.close(); } catch (Exception ignore) {}
try { if(logWriter!=null) logWriter.close(); } catch (Exception ignore) {}
}
}

View File

@ -0,0 +1,29 @@
package com.imdroid.sideslope.rtkrcv;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/rtk")
public class RtkrcvController {
@Autowired
RtkrcvManager manager;
@PostMapping("/start")
public String start(@RequestParam("device_id") String deviceId,
@RequestParam(value = "clear_log", required = false) Boolean clearLog){
return manager.start(deviceId, clearLog!=null && clearLog);
}
@PostMapping("/stop")
public String stop(@RequestParam("device_id") String deviceId){
manager.stop(deviceId);
return "ok";
}
@GetMapping("/log_exists")
public boolean logExists(@RequestParam("device_id") String deviceId){
java.nio.file.Path p = java.nio.file.Paths.get("/opt/rtk/"+deviceId+"/rtkrcv.log");
return java.nio.file.Files.exists(p);
}
}

View File

@ -0,0 +1,206 @@
package com.imdroid.sideslope.rtkrcv;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imdroid.secapi.client.BeidouClient;
import com.imdroid.secapi.dto.RtkGroup;
import com.imdroid.secapi.dto.RtkGroupMapper;
import com.imdroid.secapi.dto.RtkProfile;
import com.imdroid.secapi.dto.RtkProfileMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.*;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
@Service
public class RtkrcvManager {
@Autowired
RtkProfileMapper profileMapper;
@Autowired
RtkGroupMapper groupMapper;
@Autowired
BeidouClient beidouClient;
@Autowired
com.imdroid.sideslope.service.DeviceService deviceService;
private final Map<String, Process> procMap = new HashMap<>();
private final Map<String, RtkOutServer> outServerMap = new HashMap<>();
public synchronized String start(String deviceId, boolean clearLog){
RtkProfile profile = getOrCreateProfile(deviceId);
if(profile.getRtkgroup_id()==null){
return "profile group id is null";
}
RtkGroup group = groupMapper.selectById(profile.getRtkgroup_id());
if(group == null){
return "group not found";
}
ensureDefaults(profile, deviceId);
String retHint = "ok";
try{
com.imdroid.sideslope.service.Device dev = deviceService.findByDeviceId(deviceId);
if(dev!=null){
Double lat = dev.getLatitude()==null?0.0:dev.getLatitude();
Double lon = dev.getLongitude()==null?0.0:dev.getLongitude();
Double alt = dev.getAltitude()==null?0.0:dev.getAltitude();
Double geo = dev.getGeoidSeparation()==null?0.0:dev.getGeoidSeparation();
try{ beidouClient.onRtkGngga(deviceId, lat, lon, alt, geo); }catch(Exception ignore){}
if(!(lat==0.0 && lon==0.0 && alt==0.0)){
profile.setOut_height(geo==0.0?"ellipsoidal":"geodetic");
} else {
retHint = "all_zero";
}
}
}catch(Exception ignore){}
profileMapper.updateById(profile);
try {
startOutServerIfNeeded(deviceId, profile.getOutstr2_path());
if(clearLog) clearLogFile(deviceId);
String confPath = generateConfig(deviceId, profile, group);
Process old = procMap.get(deviceId);
if(old!=null && old.isAlive()){
old.destroy();
}
ProcessBuilder pb = new ProcessBuilder("/opt/rtk/bin/rtkrcv","-nc","-o",confPath);
pb.redirectErrorStream(true);
Process p = pb.start();
procMap.put(deviceId, p);
new Thread(() -> pumpProcessOutput(deviceId, p)).start();
return retHint;
} catch (Exception e){
return "start failed: "+ e.getMessage();
}
}
public synchronized void stop(String deviceId){
Process p = procMap.get(deviceId);
if(p!=null){
p.destroy();
procMap.remove(deviceId);
}
RtkOutServer s = outServerMap.get(deviceId);
if(s!=null){
s.close();
outServerMap.remove(deviceId);
}
}
private void pumpProcessOutput(String deviceId, Process p){
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))){
String line;
while((line = br.readLine()) != null){
try{ beidouClient.onRtkrcvAck(deviceId, line); } catch (Exception ignore) {}
}
} catch (IOException ignored) {}
}
private RtkProfile getOrCreateProfile(String deviceId){
QueryWrapper<RtkProfile> q = new QueryWrapper<>();
q.eq("device_id", deviceId);
RtkProfile p = profileMapper.selectOne(q);
if(p==null){
p = new RtkProfile();
p.setDevice_id(deviceId);
p.setPos1_posmode("static");
p.setInpstr1_type("ntripcli");
p.setOut_height("geodetic");
profileMapper.insert(p);
}
return p;
}
private void ensureDefaults(RtkProfile p, String deviceId){
if(p.getInpstr1_type()==null || p.getInpstr1_type().isEmpty()) p.setInpstr1_type("ntripcli");
if(p.getPos1_posmode()==null || p.getPos1_posmode().isEmpty()) p.setPos1_posmode("static");
if(p.getOut_height()==null || p.getOut_height().isEmpty()) p.setOut_height("geodetic");
if((p.getInpstr1_path()==null || p.getInpstr1_path().isEmpty()) && "ntripcli".equalsIgnoreCase(p.getInpstr1_type())){
p.setInpstr1_path("beidou:29832611@8.134.185.53:8001/"+deviceId);
}
if(p.getOutstr2_path()==null || p.getOutstr2_path().isEmpty()){
int port = pickFreePort();
p.setOutstr2_path("127.0.0.1:"+port);
}
}
private int pickFreePort(){
try (ServerSocket socket = new ServerSocket(0)){
return socket.getLocalPort();
} catch (IOException e){
return 24001;
}
}
private String generateConfig(String deviceId, RtkProfile profile, RtkGroup group) throws IOException {
ClassPathResource res = new ClassPathResource("rtkrcv_default.conf");
String template;
try (InputStream is = res.getInputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream()){
byte[] buf = new byte[4096];
int n;
while((n = is.read(buf)) != -1){ bos.write(buf, 0, n); }
template = bos.toString(StandardCharsets.UTF_8.name());
}
template = replaceLine(template, "pos1-posmode", profile.getPos1_posmode());
template = replaceLine(template, "out-height", profile.getOut_height());
template = replaceLine(template, "inpstr1-type", profile.getInpstr1_type());
if(profile.getInpstr1_path()!=null) template = replaceLine(template, "inpstr1-path", profile.getInpstr1_path());
if(group.getInpstr2_path()!=null) template = replaceLine(template, "inpstr2-path", group.getInpstr2_path());
if(group.getInpstr3_path()!=null) template = replaceLine(template, "inpstr3-path", group.getInpstr3_path());
if(profile.getOutstr2_path()!=null) template = replaceLine(template, "outstr2-path", profile.getOutstr2_path());
Path dir = Paths.get("/opt/rtk/"+deviceId);
Files.createDirectories(dir);
Path conf = dir.resolve("rtkrcv.conf");
Files.write(conf, template.getBytes(StandardCharsets.UTF_8));
return conf.toAbsolutePath().toString();
}
private String replaceLine(String content, String key, String value){
String[] lines = content.split("\n");
StringBuilder sb = new StringBuilder();
String prefix = key + " =";
for(String line : lines){
if(line.trim().startsWith(key+" =") || line.trim().startsWith(key+" =") || line.trim().startsWith(key+" =")){
sb.append(key).append(" =").append(value).append("\n");
}else{
sb.append(line).append("\n");
}
}
return sb.toString();
}
private void startOutServerIfNeeded(String deviceId, String outstr2Path){
if(outstr2Path == null || outstr2Path.isEmpty()) return;
if(outServerMap.containsKey(deviceId)) return;
try{
int port = parsePort(outstr2Path);
RtkOutServer server = new RtkOutServer(deviceId, port, beidouClient);
server.start();
outServerMap.put(deviceId, server);
}catch(Exception ignored){ }
}
private int parsePort(String hostPort){
int idx = hostPort.lastIndexOf(':');
if(idx>0){
return Integer.parseInt(hostPort.substring(idx+1));
}
return Integer.parseInt(hostPort);
}
private void clearLogFile(String deviceId){
Path dir = Paths.get("/opt/rtk/"+deviceId);
Path log = dir.resolve("rtkrcv.log");
try {
Files.createDirectories(dir);
Files.write(log, new byte[0]);
} catch (IOException ignored) {}
}
}

View File

@ -62,6 +62,10 @@ public class Device {
Double latitude;
Double longitude;
Double altitude;
// 大地水准面相对椭球面的高度差
// 博通 GGA 此处可能非零影响 RTK 运算故需通过此属性判断
Double geoidSeparation = 0.0;
Double ecefx;
Double ecefy;
Double ecefz;
@ -93,6 +97,11 @@ public class Device {
byte dataChannelType = CHANNEL_TYPE_UDP; // 0:TCP;1:DUP
int lasQuality = 0;
Double minAngleX;
Double maxAngleX;
Double minAngleY;
Double maxAngleY;
public void updateRx(int head, int bytes,int count){
lastRxHead = head;
@ -155,6 +164,11 @@ public class Device {
fixedNum = 0;
floatNum = 0;
jumpCount = 0;
minAngleX=null;
minAngleY=null;
maxAngleX=null;
maxAngleY=null;
}
public void clearD342Stat(){
@ -169,4 +183,24 @@ public class Device {
abnormalD341Num = 0;
}
public void updateMinMaxAngle(double x, double y){
if(minAngleX == null || maxAngleX == null){
minAngleX = x;
maxAngleX = x;
}
else{
if(x<minAngleX) minAngleX = x;
if(x>maxAngleX) maxAngleX = x;
}
if(minAngleY == null || maxAngleY == null){
minAngleY = y;
maxAngleY = y;
}
else{
if(y<minAngleY) minAngleY = y;
if(y>maxAngleY) maxAngleY = y;
}
}
}

View File

@ -7,9 +7,9 @@ spring.application.build=20230924
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = none
spring.jpa.database-platform = org.hibernate.dialect.MySQLDialect
spring.datasource.url = jdbc:mysql://localhost:3306/beidou?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
spring.datasource.username = admin
spring.datasource.password = DBMgr_2022
spring.datasource.url = jdbc:mysql://8.134.185.53:3306/beidou?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.username=remote
spring.datasource.password=root
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
spring.jackson.dateFormat = yyyy-MM-dd HH:mm:ss

View File

@ -0,0 +1,161 @@
# RTKNAVI options (template)
pos1-posmode =static
pos1-frequency =l1+l2+l5
pos1-soltype =forward
pos1-elmask =10
pos1-snrmask_r =on
pos1-snrmask_b =on
pos1-snrmask_L1 =0,0,0,0,0,0,0,0,0
pos1-snrmask_L2 =0,0,0,0,0,0,0,0,0
pos1-snrmask_L5 =0,0,0,0,0,0,0,0,0
pos1-dynamics =off
pos1-tidecorr =off
pos1-ionoopt =brdc
pos1-tropopt =saas
pos1-sateph =brdc+sbas
pos1-posopt1 =off
pos1-posopt2 =off
pos1-posopt3 =off
pos1-posopt4 =off
pos1-posopt5 =off
pos1-posopt6 =off
pos1-exclsats =
pos1-navsys =45
pos2-armode =continuous
pos2-gloarmode =fix-and-hold
pos2-bdsarmode =on
pos2-arfilter =on
pos2-arthres =3
pos2-arthresmin =2
pos2-arthresmax =2
pos2-arthres1 =0.1
pos2-arthres2 =0
pos2-arthres3 =1e-09
pos2-arthres4 =1e-05
pos2-varholdamb =0.1
pos2-gainholdamb =0.01
pos2-arlockcnt =5
pos2-minfixsats =4
pos2-minholdsats =5
pos2-mindropsats =10
pos2-arelmask =10
pos2-arminfix =20
pos2-armaxiter =1
pos2-elmaskhold =10
pos2-aroutcnt =20
pos2-maxage =30
pos2-syncsol =off
pos2-slipthres =0.05
pos2-dopthres =0
pos2-rejionno =5
pos2-rejcode =30
pos2-niter =1
pos2-baselen =0
pos2-basesig =0
out-solformat =llh
out-outhead =on
out-outopt =on
out-outvel =off
out-timesys =gpst
out-timeform =hms
out-timendec =3
out-degform =deg
out-fieldsep =
out-outsingle =off
out-maxsolstd =0
out-height =geodetic
out-geoid =internal
out-solstatic =all
out-nmeaintv1 =0
out-nmeaintv2 =0
out-outstat =residual
stats-eratio1 =300
stats-eratio2 =300
stats-eratio5 =300
stats-errphase =0.003
stats-errphaseel =0.003
stats-errphasebl =0
stats-errdoppler =1
stats-snrmax =52
stats-errsnr =0
stats-errrcv =0
stats-stdbias =30
stats-stdiono =0.03
stats-stdtrop =0.3
stats-prnaccelh =3
stats-prnaccelv =1
stats-prnbias =0.0001
stats-prniono =0.001
stats-prntrop =0.0001
stats-prnpos =0
stats-clkstab =5e-12
ant1-postype =llh
ant1-pos1 =90
ant1-pos2 =0
ant1-pos3 =-6335367.6285
ant1-anttype =
ant1-antdele =0
ant1-antdeln =0
ant1-antdelu =0
ant2-postype =rtcm
ant2-pos1 =0
ant2-pos2 =0
ant2-pos3 =0
ant2-anttype =
ant2-antdele =0
ant2-antdeln =0
ant2-antdelu =0
ant2-maxaveep =1
ant2-initrst =on
misc-timeinterp =off
misc-sbasatsel =0
misc-rnxopt1 =
misc-rnxopt2 =
misc-pppopt =
file-satantfile =
file-rcvantfile =
file-staposfile =
file-geoidfile =
file-ionofile =
file-dcbfile =
file-eopfile =
file-blqfile =
file-tempdir =./RTKNAVI
file-geexefile =
file-solstatfile =
file-tracefile =
inpstr1-type =ntripcli
inpstr2-type =ntripcli
inpstr3-type =ntripcli
inpstr1-path =beidou:29832611@8.134.185.53:8001/6539837
inpstr2-path =ytcors14847:fyx25943@gnss.ytcors.cn:8003/RTCM33GRCEJpro
inpstr3-path =Ming:@Zhang12345@ntrip.data.gnss.ga.gov.au:2101/BCEP00BKG0
inpstr1-format =rtcm3
inpstr2-format =rtcm3
inpstr3-format =rtcm3
inpstr2-nmeareq =single
inpstr2-nmealat =
inpstr2-nmealon =
outstr1-type =off
outstr2-type =tcpcli
outstr1-path =127.0.0.1:60000
outstr2-path =127.0.0.1:24001
outstr1-format =xyz
outstr2-format =llh
logstr1-type =off
logstr2-type =off
logstr3-type =off
logstr1-path =
logstr2-path =
logstr3-path =
misc-svrcycle =10
misc-timeout =10000
misc-reconnect =10000
misc-nmeacycle =5000
misc-buffsize =32768
misc-navmsgsel =all
misc-proxyaddr =
misc-fswapmargin =30
console-passwd =

View File

@ -36,6 +36,10 @@ public class APIController extends BasicController {
WarningMsgMapper warningMsgMapper;
@Autowired
GnssStatusMapper gnssStatusMapper;
@Autowired
com.imdroid.secapi.dto.RtkSolutionMapper rtkSolutionMapper;
@Autowired
com.imdroid.beidou.service.RtkMonitorService rtkMonitorService;
/****** config ack *******/
@PostMapping(value = "/api/config_ack")
@ -44,20 +48,24 @@ public class APIController extends BasicController {
GnssDevice device = deviceMapper.queryByDeviceId(deviceId);
if (device == null) return null;
int msgType = Integer.parseInt(configAck.substring(0, 4), 16);
byte[] hexValues = ByteUtil.hexStringTobyte(configAck);
// d3 12 00 12 00 81 e7 6f
if(hexValues.length < 9) return null;
int msgType = ByteUtil.getUnsignedShort(hexValues[0],hexValues[1]);
byte code = hexValues[8];
// 配置是否成功
if (msgType == 0xd311) {
//最后一个字节为1表示配置成功0表示配置失败
if (configAck.endsWith("01")) {
if (hexValues[hexValues.length-1] == 1) {
device.setSyn(true);
deviceMapper.updateById(device);
}
} else if (msgType == 0xd312) {
} else if (msgType == 0xd312 || msgType == 0xd313) {
// 工作周期一致性检查
checkWorkCycle(device, configAck);
} else if (msgType == 0xd313) {
if(device.getChange_flag()!=0) {
if(device.clearChangeFlag(configAck)){
if(msgType == 0xd312 && code == 1) checkWorkCycle(device, hexValues);
else if((code&0xF0) == 0x50 && device.getChange_flag()!=0){
if(device.clearChangeFlag(hexValues)){
deviceMapper.updateById(device);
}
}
@ -90,10 +98,9 @@ public class APIController extends BasicController {
return null;
}
void checkWorkCycle(GnssDevice device, String cfgData) {
void checkWorkCycle(GnssDevice device, byte[] hexValues) {
// d3 12 00 12 00 81 e7 6f 01 0a09000001c2dd7ae1419ee148
int pos = 9;
byte[] hexValues = ByteUtil.hexStringTobyte(cfgData);
byte workCycle = hexValues[pos++];
byte workDur = hexValues[pos++];
byte workOffset = hexValues[pos++];
@ -356,4 +363,74 @@ public class APIController extends BasicController {
// 保存升级记录
return null;
}
}
@PostMapping(value = "/api/rtkrcv_ack")
@ResponseBody
public String onRtkRcvAck(String deviceId, String data) {
if(data == null) data = "";
String line = data.trim();
try {
String time = "";
double lat = 0, lon = 0, h = 0;
int q = -1, ns = -1;
String s = line;
if(s.startsWith("RTK ")){
String[] ps = s.split("\\s+");
// RTK dev yyyy/mm/dd hh:mm:ss ...
if(ps.length > 4) {
s = s.substring(s.indexOf(ps[3]));
}
}
String[] tok = s.split("\\s+");
if(tok.length >= 7){
time = tok[0] + (tok[1].contains(":")? (" " + tok[1]) : "");
int base = tok[1].contains(":")? 2 : 1;
lat = Double.parseDouble(tok[base]);
lon = Double.parseDouble(tok[base+1]);
h = Double.parseDouble(tok[base+2]);
q = Integer.parseInt(tok[base+3]);
ns = Integer.parseInt(tok[base+4]);
}
com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject();
obj.put("type","rtk");
obj.put("deviceId", deviceId);
obj.put("time", time);
obj.put("lat", lat);
obj.put("lon", lon);
obj.put("h", h);
obj.put("q", q);
obj.put("ns", ns);
obj.put("raw", line);
RtkrcvWebSocketServer.sendMessageToAll(obj.toJSONString());
if(q == 1){
com.imdroid.secapi.dto.RtkSolution sol = new com.imdroid.secapi.dto.RtkSolution();
sol.setDevice_id(deviceId);
sol.setLatitude(lat);
sol.setLongitude(lon);
sol.setAltitude(h);
sol.setStatus((short)1);
try{ rtkSolutionMapper.insert(sol);}catch(Exception ignore){}
}
if(q >= 0){
rtkMonitorService.onSolution(deviceId, q);
}
} catch (Exception e){
String msg = "RTK " + deviceId + " " + line;
RtkrcvWebSocketServer.sendMessageToAll(msg);
}
return "ok";
}
@PostMapping(value = "/api/rtk_gngga")
@ResponseBody
public String onRtkGngga(String deviceId, Double lat, Double lon, Double alt, Double geo) {
com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject();
obj.put("type","rtk_gngga");
obj.put("deviceId", deviceId);
obj.put("lat", lat);
obj.put("lon", lon);
obj.put("alt", alt);
obj.put("geo", geo);
RtkrcvWebSocketServer.sendMessageToAll(obj.toJSONString());
return "ok";
}
}

View File

@ -48,6 +48,8 @@ public class DeviceSummaryController extends BasicController{
jsonObject.put("satNum",gnssStatus.getSatelliteinuse()>=12?"多于12":gnssStatus.getSatelliteinuse());
jsonObject.put("rxBytes",gnssStatus.getB562bytes()+gnssStatus.getD3xxbytes());
jsonObject.put("rssi",gnssStatus.getRssi());
jsonObject.put("loc",NumberUtils.scale(gnssStatus.getLongitude(), 8)
+","+NumberUtils.scale(gnssStatus.getLatitude(), 8));
}
return jsonObject;
}

View File

@ -145,6 +145,11 @@ public class GnssDeviceController extends BasicController{
if (StringUtils.hasText(deviceid)) {
queryWrapper.like("deviceid", deviceid);
}
//型号
Integer model = search.getInteger("model");
if (model!=null) {
queryWrapper.eq("model", model);
}
//父设备号
String parentid = search.getString("parentid");
if (StringUtils.hasText(parentid)) {
@ -155,6 +160,11 @@ public class GnssDeviceController extends BasicController{
if (StringUtils.hasText(project_id)) {
queryWrapper.like("project_id", project_id);
}
//桩号
String sector = search.getString("sector");
if (StringUtils.hasText(sector)) {
queryWrapper.like("sector", sector);
}
//所属组织
String tenantname = search.getString("tenantname");
if (StringUtils.hasText(tenantname)) {

View File

@ -99,7 +99,7 @@ public class GnssStatusController extends BasicController implements CommonExcel
public MPJQueryWrapper<GnssStatus> prepareQueryWrapper() {
return new MPJQueryWrapper<GnssStatus>()
.selectAll(GnssStatus.class)
.select("d.devicetype as devicetype,d.remark as remark,d.iccid as iccid")
.select("d.devicetype as devicetype,d.remark as remark,d.iccid as iccid,d.tenantname as tenantname")
.leftJoin("gnssdevices d on t.deviceid = d.deviceid")
.orderByDesc("t.updatetime");
}

View File

@ -0,0 +1,41 @@
package com.imdroid.beidou.controller;
import com.imdroid.beidou.common.HttpResult;
import com.imdroid.beidou.service.GroupRtkScheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class GroupRtkController {
@Autowired
GroupRtkScheduler scheduler;
@PostMapping("/rtk/group/start")
public HttpResult start(@RequestParam("group_id") Integer groupId,
@RequestParam(value = "clear_log", required = false) Boolean clearLog){
scheduler.startGroup(groupId, clearLog!=null && clearLog);
return HttpResult.success("ok");
}
@PostMapping("/rtk/group/stop")
public HttpResult stop(@RequestParam("group_id") Integer groupId){
scheduler.stopGroup(groupId);
return HttpResult.success("ok");
}
@GetMapping("/rtk/group/status")
public Map<String,Object> status(@RequestParam("group_id") Integer groupId){
List<Map<String,Object>> data = scheduler.getStatus(groupId);
Map<String,Object> r = new HashMap<>();
r.put("code",0);
r.put("data", data);
r.put("count", data.size());
r.put("msg","");
return r;
}
}

View File

@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;
@ -39,15 +40,16 @@ public class IndexController extends BasicController{
@RequestMapping("/page/device_overview")
public String deviceOverview(Model m, HttpSession session) {
initModel(m, session);
int deviceDeployedNum;
int deviceOfflineNum=0;
int deviceDeployedNum = 0;
int deviceOfflineNum = 0;
int deviceOfflineNumIn24h = 0;
int warning1Num=0;//一般告警
int warning2Num=0;//严重告警
int noGGA = 0;
int noFix = 0;
long deviceNoFwdNum=0;
//未推送设备数同时满足非SAAS服务商使用状态正常fwd_group_id和fwd_group_id2都为"不推送"的设备
Long deviceNoFwdNum;
QueryWrapper<GnssDevice> noFwdQueryWrapper = new QueryWrapper<>();
if (tenantId != Tenant.SAAS_PROVIDER_ID) {
// 非SAAS服务商查询当前租户
@ -63,7 +65,7 @@ public class IndexController extends BasicController{
// fwd_group_id为"不推送"
noFwdQueryWrapper.eq("fwd_group_id", GnssGroupFwd.FWD_TYPE_NONE);
// fwd_group_id2为"不推送"或为null
noFwdQueryWrapper.and(wrapper -> wrapper.eq("fwd_group_id2", GnssGroupFwd.FWD_TYPE_NONE).or().isNull("fwd_group_id2"));
//noFwdQueryWrapper.and(wrapper -> wrapper.eq("fwd_group_id2", GnssGroupFwd.FWD_TYPE_NONE).or().isNull("fwd_group_id2"));
deviceNoFwdNum = gnssDeviceMapper.selectCount(noFwdQueryWrapper);
List<GnssStatusJoin> deviceList;
@ -76,19 +78,26 @@ public class IndexController extends BasicController{
deviceDeployedNum = deviceList.size();
//for(GnssStatusJoin status:deviceList){遍历删除异常
LocalDateTime now = LocalDateTime.now();
Iterator<GnssStatusJoin> iterator = deviceList.iterator();
while(iterator.hasNext()){
GnssStatusJoin status=iterator.next();
if(status.getState() == GnssStatus.STATE_OFFLINE) {
deviceOfflineNum++;
if(status.getUpdatetime()!=null &&
status.getUpdatetime().isAfter(now.minusHours(24))){
deviceOfflineNumIn24h++;
status.setWarning((short) 3);
}
else {
status.setWarning((short) 2);
}
}
else if((status.getWarningcode()&WarningCfg.TYPE_CONT_INVALID_RESULT) != 0) {
noFix++;
status.setWarning((short) 4);
}
if(status.getWarning() == WarningCfg.LEVEL_1) warning1Num++;
else if(status.getWarning() == WarningCfg.LEVEL_2) warning2Num++;
// 剔除无经纬度的对象不在地图上显示
if(status.getLatitude()==null || status.getLongitude()==null){
noGGA++;
@ -115,6 +124,7 @@ public class IndexController extends BasicController{
m.addAttribute("deviceDeployedNum", deviceDeployedNum);
m.addAttribute("deviceOnlineNum", deviceDeployedNum-deviceOfflineNum);
m.addAttribute("deviceOfflineNum", deviceOfflineNum);
m.addAttribute("deviceOfflineNumIn24h", deviceOfflineNumIn24h);
m.addAttribute("warning1Num", warning1Num);
m.addAttribute("warning2Num", warning2Num);
m.addAttribute("warningTotalNum", warning1Num+warning2Num);
@ -144,7 +154,7 @@ public class IndexController extends BasicController{
Integer tenantId = getTenantId(session);
if(tenantId == Tenant.SAAS_PROVIDER_ID) {
queryWrapper.selectAll(GnssStatus.class)
.select("d.name as name", "d.devicetype as devicetype", "d.project_id as project_id")
.select("d.name as name", "d.devicetype as devicetype", "d.tenantname as tenantname","d.project_id as project_id")
.leftJoin("gnssdevices d on t.deviceid = d.deviceid")
.ne("d.tenantid", Tenant.SAAS_PROVIDER_ID)
.eq("opmode", GnssDevice.OP_MODE_USE)
@ -152,7 +162,7 @@ public class IndexController extends BasicController{
}
else{
queryWrapper.selectAll(GnssStatus.class)
.select("d.name as name", "d.devicetype as devicetype", "d.project_id as project_id")
.select("d.name as name", "d.devicetype as devicetype", "d.tenantname as tenantname", "d.project_id as project_id")
.leftJoin("gnssdevices d on t.deviceid = d.deviceid")
.eq("d.tenantid", tenantId)
.eq("opmode", GnssDevice.OP_MODE_USE)
@ -164,6 +174,10 @@ public class IndexController extends BasicController{
if(query.equals("offline")){
queryWrapper.eq("state",GnssStatus.STATE_OFFLINE);
}
else if(query.equals("offline_in24h")){
queryWrapper.eq("state",GnssStatus.STATE_OFFLINE);
queryWrapper.ge("t.updatetime",LocalDateTime.now().minusHours(24));
}
else if(query.equals("nofixed")){
queryWrapper.eq("warningcode&"+WarningCfg.TYPE_CONT_INVALID_RESULT,WarningCfg.TYPE_CONT_INVALID_RESULT);
}

View File

@ -0,0 +1,209 @@
package com.imdroid.beidou.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.imdroid.beidou.common.HttpResult;
import com.imdroid.secapi.dto.OpLogManager;
import com.imdroid.secapi.dto.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
@Controller
public class RtkGroupController extends BasicController {
@Autowired
RtkGroupMapper rtkGroupMapper;
@Autowired
RtkProfileMapper rtkProfileMapper;
@Autowired
GnssDeviceMapper gnssDeviceMapper;
@Autowired
OpLogManager opLogManager;
@RequestMapping("/page/table/rtk_group_edit")
public String rtkGroupEdit(Model m, HttpSession session) {
initModel(m, session);
return "/page/table/rtk_group_edit";
}
@RequestMapping("/page/table/rtk_profile_edit")
public String rtkProfileEdit(Model m, HttpSession session) {
initModel(m, session);
return "/page/table/rtk_profile_edit";
}
@RequestMapping("/rtk/group/list")
@ResponseBody
public JSONObject list(int page, int limit) {
Page<RtkGroup> pageable = new Page<>(page, limit);
IPage<RtkGroup> cs = rtkGroupMapper.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;
}
@RequestMapping("/rtk/profile/list")
@ResponseBody
public com.alibaba.fastjson.JSONObject listProfiles(@RequestParam int page,
@RequestParam int limit,
@RequestParam(value = "group_id", required = false) Integer groupId,
@RequestParam(value = "device_id", required = false) String deviceId) {
com.baomidou.mybatisplus.extension.plugins.pagination.Page<RtkProfile> pageable = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, limit);
QueryWrapper<RtkProfile> wrapper = new QueryWrapper<>();
if (groupId != null) wrapper.eq("rtkgroup_id", groupId);
if (deviceId != null && !deviceId.isEmpty()) wrapper.like("device_id", deviceId);
com.baomidou.mybatisplus.core.metadata.IPage<RtkProfile> cs = rtkProfileMapper.selectPage(pageable, wrapper);
com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
jsonObject.put("code", 0);
jsonObject.put("msg", "");
jsonObject.put("count", cs.getTotal());
jsonObject.put("data", cs.getRecords());
return jsonObject;
}
@PostMapping("/rtk/profile/update")
@ResponseBody
public HttpResult updateProfile(@RequestBody com.alibaba.fastjson.JSONObject object) {
RtkProfile profile = com.alibaba.fastjson.JSONObject.toJavaObject(object, RtkProfile.class);
if ((profile.getId() == null) && (profile.getDevice_id() == null || profile.getDevice_id().isEmpty())) {
return HttpResult.fail("device_id不能为空");
}
if (profile.getInpstr1_type() == null || profile.getInpstr1_type().isEmpty()) profile.setInpstr1_type("ntripcli");
if (profile.getPos1_posmode() == null || profile.getPos1_posmode().isEmpty()) profile.setPos1_posmode("static");
if (profile.getOut_height() == null || profile.getOut_height().isEmpty()) profile.setOut_height("geodetic");
RtkProfile exist = null;
if (profile.getId() != null) exist = rtkProfileMapper.selectById(profile.getId());
if (exist == null && profile.getDevice_id() != null) {
QueryWrapper<RtkProfile> q = new QueryWrapper<>();
q.eq("device_id", profile.getDevice_id());
exist = rtkProfileMapper.selectOne(q);
}
int num;
if (exist == null) {
num = rtkProfileMapper.insert(profile);
} else {
profile.setId(exist.getId());
num = rtkProfileMapper.updateById(profile);
}
if (num == 0) return HttpResult.fail("保存失败");
return HttpResult.success("ok");
}
@GetMapping("/rtk/transfer/options")
@ResponseBody
public HttpResult transferOptions(@RequestParam("group_id") Integer groupId) {
java.util.List<GnssDevice> all = gnssDeviceMapper.selectList(null);
java.util.List<java.util.Map<String, Object>> data = new java.util.ArrayList<>();
for (GnssDevice d : all) {
java.util.HashMap<String, Object> m = new java.util.HashMap<>();
m.put("value", d.getDeviceid());
String title = d.getDeviceid();
if (d.getName() != null && !d.getName().isEmpty()) title += " " + d.getName();
m.put("title", title);
data.add(m);
}
QueryWrapper<RtkProfile> qw = new QueryWrapper<>();
qw.eq("rtkgroup_id", groupId);
java.util.List<RtkProfile> profiles = rtkProfileMapper.selectList(qw);
java.util.List<String> selected = new java.util.ArrayList<>();
for (RtkProfile p : profiles) selected.add(p.getDevice_id());
java.util.HashMap<String, Object> resp = new java.util.HashMap<>();
resp.put("data", data);
resp.put("value", selected);
return HttpResult.success(resp);
}
@PostMapping("/rtk/profile/save_group_devices")
@ResponseBody
public HttpResult saveGroupDevices(@RequestBody JSONObject body) {
Integer groupId = body.getInteger("group_id");
java.util.List<String> deviceIds = body.getJSONArray("device_ids").toJavaList(String.class);
QueryWrapper<RtkProfile> qw = new QueryWrapper<>();
qw.eq("rtkgroup_id", groupId);
java.util.List<RtkProfile> current = rtkProfileMapper.selectList(qw);
java.util.HashSet<String> want = new java.util.HashSet<>(deviceIds);
java.util.HashSet<String> exists = new java.util.HashSet<>();
for (RtkProfile p : current) exists.add(p.getDevice_id());
for (String did : deviceIds) {
QueryWrapper<RtkProfile> q1 = new QueryWrapper<>();
q1.eq("device_id", did);
RtkProfile prof = rtkProfileMapper.selectOne(q1);
if (prof == null) {
RtkProfile np = new RtkProfile();
np.setDevice_id(did);
np.setRtkgroup_id(groupId);
np.setPos1_posmode("static");
np.setInpstr1_type("ntripcli");
np.setOut_height("geodetic");
np.setInpstr1_path("");
np.setOutstr2_path("");
rtkProfileMapper.insert(np);
} else if (prof.getRtkgroup_id() == null || !prof.getRtkgroup_id().equals(groupId)) {
prof.setRtkgroup_id(groupId);
rtkProfileMapper.updateById(prof);
}
}
for (RtkProfile p : current) {
if (!want.contains(p.getDevice_id())) {
p.setRtkgroup_id(0);
rtkProfileMapper.updateById(p);
}
}
return HttpResult.success("ok");
}
@PostMapping("/rtk/group/update")
@ResponseBody
public HttpResult update(HttpSession session, @RequestBody JSONObject object) {
int num;
RtkGroup group = JSONObject.toJavaObject(object, RtkGroup.class);
if (group.getName() == null || group.getName().trim().isEmpty()) {
return HttpResult.fail("名称不能为空");
}
if (group.getInpstr2_path() == null || group.getInpstr2_path().isEmpty()) {
group.setInpstr2_path("ytcors14847:fyx25943@gnss.ytcors.cn:8003/RTCM33GRCEJpro");
}
if (group.getInpstr3_path() == null || group.getInpstr3_path().isEmpty()) {
group.setInpstr3_path("Ming:@Zhang12345@ntrip.data.gnss.ga.gov.au:2101/BCEP00BKG0");
}
if (group.getId() != null) {
num = rtkGroupMapper.updateById(group);
opLogManager.addLog(getLoginUser(session), getTenantId(session),
OpLogManager.OP_TYPE_UPDATE, OpLogManager.OP_OBJ_GROUP, group.getName());
} else {
num = rtkGroupMapper.insert(group);
opLogManager.addLog(getLoginUser(session), getTenantId(session),
OpLogManager.OP_TYPE_ADD, OpLogManager.OP_OBJ_GROUP, group.getName());
}
if (num == 0) {
return HttpResult.fail("保存失败");
} else return HttpResult.success("ok");
}
@PostMapping("/rtk/group/delete")
@ResponseBody
public HttpResult delete(HttpSession session, @RequestParam int del_id) {
RtkGroup group = rtkGroupMapper.selectById(del_id);
if (group == null) return HttpResult.fail("记录不存在");
int num = rtkGroupMapper.deleteById(del_id);
opLogManager.addLog(getLoginUser(session), getTenantId(session),
OpLogManager.OP_TYPE_DEL, OpLogManager.OP_OBJ_GROUP, group.getName());
if (num == 0) {
return HttpResult.fail("删除失败");
} else return HttpResult.success("ok");
}
}

View File

@ -0,0 +1,17 @@
package com.imdroid.beidou.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
@Controller
public class RtkrcvController extends BasicController {
@RequestMapping("/page/rtkrcv")
public String rtkrcv(Model m, HttpSession session) {
initModel(m, session);
return "/page/rtkrcv";
}
}

View File

@ -0,0 +1,44 @@
package com.imdroid.beidou.controller;
import com.imdroid.beidou.common.HttpResult;
import com.imdroid.secapi.client.RtkrcvClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class RtkrcvProxyController {
@Autowired
RtkrcvClient rtkrcvClient;
@Autowired
com.imdroid.beidou.service.RtkMonitorService rtkMonitorService;
@PostMapping("/rtk/start")
@ResponseBody
public HttpResult start(@RequestParam("device_id") String deviceId,
@RequestParam(value = "clear_log", required = false) Boolean clearLog){
String res = rtkrcvClient.start(deviceId, clearLog!=null && clearLog);
rtkMonitorService.onStart(deviceId);
if(res==null) return HttpResult.fail("start failed");
return HttpResult.success(res);
}
@PostMapping("/rtk/stop")
@ResponseBody
public HttpResult stop(@RequestParam("device_id") String deviceId){
String res = rtkrcvClient.stop(deviceId);
rtkMonitorService.onStop(deviceId);
if(res==null) return HttpResult.fail("stop failed");
return HttpResult.success(res);
}
@GetMapping("/rtk/log_exists")
@ResponseBody
public Boolean logExists(@RequestParam("device_id") String deviceId){
Boolean exists = rtkrcvClient.logExists(deviceId);
return exists != null && exists;
}
}

View File

@ -0,0 +1,69 @@
package com.imdroid.beidou.controller;
import com.imdroid.beidou.config.WebSocketConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint(value = "/websocket/rtkrcv", configurator = WebSocketConfig.class)
@Component
@Slf4j
public class RtkrcvWebSocketServer {
private static final Map<String, Session> webSocketSet = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session){
HttpSession httpSession= (HttpSession) session.getUserProperties().get(HttpSession.class.getName());
if(httpSession!=null){
webSocketSet.put(httpSession.getId(), session);
}
log.info("rtkrcv websocket opened, total: {}", webSocketSet.size());
}
@OnClose
public void onClose(Session session){
HttpSession httpSession= (HttpSession) session.getUserProperties().get(HttpSession.class.getName());
if(httpSession!=null){
webSocketSet.remove(httpSession.getId());
}
log.info("rtkrcv websocket closed, total: {}", webSocketSet.size());
}
@OnMessage
public void onMessage(String msg, Session session){
}
@OnError
public void onError(Session session, Throwable error){
log.error("rtkrcv websocket error", error);
}
public static void sendMessageToAll(String msg){
try {
for (Session item : webSocketSet.values()) {
item.getBasicRemote().sendText(msg);
}
}
catch (Exception e){
log.error("rtkrcv websocket send msg error:", e);
}
}
public static void sendMessage(HttpSession httpSession, String msg) {
Session session = webSocketSet.get(httpSession.getId());
if(session != null){
try {
session.getBasicRemote().sendText(msg);
}
catch (Exception e){
log.error("rtkrcv websocket send msg error:", e);
}
}
}
}

View File

@ -0,0 +1,124 @@
package com.imdroid.beidou.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imdroid.beidou.controller.RtkrcvWebSocketServer;
import com.imdroid.secapi.client.RtkrcvClient;
import com.imdroid.secapi.dto.RtkProfile;
import com.imdroid.secapi.dto.RtkProfileMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class GroupRtkScheduler {
static final int MAX_RUN = 6;
@Autowired
RtkrcvClient rtkrcvClient;
@Autowired
RtkProfileMapper rtkProfileMapper;
static class GroupState {
Deque<String> queue = new ArrayDeque<>();
Set<String> running = new HashSet<>();
Set<String> done = new HashSet<>();
boolean active = false;
boolean clearLog = false;
}
private final Map<Integer, GroupState> groups = new ConcurrentHashMap<>();
public synchronized void startGroup(Integer groupId, boolean clearLog){
GroupState st = groups.computeIfAbsent(groupId, k -> new GroupState());
st.queue.clear();
st.running.clear();
st.done.clear();
st.active = true;
st.clearLog = clearLog;
QueryWrapper<RtkProfile> qw = new QueryWrapper<>();
qw.eq("rtkgroup_id", groupId);
List<RtkProfile> list = rtkProfileMapper.selectList(qw);
for(RtkProfile p:list){
st.queue.addLast(p.getDevice_id());
pushStatus(groupId, p.getDevice_id(), "QUEUED");
}
}
public synchronized void stopGroup(Integer groupId){
GroupState st = groups.get(groupId);
if(st==null) return;
for(String dev: new ArrayList<>(st.running)){
try{ rtkrcvClient.stop(dev);}catch(Exception ignored){}
pushStatus(groupId, dev, "DONE");
}
st.running.clear();
st.queue.clear();
st.active = false;
}
public synchronized List<Map<String,Object>> getStatus(Integer groupId){
GroupState st = groups.get(groupId);
List<Map<String,Object>> list = new ArrayList<>();
if(st==null) return list;
for(String d: st.running){ list.add(row(d, "RUNNING")); }
for(String d: st.queue){ list.add(row(d, "QUEUED")); }
for(String d: st.done){ list.add(row(d, "DONE")); }
return list;
}
private Map<String,Object> row(String dev, String status){
Map<String,Object> m = new HashMap<>();
m.put("device_id", dev);
m.put("status", status);
return m;
}
public synchronized void onDeviceAutoStop(String deviceId){
Integer gid = getGroupId(deviceId);
if(gid==null) return;
GroupState st = groups.get(gid);
if(st==null) return;
st.running.remove(deviceId);
st.done.add(deviceId);
pushStatus(gid, deviceId, "DONE");
}
private Integer getGroupId(String deviceId){
QueryWrapper<RtkProfile> qw = new QueryWrapper<>();
qw.eq("device_id", deviceId);
RtkProfile p = rtkProfileMapper.selectOne(qw);
return p!=null? p.getRtkgroup_id(): null;
}
@Scheduled(fixedRate = 1000)
public void tick(){
for(Map.Entry<Integer, GroupState> e: groups.entrySet()){
Integer gid = e.getKey();
GroupState st = e.getValue();
if(!st.active) continue;
while(st.running.size() < MAX_RUN && !st.queue.isEmpty()){
String dev = st.queue.pollFirst();
boolean started = false;
try{ rtkrcvClient.start(dev, st.clearLog); started = true; }catch(Exception ignored){}
if(started){
st.running.add(dev);
pushStatus(gid, dev, "RUNNING");
} else {
pushStatus(gid, dev, "FAILED");
}
}
}
}
private void pushStatus(Integer gid, String dev, String status){
com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject();
obj.put("type","rtk_group");
obj.put("groupId", gid);
obj.put("deviceId", dev);
obj.put("status", status);
RtkrcvWebSocketServer.sendMessageToAll(obj.toJSONString());
}
}

View File

@ -0,0 +1,58 @@
package com.imdroid.beidou.service;
import com.imdroid.secapi.client.RtkrcvClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class RtkMonitorService {
private final Map<String, Integer> consecFix = new ConcurrentHashMap<>();
private final Map<String, Boolean> running = new ConcurrentHashMap<>();
@Autowired
RtkrcvClient rtkrcvClient;
@Autowired
GroupRtkScheduler groupRtkScheduler;
public void onStart(String deviceId){
running.put(deviceId, true);
consecFix.put(deviceId, 0);
}
public void onStop(String deviceId){
running.remove(deviceId);
consecFix.remove(deviceId);
}
public void onSolution(String deviceId, int q){
if(Boolean.TRUE.equals(running.get(deviceId))){
if(q == 1){
consecFix.merge(deviceId, 1, Integer::sum);
} else {
consecFix.put(deviceId, 0);
}
}
}
@Scheduled(fixedRate = 30000)
public void check(){
for(Map.Entry<String, Integer> e : consecFix.entrySet()){
String dev = e.getKey();
Integer cnt = e.getValue();
if(Boolean.TRUE.equals(running.get(dev)) && cnt != null && cnt >= 40){
try { rtkrcvClient.stop(dev); } catch (Exception ignore) {}
running.remove(dev);
try {
com.alibaba.fastjson.JSONObject ctrl = new com.alibaba.fastjson.JSONObject();
ctrl.put("type","rtk_ctrl");
ctrl.put("action","auto_stop");
ctrl.put("deviceId", dev);
com.imdroid.beidou.controller.RtkrcvWebSocketServer.sendMessageToAll(ctrl.toJSONString());
} catch (Exception ignore) {}
try { groupRtkScheduler.onDeviceAutoStop(dev);} catch (Exception ignore) {}
}
}
}
}

View File

@ -2,10 +2,10 @@
#spring.datasource.url=jdbc:mysql://192.168.101.54:3306/beidou?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true
#spring.datasource.username=beidou
#spring.datasource.password=Passw0rd#123!
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/beidou?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true
#spring.datasource.url=jdbc:mysql://139.9.51.237:3306/beidou?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true
spring.datasource.username=admin
spring.datasource.password=DBMgr_2022
spring.datasource.url=jdbc:mysql://8.134.185.53:3306/beidou?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true
#spring.datasource.url=jdbc:mysql://139.9.51.237:3306/beidou?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.username=remote
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MYSQL5InnoDBDialect
@ -51,4 +51,7 @@ warning.log.directory=./log
sim.url = http://120.78.169.220:8089
sim.username = gzyzdz
sim.key = 632629d1269a202c9d49a574623e4e4c
sim.key = 632629d1269a202c9d49a574623e4e4c
version.path = "./"
ver_server.connect.cmd = ""

View File

@ -0,0 +1,162 @@
# RTKNAVI options (2024/12/09 02:35:36, v.demo5 b34k)
pos1-posmode =static # (0:single,1:dgps,2:kinematic,3:static,4:static-start,5:movingbase,6:fixed,7:ppp-kine,8:ppp-static,9:ppp-fixed)
pos1-frequency =l1+l2+l5 # (1:l1,2:l1+l2,3:l1+l2+l5,4:l1+l2+l5+l6)
pos1-soltype =forward # (0:forward,1:backward,2:combined,3:combined-nophasereset)
pos1-elmask =10 # (deg)
pos1-snrmask_r =on # (0:off,1:on)
pos1-snrmask_b =on # (0:off,1:on)
pos1-snrmask_L1 =0,0,0,0,0,0,0,0,0
pos1-snrmask_L2 =0,0,0,0,0,0,0,0,0
pos1-snrmask_L5 =0,0,0,0,0,0,0,0,0
pos1-dynamics =off # (0:off,1:on)
pos1-tidecorr =off # (0:off,1:on,2:otl)
pos1-ionoopt =brdc # (0:off,1:brdc,2:sbas,3:dual-freq,4:est-stec,5:ionex-tec,6:qzs-brdc)
pos1-tropopt =saas # (0:off,1:saas,2:sbas,3:est-ztd,4:est-ztdgrad)
pos1-sateph =brdc+sbas # (0:brdc,1:precise,2:brdc+sbas,3:brdc+ssrapc,4:brdc+ssrcom)
pos1-posopt1 =off # (0:off,1:on)
pos1-posopt2 =off # (0:off,1:on)
pos1-posopt3 =off # (0:off,1:on,2:precise)
pos1-posopt4 =off # (0:off,1:on)
pos1-posopt5 =off # (0:off,1:on)
pos1-posopt6 =off # (0:off,1:on)
pos1-exclsats = # (prn ...)
pos1-navsys =45 # (1:gps+2:sbas+4:glo+8:gal+16:qzs+32:bds+64:navic)
pos2-armode =continuous # (0:off,1:continuous,2:instantaneous,3:fix-and-hold)
pos2-gloarmode =fix-and-hold # (0:off,1:on,2:autocal,3:fix-and-hold)
pos2-bdsarmode =on # (0:off,1:on)
pos2-arfilter =on # (0:off,1:on)
pos2-arthres =3
pos2-arthresmin =2
pos2-arthresmax =2
pos2-arthres1 =0.1
pos2-arthres2 =0
pos2-arthres3 =1e-09
pos2-arthres4 =1e-05
pos2-varholdamb =0.1 # (cyc^2)
pos2-gainholdamb =0.01
pos2-arlockcnt =5
pos2-minfixsats =4
pos2-minholdsats =5
pos2-mindropsats =10
pos2-arelmask =10 # (deg)
pos2-arminfix =20
pos2-armaxiter =1
pos2-elmaskhold =10 # (deg)
pos2-aroutcnt =20
pos2-maxage =30 # (s)
pos2-syncsol =off # (0:off,1:on)
pos2-slipthres =0.05 # (m)
pos2-dopthres =0 # (m)
pos2-rejionno =5 # (m)
pos2-rejcode =30 # (m)
pos2-niter =1
pos2-baselen =0 # (m)
pos2-basesig =0 # (m)
out-solformat =llh # (0:llh,1:xyz,2:enu,3:nmea)
out-outhead =on # (0:off,1:on)
out-outopt =on # (0:off,1:on)
out-outvel =off # (0:off,1:on)
out-timesys =gpst # (0:gpst,1:utc,2:jst)
out-timeform =hms # (0:tow,1:hms)
out-timendec =3
out-degform =deg # (0:deg,1:dms)
out-fieldsep =
out-outsingle =off # (0:off,1:on)
out-maxsolstd =0 # (m)
out-height =1 # (0:ellipsoidal,1:geodetic)
out-geoid =internal # (0:internal,1:egm96,2:egm08_2.5,3:egm08_1,4:gsi2000)
out-solstatic =all # (0:all,1:single)
out-nmeaintv1 =0 # (s)
out-nmeaintv2 =0 # (s)
out-outstat =residual # (0:off,1:state,2:residual)
stats-eratio1 =300
stats-eratio2 =300
stats-eratio5 =300
stats-errphase =0.003 # (m)
stats-errphaseel =0.003 # (m)
stats-errphasebl =0 # (m/10km)
stats-errdoppler =1 # (Hz)
stats-snrmax =52 # (dB.Hz)
stats-errsnr =0 # (m)
stats-errrcv =0 # ( )
stats-stdbias =30 # (m)
stats-stdiono =0.03 # (m)
stats-stdtrop =0.3 # (m)
stats-prnaccelh =3 # (m/s^2)
stats-prnaccelv =1 # (m/s^2)
stats-prnbias =0.0001 # (m)
stats-prniono =0.001 # (m)
stats-prntrop =0.0001 # (m)
stats-prnpos =0 # (m)
stats-clkstab =5e-12 # (s/s)
ant1-postype =llh # (0:llh,1:xyz,2:single,3:posfile,4:rinexhead,5:rtcm,6:raw)
ant1-pos1 =90 # (deg|m)
ant1-pos2 =0 # (deg|m)
ant1-pos3 =-6335367.6285 # (m|m)
ant1-anttype =
ant1-antdele =0 # (m)
ant1-antdeln =0 # (m)
ant1-antdelu =0 # (m)
ant2-postype =rtcm # (0:llh,1:xyz,2:single,3:posfile,4:rinexhead,5:rtcm,6:raw)
ant2-pos1 =0 # (deg|m)
ant2-pos2 =0 # (deg|m)
ant2-pos3 =0 # (m|m)
ant2-anttype =
ant2-antdele =0 # (m)
ant2-antdeln =0 # (m)
ant2-antdelu =0 # (m)
ant2-maxaveep =1
ant2-initrst =on # (0:off,1:on)
misc-timeinterp =off # (0:off,1:on)
misc-sbasatsel =0 # (0:all)
misc-rnxopt1 =
misc-rnxopt2 =
misc-pppopt =
file-satantfile =
file-rcvantfile =
file-staposfile =
file-geoidfile =
file-ionofile =
file-dcbfile =
file-eopfile =
file-blqfile =
file-tempdir =./RTKNAVI
file-geexefile =
file-solstatfile =
file-tracefile =
#
inpstr1-type =ntripcli # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripcli,7:ftp,8:http)
inpstr2-type =ntripcli # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripcli,7:ftp,8:http)
inpstr3-type =ntripcli # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripcli,7:ftp,8:http)
inpstr1-path =beidou:29832611@8.134.185.53:8001/6539837
inpstr2-path =ytcors14847:fyx25943@gnss.ytcors.cn:8003/RTCM33GRCEJpro
inpstr3-path =Ming:@Zhang12345@ntrip.data.gnss.ga.gov.au:2101/BCEP00BKG0
inpstr1-format =rtcm3 # (0:rtcm2,1:rtcm3,2:oem4,4:ubx,5:swift,6:hemis,7:skytraq,8:javad,9:nvs,10:binex,11:rt17,12:sbf,15:sp3)
inpstr2-format =rtcm3 # (0:rtcm2,1:rtcm3,2:oem4,4:ubx,5:swift,6:hemis,7:skytraq,8:javad,9:nvs,10:binex,11:rt17,12:sbf,15:sp3)
inpstr3-format =rtcm3 # (0:rtcm2,1:rtcm3,2:oem4,4:ubx,5:swift,6:hemis,7:skytraq,8:javad,9:nvs,10:binex,11:rt17,12:sbf,15:sp3)
inpstr2-nmeareq =single # (0:off,1:latlon,2:single)
inpstr2-nmealat = # (deg)
inpstr2-nmealon = # (deg)
outstr1-type =off # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
outstr2-type =tcpcli # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
outstr1-path =127.0.0.1:60000
outstr2-path =127.0.0.1:24001
outstr1-format =xyz # (0:llh,1:xyz,2:enu,3:nmea:4:stat)
outstr2-format =llh # (0:llh,1:xyz,2:enu,3:nmea:4:stat)
logstr1-type =off # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
logstr2-type =off # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
logstr3-type =off # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
logstr1-path =
logstr2-path =
logstr3-path =
misc-svrcycle =10 # (ms)
misc-timeout =10000 # (ms)
misc-reconnect =10000 # (ms)
misc-nmeacycle =5000 # (ms)
misc-buffsize =32768 # (bytes)
misc-navmsgsel =all # (0:all,1:rover,2:base,3:corr)
misc-proxyaddr =
misc-fswapmargin =30 # (s)
console-passwd =

View File

@ -82,6 +82,12 @@
"href": "page/cmd_line",
"icon": "fa fa-minus",
"target": "_self"
},
{
"title": "定位管理",
"href": "page/rtkrcv",
"icon": "fa fa-minus",
"target": "_self"
}
]
},

View File

@ -1,9 +1,9 @@
var DeviceMarkers = (function() {
'use strict';
var greenFeatures = [];
var orangeFeatures = [];
var redFeatures = [];
var invalidDataFeatures = [];
var offlineIn24hFeatures = [];
var offlineFeatures = [];
var allFeatures = [];
var myLocationFeature = null;
var myLocationInterval = null;
@ -43,9 +43,9 @@ var DeviceMarkers = (function() {
var scale = isHovered ? 0.85 : 0.7;
// 根据告警级别选择图标
if (deviceInfo.warning == 2) {
if (deviceInfo.warning == 2||deviceInfo.warning == 3) {
iconSrc = '../images/loc1_red.png';
} else if (deviceInfo.warning == 1) {
} else if (deviceInfo.warning == 4) {
iconSrc = '../images/loc1_orange.png';
} else {
iconSrc = '../images/loc1_green.png';
@ -85,9 +85,9 @@ var DeviceMarkers = (function() {
var savedMyLocationFeature = myLocationFeature;
vectorSource.clear();
greenFeatures = [];
orangeFeatures = [];
redFeatures = [];
invalidDataFeatures = [];
offlineIn24hFeatures = [];
offlineFeatures = [];
allFeatures = [];
for (var i = 0; i < deviceList.length; i++) {
@ -106,11 +106,12 @@ var DeviceMarkers = (function() {
// 按告警级别分类
if (device.warning == 2) {
redFeatures.push(feature);
} else if (device.warning == 1) {
orangeFeatures.push(feature);
} else {
greenFeatures.push(feature);
offlineFeatures.push(feature);
} else if (device.warning == 3) {
offlineFeatures.push(feature);
offlineIn24hFeatures.push(feature);
} else if (device.warning == 4) {
invalidDataFeatures.push(feature);
}
allFeatures.push(feature);
@ -149,9 +150,10 @@ var DeviceMarkers = (function() {
}
}
function showWarning1Devices() {
function showWarning4Devices() {
console.log("showWarning4Devices");
if (!vectorSource) return;
console.log("showWarning4Devices:",invalidDataFeatures.length);
var savedMyLocationFeature = myLocationFeature;
vectorSource.clear();
@ -159,18 +161,18 @@ var DeviceMarkers = (function() {
vectorSource.addFeature(savedMyLocationFeature);
}
for (var i = 0; i < allFeatures.length; i++) {
vectorSource.addFeature(allFeatures[i]);
for (var i = 0; i < invalidDataFeatures.length; i++) {
vectorSource.addFeature(invalidDataFeatures[i]);
}
hideGreenFeatures();
hideRedFeatures();
//hideofflineIn24hFeatures();
//hideofflineFeatures();
}
function showWarning2Devices() {
if (!vectorSource) return;
console.log("showWarning2Devices:",offlineFeatures.length);
var savedMyLocationFeature = myLocationFeature;
vectorSource.clear();
@ -178,32 +180,33 @@ var DeviceMarkers = (function() {
vectorSource.addFeature(savedMyLocationFeature);
}
for (var i = 0; i < allFeatures.length; i++) {
vectorSource.addFeature(allFeatures[i]);
for (var i = 0; i < offlineFeatures.length; i++) {
vectorSource.addFeature(offlineFeatures[i]);
}
hideGreenFeatures();
hideOrangeFeatures();
}
function hideGreenFeatures() {
for (var i = 0; i < greenFeatures.length; i++) {
vectorSource.removeFeature(greenFeatures[i]);
}
//hideinvalidDataFeatures();
//hideofflineIn24hFeatures();
}
function hideOrangeFeatures() {
for (var i = 0; i < orangeFeatures.length; i++) {
vectorSource.removeFeature(orangeFeatures[i]);
function showWarning3Devices() {
console.log("showWarning3Devices");
if (!vectorSource) return;
console.log("showWarning3Devices:",offlineIn24hFeatures.length);
var savedMyLocationFeature = myLocationFeature;
vectorSource.clear();
if (savedMyLocationFeature) {
vectorSource.addFeature(savedMyLocationFeature);
}
}
function hideRedFeatures() {
for (var i = 0; i < redFeatures.length; i++) {
vectorSource.removeFeature(redFeatures[i]);
for (var i = 0; i < offlineIn24hFeatures.length; i++) {
vectorSource.addFeature(offlineIn24hFeatures[i]);
}
//hideinvalidDataFeatures();
//hideofflineFeatures();
}
function findDeviceById(deviceId) {
// console.log('搜索设备:', deviceId, '(类型:', typeof deviceId, ')');
// console.log('当前设备总数:', allFeatures.length);
@ -375,9 +378,9 @@ var DeviceMarkers = (function() {
function getDeviceStats() {
return {
total: allFeatures.length,
green: greenFeatures.length,
orange: orangeFeatures.length,
red: redFeatures.length
green: invalidDataFeatures.length,
orange: offlineIn24hFeatures.length,
red: offlineFeatures.length
};
}
@ -386,11 +389,9 @@ var DeviceMarkers = (function() {
createDeviceStyle: createDeviceStyle,
addDeviceMarkers: addDeviceMarkers,
showAllDevices: showAllDevices,
showWarning1Devices: showWarning1Devices,
showWarning2Devices: showWarning2Devices,
hideGreenFeatures: hideGreenFeatures,
hideOrangeFeatures: hideOrangeFeatures,
hideRedFeatures: hideRedFeatures,
showWarning3Devices: showWarning3Devices,
showWarning4Devices: showWarning4Devices,
findDeviceById: findDeviceById,
locateDevice: locateDevice,
getMyLocation: getMyLocation,
@ -402,9 +403,9 @@ var DeviceMarkers = (function() {
getDeviceStats: getDeviceStats,
getAllFeatures: function() { return allFeatures; },
getGreenFeatures: function() { return greenFeatures; },
getOrangeFeatures: function() { return orangeFeatures; },
getRedFeatures: function() { return redFeatures; },
getinvalidDataFeatures: function() { return invalidDataFeatures; },
getofflineIn24hFeatures: function() { return offlineIn24hFeatures; },
getofflineFeatures: function() { return offlineFeatures; },
getMyLocationFeature: function() { return myLocationFeature; }
};
})();

View File

@ -292,12 +292,16 @@ var MapCore = (function() {
function onWarningFilterChange() {
var filterValue = document.getElementById('warningFilter').value;
console.log("onWarningFilterChange: ",filterValue);
switch(filterValue) {
case 'all':
SearchFilter.showAllDevices();
break;
case 'warning1':
SearchFilter.showWarning1Devices();
case 'warning4':
SearchFilter.showWarning4Devices();
break;
case 'warning3':
SearchFilter.showWarning3Devices();
break;
case 'warning2':
SearchFilter.showWarning2Devices();

View File

@ -187,10 +187,13 @@ var SearchFilter = (function() {
showAllDevices();
break;
case 2:
showWarning1Devices();
showWarning2Devices();
break;
case 3:
showWarning2Devices();
showWarning3Devices();
break;
case 4:
showWarning4Devices();
break;
}
}
@ -203,20 +206,30 @@ var SearchFilter = (function() {
updateFilterSelect('all');
}
function showWarning1Devices() {
currentSearchedDevice = null;
clearSearchInput();
DeviceMarkers.showWarning1Devices();
markerState = 2;
updateFilterSelect('warning1');
}
function showWarning2Devices() {
console.log("SearchFilter.showWarning2Devices");
currentSearchedDevice = null;
clearSearchInput();
DeviceMarkers.showWarning2Devices();
markerState = 3;
markerState = 2;
updateFilterSelect('warning2');
}
function showWarning3Devices() {
console.log("SearchFilter.showWarning3Devices");
currentSearchedDevice = null;
clearSearchInput();
DeviceMarkers.showWarning3Devices();
markerState = 3;
updateFilterSelect('warning3');
}
function showWarning4Devices() {
console.log("SearchFilter.showWarning4Devices");
currentSearchedDevice = null;
clearSearchInput();
DeviceMarkers.showWarning4Devices();
markerState = 4;
updateFilterSelect('warning4');
}
function clearSearchInput() {
var searchInput = document.getElementById('deviceSearchNew');
if (searchInput) {
@ -233,13 +246,15 @@ var SearchFilter = (function() {
function filterDevicesByStatus(statusType) {
switch(statusType) {
case 'warning1':
showWarning1Devices();
break;
case 'warning2':
showWarning2Devices();
break;
case 'offline':
case 'warning3':
showWarning3Devices();
break;
case 'warning4':
showWarning4Devices();
break;
case 'no_fwd':
case 'nofixed':
case 'nogga':
@ -311,8 +326,9 @@ var SearchFilter = (function() {
searchDevice: searchDevice,
clearSearch: clearSearch,
showAllDevices: showAllDevices,
showWarning1Devices: showWarning1Devices,
showWarning2Devices: showWarning2Devices,
showWarning3Devices: showWarning3Devices,
showWarning4Devices: showWarning4Devices,
filterDevicesByStatus: filterDevicesByStatus,
queryDevices: queryDevices,
locateDeviceOnMap: locateDeviceOnMap,

View File

@ -836,6 +836,11 @@
<span>掉线数</span>
<span class="stat-number" th:text="${deviceOfflineNum}">22</span>
</div>
<div class="stat-item" onclick="queryDevices('offline_in24h')" th:if="${deviceOfflineNumIn24h > 0}">
<span class="stat-dot dot-gray"></span>
<span>24小时掉线数</span>
<span class="stat-number" th:text="${deviceOfflineNumIn24h}">22</span>
</div>
<div class="stat-item" onclick="queryDevices('no_fwd')" th:if="${deviceNoFwdNum > 0}">
<span class="stat-dot dot-orange"></span>
<span>未推送数</span>
@ -846,16 +851,6 @@
<span>长期无效解</span>
<span class="stat-number" th:text="${noFix}">10</span>
</div>
<div class="stat-item" onclick="queryDevices('warning2')" th:if="${warning2Num > 0}">
<span class="stat-dot dot-red"></span>
<span>严重告警</span>
<span class="stat-number" th:text="${warning2Num}">40</span>
</div>
<div class="stat-item" onclick="queryDevices('warning1')" th:if="${warning1Num > 0}">
<span class="stat-dot dot-orange"></span>
<span>一般告警</span>
<span class="stat-number" th:text="${warning1Num}">79</span>
</div>
<div class="stat-item" onclick="queryDevices('nogga')" th:if="${noGGA > 0}">
<span class="stat-dot dot-gray"></span>
<span>无GGA告警</span>
@ -925,8 +920,9 @@
<div class="toolbar-item">
<select id="warningFilter" class="toolbar-select" onchange="onWarningFilterChange()">
<option value="all">全部设备</option>
<option value="warning1">一般告警</option>
<option value="warning2" selected>严重告警</option>
<option value="warning2">离线</option>
<option value="warning3">24小时内离线</option>
<option value="warning4">长期无效解</option>
</select>
</div>

View File

@ -24,6 +24,15 @@
<input type="text" name="deviceid" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label required">产品型号</label>
<div class="layui-input-inline">
<select name="model" id="model" lay-search="">
<option value="0">G505</option>
<option value="1">G510</option>
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">关联基站号</label>
<div class="layui-input-inline">
@ -36,6 +45,12 @@
<input type="text" name="project_id" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">桩号</label>
<div class="layui-input-inline">
<input type="text" name="sector" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline" th:if="${tenant_id==0}">
<label class="layui-form-label">所属部门</label>
<div class="layui-input-inline">

View File

@ -65,6 +65,12 @@
<input type="text" name="rssi" id="rssi" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">经纬度</label>
<div class="layui-input-inline">
<input type="text" name="loc" id="loc" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卫星</label>
<div class="layui-input-inline">
@ -100,7 +106,6 @@
url:"/device_summary/query",
data: data.field,
success: function (result) {
console.log(result);
if(result.code == 0){
layer.alert(result.msg);
}
@ -119,6 +124,7 @@
$('#rxBytes').val(result.rxBytes)
$('#deviceType').val(result.deviceType)
$('#rssi').val(result.rssi)
$('#loc').val(result.loc)
form.render();
}
},

View File

@ -92,6 +92,7 @@
table = layui.table;
var cfg_cols = [
{field: 'deviceid', title: '设备号', sort: true},
{field: 'tenantname', title: '部门', sort: true},
{field: 'devicetype', title: '设备类型',templet: '#typeTrans'},
{field: 'updatetime', title: '更新时间', templet: "<div>{{layui.util.toDateString(d.updatetime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
{field: 'state', title: '状态',templet: '#stateTrans'},

View File

@ -0,0 +1,523 @@
<!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>
.layui-tab-content{padding: 1.2em;}
.transfer-wrap{display:flex;justify-content:center;align-items:flex-start;padding:1em 0}
.transfer-inner{min-width:720px}
#rtk-transfer .layui-transfer{display:flex;justify-content:center}
#rtk-transfer .layui-transfer .layui-transfer-box{width:50% !important;height:360px !important}
/* RTK panel ~80vh */
#rtk-panel{height:80vh;}
#rtk-info{max-height:calc(80vh - 120px);} /* leave room for header/badges */
</style>
</head>
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<div class="layui-tab layui-tab-card" lay-filter="rtk-tab">
<ul class="layui-tab-title">
<li class="layui-this">分组管理</li>
<li>设备管理</li>
<li>单设备定位</li>
<li>组设备定位</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<div class="layui-row">
<div class="layui-col-md12">
<div class="btn-bar">
<button class="layui-btn" id="btn-group-add">新增分组</button>
<button class="layui-btn layui-btn-normal" id="btn-group-edit">编辑分组</button>
<button class="layui-btn layui-btn-danger" id="btn-group-del">删除分组</button>
<button class="layui-btn layui-btn-warm" id="btn-transfer-save">保存设备分配</button>
</div>
</div>
</div>
<div style="margin-top:10px;">
<table class="layui-hide" id="table-group" lay-filter="table-group-filter"></table>
</div>
<div class="transfer-wrap">
<div class="transfer-inner">
<div id="rtk-transfer"></div>
</div>
</div>
</div>
<div class="layui-tab-item">
<div class="layui-row" style="margin:10px 0;">
<div class="layui-col-md12">
<div class="layui-form layui-form-pane">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">分组</label>
<div class="layui-input-inline">
<select id="sel-group" lay-search=""><option value="">全部</option></select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">设备号</label>
<div class="layui-input-inline">
<input type="text" id="q-deviceid" placeholder="设备号" autocomplete="off" class="layui-input" />
</div>
</div>
<div class="layui-inline">
<button class="layui-btn" id="btn-device-search">搜索</button>
</div>
</div>
</div>
</div>
</div>
<div style="margin-top:10px;">
<table class="layui-hide" id="table-device" lay-filter="table-device-filter"></table>
</div>
</div>
<div class="layui-tab-item">
<div class="layui-row">
<div class="layui-col-md12">
<form class="layui-form layui-form-pane" action="" id="rtk-form">
<div class="layui-form-item">
<label class="layui-form-label">分组</label>
<div class="layui-input-block">
<select id="rtk-group" lay-search="" lay-filter="rtk-group"></select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">设备</label>
<div class="layui-input-block">
<select id="rtk-device" lay-search="" lay-filter="rtk-device"></select>
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn" id="btn-rtk-start" type="button">开始定位</button>
</div>
</form>
</div>
</div>
<div class="layui-row" style="margin-top: 1em;">
<div class="layui-col-md12">
<div id="rtk-panel" style="border: 1px solid #e6e6e6;border-radius: 2px;padding: 1em;">
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px;">
<div class="layui-text">RTKRCV</div>
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
<span id="rtk-sol-badge" class="layui-badge layui-bg-gray">--</span>
<span id="rtk-sat-badge" class="layui-badge layui-bg-gray">--</span>
<span id="rtk-llh" class="layui-badge-rim">LLH: --</span>
<span id="rtk-gngga" class="layui-badge-rim">GNGGA: --</span>
</div>
</div>
<hr>
<pre id="rtk-info" style="white-space: pre-wrap;word-break: break-all;overflow-y:auto;"></pre>
</div>
</div>
</div>
</div>
<div class="layui-tab-item">
<div class="layui-row">
<div class="layui-col-md12">
<form class="layui-form layui-form-pane" action="" id="rtk-group-form">
<div class="layui-form-item">
<label class="layui-form-label">分组</label>
<div class="layui-input-block">
<select id="gr-group" lay-search="" lay-filter="gr-group"></select>
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn" id="btn-gr-start" type="button">开始组定位</button>
</div>
</form>
</div>
</div>
<div class="layui-row" style="margin-top: 1em;">
<div class="layui-col-md12">
<table class="layui-hide" id="group-table" lay-filter="group-table-filter"></table>
</div>
</div>
<div class="layui-row" style="margin-top: 1em;">
<div class="layui-col-md12">
<div id="group-panel" style="border:1px solid #e6e6e6;border-radius:2px;padding:1em;height:60vh;">
<div class="layui-text">组定位消息</div>
<hr>
<pre id="group-info" style="white-space: pre-wrap;word-break: break-all;overflow-y:auto;height:calc(60vh - 80px);"></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
<script>
layui.use(['form','table','element','layer','transfer'], function(){
var $ = layui.$,
form = layui.form,
table = layui.table,
element = layui.element,
layer = layui.layer,
transfer = layui.transfer;
form.render();
var selectedGroup = null;
var transferIns = null;
var groupTableIns = table.render({
elem: '#table-group',
url: '/rtk/group/list',
cols: [ [
{type:'radio', width:50},
{field:'id', title:'ID', width:90},
{field:'name', title:'分组名称', width:180},
{field:'inpstr2_path', title:'inpstr2-path'},
{field:'inpstr3_path', title:'inpstr3-path'}
] ],
limits: [10,20,50,100],
limit: 10,
page: true
});
table.on('radio(table-group-filter)', function(obj){
selectedGroup = obj.data;
loadTransfer();
});
$('#btn-group-add').on('click', function(){
var index = layer.open({
title: '新增分组',
type: 2,
shade: 0.2,
maxmin:true,
shadeClose: true,
area: ['600px', '420px'],
content: '../page/table/rtk_group_edit'
});
});
$('#btn-group-edit').on('click', function(){
if(!selectedGroup){ layer.msg('请先选择一行'); return; }
var index = layer.open({
title: '编辑分组',
type: 2,
shade: 0.2,
maxmin:true,
shadeClose: true,
area: ['600px', '420px'],
content: '../page/table/rtk_group_edit',
success: function(layero, idx){
var iframeWin = window[layero.find('iframe')[0]['name']];
if(iframeWin && iframeWin.setParams){
iframeWin.setParams(selectedGroup);
}
}
});
});
$('#btn-group-del').on('click', function(){
if(!selectedGroup){ layer.msg('请先选择一行'); return; }
layer.confirm('确认删除该分组?', function(i){
$.post('/rtk/group/delete', {del_id: selectedGroup.id}, function(res){
if(res === 'ok' || res.code === 0){
onRtkGroupUpdated();
layer.close(i);
} else {
layer.alert(res.msg || '删除失败');
}
}).fail(function(){ layer.alert('请求失败'); });
});
});
window.onRtkGroupUpdated = function(){
selectedGroup = null;
groupTableIns.reload({page:{curr:1}});
}
function loadTransfer(){
if(!selectedGroup) return;
$.get('/rtk/transfer/options', {group_id: selectedGroup.id}, function(res){
if(res.code !== 0){ layer.alert(res.msg || '加载失败'); return; }
var opt = res.data;
if(transferIns){ $("#rtk-transfer").html(''); }
transferIns = transfer.render({
elem: '#rtk-transfer',
title: ['待加入设备','组内设备'],
data: opt.data,
value: opt.value,
id: 'rtk-transfer-id',
showSearch: true,
parseData: function(item){ return {value: item.value, title: item.title}; }
});
// 覆盖渲染后的内联尺寸
var boxes = $('#rtk-transfer .layui-transfer .layui-transfer-box');
if(boxes && boxes.length){ boxes.css({width:'50%', height:'360px'}); }
}).fail(function(){ layer.alert('请求失败'); });
}
$('#btn-transfer-save').on('click', function(){
if(!selectedGroup){ layer.msg('请先选择分组'); return; }
var vals = transfer.getData('rtk-transfer-id').map(function(it){ return it.value; });
$.ajax({
type: 'POST',
url: '/rtk/profile/save_group_devices',
contentType: 'application/json;charset=UTF-8',
data: JSON.stringify({group_id: selectedGroup.id, device_ids: vals}),
success: function(result){
if(result.code === 0){ layer.msg('已保存'); }
else { layer.alert(result.msg || '保存失败'); }
},
error: function(){ layer.alert('请求失败'); }
});
});
function loadGroupSelect(){
$.get('/rtk/group/list', {page:1, limit:1000}, function(res){
if(res.code===0){
var sel = $('#sel-group');
sel.empty(); sel.append('<option value="">全部</option>');
res.data.forEach(function(g){ sel.append('<option value="'+g.id+'">'+g.name+'</option>'); });
form.render('select');
var gr = $('#gr-group');
gr.empty(); gr.append('<option value="">请选择</option>');
res.data.forEach(function(g){ gr.append('<option value="'+g.id+'">'+g.name+'</option>'); });
form.render('select');
}
});
}
loadGroupSelect();
var deviceTableIns = table.render({
elem: '#table-device',
url: '/rtk/profile/list',
id: 'rtk-profile-table',
cols: [ [
{type:'numbers', title:'序号', width:70},
{field:'id', title:'ID', width:80},
{field:'device_id', title:'设备号', width:140},
{field:'rtkgroup_id', title:'分组ID', width:100},
{field:'pos1_posmode', title:'pos1-posmode', width:130},
{field:'inpstr1_type', title:'inpstr1-type', width:120},
{field:'inpstr1_path', title:'inpstr1-path'},
{field:'outstr2_path', title:'outstr2-path'},
{field:'out_height', title:'out-height', width:120},
{title: '操作', toolbar: '#rtkProfileBar', fixed: 'right', width: 100}
] ],
limits: [10,20,50,100],
limit: 10,
page: true
});
$('#btn-device-search').on('click', function(){
var gid = $('#sel-group').val();
var did = $('#q-deviceid').val();
deviceTableIns.reload({where:{group_id: gid, device_id: did}, page:{curr:1}});
});
table.on('tool(table-device-filter)', function(obj){
var data = obj.data;
if(obj.event === 'edit'){
var index = layer.open({
title: '编辑设备配置',
type: 2,
shade: 0.2,
maxmin:true,
shadeClose: true,
area: ['700px', '520px'],
content: '../page/table/rtk_profile_edit',
success: function(layero){
var iframeWin = window[layero.find('iframe')[0]['name']];
if(iframeWin && iframeWin.setParams){ iframeWin.setParams(data); }
}
});
}
});
// toolbar template for device table
var rtkWs = null;
var rtkSelectedDevice = '';
var rtkRunning = false;
function connectRtkWs(){
if(rtkWs && rtkWs.readyState === WebSocket.OPEN) return;
var curPath = window.document.location.href;
var pathName = window.document.location.pathname;
var pos = curPath.indexOf(pathName);
var basePath = curPath.substring(0, pos);
var wsUrl = (basePath+"/websocket/rtkrcv").replace("http","ws");
rtkWs = new WebSocket(wsUrl);
rtkWs.onopen = function(){ };
rtkWs.onerror = function(){ layer.msg('RTK WebSocket 连接失败'); };
rtkWs.onclose = function(){ };
rtkWs.onmessage = function (event) {
var msg = event.data || '';
var info = $('#rtk-info');
try {
var obj = JSON.parse(msg);
if(obj && obj.type==='rtk'){
if(rtkSelectedDevice && obj.deviceId !== rtkSelectedDevice) return;
var sol = (obj.q===1)?'固定解':((obj.q===2)?'浮点解':'--');
var solCls = (obj.q===1)?'layui-bg-green':((obj.q===2)?'layui-bg-orange':'layui-bg-gray');
$('#rtk-sol-badge').attr('class','layui-badge ' + solCls).text(sol);
var ns = (obj.ns!=null? obj.ns : '--');
$('#rtk-sat-badge').attr('class','layui-badge layui-bg-blue').text('卫星数: '+ns);
var llh = (obj.lat && obj.lon && obj.h!=null)? (obj.lat.toFixed(9)+' '+obj.lon.toFixed(9)+' '+obj.h) : '--';
$('#rtk-llh').text('LLH: ' + llh);
info.text(info.text() + (obj.raw || msg) + "\n");
} else if(obj && obj.type==='rtk_gngga'){
if(rtkSelectedDevice && obj.deviceId !== rtkSelectedDevice) return;
var gtxt = 'lat='+ (obj.lat!=null?obj.lat:0) + ', lon='+(obj.lon!=null?obj.lon:0)+', alt='+(obj.alt!=null?obj.alt:0)+', geo='+(obj.geo!=null?obj.geo:0);
$('#rtk-gngga').text('GNGGA: ' + gtxt);
} else if(obj && obj.type==='rtk_ctrl'){
if(obj.action==='auto_stop' && (!rtkSelectedDevice || obj.deviceId === rtkSelectedDevice)){
rtkRunning = false; updateToggleBtn();
layer.msg('已自动停止: 连续固定解达阈值');
}
if(groupSelectedId){
if(obj.deviceId){ groupStatus[obj.deviceId]='DONE'; renderGroupTable(); }
}
} else if(obj && obj.type==='rtk_group'){
if(groupSelectedId && obj.groupId==groupSelectedId){
groupStatus[obj.deviceId]=obj.status; renderGroupTable();
}
} else {
info.text(info.text() + msg + "\n");
}
} catch(e){
var parts = msg.trim().split(/\s+/);
var ok = parts.length >= 3 && parts[0] === 'RTK';
if(ok){
var did = parts[1];
if(rtkSelectedDevice && did !== rtkSelectedDevice) return;
}
info.text(info.text() + msg + "\n");
}
var el = document.getElementById('rtk-info');
el.scrollTop = el.scrollHeight;
if(groupSelectedId){
try{ var o=JSON.parse(msg); if(o && o.type==='rtk' && groupDevices.has(o.deviceId)){ appendGroupLog(o.raw || msg); } }catch(ex){ var pp=msg.trim().split(/\s+/); if(pp[0]==='RTK' && groupDevices.has(pp[1])) appendGroupLog(msg); }
}
};
}
function closeRtkWs(){ if(rtkWs){ try{ rtkWs.close(); }catch(e){} rtkWs=null; } }
function loadRtkGroups(){
$.get('/rtk/group/list', {page:1,limit:1000}, function(res){
if(res.code===0){
var sel = $('#rtk-group'); sel.empty();
sel.append('<option value="">请选择</option>');
(res.data||[]).forEach(function(g){ sel.append('<option value="'+g.id+'">'+g.name+'</option>'); });
form.render('select');
}
});
}
function loadRtkDevices(gid){
var devSel = $('#rtk-device'); devSel.empty();
devSel.append('<option value="">请选择</option>');
if(!gid){ form.render('select'); return; }
$.get('/rtk/profile/list', {page:1,limit:1000, group_id: gid}, function(res){
if(res.code===0){
(res.data||[]).forEach(function(p){ devSel.append('<option value="'+p.device_id+'">'+p.device_id+'</option>'); });
form.render('select');
}
});
}
loadRtkGroups();
form.on('select(rtk-group)', function(data){
loadRtkDevices(data.value);
});
form.on('select(rtk-device)', function(data){ rtkSelectedDevice = data.value; });
function updateToggleBtn(){
var btn = $('#btn-rtk-start');
if(rtkRunning){
btn.text('停止定位');
btn.addClass('layui-btn-danger');
} else {
btn.text('开始定位');
btn.removeClass('layui-btn-danger');
}
}
updateToggleBtn();
$('#btn-rtk-start').on('click', function(){
rtkSelectedDevice = $('#rtk-device').val();
if(!rtkSelectedDevice){ layer.msg('请选择设备'); return; }
if(!rtkRunning){
$.get('/rtk/log_exists', {device_id: rtkSelectedDevice}, function(exists){
function doStart(clear){
$.post('/rtk/start', {device_id: rtkSelectedDevice, clear_log: clear}, function(res){
if(res && res.code === 0){
if(res.data === 'ok' || res.data === 'all_zero'){
connectRtkWs(); rtkRunning = true; updateToggleBtn();
if(res.data === 'all_zero'){ layer.msg('GNGGA全零使用默认配置启动'); }
} else {
layer.msg(res.data||'已处理');
}
} else { layer.alert((res&&res.msg)||'启动失败'); }
}).fail(function(){ layer.alert('启动请求失败'); });
}
if(exists){
layer.confirm('检测到已有日志,是否清空后再开始?', {btn:['清空','追加','取消']}, function(idx){
layer.close(idx); doStart(true);
}, function(idx){
layer.close(idx); doStart(false);
});
} else {
doStart(false);
}
}).fail(function(){ layer.alert('检查日志失败'); });
} else {
$.post('/rtk/stop', {device_id: rtkSelectedDevice}, function(){
rtkRunning = false; updateToggleBtn();
closeRtkWs();
});
}
});
var grRunning = false; function updateGrBtn(){ var b=$('#btn-gr-start'); if(grRunning){b.text('停止组定位').addClass('layui-btn-danger');} else {b.text('开始组定位').removeClass('layui-btn-danger');} }
updateGrBtn();
var groupSelectedId = null; var groupDevices = new Set(); var groupStatus = {};
form.on('select(gr-group)', function(data){ groupSelectedId = data.value; reloadGroupStatus(); loadGroupDevices(); });
function loadGroupDevices(){ groupDevices.clear(); if(!groupSelectedId)return; $.get('/rtk/profile/list',{page:1,limit:1000, group_id: groupSelectedId}, function(res){ if(res.code===0){ (res.data||[]).forEach(function(p){ groupDevices.add(p.device_id);}); renderGroupTable(); } }); }
function renderGroupTable(){
table.render({
elem: '#group-table',
data: Array.from(groupDevices).map(function(d){ return {device_id:d, status: (groupStatus[d]||'--')}; }),
cols: [ [
{field:'device_id', title:'设备号'},
{field:'status', title:'状态', templet: function(d){
var s=d.status;
var m={'RUNNING':'layui-bg-green','QUEUED':'','DONE':'layui-bg-blue','FAILED':'layui-bg-red'};
var cls=m[s]||'';
return '<span class="layui-badge '+cls+'">'+(s||'--')+'</span>';
}}
] ],
page: true,
limit: 10,
limits: [10,20,50,100]
});
}
function reloadGroupStatus(){ if(!groupSelectedId) return; $.get('/rtk/group/status',{group_id: groupSelectedId}, function(res){ if(res.code===0){ (res.data||[]).forEach(function(r){ groupStatus[r.device_id]=r.status; }); renderGroupTable(); } }); }
$('#btn-gr-start').on('click', function(){ if(!groupSelectedId){ layer.msg('请选择分组'); return; } if(!grRunning){ layer.confirm('是否清空日志后开始?',{btn:['清空','追加','取消']}, function(idx){ layer.close(idx); $.post('/rtk/group/start',{group_id: groupSelectedId, clear_log:true}, function(){ grRunning=true; updateGrBtn(); connectRtkWs(); }); }, function(idx){ layer.close(idx); $.post('/rtk/group/start',{group_id: groupSelectedId, clear_log:false}, function(){ grRunning=true; updateGrBtn(); connectRtkWs(); }); }); } else { $.post('/rtk/group/stop',{group_id: groupSelectedId}, function(){ grRunning=false; updateGrBtn(); }); } });
function appendGroupLog(msg){ var gi=$('#group-info'); gi.text(gi.text()+msg+'\n'); var el=document.getElementById('group-info'); el.scrollTop=el.scrollHeight; }
});
</script>
<script type="text/html" id="rtkProfileBar">
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit">编辑</a>
</script>
</body>
</html>

View File

@ -94,9 +94,10 @@
url: '/gnss/q_status/list',
cols: [[
{field: 'deviceid', title: '设备号', width: 120, templet: '#deviceIdTpl', align: 'center',sort: true},
{field: 'devicetype', title: '设备类型', templet: '#typeTrans', width: 100},
{field: 'devicetype', title: '设备类型', templet: '#typeTrans', width: 80},
{field: 'tenantname', title: '部门', width: 100},
{field: 'project_id', title: '项目号', width: 100},
{field: 'name', title: '工点', width: 150},
{field: 'name', title: '工点', width: 100},
{field: 'updatetime', title: '更新时间', templet: "<div>{{layui.util.toDateString(d.updatetime, 'yyyy-MM-dd HH:mm:ss')}}</div>", width: 170},
{field: 'state', title: '状态', templet: '#stateTrans', width: 80, align: 'center'},
{field: 'warning', title: '告警', templet: '#warningTrans', width: 80, align: 'center'},

View File

@ -0,0 +1,101 @@
<!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>
<script>
var default_inpstr2 = 'ytcors14847:fyx25943@gnss.ytcors.cn:8003/RTCM33GRCEJpro';
var default_inpstr3 = 'Ming:@Zhang12345@ntrip.data.gnss.ga.gov.au:2101/BCEP00BKG0';
</script>
</head>
<body>
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label">ID</label>
<div class="layui-input-block">
<input type="number" name="id" id="id" placeholder="留空自动生成" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">名称</label>
<div class="layui-input-block">
<input type="text" name="name" id="name" lay-verify="required" lay-reqtext="不能为空" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">inpstr2-path</label>
<div class="layui-input-block">
<input type="text" name="inpstr2_path" id="inpstr2_path" lay-verify="required" lay-reqtext="不能为空" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">inpstr3-path</label>
<div class="layui-input-block">
<input type="text" name="inpstr3_path" id="inpstr3_path" lay-verify="required" lay-reqtext="不能为空" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">确认保存</button>
</div>
</div>
</div>
<script src="../../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
<script>
layui.use(['form'], function () {
var form = layui.form,
$ = layui.$;
var iframeIndex = parent.layer.getFrameIndex(window.name);
$(function(){
if(!$('#inpstr2_path').val()) $('#inpstr2_path').val(default_inpstr2);
if(!$('#inpstr3_path').val()) $('#inpstr3_path').val(default_inpstr3);
});
form.on('submit(saveBtn)', function (data) {
if(!data.field.id){ delete data.field.id; }
$.ajax({
type:"POST",
url:"/rtk/group/update",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify(data.field),
success: function (result) {
if(result === 'ok' || result.code === 0){
if(parent.onRtkGroupUpdated) parent.onRtkGroupUpdated();
parent.layer.close(iframeIndex);
} else {
layer.alert(result.msg || '保存失败');
}
},
error: function () {
parent.layer.close(iframeIndex);
}
});
return false;
});
});
function setParams(data){
var form = layui.form,
$ = layui.$;
$('#id').val(data.id).attr('readonly', true);
$('#name').val(data.name);
$('#inpstr2_path').val(data.inpstr2_path);
$('#inpstr3_path').val(data.inpstr3_path);
form.render();
}
</script>
</body>
</html>

View File

@ -0,0 +1,118 @@
<!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:#fff;}</style>
</head>
<body>
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label">ID</label>
<div class="layui-input-block">
<input type="number" name="id" id="id" readonly class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">设备号</label>
<div class="layui-input-block">
<input type="text" name="device_id" id="device_id" lay-verify="required" lay-reqtext="不能为空" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">分组ID</label>
<div class="layui-input-block">
<input type="number" name="rtkgroup_id" id="rtkgroup_id" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">pos1-posmode</label>
<div class="layui-input-inline">
<select name="pos1_posmode" id="pos1_posmode">
<option value="static">static</option>
<option value="kinematic">kinematic</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">inpstr1-type</label>
<div class="layui-input-inline">
<select name="inpstr1_type" id="inpstr1_type">
<option value="ntripcli">ntripcli</option>
<option value="tcpcli">tcpcli</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">inpstr1-path</label>
<div class="layui-input-block">
<input type="text" name="inpstr1_path" id="inpstr1_path" placeholder="可留空" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">outstr2-path</label>
<div class="layui-input-block">
<input type="text" name="outstr2_path" id="outstr2_path" placeholder="可留空" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">out-height</label>
<div class="layui-input-inline">
<select name="out_height" id="out_height">
<option value="ellipsoidal">ellipsoidal</option>
<option value="geodetic" selected>geodetic</option>
</select>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">保存</button>
</div>
</div>
</div>
<script src="../../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
<script>
layui.use(['form'], function(){
var form = layui.form, $ = layui.$;
var iframeIndex = parent.layer.getFrameIndex(window.name);
form.on('submit(saveBtn)', function(data){
$.ajax({
type: 'POST',
url: '/rtk/profile/update',
contentType: 'application/json;charset=UTF-8',
data: JSON.stringify(data.field),
success: function(res){
if(res.code === 0){
parent.layui.table.reload('rtk-profile-table');
parent.layer.close(iframeIndex);
} else { layer.alert(res.msg || '保存失败'); }
},
error: function(){ layer.alert('请求失败'); }
});
return false;
});
});
function setParams(d){
var form = layui.form, $ = layui.$;
if(d.id) $('#id').val(d.id);
if(d.device_id) $('#device_id').val(d.device_id);
if(d.rtkgroup_id!=null) $('#rtkgroup_id').val(d.rtkgroup_id);
$('#pos1_posmode').val(d.pos1_posmode||'static');
$('#inpstr1_type').val(d.inpstr1_type||'ntripcli');
$('#inpstr1_path').val(d.inpstr1_path||'');
$('#outstr2_path').val(d.outstr2_path||'');
$('#out_height').val(d.out_height||'geodetic');
form.render();
}
</script>
</body>
</html>

View File

@ -54,13 +54,12 @@ public class D350SurfaceInclineMessageExecutor implements Executor<D350SurfaceIn
logger.debug("{} not existed", deviceId);
return null;
}
SurfaceInclineData data = message.getInclineData();
data.setTenantid(device.device.getTenantid());
forwardData(device, data);
ThreadManager.getFixedThreadPool().submit(() -> {
SurfaceInclineData data = message.getInclineData();
data.setTenantid(device.device.getTenantid());
dataMapper.insert(data);
forwardData(device, data);
});
return null;
}
@ -122,9 +121,11 @@ public class D350SurfaceInclineMessageExecutor implements Executor<D350SurfaceIn
if (device.sensorNum == 2) {
if (data.getSensorid()==1) device.data1 = data;
else {
data1.setAngley(data.getAnglex());
data1.setAccy(data.getAccx());
dataForwarder.send(device.device, data1);
data.setAngley(data.getAnglex());
data.setAnglex(data1.getAnglex());
//data1.setAngley(data.getAnglex());
//data1.setAccy(data.getAccx());
dataForwarder.send(device.device, data);
}
}
}