增加了websocket实现命令行交互;完善了参数配置功能,包括UI配置和命令行配置;增加了消息收发浏览UI

This commit is contained in:
weidong 2023-10-29 17:49:06 +08:00
parent 555c88d4b9
commit cdf74e9d5c
42 changed files with 874 additions and 514 deletions

View File

@ -56,6 +56,17 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- SpringCloud OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

View File

@ -0,0 +1,18 @@
package com.imdroid.secapi.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name="BeidouClient",url = "http://localhost:8668/api")
public interface BeidouClient {
@PostMapping("/config_ack")
String onConfigAck(@RequestParam(name = "deviceId") String deviceId, @RequestParam(name = "configAck") String configAck);
@PostMapping("/device_active")
String onDeviceActive(@RequestParam(name = "deviceId") String deviceId);
@PostMapping("/device_stop")
String onDeviceStop(@RequestParam(name = "deviceId") String deviceId);
}

View File

@ -0,0 +1,15 @@
package com.imdroid.secapi.client;
import lombok.Data;
@Data
public class HttpResp<T> {
public static int HTTP_RSP_OK = 0;
public static int HTTP_RSP_FAILED = -1;
private int code = HTTP_RSP_OK;
private String responseMessage;
private String responseType;
private int rows;
private T responseObject;
}

View File

@ -0,0 +1,11 @@
package com.imdroid.secapi.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name="RtcmClient",url = "http://localhost:9904/gnss")
public interface RtcmClient {
@PostMapping("/config")
HttpResp config(@RequestParam(name = "deviceId") String deviceId, @RequestParam(name = "configuration") String configData);
}

View File

@ -15,6 +15,9 @@ import java.sql.Timestamp;
@Data
@TableName(value = "gnssdevices")
public class GnssDevice {
public static final short TYPE_ROVER = 0;
public static final short TYPE_REFERENCE_STATION = 1;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private Short opmode;
@ -28,12 +31,11 @@ public class GnssDevice {
private Integer devicetype;
private Integer tenantid;
private String tenantname;
private String gnssconfiguration;
private Integer project_id = 0;
private Integer group_id = 1;
private Integer group_id = 1; //组参数缓存自动下发
private Integer calc_group_id = 1;
private Integer fwd_group_id = 0;
private Boolean syn;
private Boolean syn; //组参数是否同步
private String pictures;
public String getObjectName(){

View File

@ -1,6 +1,7 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.annotation.TableName;
import com.imdroid.secapi.utils.HexUtil;
import lombok.Data;
import java.io.Serializable;
@ -13,11 +14,24 @@ import java.io.Serializable;
@Data
@TableName(value = "gnssgroup")
public class GnssGroup implements Serializable {
public final static short LOW_POWER_MODE = 0;
public final static short NORMAL_POWER_MODE = 1;
Integer id;
int work_cycle;
int active_time;
int active_offset;
short sample;
short rs_adv; // reference station only
short power_mode;
int device_num;
public String getConfigCmd(GnssDevice device){
String cmd = "D3110009";
cmd += HexUtil.Int2HexString(Integer.parseInt(device.getDeviceid()))
+HexUtil.Byte2HexString((byte) work_cycle)
+HexUtil.Byte2HexString((byte) active_time)
+HexUtil.Byte2HexString((byte) active_offset)
+((device.getDevicetype() == GnssDevice.TYPE_ROVER)?"00":HexUtil.Byte2HexString((byte) rs_adv))
+HexUtil.Byte2HexString((byte) power_mode);
return cmd;
}
}

View File

@ -0,0 +1,25 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.sql.Timestamp;
/**
* GNSS收发统计消息每个工作周期结束的时候统计一次
*
* @author LiGang
*/
@Data
@TableName(value = "gnssmsg")
public class GnssMsg {
@TableId(value = "id", type = IdType.AUTO)
Long id;
Timestamp createtime;
String deviceid;
int msgtype;
int msglen;
String content;//只记录部分
}

View File

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

View File

@ -1,10 +1,10 @@
package com.imdroid.secapi.dto;
import com.github.yulichang.base.MPJBaseMapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface GnssStatusMsgMapper extends MPJBaseMapper<GnssStatusMsg> {
public interface GnssStatusMsgMapper extends BaseMapper<GnssStatusMsg> {
}

View File

@ -1,10 +1,10 @@
package com.imdroid.secapi.dto;
import com.github.yulichang.base.MPJBaseMapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface GnssTrxMsgMapper extends MPJBaseMapper<GnssTrxMsg> {
public interface GnssTrxMsgMapper extends BaseMapper<GnssTrxMsg> {
}

View File

@ -0,0 +1,69 @@
package com.imdroid.secapi.utils;
public class HexUtil {
static public String Byte2HexString(byte value){
String sv = "";
byte bv = (byte) ((value >> 4)&0x0F);
if(bv<10) sv+=bv;
else sv+=(char) ('A'+bv-10);
bv = (byte) (value & 0x0F);
if(bv<10) sv+=bv;
else sv+=(char) ('A'+bv-10);
return sv;
}
static public String Short2HexString(short value){
String sv = "";
sv += Byte2HexString((byte) (value>>8));
sv += Byte2HexString((byte) (value));
return sv;
}
static public String Int2HexString(int value){
String sv = "";
sv += Byte2HexString((byte) (value>>24));
sv += Byte2HexString((byte) (value>>16));
sv += Byte2HexString((byte) (value>>8));
sv += Byte2HexString((byte) (value));
return sv;
}
static public String String2HexString(String value){
byte[] data = value.getBytes();
String sv = "";
for(int i=0; i< data.length; i++) sv += Byte2HexString(data[i]);
return sv;
}
static public String HexString2String(String value){
String sv ="";
int pos=0;
int trans=0;
short bv = 0;
while(pos<value.length()){
char ch = value.charAt(pos);
if(ch<='9' && ch>='0'){
bv = (short) (bv*16 + ch - '0');
trans++;
}
else if(ch<='F' && ch>='A'){
bv = (short) (bv*16 + ch - 'A' + 10);
trans++;
}
else if(ch<='f' && ch>='a'){
bv = (short) (bv*16 + ch - 'a' + 10);
trans++;
}
if(trans==2) {
sv += (char) bv;
bv = 0;
trans = 0;
}
pos++;
}
return sv;
}
}

View File

@ -4,6 +4,7 @@ import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@ -17,7 +18,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@ComponentScan({"io.dt20.*", "com.imdroid.*"})
@EntityScan({"io.dt20.common.persistence", "com.imdroid.*"})
@EnableJpaRepositories({"io.dt20.common.repo", "com.imdroid.*"})
@EnableFeignClients(basePackages = "com.imdroid.*")
public class SideSlopeRtcmApp {
public static void main(String[] args) {

View File

@ -1,7 +1,9 @@
package com.imdroid.sideslope.executor;
import com.imdroid.secapi.client.BeidouClient;
import com.imdroid.sideslope.bd.ByteUtil;
import com.imdroid.sideslope.message.D31xConfigAckMessage;
import com.imdroid.sideslope.message.D3F0SelfCheckMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
@ -10,14 +12,18 @@ import org.springframework.stereotype.Component;
*/
@Component
public class D31xConfigAckMessageExecutor implements Executor<D31xConfigAckMessage, Void> {
@Autowired
private BeidouClient beidouClient;
@Override
public Void execute(D31xConfigAckMessage message) {
// 转发应答
beidouClient.onConfigAck(message.getId(), ByteUtil.bytesToHexString(message.getSrcData()));
return null;
}
@Override
public Class<?> getMessageType() {
return D3F0SelfCheckMessage.class;
return D31xConfigAckMessage.class;
}
}

View File

@ -1,5 +1,6 @@
package com.imdroid.sideslope.executor;
import com.imdroid.secapi.client.BeidouClient;
import com.imdroid.sideslope.message.D3F0SelfCheckMessage;
import com.imdroid.sideslope.service.DataPersistService;
import com.imdroid.sideslope.util.ThreadManager;
@ -15,9 +16,13 @@ public class D3F0SelfCheckMessageExecutor implements Executor<D3F0SelfCheckMessa
@Autowired
private DataPersistService dataPersistService;
@Autowired
private BeidouClient beidouClient;
@Override
public Void execute(D3F0SelfCheckMessage message) {
// 通知beidou服务
beidouClient.onDeviceActive(message.getId());
// 存储最新设备状态信息到数据库中
ThreadManager.getFixedThreadPool().submit(() -> {
dataPersistService.saveDeviceState(message);

View File

@ -1,5 +1,6 @@
package com.imdroid.sideslope.executor;
import com.imdroid.secapi.client.BeidouClient;
import com.imdroid.sideslope.message.D3F2StopIndicationMessage;
import com.imdroid.sideslope.sal.DeviceService;
import com.imdroid.sideslope.service.DataPersistService;
@ -23,6 +24,8 @@ public class D3F2StopIndicationMessageExecutor implements Executor<D3F2StopIndic
@Resource(name = "local")
private DeviceService deviceService;
@Autowired
private BeidouClient beidouClient;
@Override
public Void execute(D3F2StopIndicationMessage message) {
@ -36,6 +39,10 @@ public class D3F2StopIndicationMessageExecutor implements Executor<D3F2StopIndic
ThreadManager.getFixedThreadPool().submit(() -> {
deviceService.updateLatestDataTime(message.getId(), now);
});
// 通知beidou服务
beidouClient.onDeviceStop(message.getId());
return null;
}

View File

@ -8,7 +8,7 @@ import io.netty.buffer.ByteBuf;
* @date 2023/2/2 20:32
*/
public abstract class BaseMessage {
protected byte[] header;
protected short header;
protected String id;
protected int len;
protected int pps;
@ -19,8 +19,7 @@ public abstract class BaseMessage {
if (shouldDecodeHeader()) {
// read操作会移动ByteBuf内部指针除D331外其他都用read来读
int packetLen = src.readableBytes();
this.header = new byte[2];
src.readBytes(header);
this.header = src.readShort();
this.len = src.readUnsignedShort();
this.id = String.valueOf(src.readUnsignedInt());
@ -39,11 +38,11 @@ public abstract class BaseMessage {
return true;
}
public byte[] getHeader() {
public short getHeader() {
return header;
}
public void setHeader(byte[] header) {
public void setHeader(short header) {
this.header = header;
}

View File

@ -12,8 +12,7 @@ public class D31xConfigAckMessage extends BaseMessage {
@Override
public void decodeBody(ByteBuf src) {
// get操作不会移动指针这样可以确保整个全转发出去
this.header = new byte[2];
src.getBytes(0, header); // flag
this.header = src.getShort(0); // flag
this.len = src.getUnsignedShort(2); // length
this.id = String.valueOf(src.getUnsignedInt(4)); //id
if (src.readableBytes() - 4 != this.len) {

View File

@ -12,8 +12,7 @@ public class D331RtcmMessage extends BaseMessage {
@Override
public void decodeBody(ByteBuf src) {
// get操作不会移动指针这样可以确保整个全转发出去
this.header = new byte[2];
src.getBytes(0, header); // flag
this.header = src.getShort(0); // flag
this.len = src.getUnsignedShort(2); // length
this.id = String.valueOf(src.getUnsignedInt(4)); //id
if (src.readableBytes() - 4 != this.len) {

View File

@ -52,10 +52,9 @@ public class DeviceChannel {
}
}
public void updateRxBytes(int bytes, byte[] header){
public void updateRxBytes(int bytes, short header){
rxbytes += bytes;
int gnss_bytes = header[0]<<8 | header[1];
if(gnss_bytes == 0xd331) d3xxbytes += bytes;
else if(gnss_bytes == 0xd341) b562bytes += bytes;
if(header == (short) 0xd331) d3xxbytes += bytes;
else if(header == (short)0xd341) b562bytes += bytes;
}
}

View File

@ -4,7 +4,10 @@ import com.imdroid.sideslope.message.D3F0SelfCheckMessage;
import com.imdroid.sideslope.message.D3F2StopIndicationMessage;
/**
* 设备数据持久化接口
* 设备数据持久化接口保存
* 1消息内容自检消息停止消息配置消息和应答
* 2设备状态解析自检消息
* 3收发统计解析停止消息以及channel收发统计
*
* @author LiGang
*/
@ -13,4 +16,5 @@ public interface DataPersistService {
void saveDeviceState(D3F0SelfCheckMessage d3F0SelfCheckMessage);
void saveDeviceTrxStat(D3F2StopIndicationMessage d3F2StopIndicationMessage);
}

View File

@ -1,11 +1,14 @@
package com.imdroid.sideslope.service;
import com.imdroid.secapi.dto.*;
import com.imdroid.sideslope.message.BaseMessage;
import com.imdroid.sideslope.message.D3F0SelfCheckMessage;
import com.imdroid.sideslope.message.D3F2StopIndicationMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.Timestamp;
/**
* 设备数据持久化实现
*
@ -22,6 +25,8 @@ public class DataPersistServiceImpl implements DataPersistService {
@Autowired
private GnssTrxMsgMapper trxMsgMapper;
@Autowired
private GnssMsgMapper msgMapper;
@Override
public void saveDeviceState(D3F0SelfCheckMessage message) {
@ -54,6 +59,9 @@ public class DataPersistServiceImpl implements DataPersistService {
if(new_flag) deviceStateRepository.insert(deviceState);
else deviceStateRepository.updateById(deviceState);
// 保存消息摘要
saveMsg(message);
} catch (Exception e) {
e.printStackTrace();
}
@ -64,6 +72,8 @@ public class DataPersistServiceImpl implements DataPersistService {
try {
// 添加到trxmsg里
trxMsgMapper.insert(message.getTrxMsg());
// 保存消息摘要
saveMsg(message);
// 检测该对象是否已存在
GnssStatus deviceState = deviceStateRepository.getByDeviceId(message.getId());
@ -77,10 +87,18 @@ public class DataPersistServiceImpl implements DataPersistService {
deviceState.setState(GnssStatus.STATE_IDLE);
deviceStateRepository.updateById(deviceState);
}
} catch (Exception e) {
e.printStackTrace();
}
}
void saveMsg(BaseMessage message){
GnssMsg gnssMsg = new GnssMsg();
gnssMsg.setCreatetime(new Timestamp(System.currentTimeMillis()));
gnssMsg.setDeviceid(message.getId());
gnssMsg.setMsgtype(((int)message.getHeader())&0xFFFF);
gnssMsg.setMsglen(message.getLen());
msgMapper.insert(gnssMsg);
}
}

View File

@ -1,5 +1,6 @@
package com.imdroid.sideslope.web;
import com.imdroid.secapi.client.HttpResp;
import com.imdroid.sideslope.calc.GNSSCalcService;
import com.imdroid.sideslope.server.DeviceChannel;
import com.imdroid.sideslope.server.OnlineChannels;
@ -9,6 +10,7 @@ import io.netty.buffer.Unpooled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@ -29,39 +31,12 @@ public class ApiController {
@Autowired
private GNSSCalcService gnssCalcService;
@RequestMapping(value = "/config")
@PostMapping(value = "/config")
public HttpResp config(String deviceId, String configuration) {
Map<String, Object> status = new HashMap<>();
HttpResp resp = new HttpResp();
OnlineChannels.INSTANCE
.get(deviceId)
.filter(DeviceChannel::isOnline)
.ifPresent(x -> {
status.put("status", "Online");
status.put("lastUpdate", x.getLastTime());
// send command
ByteBuf buf = Unpooled.buffer();
byte[] data = getBinaryData(ConfigDataTypeEnum.JSON, configuration);
logger.info("send command:{}", configuration);
buf.writeBytes(data);
x.writeAndFlush(buf);
});
if (status.isEmpty()) {
status.put("status", "Offline");
resp.setResponseCode("-10001");
resp.setResponseMessage("Offline.");
} else {
resp.setResponseMessage("Command sent.");
}
resp.setResponseObject(status);
return resp;
}
@RequestMapping(value = "/gnssconfig")
public HttpResp gnssConfig(String deviceId, String configuration) {
Map<String, Object> status = new HashMap<>();
HttpResp resp = new HttpResp();
OnlineChannels.INSTANCE
OnlineChannels onlineChannels = OnlineChannels.INSTANCE;
onlineChannels
.get(deviceId)
.filter(DeviceChannel::isOnline)
.ifPresent(x -> {
@ -76,7 +51,7 @@ public class ApiController {
});
if (status.isEmpty()) {
status.put("status", "Offline");
resp.setResponseCode("-10001");
resp.setCode(HttpResp.HTTP_RSP_FAILED);
resp.setResponseMessage("Offline.");
} else {
resp.setResponseMessage("Command sent.");

View File

@ -1,49 +0,0 @@
package com.imdroid.sideslope.web;
public class HttpResp<T> {
private String responseCode = "10000";
private String responseMessage;
private String responseType;
private int rows;
private T responseObject;
public String getResponseCode() {
return responseCode;
}
public void setResponseCode(String responseCode) {
this.responseCode = responseCode;
}
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
public String getResponseType() {
return responseType;
}
public void setResponseType(String responseType) {
this.responseType = responseType;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public T getResponseObject() {
return responseObject;
}
public void setResponseObject(T responseObject) {
this.responseObject = responseObject;
}
}

View File

@ -9,7 +9,6 @@
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.imdroid</groupId>
<artifactId>sec-beidou</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>beidou</name>
@ -123,12 +122,16 @@
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- module sec-beidou-rtcm-api -->
<dependency>
<groupId>com.imdroid</groupId>
<artifactId>sec-beidou-rtcm-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -21,6 +21,6 @@ public class WebMvcConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/*.js","/**/*.css","/**/*.jpg","/**/*.png", "/do_login", "/mini-register", "/login", "/test/**");
.excludePathPatterns("/**/*.js","/**/*.css","/**/*.jpg","/**/*.png", "/do_login", "/mini-register", "/login", "/test/**", "/api/**");
}
}

View File

@ -0,0 +1,13 @@
package com.imdroid.beidou.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}

View File

@ -0,0 +1,107 @@
package com.imdroid.beidou.controller;
import com.imdroid.secapi.client.RtcmClient;
import com.imdroid.secapi.dto.*;
import com.imdroid.secapi.utils.HexUtil;
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.ResponseBody;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
@Controller
public class APIController extends BasicController{
SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
@Autowired
RtcmClient rtcmClient;
@Autowired
GnssDeviceMapper deviceMapper;
@Autowired
GnssGroupMapper groupMapper;
@Autowired
GnssMsgMapper msgMapper;
/****** config ack *******/
@PostMapping(value = "/api/config_ack")
@ResponseBody
public String onConfigAck(String deviceId, String configAck) {
int msgType = Integer.parseInt(configAck.substring(0,4),16);
// 配置是否成功
if(msgType == 0xd311){
//最后一个字节为1表示配置成功0表示配置失败
if(configAck.endsWith("01")){
GnssDevice device = deviceMapper.queryByDeviceId(deviceId);
if(device!=null){
device.setSyn(true);
deviceMapper.updateById(device);
}
}
}
// 保存
saveMsg(deviceId, msgType, configAck);
// 命令行显示
String rxInfo = "RX "+ dateFormat.format(System.currentTimeMillis())+
" "+deviceId+" ";
if(msgType == 0xd31a){
//转成字符串
String dtuAck = configAck.substring(9*2);
rxInfo += configAck+"("+HexUtil.HexString2String(dtuAck)+")";
}
else{
rxInfo += configAck;
}
WebSocketServer.sendMessageToAll(rxInfo);
return null;
}
/****** device active *******/
@PostMapping(value = "/api/device_active")
@ResponseBody
public String onDeviceActive(String deviceId) {
// 检查有没有待配置的参数
GnssDevice device = deviceMapper.queryByDeviceId(deviceId);
if(device!=null && !device.getSyn()){
GnssGroup gnssGroup = groupMapper.selectById(device.getGroup_id());
if(gnssGroup != null){
String config = gnssGroup.getConfigCmd(device);
if(config != null){
rtcmClient.config(deviceId, config);
// 保存
saveMsg(deviceId, 0xd311, config);
}
}
}
return null;
}
/****** device stop *******/
@PostMapping(value = "/api/device_stop")
@ResponseBody
public String onDeviceStop(String deviceId) {
return null;
}
void saveMsg(String deviceId, int msgType, String content){
GnssMsg gnssMsg = new GnssMsg();
gnssMsg.setCreatetime(new Timestamp(System.currentTimeMillis()));
gnssMsg.setDeviceid(deviceId);
gnssMsg.setMsgtype(msgType);
if(content==null) content="";
gnssMsg.setMsglen(content.length()/2);
int saveContentLen = content.length();
if(saveContentLen<=128)
gnssMsg.setContent(content);
else
gnssMsg.setContent(content.substring(0,128));
msgMapper.insert(gnssMsg);
}
}

View File

@ -0,0 +1,86 @@
package com.imdroid.beidou.controller;
import com.imdroid.beidou.common.HttpResult;
import com.imdroid.secapi.client.HttpResp;
import com.imdroid.secapi.client.RtcmClient;
import com.imdroid.secapi.dto.GnssMsg;
import com.imdroid.secapi.dto.GnssMsgMapper;
import com.imdroid.secapi.utils.HexUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.HashMap;
@Controller
public class CmdLineController extends BasicController{
SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
@Autowired
RtcmClient rtcmClient;
@Autowired
GnssMsgMapper msgMapper;
/**** 推送页面 *****/
@RequestMapping("/page/cmd_line")
public String gnssDevCfg(Model m, HttpSession session) {
initModel(m, session);
return "/page/cmd_line";
}
/****** 发送指令 *******/
@PostMapping(value = "/gnss/config_cmd")
@ResponseBody
public HttpResult configCmd(@RequestParam("tx_win") String cmd,
@RequestParam("device_id") String deviceId,
@RequestParam("cmd_type") int cmdType) {
String sendCmd = cmd.replaceAll(" +","");
short len = (short) (sendCmd.length()/2);
int msgType;
if(cmdType == 0){ // GNSS
msgType = 0xD310;
len += 4;
sendCmd = "D310"+ HexUtil.Short2HexString(len)+
HexUtil.Int2HexString(Integer.parseInt(deviceId))+sendCmd;
}
else if(cmdType == 1){ // DTU
msgType = 0xD31A;
len += 5;
sendCmd = "D31A"+ HexUtil.Short2HexString(len)+
HexUtil.Int2HexString(Integer.parseInt(deviceId))+
"01"+HexUtil.String2HexString(sendCmd);
}
else {
msgType = 0xD311;
len += 4;
sendCmd = "D311"+ HexUtil.Short2HexString(len)+
HexUtil.Int2HexString(Integer.parseInt(deviceId))+sendCmd;
}
HttpResp<HashMap<String, Object>> rsp = rtcmClient.config(deviceId,sendCmd);
String txInfo = "TX "+ dateFormat.format(System.currentTimeMillis())+
" "+deviceId+" "+sendCmd;
if(cmdType == 1) txInfo += "("+cmd+")";
if(rsp.getCode() != HttpResp.HTTP_RSP_OK){
txInfo += "\r\n" + rsp.getResponseMessage();
}
// 保存
GnssMsg gnssMsg = new GnssMsg();
gnssMsg.setCreatetime(new Timestamp(System.currentTimeMillis()));
gnssMsg.setDeviceid(deviceId);
gnssMsg.setMsgtype(msgType);
gnssMsg.setMsglen(len);
gnssMsg.setContent(cmd);
msgMapper.insert(gnssMsg);
return HttpResult.success(txInfo);
}
}

View File

@ -1,5 +1,6 @@
package com.imdroid.beidou.controller;
import com.imdroid.secapi.client.RtcmClient;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -37,6 +38,9 @@ public class GnssDeviceController extends BasicController{
@Autowired
private GnssStatusMapper gnssStatusMapper;
@Autowired
RtcmClient rtcmClient;
/**** 推送页面 *****/
@RequestMapping("/page/gnss_dev_cfg")
public String gnssDevCfg(Model m, HttpSession session) {
@ -118,6 +122,9 @@ public class GnssDeviceController extends BasicController{
//新增或更新
int num = 0;
if(null != old_device) {
if(old_device.getGroup_id()!=device.getGroup_id()){
device.setSyn(false);
}
device.setId(old_device.getId());
device.setCreateuser(old_device.getCreateuser());
device.setCreatetime(old_device.getCreatetime());
@ -126,11 +133,14 @@ public class GnssDeviceController extends BasicController{
else{
device.setCreatetime(new Timestamp(System.currentTimeMillis()));
device.setCreateuser((String) session.getAttribute("login_user"));
device.setSyn(false);
num = gnssDeviceMapper.insert(device); //id自增
}
if (num == 0) {
return HttpResult.failed();
} else return HttpResult.ok();
} else {
return HttpResult.ok();
}
}
@PostMapping("/gnss/device/delete")
@ -236,4 +246,5 @@ public class GnssDeviceController extends BasicController{
jsonObject.put("data", deviceListVOList);
return jsonObject;
}
}

View File

@ -0,0 +1,81 @@
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.secapi.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class GnssMsgController extends BasicController{
@Autowired
GnssMsgMapper msgMapper;
@Autowired
GnssStatusMsgMapper statusMsgMapper;
@Autowired
GnssTrxMsgMapper trxMsgMapper;
/**** 推送页面 *****/
@RequestMapping("/page/gnss_msg")
public String gnssMsg(){
return "/page/gnss_msg";
}
@RequestMapping("/page/gnss_msg_status")
public String gnssStatusMsg() {
return "/page/gnss_msg_status";
}
@RequestMapping("/page/gnss_msg_trx")
public String gnssTrxMsg(){
return "/page/gnss_msg_trx";
}
/**** 推送数据 *****/
@RequestMapping("/gnss/msg/list_all")
@ResponseBody
public JSONObject listMsg(int page, int limit) {
Page<GnssMsg> pageable = new Page<>(page, limit);
IPage<GnssMsg> cs = msgMapper.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("/gnss/msg/list_status")
@ResponseBody
public JSONObject listStatusMsg(int page, int limit) {
Page<GnssStatusMsg> pageable = new Page<>(page, limit);
IPage<GnssStatusMsg> cs = statusMsgMapper.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("/gnss/msg/list_trx")
@ResponseBody
public JSONObject listTrxMsg(int page, int limit) {
Page<GnssTrxMsg> pageable = new Page<>(page, limit);
IPage<GnssTrxMsg> cs = trxMsgMapper.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;
}
}

View File

@ -40,22 +40,6 @@ public class LayuiController extends BasicController{
return "/page/gnss_data_tools";
}
@RequestMapping("/page/gnss_msg")
public String gnssMsg()throws Exception {
return "/page/gnss_msg";
}
@RequestMapping("/page/gnss_msg_status")
public String gnssMsgStatus()throws Exception {
return "/page/gnss_msg_status";
}
@RequestMapping("/page/gnss_msg_trx")
public String gnssMsgTrx()throws Exception {
return "/page/gnss_msg_trx";
}
@RequestMapping("/page/warning")
public String warning()throws Exception {
return "/page/warning";

View File

@ -0,0 +1,52 @@
package com.imdroid.beidou.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.CopyOnWriteArraySet;
//单例
@ServerEndpoint("/websocket")
@Component
@Slf4j
public class WebSocketServer {
// thread safety counter
// thread safety set to hold websocket objects
private static CopyOnWriteArraySet<Session> webSocketSet = new CopyOnWriteArraySet<>();
@OnOpen
public void onOpen(Session session){
webSocketSet.add(session);
log.info("new websocket opened, total socket num: "+ webSocketSet.size());
}
@OnClose
public void onClose(Session session){
webSocketSet.remove(session);
log.info("websocket closed, total socket num: "+ webSocketSet.size());
}
@OnMessage
public void onMessage(String msg, Session session){
log.info("received msg from "+session.getBasicRemote());
}
@OnError
public void onError(Session session, Throwable error){
error.printStackTrace();
}
public static void sendMessageToAll(String msg){
try {
for (Session item : webSocketSet) {
item.getBasicRemote().sendText(msg);
}
}
catch (Exception e){
log.error("websocket send msg error:", e);
}
}
}

View File

@ -6,9 +6,18 @@ import lombok.Data;
@Data
@TableName(value = "gnssgroupfwd")
public class GnssGroupFwd {
public static final short FWD_TYPE_NONE = 0; //不转发
public static final short FWD_TYPE_CALC = 1; //按模板转发解算结果
public static final short FWD_TYPE_RAW = 2; //转发原始数据D331/D341
public static final short FWD_TYPE_NTRIP = 3; //按ntrip协议转发
int id;
String description;
String addr;
int port;
short type1;
String addr1;
int port1;
String fwd_template;
short type2;
String addr2;
int port2;
int device_num;
}

View File

@ -34,7 +34,6 @@ CREATE TABLE IF NOT EXISTS `gnssdevices` (
`devicetype` smallint DEFAULT 0,
`tenantid` int NOT NULL,
`tenantname` varchar(100) NOT NULL,
`gnssconfiguration` varchar(100) NOT NULL,
`project_id` int NOT NULL DEFAULT 0 COMMENT '项目id',
`group_id` int DEFAULT 1,
`calc_group_id` int DEFAULT 1,
@ -49,7 +48,7 @@ CREATE TABLE IF NOT EXISTS `gnssgroup` (
`work_cycle` int DEFAULT 0,
`active_time` int DEFAULT 0,
`active_offset` int DEFAULT 0,
`sample` smallint DEFAULT 0,
`rs_adv` smallint DEFAULT 0,
`power_mode` smallint DEFAULT 0,
`device_num` int DEFAULT 0,
PRIMARY KEY (`id`)
@ -69,11 +68,16 @@ CREATE TABLE IF NOT EXISTS `gnssgroupcalc` (
CREATE TABLE IF NOT EXISTS `gnssgroupfwd` (
`id` int NOT NULL,
`description` varchar(128) DEFAULT NULL,
`addr` varchar(128) DEFAULT NULL,
`port` int DEFAULT 0,
`type1` smallint DEFAULT 0,
`addr1` varchar(128) DEFAULT NULL,
`port1` int DEFAULT 0,
`type2` smallint DEFAULT 0,
`addr2` varchar(128) DEFAULT NULL,
`port2` int DEFAULT 0,
`fwd_template` longtext,
`device_num` int DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `gnssstatus` (
`id` bigint AUTO_INCREMENT,
@ -97,7 +101,7 @@ CREATE TABLE IF NOT EXISTS `gnssstatus` (
`state` smallint DEFAULT 0,
`warning` smallint DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `gnssdevicelocationrecords` (
`id` bigint AUTO_INCREMENT,
@ -138,6 +142,16 @@ CREATE TABLE IF NOT EXISTS `gnsssinglecalcdata` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `gnssmsg` (
`id` bigint AUTO_INCREMENT,
`createtime` datetime DEFAULT NULL,
`deviceid` varchar(20) NOT NULL,
`msgtype` int default 0,
`msglen` int default 0,
`content` varchar(128) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `gnssstatusmsg` (
`id` bigint AUTO_INCREMENT,
`updatetime` datetime DEFAULT NULL,

View File

@ -56,7 +56,13 @@
"href": "page/gnss_dev_cfg",
"icon": "fa fa-minus",
"target": "_self"
}
},
{
"title": "命令行",
"href": "page/cmd_line",
"icon": "fa fa-minus",
"target": "_self"
}
]
},
{

View File

@ -1,351 +1,145 @@
<style>
.rx_win_style{
min-height:300px;
background-color: lightgray;
}
</style>
<div class="layuimini-container layuimini-page-anim">
<div class="layuimini-main">
<div class="layui-card top-panel">
<div class="layui-card-header">接收窗口</div>
<div class="layui-card-body">
<table class="layui-hide" id="baseParaTableId" lay-filter="baseParaTableFilter"></table>
<div class="layui-row layui-col-space1">
<div class="layui-col-md12">
<div class="layui-form-item">
<textarea name="rx_win" id="rx_win" class="layui-textarea rx_win_style" readonly></textarea>
</div>
</div>
<form class="layui-form" action="">
<div class="layui-col-md12">
<div class="layui-form-item">
<textarea name="tx_win" id="tx_win" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-col-md12">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">设备ID</label>
<div class="layui-input-inline">
<input type="text" name="device_id" lay-verify="required|number" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">指令类型</label>
<div class="layui-input-inline">
<select name="cmd_type" lay-filter="cmd_type">
<option value="0">GNSS</option>
<option value="1">DTU</option>
<option value="2">MPU</option>
</select>
</div>
</div>
<div class="layui-inline">
<div class="layui-input-block">
<button class="layui-btn" lay-submit="" lay-filter="send_btn">发送</button>
<button class="layui-btn" lay-submit="" lay-filter="clear_btn">清屏</button>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="layui-card top-panel">
<div class="layui-card-header">解算参数</div>
<div class="layui-card-body">
<table class="layui-hide" id="calcParaTableId" lay-filter="calcParaTableFilter"></table>
</div>
</div>
<div class="layui-card top-panel">
<div class="layui-card-header">推送参数</div>
<div class="layui-card-body">
<table class="layui-hide" id="forwardParaTableId" lay-filter="forwardParaTableFilter"></table>
</div>
</div>
</div>
<script type="text/html" id="currentTableBar">
<a class="layui-btn layui-btn-normal layui-btn-xs data-count-edit" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-xs layui-btn-danger data-count-delete" lay-event="delete">删除</a>
</script>
<script type="text/html" id="toolbarTable">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-normal layui-btn-sm data-add-btn" lay-event="add"> 添加 </button>
</div>
</script>
</div>
</div>
<script th:inline="none">
layui.use(['form', 'table','miniPage','element'], function () {
var $ = layui.jquery,
form = layui.form,
table = layui.table,
miniPage = layui.miniPage;
<script>
layui.use(['form'], function () {
var form = layui.form
, $ = layui.$;
var rxWin = $("#rx_win");
/**
** 基本参数组
**/
table.render({
elem: '#baseParaTableId',
url: '/gnss/group/list',//假数据
toolbar: '#toolbarTable',
cols: [[
{field: 'id', title: '组号', sort: true},
{field: 'work_cycle', title: '工作周期(分钟)'},
{field: 'active_time', title: '激活时长(分钟)'},
{field: 'active_offset', title: '偏置(分钟)'},
{field: 'sample', title: '采样周期(秒)'},
{field: 'power_mode', title: '功耗模式',templet: '#modeTrans'},
{field: 'device_num', title: '关联设备数'},
{title: '操作', toolbar: '#currentTableBar', align: "center"}
]],
limit: 10,
page: true,
skin: 'line'
* 初始化表单,要加上,不然刷新部分组件可能会不加载
*/
form.render();
//监听提交
form.on('submit(send_btn)', function (data) {
//console.log((data.field));
$.ajax({
type:"POST",
url:"/gnss/config_cmd",
data: data.field,
success: function (result) {
//console.log(result);
if(result.code == 0){
rxWin.val(rxWin.val() +result.data+ "\r\n");
var obj = document.getElementById("rx_win");
obj.scrollTop = obj.scrollHeight;
}
else{
layer.alert(result.data, {
title: '错误信息'
})
}
//
},
error: function () {
console.log("ajax error");
}
});
return false;
});
table.on('toolbar(baseParaTableFilter)', function (obj) {
if (obj.event === 'add') { // 监听添加操作
var content = miniPage.getHrefContent('page/table/gnss_add_group');
var openWH = miniPage.getOpenWidthHeight();
var index = layer.open({
title: '新增分组',
type: 1,
shade: 0.2,
maxmin:true,
shadeClose: true,
area: [openWH[0] + 'px', openWH[1] + 'px'],
offset: [openWH[2] + 'px', openWH[3] + 'px'],
content: content
});
$(window).on("resize", function () {
layer.full(index);
});
}
form.on('submit(clear_btn)', function (data) {
rxWin.val("");
return false;
});
table.on('tool(baseParaTableFilter)', function (obj) {
var data = obj.data;
if (obj.event === 'edit') {
var content = miniPage.getHrefContent('page/table/gnss_add_group');
var openWH = miniPage.getOpenWidthHeight();
var index = layer.open({
title: '修改分组',
type: 1,// 2表示弹出的是iframe1表示弹出的是层
shade: 0.2,
maxmin:true,
shadeClose: true,
area: [openWH[0] + 'px', openWH[1] + 'px'],
offset: [openWH[2] + 'px', openWH[3] + 'px'],
content: content,
success: function (layero, index) {
var group_id = layero.find('#id');
group_id.val(data.id);
group_id.attr('readonly',true);
layero.find('#work_cycle').val(data.work_cycle);
layero.find('#active_time').val(data.active_time);
layero.find('#active_offset').val(data.active_offset);
layero.find('#sample').val(data.sample);
layero.find('#power_mode').val(data.power_mode);
}
});
$(window).on("resize", function () {
layer.full(index);
});
return false;
} else if (obj.event === 'delete') {
layer.confirm('确定删除参数组'+data.id+"?", function(index){
$.ajax({
type:"POST",
url:"/gnss/group/delete",
data:{
'del_id':data.id
},
success: function (data) {
//data是cotroller相应处理函数的返回值
table.reload('baseParaTableId');
},
error: function () {
console.log("ajax error");
}
});
layer.close(index);
});
}
});
/**
** 解算参数组
**/
table.render({
elem: '#calcParaTableId',
url: '/gnss/group/list_calc',//假数据
toolbar: '#toolbarTable',
cols: [[
{field: 'id', title: '组号', sort: true},
{field: 'long_cycle', title: '长周期(小时)'},
{field: 'short_cycle', title: '短周期(小时)'},
{field: 'shock', title: '切换门限'},
{field: 'h_weight', title: '惯导水平权重'},
{field: 'v_weight', title: '惯导垂直权重'},
{field: 'device_num', title: '关联设备数'},
{title: '操作', toolbar: '#currentTableBar', align: "center"}
]],
limit: 10,
page: true,
skin: 'line'
});
table.on('toolbar(calcParaTableFilter)', function (obj) {
if (obj.event === 'add') { // 监听添加操作
var content = miniPage.getHrefContent('page/table/gnss_add_group_calc');
var openWH = miniPage.getOpenWidthHeight();
var index = layer.open({
title: '新增解算参数组',
type: 1,
shade: 0.2,
maxmin:true,
shadeClose: true,
area: [openWH[0] + 'px', openWH[1] + 'px'],
offset: [openWH[2] + 'px', openWH[3] + 'px'],
content: content
});
$(window).on("resize", function () {
layer.full(index);
});
}
});
table.on('tool(calcParaTableFilter)', function (obj) {
var data = obj.data;
if (obj.event === 'edit') {
var content = miniPage.getHrefContent('page/table/gnss_add_group_calc');
var openWH = miniPage.getOpenWidthHeight();
var index = layer.open({
title: '修改解算参数组',
type: 1,// 2表示弹出的是iframe1表示弹出的是层
shade: 0.2,
maxmin:true,
shadeClose: true,
area: [openWH[0] + 'px', openWH[1] + 'px'],
offset: [openWH[2] + 'px', openWH[3] + 'px'],
content: content,
success: function (layero, index) {
var group_id = layero.find('#id');
group_id.val(data.id);
group_id.attr('readonly',true);
layero.find('#long_cycle').val(data.long_cycle);
layero.find('#short_cycle').val(data.short_cycle);
layero.find('#shock').val(data.shock);
layero.find('#h_weight').val(data.h_weight);
layero.find('#v_weight').val(data.v_weight);
}
});
$(window).on("resize", function () {
layer.full(index);
});
return false;
} else if (obj.event === 'delete') {
layer.confirm('确定删除参数组'+data.id+"?", function(index){
$.ajax({
type:"POST",
url:"/gnss/group/delete_calc",
data:{
'del_id':data.id
},
success: function (data) {
//data是cotroller相应处理函数的返回值
table.reload('calcParaTableId');
},
error: function () {
console.log("ajax error");
}
});
layer.close(index);
});
}
});
/**
** 推送参数组
**/
table.render({
elem: '#forwardParaTableId',
url: '/gnss/group/list_fwd',//假数据
toolbar: '#toolbarTable',
cols: [[
{field: 'id', title: '组号', sort: true},
{field: 'description', title: '名称'},
{field: 'addr', title: '地址'},
{field: 'port', title: '端口号'},
{field: 'fwd_template', title: '模板'},
{field: 'device_num', title: '关联设备数'},
{title: '操作', minWidth: 150, toolbar: '#currentTableBar', align: "center"}
]],
limit: 10,
page: true,
skin: 'line'
});
table.on('toolbar(forwardParaTableFilter)', function (obj) {
if (obj.event === 'add') { // 监听添加操作
var content = miniPage.getHrefContent('page/table/gnss_add_group_fwd');
var openWH = miniPage.getOpenWidthHeight();
var index = layer.open({
title: '新增分组',
type: 1,
shade: 0.2,
maxmin:true,
shadeClose: true,
area: [openWH[0] + 'px', openWH[1] + 'px'],
offset: [openWH[2] + 'px', openWH[3] + 'px'],
content: content,
});
$(window).on("resize", function () {
layer.full(index);
});
}
});
table.on('tool(forwardParaTableFilter)', function (obj) {
var data = obj.data;
if (obj.event === 'edit') {
var content = miniPage.getHrefContent('page/table/gnss_add_group_fwd');
var openWH = miniPage.getOpenWidthHeight();
var index = layer.open({
title: '修改分组',
type: 1,
shade: 0.2,
maxmin:true,
shadeClose: true,
area: [openWH[0] + 'px', openWH[1] + 'px'],
offset: [openWH[2] + 'px', openWH[3] + 'px'],
content: content,
success: function (layero, index) {
var group_id = layero.find('#id');
group_id.val(data.id);
group_id.attr('readonly',true);
layero.find('#description').val(data.description);
layero.find('#addr').val(data.addr);
layero.find('#port').val(data.port);
layero.find('#fwd_template').val(data.fwd_template);
}
});
$(window).on("resize", function () {
layer.full(index);
});
return false;
} else if (obj.event === 'delete') {
layer.confirm('确定删除参数组'+data.id+"?", function(index){
$.ajax({
type:"POST",
url:"/gnss/group/delete_fwd",
data:{
'del_id':data.id
},
success: function (data) {
//data是cotroller相应处理函数的返回值
table.reload('forwardParaTableId');
},
error: function () {
console.log("ajax error");
}
});
layer.close(index);
});
}
});
});
function onBaseParaUpdated(){
layui.table.reload('baseParaTableId');
}
function onCalcParaUpdated(){
layui.table.reload('calcParaTableId');
}
function onFwdParaUpdated(){
layui.table.reload('forwardParaTableId');
}
</script>
<script type="text/html" id="modeTrans">
{{# if(d.power_mode == 0){ }}
<span>正常</span>
{{# } else if(d.power_mode == 1){ }}
<span>低功耗</span>
{{# } else { }}
<span>极低功耗</span>
{{# } }}
<!-- Web socket operations-->
<script type="text/javascript">
layui.$(function(){
if(typeof(WebSocket) == "undefined"){
console.log("WebSocket not support for this WebBrowser!")
}
else{
connectWebSocket();
}
})
//建立WebSocket连接
function connectWebSocket() {
//获取当前网址,如: http://localhost:8080/ems/Pages/Basic/Person.jsp
var curPath = window.document.location.href;
//获取主机地址之后的目录,如: /ems/Pages/Basic/Person.jsp
var pathName = window.document.location.pathname;
var pos = curPath.indexOf(pathName);
//获取主机地址,如: http://localhost:8080
var basePath = curPath.substring(0, pos);
//建立webSocket连接
//var webSocktPath = (basePath+"/websocket").replace("https","wss");
var webSocktPath = (basePath+"/websocket").replace("http","ws");
var websocket = new WebSocket(webSocktPath);
//打开webSokcet连接时回调该函数
websocket.onopen = function () {
console.log("Socket open");
}
websocket.onerror = function (){
console.log("Socket error");
}
//接收信息
websocket.onmessage = function (event) {
var rxWin = layui.$('#rx_win');
rxWin.val(rxWin.val()+event.data + "\r\n");
var obj = document.getElementById("rx_win");
obj.scrollTop = obj.scrollHeight;
}
}
</script>

View File

@ -47,14 +47,14 @@
**/
table.render({
elem: '#baseParaTableId',
url: '/gnss/group/list',//假数据
url: '/gnss/group/list',
toolbar: '#toolbarTable',
cols: [[
{field: 'id', title: '组号', sort: true},
{field: 'work_cycle', title: '工作周期(分钟)'},
{field: 'active_time', title: '激活时长(分钟)'},
{field: 'active_offset', title: '偏置(分钟)'},
{field: 'sample', title: '采样周期(秒'},
{field: 'rs_adv', title: '基准站提前时间(分钟'},
{field: 'power_mode', title: '功耗模式',templet: '#modeTrans'},
{field: 'device_num', title: '关联设备数'},
{title: '操作', toolbar: '#currentTableBar', align: "center"}
@ -109,7 +109,7 @@
layero.find('#work_cycle').val(data.work_cycle);
layero.find('#active_time').val(data.active_time);
layero.find('#active_offset').val(data.active_offset);
layero.find('#sample').val(data.sample);
layero.find('#rs_adv').val(data.rs_adv);
layero.find('#power_mode').val(data.power_mode);
}
});
@ -244,10 +244,11 @@
cols: [[
{field: 'id', title: '组号', sort: true},
{field: 'description', title: '名称'},
{field: 'addr', title: '地址'},
{field: 'port', title: '端口号'},
{field: 'fwd_template', title: '模板'},
{field: 'device_num', title: '关联设备数'},
{field: 'type1', title: '目标1类型', templet: '#type1Trans'},
{field: 'addr1', title: '目标1地址', templet: '<div >{{d.addr1}}:{{d.port1}}</div>'},
{field: 'type2', title: '目标2类型', templet: '#type2Trans'},
{field: 'addr2', title: '目标2地址', templet: '<div >{{d.addr2}}:{{d.port2}}</div>'},
{field: 'device_num', title: '关联设备数'},
{title: '操作', minWidth: 150, toolbar: '#currentTableBar', align: "center"}
]],
limit: 10,
@ -297,8 +298,12 @@
group_id.val(data.id);
group_id.attr('readonly',true);
layero.find('#description').val(data.description);
layero.find('#addr').val(data.addr);
layero.find('#port').val(data.port);
layero.find('#type1').val(data.type1);
layero.find('#addr1').val(data.addr1);
layero.find('#port1').val(data.port1);
layero.find('#type2').val(data.type2);
layero.find('#addr2').val(data.addr2);
layero.find('#port2').val(data.port2);
layero.find('#fwd_template').val(data.fwd_template);
}
});
@ -342,10 +347,31 @@
<script type="text/html" id="modeTrans">
{{# if(d.power_mode == 0){ }}
<span>正常</span>
{{# } else if(d.power_mode == 1){ }}
<span>低功耗</span>
{{# } else { }}
<span>极低功耗</span>
<span>正常</span>
{{# } }}
</script>
<script type="text/html" id="type1Trans">
{{# if(d.type1 == 0){ }}
<span>禁用</span>
{{# } else if(d.type1 == 1){ }}
<span>解算结果</span>
{{# } else if(d.type1 == 2){ }}
<span>原始数据</span>
{{# } else { }}
<span>NTRIP</span>
{{# } }}
</script>
<script type="text/html" id="type2Trans">
{{# if(d.type2 == 0){ }}
<span>禁用</span>
{{# } else if(d.type2 == 1){ }}
<span>解算结果</span>
{{# } else if(d.type2 == 2){ }}
<span>原始数据</span>
{{# } else { }}
<span>NTRIP</span>
{{# } }}
</script>

View File

@ -48,12 +48,13 @@
table.render({
elem: '#currentTableId',
url: 'api/gnss_msg.json',//假数据
url: '/gnss/msg/list_all',
cols: [[
{field: 'device_id', title: '设备号'},
{field: 'report_time', title: '时间', sort: true},
{field: 'type', title: '消息类型', sort: true},
{field: 'content', title: '内容摘要', minWidth: 150}
{field: 'deviceid', title: '设备号'},
{field: 'createtime', title: '上报时间', templet: "<div>{{layui.util.toDateString(d.createtime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
{field: 'msgtype', title: '消息类型', templet: "<div>{{d.msgtype.toString(16)}}</div>"},
{field: 'msglen', title: '消息长度'},
{field: 'content', title: '内容', width: '50%'}
]],
limits: [10, 15, 20, 25, 50, 100],
limit: 15,

View File

@ -38,11 +38,11 @@
table.render({
elem: '#currentTableId',
url: 'api/gnss_msg_status.json',//假数据
url: '/gnss/msg/list_status',
cols: [[
{field: 'device_id', title: '设备号'},
{field: 'report_time', title: '时间'},
{field: 'local_time', title: '设备时间'},
{field: 'deviceid', title: '设备号'},
{field: 'updatetime', title: '上报时间', templet: "<div>{{layui.util.toDateString(d.updatetime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
{field: 'devicetime', title: '设备时间', templet: "<div>{{layui.util.toDateString(d.devicetime, 'HH:mm:ss')}}</div>"},
{field: 'roll', title: 'roll'},
{field: 'pitch', title: 'pitch'},
{field: 'yaw', title: 'yaw'},

View File

@ -48,19 +48,21 @@
table.render({
elem: '#currentTableId',
url: 'api/gnss_msg_trx.json',//假数据
url: '/gnss/msg/list_trx',
cols: [[
{field: 'device_id', title: '设备号'},
{field: 'report_time', title: '时间'},
{field: 'local_time', title: '设备时间'},
{field: 'server_rx', title: '服务端收'},
{field: 'server_tx', title: '服务端发'},
{field: 'uart1_rx', title: '串口1收'},
{field: 'uart1_tx', title: '串口1发'},
{field: 'uart1_unknown', title: '串口1未知'},
{field: 'uart2_rx', title: '串口2收'},
{field: 'uart2_tx', title: '串口2发'},
{field: 'uart2_unknown', title: '串口2未知'}
{field: 'deviceid', title: '设备号'},
{field: 'updatetime', title: '上报时间', templet: "<div>{{layui.util.toDateString(d.updatetime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
{field: 'devicetime', title: '设备时间', templet: "<div>{{layui.util.toDateString(d.devicetime, 'HH:mm:ss')}}</div>"},
{field: 'd3xxbytes', title: 'D3XX'},
{field: 'b562bytes', title: 'B562'},
{field: 'servertxbytes', title: '服务端发'},
{field: 'serverrxbytes', title: '服务端收'},
{field: 'uart1txbytes', title: '串口1发'},
{field: 'uart1rxbytes', title: '串口1收'},
{field: 'uart1unknown', title: '串口1未知'},
{field: 'uart2txbytes', title: '串口1发'},
{field: 'uart2rxbytes', title: '串口1收'},
{field: 'uart2unknown', title: '串口1未知'}
]],
limits: [10, 15, 20, 25, 50, 100],
limit: 15,

View File

@ -26,18 +26,17 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">采样周期(秒)</label>
<label class="layui-form-label">基准站提前时间(分钟)</label>
<div class="layui-input-block">
<input type="number" name="sample" id="sample" placeholder="请输入采样周期" value="1" class="layui-input">
<input type="number" name="rs_adv" id="rs_adv" placeholder="请输入基准站提前时间" value="0" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">功耗模式</label>
<div class="layui-input-inline">
<select name="power_mode" id="power_mode" lay-filter="power_mode">
<option value="0">正常</option>
<option value="1">低功耗</option>
<option value="2">极低功耗</option>
<option value="0">低功耗</option>
<option value="1">正常</option>
</select>
</div>
</div>

View File

@ -14,19 +14,53 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">推送地址</label>
<div class="layui-input-block">
<input type="text" name="addr" id="addr" lay-verify="required" lay-reqtext="不能为空" placeholder="请输入域名或IP" value="4" class="layui-input">
<label class="layui-form-label">目标1类型</label>
<div class="layui-input-inline">
<select name="type1" id="type1" lay-filter="type1">
<option value="0">禁用</option>
<option value="1">解算结果</option>
<option value="2">原始数据</option>
<option value="3">NTRIP</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">端口号</label>
<label class="layui-form-label required">目标1地址</label>
<div class="layui-input-block">
<input type="number" name="port" id="port" lay-verify="required" lay-reqtext="不能为空" placeholder="请输入Shock值" value="1.5" class="layui-input">
<input type="text" name="addr1" id="addr1" lay-verify="required" lay-reqtext="不能为空" placeholder="请输入域名或IP" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">推送模板</label>
<label class="layui-form-label required">目标1端口号</label>
<div class="layui-input-block">
<input type="number" name="port1" id="port1" lay-verify="required" lay-reqtext="不能为空" placeholder="请输入端口号" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">目标2类型</label>
<div class="layui-input-inline">
<select name="type2" id="type2" lay-filter="type1">
<option value="0">禁用</option>
<option value="1">解算结果</option>
<option value="2">原始数据</option>
<option value="3">NTRIP</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">目标2地址</label>
<div class="layui-input-block">
<input type="text" name="addr2" id="addr2" placeholder="请输入域名或IP" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">目标2端口号</label>
<div class="layui-input-block">
<input type="number" name="port2" id="port2" placeholder="请输入端口号" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">解算结果推送模板</label>
<div class="layui-input-block">
<textarea name="fwd_template" id="fwd_template" class="layui-textarea"></textarea>
</div>