Compare commits
38 Commits
develop
...
feature/rt
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a4def045a | |||
| 0698640b26 | |||
| 1ddfb78c41 | |||
| 2d15995384 | |||
|
|
e852b7c640 | ||
|
|
15b4cbbe3e | ||
|
|
032ab5c46b | ||
| 1875b5e848 | |||
| 897507b97b | |||
|
|
40cee949a9 | ||
|
|
f68d548a2c | ||
|
|
2fd12b9361 | ||
|
|
e286c355ff | ||
|
|
cc5f3d0772 | ||
|
|
1e9e4b8680 | ||
|
|
6d0aa977fa | ||
|
|
1045e33d1a | ||
|
|
6d170eaed0 | ||
|
|
bedb2e3ef5 | ||
|
|
0acd7d31b8 | ||
|
|
063716d539 | ||
|
|
1996ef79d4 | ||
|
|
87d6f6073d | ||
|
|
cef333b56b | ||
|
|
21a6d2a8df | ||
|
|
db9d27b3a0 | ||
|
|
d250bfc671 | ||
| 7555c4c72b | |||
| d8456d165b | |||
|
|
983efb2d6b | ||
| 1191c17c35 | |||
|
|
f9b85cd2d5 | ||
|
|
a6f711f18d | ||
|
|
e6ad5a0a3d | ||
|
|
ae28807818 | ||
|
|
29af023a4f | ||
|
|
ee5bae1174 | ||
|
|
893fe1ea78 |
@ -19,7 +19,8 @@ public class OpLogManager {
|
||||
public final static Short OP_OBJ_GROUP = 6;
|
||||
public final static Short OP_OBJ_WARNING = 7;
|
||||
public final static Short OP_OBJ_SYS = 8;
|
||||
public final static Short OP_OBJ_DEVICE_MSG = 9;
|
||||
public final static Short OP_OBJ_FWD_RECORD = 9;
|
||||
|
||||
|
||||
@Autowired
|
||||
OpLogMapper opLogMapper;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.imdroid.beidou_fwd.service;
|
||||
|
||||
import com.imdroid.common.util.ThreadManager;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@ -13,7 +12,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class TCPClient {
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
@ -29,7 +27,7 @@ public class TCPClient {
|
||||
TCPListener listener;
|
||||
|
||||
public void start() {
|
||||
new Thread(this::connect, "forwarder tcp-client").start();
|
||||
//new Thread(this::connect, host+":"+port+" forwarder tcp-client").start();
|
||||
}
|
||||
|
||||
public void init(String dest_addr, int dest_port, TCPListener listener) {
|
||||
@ -54,7 +52,7 @@ public class TCPClient {
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
logger.info("netty client starting");
|
||||
logger.info("{}:{} tcp connecting...",host,port);
|
||||
//启动客户端去连接服务器端
|
||||
try {
|
||||
ChannelFuture cf = bootstrap.connect(host, port);
|
||||
@ -62,17 +60,22 @@ public class TCPClient {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
logger.info("{}:{} tcp connect failed. {}",host,port,future.cause().toString());
|
||||
//重连交给后端线程执行
|
||||
future.channel().eventLoop().schedule(() -> {
|
||||
logger.info("tcp client reconnect");
|
||||
/*future.channel().eventLoop().schedule(() -> {
|
||||
logger.info("{}:{} tcp client reconnect",host,port);
|
||||
try {
|
||||
connect();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, 5000, TimeUnit.MILLISECONDS);
|
||||
}, 5000, TimeUnit.MILLISECONDS);*/
|
||||
} else {
|
||||
logger.info("tcp client start success!");
|
||||
/*future.channel().config().setWriteBufferWaterMark(new WriteBufferWaterMark(
|
||||
1024 * 1024, // low
|
||||
4 *1024*1024 // high
|
||||
));*/
|
||||
logger.info("{}:{} tcp client start success!",host,port);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -80,18 +83,35 @@ public class TCPClient {
|
||||
this.channel = cf.channel();
|
||||
this.channel.closeFuture().sync();
|
||||
} catch (Exception e) {
|
||||
logger.error("netty client error:", e);
|
||||
logger.error(host+":"+port+" tcp connect error:", e);
|
||||
}
|
||||
}
|
||||
|
||||
boolean tryReconnect() throws Exception{
|
||||
new Thread(this::connect, host+":"+port+" forwarder tcp-client").start();
|
||||
for(int i=0; i<20; i++){
|
||||
Thread.sleep(50);
|
||||
if(channel!=null && channel.isActive()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void writeAndFlush(String json) {
|
||||
ByteBuf sendBuffer = Unpooled.buffer();
|
||||
sendBuffer.writeBytes(json.getBytes(StandardCharsets.UTF_8));
|
||||
//logger.info("send to {}: {}",host,json);
|
||||
if(channel==null || !channel.isActive()){
|
||||
try {
|
||||
if(!tryReconnect()) return;
|
||||
} catch (Exception e) {
|
||||
logger.error(e.toString());
|
||||
}
|
||||
}
|
||||
channel.writeAndFlush(sendBuffer).addListener(future -> {
|
||||
if (future.isSuccess()) {
|
||||
logger.info("send to tcp:"+host+" succeed.");
|
||||
} else {
|
||||
logger.info("send to tcp:"+host+" failed.");
|
||||
logger.info("send to tcp: {} failed. {}",host,future.cause().toString());
|
||||
if(listener!=null){
|
||||
listener.onMessage("failed");
|
||||
}
|
||||
@ -103,8 +123,8 @@ public class TCPClient {
|
||||
connectTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void onDisconnect(){
|
||||
if(connectTime.isBefore(LocalDateTime.now().minusMinutes(1))) {
|
||||
public void onDisconnect(boolean isIdle){
|
||||
/*if(connectTime.isBefore(LocalDateTime.now().minusMinutes(1))) {
|
||||
connect();
|
||||
}
|
||||
else{
|
||||
@ -114,8 +134,8 @@ public class TCPClient {
|
||||
} catch (Exception e) {
|
||||
logger.error(e.toString());
|
||||
}
|
||||
},60, TimeUnit.SECONDS);
|
||||
}
|
||||
},isIdle?30:10, TimeUnit.SECONDS);
|
||||
}*/
|
||||
}
|
||||
|
||||
public void onMessage(String msg){
|
||||
|
||||
@ -40,13 +40,14 @@ public class TcpMessageHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
logger.info("tcp channel inactive");
|
||||
tcpClient.onDisconnect();
|
||||
tcpClient.onDisconnect(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
logger.error("TcpMessageHandler error: {}", cause.toString());
|
||||
ctx.close();
|
||||
tcpClient.onDisconnect(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -37,17 +37,17 @@ public class Forwarder {
|
||||
static boolean isFwdTableInit = true;//false;
|
||||
|
||||
@Autowired
|
||||
private GnssDeviceMapper deviceMapper;
|
||||
GnssDeviceMapper deviceMapper;
|
||||
|
||||
@Autowired
|
||||
private GnssCalcDataMapper gnssDataMapper;
|
||||
GnssCalcDataMapper gnssDataMapper;
|
||||
|
||||
@Autowired
|
||||
private FwdRecordMapper fwdRecordsMapper;
|
||||
FwdRecordMapper fwdRecordsMapper;
|
||||
@Autowired
|
||||
private ResendRecordMapper resendRecordMapper;
|
||||
ResendRecordMapper resendRecordMapper;
|
||||
@Autowired
|
||||
private GnssStatusMapper gnssStatusMapper;
|
||||
GnssStatusMapper gnssStatusMapper;
|
||||
|
||||
@Autowired
|
||||
GnssGroupFwdMapper fwdMapper;
|
||||
|
||||
@ -49,7 +49,7 @@ public class GXJKForwarder extends Forwarder {
|
||||
/**
|
||||
* 每半小时转发GNSS解算结果
|
||||
*/
|
||||
@Scheduled(cron = "0 0/10 * * * ?") // 每30分钟执行一次
|
||||
@Scheduled(cron = "0 0/30 * * * ?") // 每30分钟执行一次
|
||||
private void forwardGnss() {
|
||||
logger.debug("gxjk forwardGnss");
|
||||
if(mqttClient.isConnected()) forwardCurrentGnss();
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
package com.imdroid.beidou_fwd.task;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.imdroid.beidou_fwd.service.TCPClient;
|
||||
import com.imdroid.secapi.dto.ResendRecord;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class GXJSForwarder extends GXXfzForwarder{
|
||||
private final String FORWARDER_NAME = "广西新发展";
|
||||
@Value("${xfz.server.host}")
|
||||
private String host;
|
||||
|
||||
@Value("${xfz.server.port}")
|
||||
private int port;
|
||||
private boolean enabled=true;
|
||||
|
||||
@PostConstruct
|
||||
void registerMe(){
|
||||
init(FORWARDER_NAME, "TCP "+host+":"+port,1,FWD_DEVICE_ID,30);
|
||||
xfzTcpClient = new TCPClient();
|
||||
xfzTcpClient.init(host, port,listener);
|
||||
if(!enabled) return;
|
||||
|
||||
xfzTcpClient.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 每半小时转发GNSS解算结果
|
||||
*/
|
||||
@Scheduled(cron = "0 0/30 * * * ?") // 每30分钟执行一次
|
||||
private void forwardGnss() {
|
||||
if(!enabled) return;
|
||||
logger.debug("gxjs forwardGnss");
|
||||
forwardCurrentGnss();
|
||||
}
|
||||
|
||||
@Override
|
||||
void forwardHistoryGnss(){
|
||||
// 1.从转发记录表里检索待补传记录时间表,含设备Id,时间段
|
||||
QueryWrapper<ResendRecord> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("fwd_group_id",fwdGroupId);
|
||||
queryWrapper.eq("state",ResendRecord.STATE_BREAK_POINT);
|
||||
queryWrapper.ge("createtime", LocalDateTime.now().minusDays(30));
|
||||
List<ResendRecord> resendRecordsList = resendRecordMapper.selectList(queryWrapper);
|
||||
|
||||
if(resendRecordsList!=null && resendRecordsList.size()>0){
|
||||
//修改状态
|
||||
UpdateWrapper<ResendRecord> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("fwd_group_id",fwdGroupId);
|
||||
updateWrapper.eq("state",ResendRecord.STATE_BREAK_POINT);
|
||||
updateWrapper.ge("createtime", LocalDateTime.now().minusDays(30));
|
||||
updateWrapper.set("state",ResendRecord.STATE_FWDING);
|
||||
int updateNum = resendRecordMapper.update(null, updateWrapper);
|
||||
logger.debug("{} forward history records: {}, update {}",fwdGroupId, resendRecordsList.size(),updateNum);
|
||||
// 2.检索这个这个时间段的解算结果,如果有数据则单个终端转发,标志记录为已补传
|
||||
for(ResendRecord record:resendRecordsList){
|
||||
if(record.getProjectid()!=null)
|
||||
logger.debug("{} forward history {}",fwdGroupId, record.getProjectid());
|
||||
forwardBatchGnssRecords(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import com.imdroid.beidou_fwd.service.TCPClient;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@ -11,20 +12,31 @@ import javax.annotation.PostConstruct;
|
||||
@Component
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class GXXfz2Forwarder extends GXXfzForwarder{
|
||||
private String FORWARDER_NAME = "广西路建";
|
||||
public class GXLJForwarder extends GXXfzForwarder{
|
||||
private final String FORWARDER_NAME = "广西路建";
|
||||
@Value("${gxlj.server.host}")
|
||||
private String host;
|
||||
|
||||
@Value("${gxlj.server.port}")
|
||||
private int port;
|
||||
private boolean enabled=true;
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
void registerMe(){
|
||||
init(FORWARDER_NAME, "TCP "+host+":"+port,6,FWD_DEVICE_ID,30);
|
||||
xfzTcpClient = new TCPClient();
|
||||
xfzTcpClient.init(host, port,listener);
|
||||
if(!enabled) return;
|
||||
xfzTcpClient.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 每半小时转发GNSS解算结果
|
||||
*/
|
||||
@Scheduled(cron = "0 0/30 * * * ?") // 每30分钟执行一次
|
||||
private void forwardGnss() {
|
||||
if(!enabled) return;
|
||||
logger.debug("gxlj forwardGnss");
|
||||
forwardCurrentGnss();
|
||||
}
|
||||
}
|
||||
@ -6,31 +6,15 @@ import com.imdroid.beidou_fwd.service.TCPListener;
|
||||
import com.imdroid.common.util.GsonUtil;
|
||||
import com.imdroid.common.util.NumberUtils;
|
||||
import com.imdroid.secapi.dto.GnssCalcData;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class GXXfzForwarder extends Forwarder{
|
||||
private final String FORWARDER_NAME = "广西新发展";
|
||||
@Value("${xfz.server.host}")
|
||||
private String host;
|
||||
|
||||
@Value("${xfz.server.port}")
|
||||
private int port;
|
||||
|
||||
TCPClient xfzTcpClient;
|
||||
|
||||
static class XFZTCPListener implements TCPListener{
|
||||
class XFZTCPListener implements TCPListener{
|
||||
public static final int STATE_NO_ACK = 0;
|
||||
public static final int STATE_OK = 1;
|
||||
public static final int STATE_FAILED = 2;
|
||||
@ -51,36 +35,16 @@ public class GXXfzForwarder extends Forwarder{
|
||||
|
||||
@Override
|
||||
public void onMessage(String msg) {
|
||||
logger.info("{} client rx: {}",fwdGroupId, msg);
|
||||
if(msg.contains("succeed")) state = STATE_OK;
|
||||
else state = STATE_FAILED;
|
||||
}
|
||||
}
|
||||
XFZTCPListener listener = new XFZTCPListener();
|
||||
@PostConstruct
|
||||
void registerMe(){
|
||||
init(FORWARDER_NAME, "TCP "+host+":"+port,1,FWD_DEVICE_ID,30);
|
||||
xfzTcpClient = new TCPClient();
|
||||
xfzTcpClient.init(host, port,listener);
|
||||
xfzTcpClient.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 每半小时转发GNSS解算结果
|
||||
*/
|
||||
@Scheduled(cron = "0 0/30 * * * ?") // 每30分钟执行一次
|
||||
private void forwardGnss() {
|
||||
logger.debug("xfz forwardGnss");
|
||||
forwardCurrentGnss();
|
||||
}
|
||||
/*
|
||||
@Scheduled(cron = "0 0/10 * * * ?") // 每30分钟执行一次
|
||||
private void checkDevice() {
|
||||
//logger.debug("zny checkDevice");
|
||||
checkOfflineDevice("2345053","2350106","2350124");
|
||||
}*/
|
||||
|
||||
@Override
|
||||
int send(String projectId, List<GnssCalcData> records, LocalDateTime sentTime){
|
||||
int batchNum = 0;
|
||||
int sendNum = 0;
|
||||
if(records.size() == 0) return 0;
|
||||
|
||||
@ -104,27 +68,48 @@ public class GXXfzForwarder extends Forwarder{
|
||||
// 经纬度
|
||||
data.setDevLng(locationRecord.getR9250e());
|
||||
data.setDevLat(locationRecord.getR9250n());
|
||||
sendNum++;
|
||||
// 发送
|
||||
batchNum++;
|
||||
|
||||
if(batchNum==20){
|
||||
String json = "#" + GsonUtil.toJson(xfzTcpMessage) + "!";
|
||||
//logger.debug("project {}: forwad {} gnss records to {}",projectId, dataList.size(),fwdGroupId);
|
||||
//logger.debug(json);
|
||||
try {
|
||||
listener.clear();
|
||||
xfzTcpClient.writeAndFlush(json);
|
||||
//等待应答
|
||||
if(checkResult()) sendNum += batchNum;
|
||||
} catch (Exception e1) {
|
||||
logger.error(e1.toString());
|
||||
}
|
||||
batchNum = 0;
|
||||
dataList.clear();
|
||||
}
|
||||
|
||||
}
|
||||
String json = "#" + GsonUtil.toJson(xfzTcpMessage) + "!";
|
||||
logger.debug("project " + projectId + ": push calculation result to XFZ");
|
||||
logger.debug(json);
|
||||
try {
|
||||
listener.clear();
|
||||
xfzTcpClient.writeAndFlush(json);
|
||||
//等待应答
|
||||
if(!checkResult()) sendNum = 0;
|
||||
} catch (Exception e1) {
|
||||
sendNum = 0;
|
||||
e1.printStackTrace();
|
||||
|
||||
if(batchNum>0){
|
||||
String json = "#" + GsonUtil.toJson(xfzTcpMessage) + "!";
|
||||
logger.debug("project {}: forwad {} gnss records to {}",projectId, dataList.size(),fwdGroupId);
|
||||
logger.debug(json);
|
||||
try {
|
||||
listener.clear();
|
||||
xfzTcpClient.writeAndFlush(json);
|
||||
//等待应答
|
||||
if(checkResult()) sendNum += batchNum;
|
||||
} catch (Exception e1) {
|
||||
logger.error(e1.toString());
|
||||
}
|
||||
dataList.clear();
|
||||
}
|
||||
return sendNum;
|
||||
}
|
||||
|
||||
boolean checkResult() throws InterruptedException {
|
||||
// 等待应答,最多等1s
|
||||
// 等待应答,最多等500ms
|
||||
for(int i=0; i<10; i++){
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(50);
|
||||
if(listener.state == XFZTCPListener.STATE_OK) return true;
|
||||
else if(listener.state == XFZTCPListener.STATE_FAILED) return false;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import com.imdroid.beidou_fwd.service.TCPClient;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@ -12,20 +13,30 @@ import javax.annotation.PostConstruct;
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class GZBForwarder extends GXXfzForwarder{
|
||||
private String FORWARDER_NAME = "葛洲坝";
|
||||
private final String FORWARDER_NAME = "葛洲坝";
|
||||
@Value("${gzb.server.host}")
|
||||
private String host;
|
||||
|
||||
@Value("${gzb.server.port}")
|
||||
private int port;
|
||||
private boolean enabled=true;
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
void registerMe(){
|
||||
init(FORWARDER_NAME, "TCP "+host+":"+port,1,FWD_DEVICE_ID,30);
|
||||
xfzTcpClient = new TCPClient();
|
||||
xfzTcpClient.init(host, port,listener);
|
||||
if(!enabled) return;
|
||||
xfzTcpClient.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 每半小时转发GNSS解算结果
|
||||
*/
|
||||
@Scheduled(cron = "0 0/30 * * * ?") // 每30分钟执行一次
|
||||
private void forwardGnss() {
|
||||
if(!enabled) return;
|
||||
logger.debug("gzb forwardGnss");
|
||||
forwardCurrentGnss();
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,17 +28,18 @@ import java.util.List;
|
||||
@EnableScheduling
|
||||
public class GZYMQTTForwarder extends Forwarder {
|
||||
static final String FORWARDER_NAME = "贵州交勘院MQTT";
|
||||
@Value("${mqtt.server.brokerUrl}")
|
||||
@Value("${gzymqtt.server.brokerUrl}")
|
||||
private String brokerUrl;
|
||||
@Value("${mqtt.server.username}")
|
||||
@Value("${gzymqtt.server.username}")
|
||||
private String username;
|
||||
@Value("${mqtt.server.password}")
|
||||
@Value("${gzymqtt.server.password}")
|
||||
private String password;
|
||||
@Value("${mqtt.server.clientid}")
|
||||
@Value("${gzymqtt.server.clientid}")
|
||||
private String clientid;
|
||||
@Value("${mqtt.server.topic}")
|
||||
@Value("${gzymqtt.server.topic}")
|
||||
private String topic;
|
||||
|
||||
//@Value("${gzymqtt.server.enabled}")
|
||||
private boolean enabled=true;
|
||||
@Autowired
|
||||
GnssStatusMsgMapper statusMsgMapper;
|
||||
MQTTClient mqttClient;
|
||||
@ -47,6 +48,7 @@ public class GZYMQTTForwarder extends Forwarder {
|
||||
void registerMe() throws MqttException {
|
||||
init(FORWARDER_NAME, "MQTT "+brokerUrl,2,FWD_DEVICE_ALIAS_NAME,30);
|
||||
mqttClient = new MQTTClient(brokerUrl, username, password,clientid);
|
||||
if(!enabled) return;
|
||||
try{
|
||||
mqttClient.connect();
|
||||
}
|
||||
@ -61,6 +63,7 @@ public class GZYMQTTForwarder extends Forwarder {
|
||||
@Scheduled(cron = "0 0 0/1 * * ?") // 每小时执行一次
|
||||
//@Scheduled(cron = "0 0/5 * * * ?") // 每30分钟执行一次
|
||||
private void forwardGnss() {
|
||||
if(!enabled) return;
|
||||
logger.debug("gzy mqtt forwardGnss");
|
||||
if(mqttClient.isConnected()) {
|
||||
forwardCurrentGnss();
|
||||
@ -75,6 +78,11 @@ public class GZYMQTTForwarder extends Forwarder {
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
@Scheduled(cron = "0 0/5 * * * ?") // 每30分钟执行一次
|
||||
void forwardHistoryGnss() {
|
||||
super.forwardHistoryGnss();
|
||||
}*/
|
||||
|
||||
@Override
|
||||
int send(String projectId, List<GnssCalcData> records, LocalDateTime sentTime) {
|
||||
|
||||
@ -44,11 +44,14 @@ public class GNSSCalcFilterService {
|
||||
Integer xyfilterCycle = groupCalc.getFilter_hour();
|
||||
Integer zfilterCycle = groupCalc.getZfilter_hour();
|
||||
if(null == zfilterCycle) zfilterCycle = xyfilterCycle;
|
||||
VaryFilterCycle varyCycle = autoCycleDevices.get(deviceId);
|
||||
if(varyCycle!=null){
|
||||
xyfilterCycle = varyCycle.filterCycleHour;
|
||||
zfilterCycle = varyCycle.filterCycleHour;
|
||||
if(groupCalc.getAuto_filter()) {
|
||||
VaryFilterCycle varyCycle = autoCycleDevices.get(deviceId);
|
||||
if (varyCycle != null) {
|
||||
xyfilterCycle = varyCycle.filterCycleHour;
|
||||
zfilterCycle = varyCycle.filterCycleHour;
|
||||
}
|
||||
}
|
||||
else autoCycleDevices.remove(deviceId);
|
||||
|
||||
// 平滑处理
|
||||
calcFilterLocation(newRecord, referPos, xyfilterCycle, zfilterCycle, groupCalc.getFilter_min_hour(),
|
||||
@ -166,7 +169,7 @@ public class GNSSCalcFilterService {
|
||||
newRecord.setAuxn(NumberUtils.scaleTwo(msumN / minCount));
|
||||
newRecord.setAuxd(NumberUtils.scaleTwo(msumD / minCount));
|
||||
//滤波窗口里的时间跨度超过滤波时间的2/3才认为稳定
|
||||
newRecord.setStabled(lastRecordTime.isBefore(newRecordTime.minusHours(filterCycleHour * 2 / 3)));
|
||||
newRecord.setStabled(lastRecordTime.isBefore(newRecordTime.minusHours(filterCycleHour * 1 / 2)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -246,7 +249,7 @@ public class GNSSCalcFilterService {
|
||||
newRecord.setAuxn(NumberUtils.scaleTwo(msumN / minCount));
|
||||
newRecord.setAuxd(NumberUtils.scaleTwo(msumD / minCount));
|
||||
//滤波窗口里的时间跨度超过滤波时间的2/3才认为稳定
|
||||
newRecord.setStabled(lastRecordTime.isBefore(newRecordTime.minusHours(filterCycleHour * 2 / 3)));
|
||||
newRecord.setStabled(lastRecordTime.isBefore(newRecordTime.minusHours(filterCycleHour * 1 / 2)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,12 +9,14 @@ import com.imdroid.sideslope.bd.Gga;
|
||||
import com.imdroid.sideslope.bd.Rtcm1005;
|
||||
import com.imdroid.sideslope.message.D331RtcmMessage;
|
||||
import com.imdroid.sideslope.ntrip.UdpNtripServer;
|
||||
import com.imdroid.sideslope.service.Device;
|
||||
import com.imdroid.sideslope.service.DeviceService;
|
||||
import com.imdroid.sideslope.ntrip.RtcmUdpForwarder;
|
||||
import com.imdroid.sideslope.server.DeviceChannel;
|
||||
import com.imdroid.sideslope.server.OnlineChannels;
|
||||
import com.imdroid.sideslope.service.DataPersistService;
|
||||
import com.imdroid.sideslope.bd.RtcmGgaUtil;
|
||||
import com.imdroid.sideslope.server.tcp.RtcmSpecificDeviceTcpServer;
|
||||
import com.imdroid.sideslope.service.Device;
|
||||
import com.imdroid.sideslope.service.DeviceService;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.slf4j.Logger;
|
||||
@ -45,10 +47,14 @@ public class D331RtcmMessageExecutor implements Executor<D331RtcmMessage, Void>
|
||||
private DataPersistService dataPersistService;
|
||||
@Autowired
|
||||
UdpNtripServer ntripServer;
|
||||
@Autowired
|
||||
RtcmUdpForwarder rtcmUdpForwarder;
|
||||
@Autowired
|
||||
RtcmSpecificDeviceTcpServer rtcmSpecificDeviceTcpServer;
|
||||
|
||||
// 添加一个成员变量用于追踪每个测站最后一次转发D300数据的时间
|
||||
private final Map<String, Long> lastD300ForwardTimeMap = new ConcurrentHashMap<>();
|
||||
private static final long D300_FORWARD_INTERVAL = 10000; // 10秒,单位毫秒
|
||||
private static final long D300_FORWARD_INTERVAL = 5000; // 5秒,单位毫秒
|
||||
|
||||
@Override
|
||||
public Void execute(D331RtcmMessage message) {
|
||||
@ -288,6 +294,19 @@ public class D331RtcmMessageExecutor implements Executor<D331RtcmMessage, Void>
|
||||
//System.out.println("挂载点: " + mountpoint);
|
||||
//System.out.println("RTCM数据: " + rtcm);
|
||||
ntripServer.send(mountpoint, rtcm);
|
||||
|
||||
// 同时转发到12001端口
|
||||
rtcmUdpForwarder.forward(mountpoint, rtcm);
|
||||
|
||||
// 如果是特定设备的数据,则通过TCP服务器转发
|
||||
if (mountpoint.equals(rtcmSpecificDeviceTcpServer.getTargetDeviceId())) {
|
||||
for (String rtcmHex : rtcm) {
|
||||
byte[] rtcmData = ByteUtil.hexStringTobyte(rtcmHex);
|
||||
rtcmSpecificDeviceTcpServer.broadcastRtcmData(rtcmData);
|
||||
}
|
||||
logger.debug("Forwarded RTCM data from device {} to TCP server on port {}",
|
||||
mountpoint, rtcmSpecificDeviceTcpServer.getPort());
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("处理NTRIP数据失败, 挂载点: {}, 错误: {}", mountpoint, e.getMessage());
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
package com.imdroid.sideslope.ntrip;
|
||||
|
||||
import com.imdroid.common.util.ByteUtil;
|
||||
import com.imdroid.common.util.ThreadManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 将RTCM数据转发到指定UDP端口
|
||||
*/
|
||||
@Service
|
||||
public class RtcmUdpForwarder {
|
||||
private final Logger logger = LoggerFactory.getLogger(RtcmUdpForwarder.class);
|
||||
|
||||
@Value("${rtcm.forward.port:12001}")
|
||||
private Integer forwardPort;
|
||||
|
||||
private DatagramSocket socket;
|
||||
private DatagramPacket outPacket;
|
||||
|
||||
/**
|
||||
* 转发RTCM数据到UDP端口
|
||||
* @param mount 挂载点名称
|
||||
* @param hexRtcm 差分信息16进制字符串
|
||||
*/
|
||||
public void forward(String mount, String hexRtcm) {
|
||||
ThreadManager.getSingleThreadPool(RtcmUdpForwarder.class.getName()).execute(() -> {
|
||||
try {
|
||||
if (socket == null) socket = new DatagramSocket();
|
||||
if (outPacket == null) outPacket = new DatagramPacket(new byte[0], 0, InetAddress.getByName("localhost"), forwardPort);
|
||||
|
||||
// 转发原始RTCM数据,不添加挂载点信息
|
||||
byte[] rtcmData = ByteUtil.hexStringTobyte(hexRtcm);
|
||||
outPacket.setData(rtcmData);
|
||||
socket.send(outPacket);
|
||||
|
||||
logger.debug("Forwarded RTCM data from mountpoint {} to UDP port {}, data length: {}",
|
||||
mount, forwardPort, rtcmData.length);
|
||||
} catch (Exception e) {
|
||||
socket = null;
|
||||
outPacket = null;
|
||||
logger.error("Failed to forward RTCM data: {}", e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 转发多条RTCM数据到UDP端口
|
||||
* @param mount 挂载点名称
|
||||
* @param hexRtcmList 差分信息16进制字符串列表
|
||||
*/
|
||||
public void forward(String mount, List<String> hexRtcmList) {
|
||||
logger.debug("Forwarding {} RTCM messages from mountpoint {}", hexRtcmList.size(), mount);
|
||||
for (String hexRtcm : hexRtcmList) {
|
||||
forward(mount, hexRtcm);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package com.imdroid.sideslope.server.tcp;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* 特定设备RTCM数据TCP服务器
|
||||
*/
|
||||
@Component
|
||||
public class RtcmSpecificDeviceTcpServer implements ApplicationRunner {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RtcmSpecificDeviceTcpServer.class);
|
||||
|
||||
@Value("${rtcm.specific.device.port:12002}")
|
||||
private Integer port;
|
||||
|
||||
@Value("${rtcm.specific.device.id:3530795}")
|
||||
private String targetDeviceId;
|
||||
|
||||
// 存储所有连接的客户端通道
|
||||
private final CopyOnWriteArrayList<SocketChannel> clientChannels = new CopyOnWriteArrayList<>();
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
new Thread(this::start0, "specific-device-tcp-server").start();
|
||||
}
|
||||
|
||||
private void start0() {
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
|
||||
try {
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
bootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.option(ChannelOption.SO_BACKLOG, 128)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
// 添加到客户端列表
|
||||
clientChannels.add(ch);
|
||||
|
||||
// 当连接关闭时,从列表中移除
|
||||
ch.closeFuture().addListener(future -> clientChannels.remove(ch));
|
||||
|
||||
logger.info("New client connected: {}", ch.remoteAddress());
|
||||
}
|
||||
});
|
||||
|
||||
ChannelFuture future = bootstrap.bind(port).sync();
|
||||
logger.info("Specific device TCP server started on port {} for device {}", port, targetDeviceId);
|
||||
|
||||
future.channel().closeFuture().sync();
|
||||
} catch (Exception e) {
|
||||
logger.error("Error starting Specific device TCP server at port {}", port, e);
|
||||
} finally {
|
||||
workerGroup.shutdownGracefully();
|
||||
bossGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向所有连接的客户端发送RTCM数据
|
||||
* @param rtcmData RTCM数据字节数组
|
||||
*/
|
||||
public void broadcastRtcmData(byte[] rtcmData) {
|
||||
if (clientChannels.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (SocketChannel channel : clientChannels) {
|
||||
if (channel.isActive()) {
|
||||
channel.writeAndFlush(io.netty.buffer.Unpooled.wrappedBuffer(rtcmData));
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Broadcasted RTCM data to {} clients, data length: {}", clientChannels.size(), rtcmData.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目标设备ID
|
||||
* @return 目标设备ID
|
||||
*/
|
||||
public String getTargetDeviceId() {
|
||||
return targetDeviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器端口
|
||||
* @return 服务器端口
|
||||
*/
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
@ -74,6 +74,7 @@ public class Device {
|
||||
private BigDecimal used;
|
||||
|
||||
LocalDateTime lastRxTime;
|
||||
LocalDateTime lastD3f0f2Time;
|
||||
LocalDateTime lastD3f2Time;
|
||||
short noFixedAndFloatResult=0;
|
||||
|
||||
@ -89,13 +90,14 @@ public class Device {
|
||||
short abnormalD341Num = 0;
|
||||
byte cfgChannelType = CHANNEL_TYPE_TCP; // 0:TCP;1:DUP
|
||||
byte dataChannelType = CHANNEL_TYPE_UDP; // 0:TCP;1:DUP
|
||||
int lasQuality = 0;
|
||||
|
||||
public void updateRx(int head, int bytes,int count){
|
||||
lastRxHead = head;
|
||||
|
||||
switch (head){
|
||||
case 0xd3f0:
|
||||
//lastD3f0f2Time = LocalDateTime.now();
|
||||
lastD3f0f2Time = LocalDateTime.now();
|
||||
//clearStat();
|
||||
break;
|
||||
case 0xd3f2:
|
||||
@ -128,6 +130,7 @@ public class Device {
|
||||
}
|
||||
|
||||
public void updateCalcQuality(int quality){
|
||||
lasQuality = quality;
|
||||
if(b562AsCalc) {
|
||||
if (quality == UBXUtil.FIX_RESULT) fixedNum++;
|
||||
else if (quality == UBXUtil.FLOAT_RESULT) floatNum++;
|
||||
|
||||
@ -341,6 +341,65 @@ public class GnssDeviceController extends BasicController{
|
||||
return null;
|
||||
}
|
||||
|
||||
@PostMapping("/gnss/device/cont_loc")
|
||||
@ResponseBody
|
||||
public JSONObject contLoc(String deviceid,String createtime){
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
|
||||
//old data
|
||||
String[] paras = createtime.split(",");
|
||||
String oldDeviceId;
|
||||
if(paras.length==2){
|
||||
oldDeviceId = paras[0];
|
||||
createtime = paras[1];
|
||||
}
|
||||
else {
|
||||
oldDeviceId = deviceid;
|
||||
createtime = paras[0];
|
||||
}
|
||||
QueryWrapper<GnssCalcData> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("deviceid",oldDeviceId);
|
||||
queryWrapper.eq("enabled",1);
|
||||
queryWrapper.eq("createtime",createtime);
|
||||
queryWrapper.last("limit 1");
|
||||
queryWrapper.isNotNull("rpose");
|
||||
GnssCalcData calcOldData = gnssCalcDataMapper.selectOne(queryWrapper);
|
||||
if(calcOldData==null) {
|
||||
jsonObject.put("code",1);
|
||||
jsonObject.put("msg","该时间无有效解算结果");
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
// new data
|
||||
QueryWrapper<GnssCalcData> queryWrapper1 = new QueryWrapper<>();
|
||||
queryWrapper1.eq("deviceid",deviceid);
|
||||
queryWrapper1.eq("enabled",1);
|
||||
queryWrapper1.orderByDesc("createtime");
|
||||
queryWrapper1.last("limit 1");
|
||||
queryWrapper1.isNotNull("rpose");
|
||||
GnssCalcData calcNewData = gnssCalcDataMapper.selectOne(queryWrapper1);
|
||||
if(calcNewData==null) {
|
||||
jsonObject.put("code",1);
|
||||
jsonObject.put("msg","无有效解算结果");
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
// 原来的初始值
|
||||
QueryWrapper<GnssDevice> deviceQueryWrapper = new QueryWrapper<>();
|
||||
deviceQueryWrapper.eq("deviceid",oldDeviceId);
|
||||
deviceQueryWrapper.last("limit 1");
|
||||
GnssDevice device = gnssDeviceMapper.selectOne(deviceQueryWrapper);
|
||||
if(device.getIpose()==null) device.setIpose(0.0);
|
||||
if(device.getIposn()==null) device.setIposn(0.0);
|
||||
if(device.getIposd()==null) device.setIposd(0.0);
|
||||
|
||||
jsonObject.put("code",0);
|
||||
jsonObject.put("ipose", NumberUtils.scaleTwo(calcNewData.getRpose()-calcOldData.getRpose()+device.getIpose()));
|
||||
jsonObject.put("iposn",NumberUtils.scaleTwo(calcNewData.getRposn()-calcOldData.getRposn()+device.getIposn()));
|
||||
jsonObject.put("iposd",NumberUtils.scaleTwo(calcNewData.getRposd()-calcOldData.getRposd()+device.getIposd()));
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
void updateBasicGroupAssociatedNum(GnssDevice newCfg, GnssDevice oldCfg){
|
||||
updateBasicGroupAssociatedNum(newCfg.getGroup_id());
|
||||
updateCalcGroupAssociatedNum(newCfg.getCalc_group_id());
|
||||
|
||||
@ -2,18 +2,16 @@ package com.imdroid.beidou.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.imdroid.beidou.common.HttpResult;
|
||||
import com.imdroid.beidou.service.CommonExcelService;
|
||||
import com.imdroid.secapi.dto.GnssGroupFwd;
|
||||
import com.imdroid.secapi.dto.GnssGroupFwdMapper;
|
||||
import com.imdroid.secapi.dto.ResendRecord;
|
||||
import com.imdroid.secapi.dto.ResendRecordMapper;
|
||||
import com.imdroid.secapi.dto.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@ -22,6 +20,10 @@ public class GnssResendController extends BasicController implements CommonExcel
|
||||
GnssGroupFwdMapper gnssGroupFwdMapper;
|
||||
@Autowired
|
||||
ResendRecordMapper resendRecordMapper;
|
||||
@Autowired
|
||||
TenantMapper tenantMapper;
|
||||
@Autowired
|
||||
OpLogManager opLogManager;
|
||||
|
||||
/********* 推送页面 *********/
|
||||
@RequestMapping("/page/resend_records")
|
||||
@ -33,6 +35,19 @@ public class GnssResendController extends BasicController implements CommonExcel
|
||||
|
||||
return "/page/resend_records";
|
||||
}
|
||||
@RequestMapping("/page/table/resend_record_add")
|
||||
public String gnssAddDev(Model m, HttpSession session) {
|
||||
initModel(m, session);
|
||||
//以下用于下拉框数据
|
||||
List<Tenant> tenants = tenantMapper.selectList(null);
|
||||
List<GnssGroupFwd> gnssGroupFwds = gnssGroupFwdMapper.selectList(null);
|
||||
|
||||
m.addAttribute("tenant_list", tenants);
|
||||
m.addAttribute("gnss_group_fwd_list", gnssGroupFwds);
|
||||
m.addAttribute("device", new GnssDevice());
|
||||
|
||||
return "/page/table/resend_record_add";
|
||||
}
|
||||
|
||||
/********* 推送数据 *********/
|
||||
@RequestMapping("/fwd/resend_records")
|
||||
@ -41,6 +56,40 @@ public class GnssResendController extends BasicController implements CommonExcel
|
||||
return this.pageList(session, page, limit, searchParams);
|
||||
}
|
||||
|
||||
@PostMapping("/fwd/resend_records/add")
|
||||
@ResponseBody
|
||||
public String update(HttpSession session, @RequestBody JSONObject object) throws Exception {
|
||||
// 从请求参数中创建对象
|
||||
ResendRecord resendRecord = JSONObject.toJavaObject(object,ResendRecord.class);
|
||||
resendRecord.setCreatetime(LocalDateTime.now());
|
||||
resendRecord.setState(ResendRecord.STATE_BREAK_POINT);
|
||||
int num = resendRecordMapper.insert(resendRecord);
|
||||
if (num == 0) {
|
||||
return HttpResult.failed();
|
||||
} else {
|
||||
opLogManager.addLog(getLoginUser(session),getTenantId(session),
|
||||
OpLogManager.OP_TYPE_ADD,
|
||||
OpLogManager.OP_OBJ_FWD_RECORD,
|
||||
"create new resend record");
|
||||
return HttpResult.ok();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/fwd/resend_records/delete")
|
||||
@ResponseBody
|
||||
public String delete(HttpSession session, @RequestParam String del_id) throws Exception {
|
||||
int num = resendRecordMapper.deleteById(del_id);
|
||||
opLogManager.addLog(getLoginUser(session),getTenantId(session),
|
||||
OpLogManager.OP_TYPE_DEL,
|
||||
OpLogManager.OP_OBJ_FWD_RECORD,
|
||||
del_id + " deleted");
|
||||
if (num == 0) {
|
||||
return HttpResult.failed();
|
||||
} else{
|
||||
return HttpResult.ok();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体类的class
|
||||
*
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
var CoordinateUtils = (function() {
|
||||
'use strict';
|
||||
|
||||
var pi = 3.14159265358979324;
|
||||
var a = 6378245.0;
|
||||
var ee = 0.00669342162296594323;
|
||||
|
||||
/**
|
||||
* 判断是否在国内,不在国内则不做偏移
|
||||
*/
|
||||
function outOfChina(lon, lat) {
|
||||
if ((lon < 72.004 || lon > 137.8347) && (lat < 0.8293 || lat > 55.8271)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function transformLat(x, y) {
|
||||
var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
|
||||
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
|
||||
ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
function transformLon(x, y) {
|
||||
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
|
||||
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
|
||||
ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
function transform(wgLat, wgLon) {
|
||||
var mars_point = {lon: 0, lat: 0};
|
||||
if (outOfChina(wgLon, wgLat)) {
|
||||
mars_point.lat = wgLat;
|
||||
mars_point.lon = wgLon;
|
||||
return mars_point;
|
||||
}
|
||||
var dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
|
||||
var dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
|
||||
var radLat = wgLat / 180.0 * pi;
|
||||
var magic = Math.sin(radLat);
|
||||
magic = 1 - ee * magic * magic;
|
||||
var sqrtMagic = Math.sqrt(magic);
|
||||
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
|
||||
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
|
||||
mars_point.lat = wgLat + dLat;
|
||||
mars_point.lon = wgLon + dLon;
|
||||
return mars_point;
|
||||
}
|
||||
|
||||
|
||||
function getMapCoordinates(lat, lon, mapType) {
|
||||
var coordinates;
|
||||
if (mapType === 'amap' || mapType === 'amap_satellite') {
|
||||
// 高德地图 WGS84 转换为 GCJ-02
|
||||
var gcjCoord = transform(lat, lon);
|
||||
coordinates = ol.proj.fromLonLat([gcjCoord.lon, gcjCoord.lat]);
|
||||
} else if (mapType.startsWith('google_')) {
|
||||
// Google地图使用WGS84坐标系
|
||||
coordinates = ol.proj.fromLonLat([lon, lat]);
|
||||
} else {
|
||||
// 天地图 CGCS2000,与WGS84实质一样
|
||||
coordinates = ol.proj.fromLonLat([lon, lat]);
|
||||
}
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
transform: transform,
|
||||
getMapCoordinates: getMapCoordinates,
|
||||
outOfChina: outOfChina
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,410 @@
|
||||
var DeviceMarkers = (function() {
|
||||
'use strict';
|
||||
|
||||
var greenFeatures = [];
|
||||
var orangeFeatures = [];
|
||||
var redFeatures = [];
|
||||
var allFeatures = [];
|
||||
var myLocationFeature = null;
|
||||
var myLocationInterval = null;
|
||||
var showDeviceId = true;
|
||||
var minZoomForLabels = 4;
|
||||
|
||||
|
||||
var map = null;
|
||||
var vectorSource = null;
|
||||
var vectorLayer = null;
|
||||
|
||||
|
||||
function init(mapInstance, vectorSourceInstance, vectorLayerInstance) {
|
||||
map = mapInstance;
|
||||
vectorSource = vectorSourceInstance;
|
||||
vectorLayer = vectorLayerInstance;
|
||||
}
|
||||
|
||||
|
||||
function createDeviceStyle(feature) {
|
||||
if (feature.get('isMyLocation')) {
|
||||
return new ol.style.Style({
|
||||
image: new ol.style.Icon({
|
||||
anchor: [0.5, 1],
|
||||
src: '../images/loc_blue.png',
|
||||
scale: 0.7
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
var deviceInfo = feature.get('deviceInfo');
|
||||
if (!deviceInfo) return null;
|
||||
|
||||
var iconSrc;
|
||||
var color = '#000';
|
||||
var isHovered = feature.get('hovered') === true;
|
||||
var scale = isHovered ? 0.85 : 0.7;
|
||||
|
||||
// 根据告警级别选择图标
|
||||
if (deviceInfo.warning == 2) {
|
||||
iconSrc = '../images/loc1_red.png';
|
||||
} else if (deviceInfo.warning == 1) {
|
||||
iconSrc = '../images/loc1_orange.png';
|
||||
} else {
|
||||
iconSrc = '../images/loc1_green.png';
|
||||
}
|
||||
|
||||
var style = new ol.style.Style({
|
||||
image: new ol.style.Icon({
|
||||
anchor: [0.5, 1],
|
||||
src: iconSrc,
|
||||
scale: scale
|
||||
})
|
||||
});
|
||||
|
||||
// 根据缩放级别和设置决定是否显示设备ID
|
||||
if (showDeviceId && map && map.getView().getZoom() >= minZoomForLabels) {
|
||||
style.setText(new ol.style.Text({
|
||||
text: deviceInfo.deviceid,
|
||||
offsetY: -30,
|
||||
fill: new ol.style.Fill({
|
||||
color: isHovered ? '#1aa094' : color
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#fff',
|
||||
width: isHovered ? 3 : 2
|
||||
}),
|
||||
font: isHovered ? 'bold 12px Arial' : '12px Arial'
|
||||
}));
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
|
||||
function addDeviceMarkers(deviceList) {
|
||||
if (!vectorSource || !deviceList) return;
|
||||
|
||||
var savedMyLocationFeature = myLocationFeature;
|
||||
|
||||
vectorSource.clear();
|
||||
greenFeatures = [];
|
||||
orangeFeatures = [];
|
||||
redFeatures = [];
|
||||
allFeatures = [];
|
||||
|
||||
for (var i = 0; i < deviceList.length; i++) {
|
||||
var device = deviceList[i];
|
||||
var currentMapType = getCurrentMapType();
|
||||
var mapCoordinates = CoordinateUtils.getMapCoordinates(
|
||||
device.latitude,
|
||||
device.longitude,
|
||||
currentMapType
|
||||
);
|
||||
|
||||
var feature = new ol.Feature({
|
||||
geometry: new ol.geom.Point(mapCoordinates),
|
||||
deviceInfo: device
|
||||
});
|
||||
|
||||
// 按告警级别分类
|
||||
if (device.warning == 2) {
|
||||
redFeatures.push(feature);
|
||||
} else if (device.warning == 1) {
|
||||
orangeFeatures.push(feature);
|
||||
} else {
|
||||
greenFeatures.push(feature);
|
||||
}
|
||||
|
||||
allFeatures.push(feature);
|
||||
vectorSource.addFeature(feature);
|
||||
}
|
||||
|
||||
if (savedMyLocationFeature) {
|
||||
vectorSource.addFeature(savedMyLocationFeature);
|
||||
}
|
||||
|
||||
// 强制更新样式
|
||||
if (vectorLayer) {
|
||||
vectorLayer.changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getCurrentMapType() {
|
||||
var mapTypeSelect = document.getElementById('mapTypeSelectNew');
|
||||
return mapTypeSelect ? mapTypeSelect.value : 'tianditu_satellite';
|
||||
}
|
||||
|
||||
|
||||
function showAllDevices() {
|
||||
if (!vectorSource) return;
|
||||
|
||||
var savedMyLocationFeature = myLocationFeature;
|
||||
vectorSource.clear();
|
||||
|
||||
if (savedMyLocationFeature) {
|
||||
vectorSource.addFeature(savedMyLocationFeature);
|
||||
}
|
||||
|
||||
for (var i = 0; i < allFeatures.length; i++) {
|
||||
vectorSource.addFeature(allFeatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function showWarning1Devices() {
|
||||
if (!vectorSource) return;
|
||||
|
||||
var savedMyLocationFeature = myLocationFeature;
|
||||
vectorSource.clear();
|
||||
|
||||
if (savedMyLocationFeature) {
|
||||
vectorSource.addFeature(savedMyLocationFeature);
|
||||
}
|
||||
|
||||
for (var i = 0; i < allFeatures.length; i++) {
|
||||
vectorSource.addFeature(allFeatures[i]);
|
||||
}
|
||||
|
||||
hideGreenFeatures();
|
||||
hideRedFeatures();
|
||||
}
|
||||
|
||||
|
||||
function showWarning2Devices() {
|
||||
if (!vectorSource) return;
|
||||
|
||||
var savedMyLocationFeature = myLocationFeature;
|
||||
vectorSource.clear();
|
||||
|
||||
if (savedMyLocationFeature) {
|
||||
vectorSource.addFeature(savedMyLocationFeature);
|
||||
}
|
||||
|
||||
for (var i = 0; i < allFeatures.length; i++) {
|
||||
vectorSource.addFeature(allFeatures[i]);
|
||||
}
|
||||
|
||||
hideGreenFeatures();
|
||||
hideOrangeFeatures();
|
||||
}
|
||||
|
||||
function hideGreenFeatures() {
|
||||
for (var i = 0; i < greenFeatures.length; i++) {
|
||||
vectorSource.removeFeature(greenFeatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function hideOrangeFeatures() {
|
||||
for (var i = 0; i < orangeFeatures.length; i++) {
|
||||
vectorSource.removeFeature(orangeFeatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function hideRedFeatures() {
|
||||
for (var i = 0; i < redFeatures.length; i++) {
|
||||
vectorSource.removeFeature(redFeatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function findDeviceById(deviceId) {
|
||||
// console.log('搜索设备:', deviceId, '(类型:', typeof deviceId, ')');
|
||||
// console.log('当前设备总数:', allFeatures.length);
|
||||
|
||||
var searchTerm = String(deviceId).trim();
|
||||
|
||||
for (var i = 0; i < allFeatures.length; i++) {
|
||||
var feature = allFeatures[i];
|
||||
var deviceInfo = feature.get('deviceInfo');
|
||||
if (deviceInfo) {
|
||||
var currentDeviceId = String(deviceInfo.deviceid);
|
||||
// console.log('检查设备:', currentDeviceId, '(类型:', typeof deviceInfo.deviceid, ')');
|
||||
|
||||
if (currentDeviceId === searchTerm) {
|
||||
// console.log('精确匹配找到设备:', currentDeviceId);
|
||||
return feature;
|
||||
}
|
||||
|
||||
if (currentDeviceId.indexOf(searchTerm) !== -1) {
|
||||
// console.log('部分匹配找到设备:', currentDeviceId);
|
||||
return feature;
|
||||
}
|
||||
|
||||
if (currentDeviceId.replace(/\s+/g, '') === searchTerm.replace(/\s+/g, '')) {
|
||||
// console.log('去除空格后匹配找到设备:', currentDeviceId);
|
||||
return feature;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('未找到设备:', deviceId);
|
||||
return null;
|
||||
}
|
||||
|
||||
function locateDevice(deviceId) {
|
||||
var feature = findDeviceById(deviceId);
|
||||
if (feature && map) {
|
||||
var geometry = feature.getGeometry();
|
||||
var coordinates = geometry.getCoordinates();
|
||||
|
||||
map.getView().animate({
|
||||
center: coordinates,
|
||||
zoom: Math.max(map.getView().getZoom(), 15),
|
||||
duration: 1000
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的位置
|
||||
*/
|
||||
function getMyLocation() {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
function(position) {
|
||||
var lat = position.coords.latitude;
|
||||
var lon = position.coords.longitude;
|
||||
|
||||
var currentMapType = getCurrentMapType();
|
||||
var coordinates = CoordinateUtils.getMapCoordinates(lat, lon, currentMapType);
|
||||
|
||||
if (myLocationFeature) {
|
||||
vectorSource.removeFeature(myLocationFeature);
|
||||
}
|
||||
|
||||
myLocationFeature = new ol.Feature({
|
||||
geometry: new ol.geom.Point(coordinates),
|
||||
isMyLocation: true
|
||||
});
|
||||
|
||||
vectorSource.addFeature(myLocationFeature);
|
||||
|
||||
// map.getView().animate({
|
||||
// center: coordinates,
|
||||
// zoom: 15,
|
||||
// duration: 1000
|
||||
// });
|
||||
},
|
||||
function(error) {
|
||||
console.error('获取位置失败:', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function locateMyLocation() {
|
||||
if (myLocationFeature) {
|
||||
var geometry = myLocationFeature.getGeometry();
|
||||
var coordinates = geometry.getCoordinates();
|
||||
|
||||
map.getView().animate({
|
||||
center: coordinates,
|
||||
zoom: 15,
|
||||
duration: 1000
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
function(position) {
|
||||
var lat = position.coords.latitude;
|
||||
var lon = position.coords.longitude;
|
||||
|
||||
var currentMapType = getCurrentMapType();
|
||||
var coordinates = CoordinateUtils.getMapCoordinates(lat, lon, currentMapType);
|
||||
|
||||
if (myLocationFeature) {
|
||||
vectorSource.removeFeature(myLocationFeature);
|
||||
}
|
||||
|
||||
myLocationFeature = new ol.Feature({
|
||||
geometry: new ol.geom.Point(coordinates),
|
||||
isMyLocation: true
|
||||
});
|
||||
|
||||
vectorSource.addFeature(myLocationFeature);
|
||||
|
||||
map.getView().animate({
|
||||
center: coordinates,
|
||||
zoom: 15,
|
||||
duration: 1000
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function startLocationUpdates() {
|
||||
if (navigator.geolocation) {
|
||||
myLocationInterval = setInterval(function() {
|
||||
getMyLocation();
|
||||
}, 30000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function stopLocationUpdates() {
|
||||
if (myLocationInterval) {
|
||||
clearInterval(myLocationInterval);
|
||||
myLocationInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateMyLocationForMapType(mapType) {
|
||||
if (myLocationFeature) {
|
||||
var geometry = myLocationFeature.getGeometry();
|
||||
var coordinates = geometry.getCoordinates();
|
||||
var lonLat = ol.proj.toLonLat(coordinates);
|
||||
|
||||
var newCoordinates = CoordinateUtils.getMapCoordinates(lonLat[1], lonLat[0], mapType);
|
||||
|
||||
myLocationFeature.setGeometry(new ol.geom.Point(newCoordinates));
|
||||
}
|
||||
}
|
||||
|
||||
function setShowDeviceId(show) {
|
||||
showDeviceId = show;
|
||||
if (vectorLayer) {
|
||||
vectorLayer.changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getDeviceStats() {
|
||||
return {
|
||||
total: allFeatures.length,
|
||||
green: greenFeatures.length,
|
||||
orange: orangeFeatures.length,
|
||||
red: redFeatures.length
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
createDeviceStyle: createDeviceStyle,
|
||||
addDeviceMarkers: addDeviceMarkers,
|
||||
showAllDevices: showAllDevices,
|
||||
showWarning1Devices: showWarning1Devices,
|
||||
showWarning2Devices: showWarning2Devices,
|
||||
hideGreenFeatures: hideGreenFeatures,
|
||||
hideOrangeFeatures: hideOrangeFeatures,
|
||||
hideRedFeatures: hideRedFeatures,
|
||||
findDeviceById: findDeviceById,
|
||||
locateDevice: locateDevice,
|
||||
getMyLocation: getMyLocation,
|
||||
locateMyLocation: locateMyLocation,
|
||||
startLocationUpdates: startLocationUpdates,
|
||||
stopLocationUpdates: stopLocationUpdates,
|
||||
updateMyLocationForMapType: updateMyLocationForMapType,
|
||||
setShowDeviceId: setShowDeviceId,
|
||||
getDeviceStats: getDeviceStats,
|
||||
|
||||
getAllFeatures: function() { return allFeatures; },
|
||||
getGreenFeatures: function() { return greenFeatures; },
|
||||
getOrangeFeatures: function() { return orangeFeatures; },
|
||||
getRedFeatures: function() { return redFeatures; },
|
||||
getMyLocationFeature: function() { return myLocationFeature; }
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,149 @@
|
||||
var DeviceOverview = (function() {
|
||||
'use strict';
|
||||
|
||||
var deviceList = [];
|
||||
|
||||
function init(devices, options) {
|
||||
deviceList = devices || [];
|
||||
|
||||
window.deviceList = deviceList;
|
||||
window.userRole = options && options.role ? options.role : 'USER';
|
||||
|
||||
layui.use(['form'], function(){
|
||||
var form = layui.form;
|
||||
|
||||
form.on('select(mapTypeNew)', function(data){
|
||||
MapCore.switchMapType(data.value);
|
||||
});
|
||||
|
||||
MapCore.initialize(deviceList);
|
||||
|
||||
document.getElementById('warningFilter').value = 'all';
|
||||
SearchFilter.showAllDevices();
|
||||
});
|
||||
}
|
||||
|
||||
function onMapTypeChange() {
|
||||
return MapCore.onMapTypeChange();
|
||||
}
|
||||
|
||||
function searchDeviceNew() {
|
||||
return MapCore.searchDeviceNew();
|
||||
}
|
||||
|
||||
function onWarningFilterChange() {
|
||||
return MapCore.onWarningFilterChange();
|
||||
}
|
||||
|
||||
function toggleMapFunctionsMenu() {
|
||||
return MapCore.toggleMapFunctionsMenu();
|
||||
}
|
||||
|
||||
function toggleDeviceId() {
|
||||
return MapCore.toggleDeviceId();
|
||||
}
|
||||
|
||||
function toggleCluster() {
|
||||
return MapCore.toggleCluster();
|
||||
}
|
||||
|
||||
function toggleMeasureDistance() {
|
||||
return MeasureTools.toggleMeasureDistance();
|
||||
}
|
||||
|
||||
function finishMeasuring() {
|
||||
return MeasureTools.finishMeasuring();
|
||||
}
|
||||
|
||||
function clearMeasure() {
|
||||
return MeasureTools.clearMeasure();
|
||||
}
|
||||
|
||||
function toggleWeatherForecast() {
|
||||
return WeatherForecast.toggleWeatherForecast();
|
||||
}
|
||||
|
||||
function closeWeatherCard() {
|
||||
return WeatherForecast.closeWeatherCard();
|
||||
}
|
||||
|
||||
function showPrevForecast() {
|
||||
return WeatherForecast.showPrevForecast();
|
||||
}
|
||||
|
||||
function showNextForecast() {
|
||||
return WeatherForecast.showNextForecast();
|
||||
}
|
||||
|
||||
function queryDevices(statusType) {
|
||||
return SearchFilter.queryDevices(statusType);
|
||||
}
|
||||
|
||||
function locateDeviceOnMap(deviceId, latitude, longitude) {
|
||||
return SearchFilter.locateDeviceOnMap(deviceId, latitude, longitude);
|
||||
}
|
||||
|
||||
function locateDeviceDirectly(deviceId) {
|
||||
return SearchFilter.locateDeviceDirectly(deviceId);
|
||||
}
|
||||
|
||||
function switchMapType(mapType) {
|
||||
return MapCore.switchMapType(mapType);
|
||||
}
|
||||
|
||||
function locateMyLocation() {
|
||||
return DeviceMarkers.locateMyLocation();
|
||||
}
|
||||
|
||||
function clearDeviceFilter() {
|
||||
return SearchFilter.clearDeviceFilter();
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
|
||||
onMapTypeChange: onMapTypeChange,
|
||||
switchMapType: switchMapType,
|
||||
|
||||
searchDeviceNew: searchDeviceNew,
|
||||
onWarningFilterChange: onWarningFilterChange,
|
||||
queryDevices: queryDevices,
|
||||
locateDeviceOnMap: locateDeviceOnMap,
|
||||
locateDeviceDirectly: locateDeviceDirectly,
|
||||
locateMyLocation: locateMyLocation,
|
||||
clearDeviceFilter: clearDeviceFilter,
|
||||
|
||||
toggleMapFunctionsMenu: toggleMapFunctionsMenu,
|
||||
toggleDeviceId: toggleDeviceId,
|
||||
toggleCluster: toggleCluster,
|
||||
|
||||
toggleMeasureDistance: toggleMeasureDistance,
|
||||
finishMeasuring: finishMeasuring,
|
||||
clearMeasure: clearMeasure,
|
||||
|
||||
toggleWeatherForecast: toggleWeatherForecast,
|
||||
closeWeatherCard: closeWeatherCard,
|
||||
showPrevForecast: showPrevForecast,
|
||||
showNextForecast: showNextForecast
|
||||
};
|
||||
})();
|
||||
|
||||
window.DeviceOverview = DeviceOverview;
|
||||
window.onMapTypeChange = DeviceOverview.onMapTypeChange;
|
||||
window.searchDeviceNew = DeviceOverview.searchDeviceNew;
|
||||
window.onWarningFilterChange = DeviceOverview.onWarningFilterChange;
|
||||
window.toggleMapFunctionsMenu = DeviceOverview.toggleMapFunctionsMenu;
|
||||
window.toggleDeviceId = DeviceOverview.toggleDeviceId;
|
||||
window.toggleCluster = DeviceOverview.toggleCluster;
|
||||
window.toggleMeasureDistance = DeviceOverview.toggleMeasureDistance;
|
||||
window.finishMeasuring = DeviceOverview.finishMeasuring;
|
||||
window.clearMeasure = DeviceOverview.clearMeasure;
|
||||
window.toggleWeatherForecast = DeviceOverview.toggleWeatherForecast;
|
||||
window.closeWeatherCard = DeviceOverview.closeWeatherCard;
|
||||
window.showPrevForecast = DeviceOverview.showPrevForecast;
|
||||
window.showNextForecast = DeviceOverview.showNextForecast;
|
||||
window.queryDevices = DeviceOverview.queryDevices;
|
||||
window.locateDeviceOnMap = DeviceOverview.locateDeviceOnMap;
|
||||
window.locateDeviceDirectly = DeviceOverview.locateDeviceDirectly;
|
||||
window.locateMyLocation = DeviceOverview.locateMyLocation;
|
||||
window.clearDeviceFilter = DeviceOverview.clearDeviceFilter;
|
||||
@ -0,0 +1,347 @@
|
||||
var MapCore = (function() {
|
||||
'use strict';
|
||||
|
||||
var map = null;
|
||||
var vectorSource = null;
|
||||
var vectorLayer = null;
|
||||
var clusterSource = null;
|
||||
var clusterLayer = null;
|
||||
var currentBaseLayer = null;
|
||||
var showDeviceId = true;
|
||||
var showCluster = true;
|
||||
var minZoomForLabels = 4;
|
||||
var maxZoomForClustering = 8;
|
||||
var hoveredFeature = null;
|
||||
|
||||
|
||||
function initialize(deviceList) {
|
||||
// 创建矢量数据源和图层
|
||||
vectorSource = new ol.source.Vector();
|
||||
vectorLayer = new ol.layer.Vector({
|
||||
source: vectorSource,
|
||||
style: function(feature) {
|
||||
return DeviceMarkers.createDeviceStyle(feature);
|
||||
}
|
||||
});
|
||||
|
||||
// 创建集群数据源和图层
|
||||
clusterSource = new ol.source.Cluster({
|
||||
distance: 40,
|
||||
source: vectorSource
|
||||
});
|
||||
|
||||
clusterLayer = new ol.layer.Vector({
|
||||
source: clusterSource,
|
||||
style: function(feature) {
|
||||
var size = feature.get('features').length;
|
||||
var style = new ol.style.Style({
|
||||
image: new ol.style.Circle({
|
||||
radius: 15,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#3399CC'
|
||||
})
|
||||
}),
|
||||
text: new ol.style.Text({
|
||||
text: size.toString(),
|
||||
fill: new ol.style.Fill({
|
||||
color: '#fff'
|
||||
})
|
||||
})
|
||||
});
|
||||
return style;
|
||||
}
|
||||
});
|
||||
|
||||
var initialMapType = document.getElementById('mapTypeSelectNew').value || 'tianditu_satellite';
|
||||
currentBaseLayer = MapLayers.getLayer(initialMapType);
|
||||
|
||||
map = new ol.Map({
|
||||
target: 'map-container',
|
||||
layers: [
|
||||
currentBaseLayer,
|
||||
clusterLayer,
|
||||
vectorLayer
|
||||
],
|
||||
view: new ol.View({
|
||||
center: ol.proj.fromLonLat([116.404, 39.915]),
|
||||
zoom: 7
|
||||
})
|
||||
});
|
||||
|
||||
DeviceMarkers.init(map, vectorSource, vectorLayer);
|
||||
MeasureTools.init(map);
|
||||
WeatherForecast.init();
|
||||
|
||||
var scaleLineControl = new ol.control.ScaleLine();
|
||||
map.addControl(scaleLineControl);
|
||||
|
||||
var initialZoom = map.getView().getZoom();
|
||||
updateLayerVisibility(initialZoom);
|
||||
|
||||
map.getView().on('change:resolution', function() {
|
||||
var zoom = map.getView().getZoom();
|
||||
updateLayerVisibility(zoom);
|
||||
vectorLayer.changed();
|
||||
});
|
||||
|
||||
bindMouseEvents();
|
||||
|
||||
if (deviceList && deviceList.length > 0) {
|
||||
setCenterFromDevices(deviceList);
|
||||
DeviceMarkers.addDeviceMarkers(deviceList);
|
||||
}
|
||||
|
||||
DeviceMarkers.getMyLocation();
|
||||
DeviceMarkers.startLocationUpdates();
|
||||
|
||||
document.getElementById('showDeviceIdSwitch').checked = showDeviceId;
|
||||
document.getElementById('showClusterSwitch').checked = showCluster;
|
||||
}
|
||||
|
||||
function updateLayerVisibility(zoom) {
|
||||
if (showCluster) {
|
||||
if (zoom >= maxZoomForClustering) {
|
||||
clusterLayer.setVisible(false);
|
||||
vectorLayer.setVisible(true);
|
||||
} else {
|
||||
clusterLayer.setVisible(true);
|
||||
vectorLayer.setVisible(false);
|
||||
}
|
||||
} else {
|
||||
clusterLayer.setVisible(false);
|
||||
vectorLayer.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
function bindMouseEvents() {
|
||||
map.on('pointermove', function(evt) {
|
||||
if (evt.dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pixel = map.getEventPixel(evt.originalEvent);
|
||||
var hit = map.hasFeatureAtPixel(pixel);
|
||||
|
||||
map.getTargetElement().style.cursor = hit ? 'pointer' : '';
|
||||
|
||||
var feature = map.forEachFeatureAtPixel(pixel, function(feature) {
|
||||
return feature;
|
||||
});
|
||||
|
||||
if (hoveredFeature && hoveredFeature !== feature) {
|
||||
hoveredFeature.set('hovered', false);
|
||||
}
|
||||
|
||||
if (feature) {
|
||||
// 处理集群
|
||||
var features = feature.get('features');
|
||||
if (features && features.length === 1) {
|
||||
features[0].set('hovered', true);
|
||||
hoveredFeature = features[0];
|
||||
} else if (!features && feature.get('deviceInfo')) {
|
||||
feature.set('hovered', true);
|
||||
hoveredFeature = feature;
|
||||
}
|
||||
}
|
||||
|
||||
vectorLayer.changed();
|
||||
});
|
||||
|
||||
map.on('click', function(evt) {
|
||||
var feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) {
|
||||
return feature;
|
||||
});
|
||||
|
||||
if (feature) {
|
||||
var features = feature.get('features');
|
||||
if (features && features.length > 1) {
|
||||
// 集群点击,扩展视图
|
||||
var extent = vectorSource.getExtent();
|
||||
map.getView().fit(extent, {
|
||||
padding: [50, 50, 50, 50],
|
||||
duration: 1000
|
||||
});
|
||||
} else if (features && features.length === 1) {
|
||||
// 单个设备点击
|
||||
var deviceInfo = features[0].get('deviceInfo');
|
||||
if (deviceInfo) {
|
||||
showDeviceInfo(deviceInfo);
|
||||
// 如果天气预测开启,显示天气卡片
|
||||
if (WeatherForecast.isEnabled()) {
|
||||
WeatherForecast.showWeatherForecast(deviceInfo);
|
||||
}
|
||||
}
|
||||
} else if (feature.get('deviceInfo')) {
|
||||
var deviceInfo = feature.get('deviceInfo');
|
||||
showDeviceInfo(deviceInfo);
|
||||
// 如果天气预测开启,显示天气卡片
|
||||
if (WeatherForecast.isEnabled()) {
|
||||
WeatherForecast.showWeatherForecast(deviceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function showDeviceInfo(deviceInfo) {
|
||||
var statusText = '';
|
||||
|
||||
if (deviceInfo.warning === 2) {
|
||||
statusText = '严重告警';
|
||||
} else if (deviceInfo.warning === 1) {
|
||||
statusText = '一般告警';
|
||||
} else {
|
||||
statusText = '正常';
|
||||
}
|
||||
|
||||
var infoMsg = ' 设备: ' + deviceInfo.deviceid +
|
||||
' | 状态: ' + statusText +
|
||||
' | 坐标: ' + deviceInfo.latitude.toFixed(4) + ', ' + deviceInfo.longitude.toFixed(4);
|
||||
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg(infoMsg, {
|
||||
time: 3000,
|
||||
area: ['auto', 'auto'],
|
||||
offset: 'auto'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setCenterFromDevices(deviceList) {
|
||||
if (!deviceList || deviceList.length === 0) return;
|
||||
|
||||
var sumLat = 0, sumLon = 0;
|
||||
for (var i = 0; i < deviceList.length; i++) {
|
||||
sumLat += deviceList[i].latitude;
|
||||
sumLon += deviceList[i].longitude;
|
||||
}
|
||||
var centerLat = sumLat / deviceList.length;
|
||||
var centerLon = sumLon / deviceList.length;
|
||||
|
||||
map.getView().setCenter(ol.proj.fromLonLat([centerLon, centerLat]));
|
||||
}
|
||||
|
||||
function switchMapType(mapType) {
|
||||
if (!MapLayers.hasLayer(mapType)) {
|
||||
console.error('未知的地图类型:', mapType);
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存当前视图状态
|
||||
var currentCenter = map.getView().getCenter();
|
||||
var currentZoom = map.getView().getZoom();
|
||||
|
||||
map.removeLayer(currentBaseLayer);
|
||||
currentBaseLayer = MapLayers.getLayer(mapType);
|
||||
map.getLayers().insertAt(0, currentBaseLayer);
|
||||
|
||||
DeviceMarkers.updateMyLocationForMapType(mapType);
|
||||
|
||||
var deviceList = window.deviceList || [];
|
||||
DeviceMarkers.addDeviceMarkers(deviceList);
|
||||
|
||||
// 恢复之前的视图状态
|
||||
map.getView().setCenter(currentCenter);
|
||||
map.getView().setZoom(currentZoom);
|
||||
}
|
||||
|
||||
function toggleDeviceId() {
|
||||
showDeviceId = document.getElementById('showDeviceIdSwitch').checked;
|
||||
DeviceMarkers.setShowDeviceId(showDeviceId);
|
||||
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg(showDeviceId ? '已显示设备信息' : '已隐藏设备信息');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleCluster() {
|
||||
showCluster = document.getElementById('showClusterSwitch').checked;
|
||||
var zoom = map.getView().getZoom();
|
||||
updateLayerVisibility(zoom);
|
||||
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg(showCluster ? '已启用集群显示' : '已禁用集群显示');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleMapFunctionsMenu() {
|
||||
var menu = document.getElementById('mapFunctionsMenu');
|
||||
if (menu) {
|
||||
menu.classList.toggle('show');
|
||||
if (menu.classList.contains('show')) {
|
||||
document.addEventListener('click', closeMapFunctionsMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeMapFunctionsMenu(event) {
|
||||
var dropdown = document.getElementById('mapFunctionsMenu');
|
||||
var toggleBtn = document.querySelector('.dropdown-toggle');
|
||||
|
||||
if (dropdown && !dropdown.contains(event.target) && !toggleBtn.contains(event.target)) {
|
||||
dropdown.classList.remove('show');
|
||||
document.removeEventListener('click', closeMapFunctionsMenu);
|
||||
}
|
||||
}
|
||||
|
||||
function onMapTypeChange() {
|
||||
var mapType = document.getElementById('mapTypeSelectNew').value;
|
||||
switchMapType(mapType);
|
||||
}
|
||||
|
||||
function onWarningFilterChange() {
|
||||
var filterValue = document.getElementById('warningFilter').value;
|
||||
switch(filterValue) {
|
||||
case 'all':
|
||||
SearchFilter.showAllDevices();
|
||||
break;
|
||||
case 'warning1':
|
||||
SearchFilter.showWarning1Devices();
|
||||
break;
|
||||
case 'warning2':
|
||||
SearchFilter.showWarning2Devices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function searchDeviceNew() {
|
||||
var searchInput = document.getElementById('deviceSearchNew');
|
||||
if (searchInput) {
|
||||
var deviceId = searchInput.value.trim();
|
||||
if (deviceId) {
|
||||
SearchFilter.searchDevice(deviceId);
|
||||
} else {
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('请输入设备编号');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
function getVectorSource() {
|
||||
return vectorSource;
|
||||
}
|
||||
|
||||
function getVectorLayer() {
|
||||
return vectorLayer;
|
||||
}
|
||||
|
||||
return {
|
||||
initialize: initialize,
|
||||
switchMapType: switchMapType,
|
||||
toggleDeviceId: toggleDeviceId,
|
||||
toggleCluster: toggleCluster,
|
||||
toggleMapFunctionsMenu: toggleMapFunctionsMenu,
|
||||
onMapTypeChange: onMapTypeChange,
|
||||
onWarningFilterChange: onWarningFilterChange,
|
||||
searchDeviceNew: searchDeviceNew,
|
||||
getMap: getMap,
|
||||
getVectorSource: getVectorSource,
|
||||
getVectorLayer: getVectorLayer
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,176 @@
|
||||
var MapLayers = (function() {
|
||||
'use strict';
|
||||
|
||||
// 天地图 API 密钥 (fengyarnom@gmail.com)
|
||||
// 天地图的英文就是 TIANDITU 所以这里用拼音指代
|
||||
var TIANDITU_KEY = '0c260b8a094a4e0bc507808812cefdac';
|
||||
|
||||
function createTiandituTileLoadFunction() {
|
||||
return function(imageTile, src) {
|
||||
imageTile.getImage().src = src;
|
||||
imageTile.getImage().onerror = function() {
|
||||
// 天地图加载失败时切换到高德地图
|
||||
var mapTypeSelect = document.getElementById('mapTypeSelectNew');
|
||||
if(mapTypeSelect && mapTypeSelect.value.startsWith('tianditu_')) {
|
||||
mapTypeSelect.value = 'amap';
|
||||
if (window.DeviceOverview && typeof window.DeviceOverview.switchMapType === 'function') {
|
||||
window.DeviceOverview.switchMapType('amap');
|
||||
}
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('天地图加载失败,已自动切换到高德地图');
|
||||
}
|
||||
if (window.layui && window.layui.form) {
|
||||
window.layui.form.render('select');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var mapLayers = {
|
||||
// 高德地图
|
||||
amap: new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://webrd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}'
|
||||
})
|
||||
}),
|
||||
|
||||
// 高德卫星图
|
||||
amap_satellite: new ol.layer.Group({
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}'
|
||||
})
|
||||
}),
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://webst0{1-4}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}'
|
||||
})
|
||||
})
|
||||
]
|
||||
}),
|
||||
|
||||
// 谷歌卫星图
|
||||
google_satellite: new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://mt{0-3}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
||||
crossOrigin: 'anonymous'
|
||||
})
|
||||
}),
|
||||
|
||||
// 谷歌地形图
|
||||
google_terrain: new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://mt{0-3}.google.com/vt/lyrs=p&x={x}&y={y}&z={z}',
|
||||
crossOrigin: 'anonymous'
|
||||
})
|
||||
}),
|
||||
|
||||
// 谷歌道路图
|
||||
google_roadmap: new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://mt{0-3}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',
|
||||
crossOrigin: 'anonymous'
|
||||
})
|
||||
}),
|
||||
|
||||
// 谷歌混合图
|
||||
google_hybrid: new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://mt{0-3}.google.com/vt/lyrs=y&x={x}&y={y}&z={z}',
|
||||
crossOrigin: 'anonymous'
|
||||
})
|
||||
}),
|
||||
|
||||
// 天地图卫星影像
|
||||
tianditu_satellite: new ol.layer.Group({
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://t{0-7}.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
|
||||
tileLoadFunction: createTiandituTileLoadFunction()
|
||||
})
|
||||
}),
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://t{0-7}.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
|
||||
tileLoadFunction: createTiandituTileLoadFunction()
|
||||
})
|
||||
})
|
||||
]
|
||||
}),
|
||||
|
||||
// 天地图矢量图
|
||||
tianditu_normal: new ol.layer.Group({
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://t{0-7}.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
|
||||
tileLoadFunction: createTiandituTileLoadFunction()
|
||||
})
|
||||
}),
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://t{0-7}.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
|
||||
tileLoadFunction: createTiandituTileLoadFunction()
|
||||
})
|
||||
})
|
||||
]
|
||||
}),
|
||||
|
||||
// 天地图地形图
|
||||
tianditu_terrain: new ol.layer.Group({
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://t{0-7}.tianditu.gov.cn/ter_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ter&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
|
||||
tileLoadFunction: createTiandituTileLoadFunction()
|
||||
})
|
||||
})
|
||||
]
|
||||
}),
|
||||
|
||||
// 天地图地形混合图
|
||||
tianditu_terrain_hybrid: new ol.layer.Group({
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://t{0-7}.tianditu.gov.cn/ter_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ter&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
|
||||
tileLoadFunction: createTiandituTileLoadFunction()
|
||||
})
|
||||
}),
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://t{0-7}.tianditu.gov.cn/cta_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cta&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
|
||||
tileLoadFunction: createTiandituTileLoadFunction()
|
||||
})
|
||||
})
|
||||
]
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
function getLayer(mapType) {
|
||||
return mapLayers[mapType];
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getAllLayers() {
|
||||
return mapLayers;
|
||||
}
|
||||
|
||||
|
||||
function hasLayer(mapType) {
|
||||
return mapLayers.hasOwnProperty(mapType);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
getLayer: getLayer,
|
||||
getAllLayers: getAllLayers,
|
||||
hasLayer: hasLayer
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,299 @@
|
||||
var MeasureTools = (function() {
|
||||
'use strict';
|
||||
|
||||
var measureActive = false;
|
||||
var measureDraw = null;
|
||||
var measureSource = null;
|
||||
var measureLayer = null;
|
||||
var measureTooltips = [];
|
||||
var measureFeatures = [];
|
||||
var currentMeasureTooltips = [];
|
||||
var currentSketch = null;
|
||||
var currentListener = null;
|
||||
var segmentTooltips = [];
|
||||
var map = null;
|
||||
|
||||
function init(mapInstance) {
|
||||
map = mapInstance;
|
||||
|
||||
// 测距专用图层
|
||||
measureSource = new ol.source.Vector();
|
||||
measureLayer = new ol.layer.Vector({
|
||||
source: measureSource,
|
||||
style: new ol.style.Style({
|
||||
fill: new ol.style.Fill({ color: 'rgba(255,255,255,0.2)' }),
|
||||
stroke: new ol.style.Stroke({ color: '#ffcc33', width: 2 }),
|
||||
image: new ol.style.RegularShape({
|
||||
points: 4,
|
||||
radius: 8,
|
||||
radius2: 0,
|
||||
angle: Math.PI / 4,
|
||||
stroke: new ol.style.Stroke({ color: '#ed8936', width: 2 })
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
if (map) {
|
||||
map.addLayer(measureLayer);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleMeasureDistance() {
|
||||
if (measureActive) {
|
||||
deactivateMeasure();
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('测距功能已关闭');
|
||||
}
|
||||
} else {
|
||||
activateMeasure();
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('点击左键添加距离节点,点击右键结束测量 :)');
|
||||
}
|
||||
}
|
||||
|
||||
var menu = document.getElementById('mapFunctionsMenu');
|
||||
if (menu) {
|
||||
menu.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
function finishMeasuring() {
|
||||
if (measureActive && currentSketch && measureDraw) {
|
||||
measureDraw.finishDrawing();
|
||||
}
|
||||
}
|
||||
|
||||
function activateMeasure() {
|
||||
if (!map || !measureSource) return;
|
||||
|
||||
measureActive = true;
|
||||
|
||||
// 显示测量状态指示器
|
||||
var measureStatus = document.getElementById('measureStatus');
|
||||
if (measureStatus) {
|
||||
measureStatus.style.display = 'flex';
|
||||
}
|
||||
|
||||
measureDraw = new ol.interaction.Draw({
|
||||
source: measureSource,
|
||||
type: 'LineString',
|
||||
style: new ol.style.Style({
|
||||
fill: new ol.style.Fill({ color: 'rgba(255,255,255,0.2)' }),
|
||||
stroke: new ol.style.Stroke({ color: '#ffcc33', width: 2 }),
|
||||
image: new ol.style.RegularShape({
|
||||
points: 4,
|
||||
radius: 8,
|
||||
radius2: 0,
|
||||
angle: Math.PI / 4,
|
||||
stroke: new ol.style.Stroke({ color: '#ed8936', width: 2 })
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
map.addInteraction(measureDraw);
|
||||
|
||||
map.getViewport().addEventListener('contextmenu', function(e) {
|
||||
if (measureActive && currentSketch) {
|
||||
e.preventDefault();
|
||||
measureDraw.finishDrawing();
|
||||
}
|
||||
});
|
||||
|
||||
measureDraw.on('drawstart', function(evt) {
|
||||
currentSketch = evt.feature;
|
||||
currentMeasureTooltips = [];
|
||||
segmentTooltips = [];
|
||||
|
||||
currentListener = currentSketch.getGeometry().on('change', function(e) {
|
||||
var geom = e.target;
|
||||
var coords = geom.getCoordinates();
|
||||
|
||||
clearTemporaryTooltips();
|
||||
clearSegmentTooltips();
|
||||
|
||||
// 计算并显示每个节点的距离
|
||||
var total = 0;
|
||||
for (var i = 0; i < coords.length; i++) {
|
||||
if (i === 0) {
|
||||
// 起点
|
||||
var startTooltip = createMeasureTooltip(coords[0], '起点');
|
||||
currentMeasureTooltips.push(startTooltip);
|
||||
} else {
|
||||
// 计算段距离
|
||||
var seg = new ol.geom.LineString([coords[i-1], coords[i]]);
|
||||
var segmentLength = ol.sphere.getLength(seg);
|
||||
total += segmentLength;
|
||||
|
||||
// 显示每个节点的累计距离
|
||||
var output = formatLength(total);
|
||||
var tooltip = createMeasureTooltip(coords[i], output);
|
||||
segmentTooltips.push(tooltip);
|
||||
|
||||
if (i === coords.length - 1) {
|
||||
currentMeasureTooltips.push(tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 绘制结束
|
||||
measureDraw.on('drawend', function(evt) {
|
||||
var coords = evt.feature.getGeometry().getCoordinates();
|
||||
|
||||
clearTemporaryTooltips();
|
||||
clearSegmentTooltips();
|
||||
|
||||
var total = 0;
|
||||
for (var i = 0; i < coords.length; i++) {
|
||||
if (i === 0) {
|
||||
var startTooltip = createMeasureTooltip(coords[0], '起点', true);
|
||||
measureTooltips.push(startTooltip);
|
||||
} else {
|
||||
var seg = new ol.geom.LineString([coords[i-1], coords[i]]);
|
||||
total += ol.sphere.getLength(seg);
|
||||
|
||||
var output = formatLength(total);
|
||||
var tooltip = createMeasureTooltip(coords[i], output, true);
|
||||
measureTooltips.push(tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
measureFeatures.push(evt.feature);
|
||||
|
||||
if (currentListener) {
|
||||
ol.Observable.unByKey(currentListener);
|
||||
}
|
||||
currentSketch = null;
|
||||
currentListener = null;
|
||||
|
||||
var measureStatus = document.getElementById('measureStatus');
|
||||
if (measureStatus) {
|
||||
measureStatus.style.display = 'none';
|
||||
}
|
||||
|
||||
map.removeInteraction(measureDraw);
|
||||
measureActive = false;
|
||||
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('测量完成');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deactivateMeasure() {
|
||||
measureActive = false;
|
||||
|
||||
var measureStatus = document.getElementById('measureStatus');
|
||||
if (measureStatus) {
|
||||
measureStatus.style.display = 'none';
|
||||
}
|
||||
|
||||
if (measureDraw && map) {
|
||||
map.removeInteraction(measureDraw);
|
||||
measureDraw = null;
|
||||
}
|
||||
|
||||
if (currentListener) {
|
||||
ol.Observable.unByKey(currentListener);
|
||||
currentListener = null;
|
||||
}
|
||||
|
||||
currentSketch = null;
|
||||
clearTemporaryTooltips();
|
||||
clearSegmentTooltips();
|
||||
}
|
||||
|
||||
function createMeasureTooltip(coord, text, isStatic) {
|
||||
var elem = document.createElement('div');
|
||||
elem.className = isStatic ? 'ol-tooltip ol-tooltip-static' : 'ol-tooltip ol-tooltip-measure';
|
||||
elem.innerHTML = text;
|
||||
|
||||
var overlay = new ol.Overlay({
|
||||
element: elem,
|
||||
offset: [0, -15],
|
||||
positioning: 'bottom-center'
|
||||
});
|
||||
|
||||
overlay.setPosition(coord);
|
||||
|
||||
if (map) {
|
||||
map.addOverlay(overlay);
|
||||
}
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
function formatLength(length) {
|
||||
if (length > 1000) {
|
||||
return (Math.round(length / 100) / 10) + ' km';
|
||||
} else {
|
||||
return (Math.round(length * 10) / 10) + ' m';
|
||||
}
|
||||
}
|
||||
|
||||
function clearAllMeasureTooltips() {
|
||||
if (!map) return;
|
||||
|
||||
for (var i = 0; i < measureTooltips.length; i++) {
|
||||
map.removeOverlay(measureTooltips[i]);
|
||||
}
|
||||
measureTooltips = [];
|
||||
|
||||
clearTemporaryTooltips();
|
||||
}
|
||||
|
||||
function clearTemporaryTooltips() {
|
||||
if (!map) return;
|
||||
|
||||
for (var i = 0; i < currentMeasureTooltips.length; i++) {
|
||||
map.removeOverlay(currentMeasureTooltips[i]);
|
||||
}
|
||||
currentMeasureTooltips = [];
|
||||
}
|
||||
|
||||
function clearSegmentTooltips() {
|
||||
if (!map) return;
|
||||
|
||||
for (var i = 0; i < segmentTooltips.length; i++) {
|
||||
map.removeOverlay(segmentTooltips[i]);
|
||||
}
|
||||
segmentTooltips = [];
|
||||
}
|
||||
|
||||
function clearMeasure() {
|
||||
if (measureSource) {
|
||||
measureSource.clear();
|
||||
}
|
||||
|
||||
clearAllMeasureTooltips();
|
||||
measureFeatures = [];
|
||||
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('测距标记已清除');
|
||||
}
|
||||
|
||||
// 关闭地图功能菜单
|
||||
var menu = document.getElementById('mapFunctionsMenu');
|
||||
if (menu) {
|
||||
menu.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
function isActive() {
|
||||
return measureActive;
|
||||
}
|
||||
|
||||
function getMeasureCount() {
|
||||
return measureFeatures.length;
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
toggleMeasureDistance: toggleMeasureDistance,
|
||||
finishMeasuring: finishMeasuring,
|
||||
clearMeasure: clearMeasure,
|
||||
isActive: isActive,
|
||||
getMeasureCount: getMeasureCount
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,327 @@
|
||||
var SearchFilter = (function() {
|
||||
'use strict';
|
||||
|
||||
var currentSearchedDevice = null;
|
||||
var markerState = 3; // 1:all; 2:orange; 3:red
|
||||
var filteredDeviceIds = [];
|
||||
var isFilterActive = false;
|
||||
|
||||
function searchDevice(deviceId) {
|
||||
if (!deviceId || !deviceId.trim()) {
|
||||
clearSearch();
|
||||
return false;
|
||||
}
|
||||
|
||||
deviceId = deviceId.trim();
|
||||
|
||||
// 多设备
|
||||
if (deviceId.indexOf(',') !== -1) {
|
||||
return handleMultiDeviceSearch(deviceId);
|
||||
}
|
||||
|
||||
// 单设备
|
||||
currentSearchedDevice = deviceId;
|
||||
|
||||
var success = DeviceMarkers.locateDevice(deviceId);
|
||||
|
||||
if (success) {
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('已定位到设备: ' + deviceId);
|
||||
}
|
||||
|
||||
// 获取设备信息并显示天气预测(如果启用)
|
||||
var targetFeature = DeviceMarkers.findDeviceById(deviceId);
|
||||
if (targetFeature && window.WeatherForecast && window.WeatherForecast.isEnabled()) {
|
||||
var deviceInfo = targetFeature.get('deviceInfo');
|
||||
if (deviceInfo) {
|
||||
window.WeatherForecast.showWeatherForecast(deviceInfo);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('未找到设备: ' + deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
function handleMultiDeviceSearch(deviceIdsString) {
|
||||
var deviceIds = deviceIdsString.split(',').map(function(id) {
|
||||
return id.trim();
|
||||
}).filter(function(id) {
|
||||
return id !== '';
|
||||
});
|
||||
|
||||
if (deviceIds.length === 0) {
|
||||
clearSearch();
|
||||
return false;
|
||||
}
|
||||
|
||||
filteredDeviceIds = deviceIds;
|
||||
|
||||
var foundDevices = filterDevicesByIds(deviceIds);
|
||||
|
||||
if (foundDevices.length > 0) {
|
||||
showFilterStatus(deviceIds.length);
|
||||
|
||||
isFilterActive = true;
|
||||
|
||||
var firstDevice = foundDevices[0];
|
||||
var geometry = firstDevice.getGeometry();
|
||||
var coordinates = geometry.getCoordinates();
|
||||
|
||||
if (MapCore.getMap()) {
|
||||
MapCore.getMap().getView().animate({
|
||||
center: coordinates,
|
||||
zoom: 12,
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('已过滤显示 ' + foundDevices.length + ' 个设备');
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('未找到任何匹配的设备');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function filterDevicesByIds(deviceIds) {
|
||||
if (!DeviceMarkers.getAllFeatures) return [];
|
||||
|
||||
var allFeatures = DeviceMarkers.getAllFeatures();
|
||||
var foundDevices = [];
|
||||
var vectorSource = MapCore.getVectorSource();
|
||||
|
||||
if (!vectorSource) return [];
|
||||
|
||||
var myLocationFeature = DeviceMarkers.getMyLocationFeature();
|
||||
|
||||
vectorSource.clear();
|
||||
|
||||
if (myLocationFeature) {
|
||||
vectorSource.addFeature(myLocationFeature);
|
||||
}
|
||||
|
||||
for (var i = 0; i < allFeatures.length; i++) {
|
||||
var feature = allFeatures[i];
|
||||
var deviceInfo = feature.get('deviceInfo');
|
||||
|
||||
if (deviceInfo) {
|
||||
var deviceId = String(deviceInfo.deviceid).trim();
|
||||
|
||||
for (var j = 0; j < deviceIds.length; j++) {
|
||||
var searchId = String(deviceIds[j]).trim();
|
||||
|
||||
if (deviceId === searchId ||
|
||||
deviceId.indexOf(searchId) !== -1 ||
|
||||
deviceId.replace(/\s+/g, '') === searchId.replace(/\s+/g, '')) {
|
||||
vectorSource.addFeature(feature);
|
||||
foundDevices.push(feature);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundDevices;
|
||||
}
|
||||
|
||||
function showFilterStatus(count) {
|
||||
var filterStatus = document.getElementById('filterStatus');
|
||||
|
||||
if (filterStatus) {
|
||||
filterStatus.innerHTML =
|
||||
'<span style="color: #1aa094; font-weight: bold; font-size: 12px;">定位中</span>' +
|
||||
'<button class="toolbar-btn" onclick="clearDeviceFilter()" style="background: #f56565; margin-left: 6px;">' +
|
||||
'<i class="layui-icon layui-icon-close" style="font-size: 12px;"></i>' +
|
||||
'</button>';
|
||||
|
||||
filterStatus.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
|
||||
function hideFilterStatus() {
|
||||
var filterStatus = document.getElementById('filterStatus');
|
||||
if (filterStatus) {
|
||||
filterStatus.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function clearDeviceFilter() {
|
||||
isFilterActive = false;
|
||||
filteredDeviceIds = [];
|
||||
hideFilterStatus();
|
||||
|
||||
applyCurrentFilter();
|
||||
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('已清除设备过滤');
|
||||
}
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
currentSearchedDevice = null;
|
||||
var searchInput = document.getElementById('deviceSearchNew');
|
||||
if (searchInput) {
|
||||
searchInput.value = '';
|
||||
}
|
||||
|
||||
if (isFilterActive) {
|
||||
clearDeviceFilter();
|
||||
} else {
|
||||
// 恢复到当前的过滤状态
|
||||
applyCurrentFilter();
|
||||
}
|
||||
}
|
||||
|
||||
function applyCurrentFilter() {
|
||||
switch(markerState) {
|
||||
case 1:
|
||||
showAllDevices();
|
||||
break;
|
||||
case 2:
|
||||
showWarning1Devices();
|
||||
break;
|
||||
case 3:
|
||||
showWarning2Devices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function showAllDevices() {
|
||||
currentSearchedDevice = null;
|
||||
clearSearchInput();
|
||||
DeviceMarkers.showAllDevices();
|
||||
markerState = 1;
|
||||
updateFilterSelect('all');
|
||||
}
|
||||
|
||||
function showWarning1Devices() {
|
||||
currentSearchedDevice = null;
|
||||
clearSearchInput();
|
||||
DeviceMarkers.showWarning1Devices();
|
||||
markerState = 2;
|
||||
updateFilterSelect('warning1');
|
||||
}
|
||||
function showWarning2Devices() {
|
||||
currentSearchedDevice = null;
|
||||
clearSearchInput();
|
||||
DeviceMarkers.showWarning2Devices();
|
||||
markerState = 3;
|
||||
updateFilterSelect('warning2');
|
||||
}
|
||||
function clearSearchInput() {
|
||||
var searchInput = document.getElementById('deviceSearchNew');
|
||||
if (searchInput) {
|
||||
searchInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function updateFilterSelect(filterValue) {
|
||||
var filterSelect = document.getElementById('warningFilter');
|
||||
if (filterSelect) {
|
||||
filterSelect.value = filterValue;
|
||||
}
|
||||
}
|
||||
|
||||
function filterDevicesByStatus(statusType) {
|
||||
switch(statusType) {
|
||||
case 'warning1':
|
||||
showWarning1Devices();
|
||||
break;
|
||||
case 'warning2':
|
||||
showWarning2Devices();
|
||||
break;
|
||||
case 'offline':
|
||||
case 'no_fwd':
|
||||
case 'nofixed':
|
||||
case 'nogga':
|
||||
// 对于这些状态,显示所有设备,让用户在弹窗中选择
|
||||
showAllDevices();
|
||||
break;
|
||||
default:
|
||||
showAllDevices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function queryDevices(statusType) {
|
||||
filterDevicesByStatus(statusType);
|
||||
|
||||
// 打开设备列表弹窗
|
||||
if (window.layer && typeof window.layer.open === 'function') {
|
||||
var index = window.layer.open({
|
||||
title: '设备列表',
|
||||
type: 2,
|
||||
shade: 0.2,
|
||||
maxmin: true,
|
||||
shadeClose: true,
|
||||
anim: 2,
|
||||
offset: 'rb',
|
||||
area: ['100%', '50%'],
|
||||
content: '../page/gnss_q_status?query=' + statusType,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function locateDeviceOnMap(deviceId, latitude, longitude) {
|
||||
currentSearchedDevice = deviceId;
|
||||
var success = DeviceMarkers.locateDevice(deviceId);
|
||||
|
||||
if (success && window.layer && typeof window.layer.closeAll === 'function') {
|
||||
window.layer.closeAll();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
function locateDeviceDirectly(deviceId) {
|
||||
currentSearchedDevice = deviceId;
|
||||
return DeviceMarkers.locateDevice(deviceId);
|
||||
}
|
||||
|
||||
function getCurrentSearchedDevice() {
|
||||
return currentSearchedDevice;
|
||||
}
|
||||
|
||||
function getMarkerState() {
|
||||
return markerState;
|
||||
}
|
||||
|
||||
function setMarkerState(state) {
|
||||
markerState = state;
|
||||
}
|
||||
|
||||
function isFilterModeActive() {
|
||||
return isFilterActive;
|
||||
}
|
||||
|
||||
function getFilteredDeviceIds() {
|
||||
return filteredDeviceIds;
|
||||
}
|
||||
|
||||
return {
|
||||
searchDevice: searchDevice,
|
||||
clearSearch: clearSearch,
|
||||
showAllDevices: showAllDevices,
|
||||
showWarning1Devices: showWarning1Devices,
|
||||
showWarning2Devices: showWarning2Devices,
|
||||
filterDevicesByStatus: filterDevicesByStatus,
|
||||
queryDevices: queryDevices,
|
||||
locateDeviceOnMap: locateDeviceOnMap,
|
||||
locateDeviceDirectly: locateDeviceDirectly,
|
||||
getCurrentSearchedDevice: getCurrentSearchedDevice,
|
||||
getMarkerState: getMarkerState,
|
||||
setMarkerState: setMarkerState,
|
||||
clearDeviceFilter: clearDeviceFilter,
|
||||
isFilterModeActive: isFilterModeActive,
|
||||
getFilteredDeviceIds: getFilteredDeviceIds
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,343 @@
|
||||
var WeatherForecast = (function() {
|
||||
'use strict';
|
||||
|
||||
var weatherEnabled = false;
|
||||
var weatherData = null;
|
||||
var currentForecastIndex = 0;
|
||||
var currentWeatherDevice = null;
|
||||
|
||||
function init() {
|
||||
// 天气预报模块初始化完成
|
||||
}
|
||||
|
||||
function toggleWeatherForecast() {
|
||||
var role = window.userRole || 'USER';
|
||||
if (role !== 'SUPER_ADMIN') {
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('您没有权限使用此功能');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var enableSwitch = document.getElementById('enableWeatherSwitch');
|
||||
weatherEnabled = enableSwitch ? enableSwitch.checked : false;
|
||||
|
||||
if (weatherEnabled) {
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg(
|
||||
'搜索设备或点击地图设备图标即可自动查询天气预测',
|
||||
{time: 3000, area: ['300px', '80px']}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('天气预测功能已关闭');
|
||||
}
|
||||
closeWeatherCard();
|
||||
}
|
||||
|
||||
var menu = document.getElementById('mapFunctionsMenu');
|
||||
if (menu) {
|
||||
menu.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
function showWeatherForecast(deviceInfo) {
|
||||
currentWeatherDevice = deviceInfo;
|
||||
weatherData = null;
|
||||
|
||||
var role = window.userRole || 'USER';
|
||||
if (role !== 'SUPER_ADMIN') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!weatherEnabled) {
|
||||
if (window.layer && typeof window.layer.msg === 'function') {
|
||||
window.layer.msg('天气预测功能未启用', {time: 2000});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var deviceIdElement = document.getElementById('weatherDeviceId');
|
||||
var deviceCoordsElement = document.getElementById('weatherDeviceCoords');
|
||||
|
||||
if (deviceIdElement) {
|
||||
deviceIdElement.textContent = '设备: ' + deviceInfo.deviceid;
|
||||
}
|
||||
|
||||
if (deviceCoordsElement) {
|
||||
deviceCoordsElement.textContent =
|
||||
'坐标: ' + deviceInfo.latitude.toFixed(4) + ', ' + deviceInfo.longitude.toFixed(4);
|
||||
}
|
||||
|
||||
var weatherCard = document.getElementById('weatherForecastCard');
|
||||
if (weatherCard) {
|
||||
weatherCard.style.display = 'block';
|
||||
}
|
||||
|
||||
var contentElement = document.getElementById('weatherForecastContent');
|
||||
if (contentElement) {
|
||||
contentElement.innerHTML =
|
||||
'<div class="weather-loading">' +
|
||||
'<i class="layui-icon layui-icon-loading layui-icon-anim-rotate"></i>' +
|
||||
'<p>正在获取天气预测数据...</p>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
var prevBtn = document.getElementById('prevForecast');
|
||||
var nextBtn = document.getElementById('nextForecast');
|
||||
var timeDisplay = document.getElementById('forecastTimeDisplay');
|
||||
|
||||
if (prevBtn) prevBtn.disabled = true;
|
||||
if (nextBtn) nextBtn.disabled = true;
|
||||
if (timeDisplay) timeDisplay.textContent = '--:--';
|
||||
|
||||
fetchWeatherData(deviceInfo.latitude, deviceInfo.longitude);
|
||||
}
|
||||
|
||||
function closeWeatherCard() {
|
||||
var weatherCard = document.getElementById('weatherForecastCard');
|
||||
if (weatherCard) {
|
||||
weatherCard.style.display = 'none';
|
||||
}
|
||||
currentWeatherDevice = null;
|
||||
weatherData = null;
|
||||
}
|
||||
|
||||
function showPrevForecast() {
|
||||
if (!weatherData || currentForecastIndex <= 0) return;
|
||||
|
||||
currentForecastIndex--;
|
||||
displayCurrentForecast();
|
||||
updateForecastNavigation();
|
||||
}
|
||||
|
||||
function showNextForecast() {
|
||||
if (!weatherData || !weatherData.ts || currentForecastIndex >= weatherData.ts.length - 1) return;
|
||||
|
||||
currentForecastIndex++;
|
||||
displayCurrentForecast();
|
||||
updateForecastNavigation();
|
||||
}
|
||||
|
||||
function updateForecastNavigation() {
|
||||
if (!weatherData || !weatherData.ts) return;
|
||||
|
||||
var prevBtn = document.getElementById('prevForecast');
|
||||
var nextBtn = document.getElementById('nextForecast');
|
||||
var timeDisplay = document.getElementById('forecastTimeDisplay');
|
||||
|
||||
if (prevBtn) prevBtn.disabled = (currentForecastIndex <= 0);
|
||||
if (nextBtn) nextBtn.disabled = (currentForecastIndex >= weatherData.ts.length - 1);
|
||||
|
||||
if (timeDisplay) {
|
||||
var timestamp = weatherData.ts[currentForecastIndex];
|
||||
var date = new Date(timestamp);
|
||||
timeDisplay.textContent = formatDateTime(date);
|
||||
}
|
||||
}
|
||||
|
||||
function fetchWeatherData(lat, lon) {
|
||||
var url = 'https://api.open-meteo.com/v1/forecast?' +
|
||||
'latitude=' + lat.toFixed(4) +
|
||||
'&longitude=' + lon.toFixed(4) +
|
||||
'¤t=temperature_2m,wind_speed_10m,wind_direction_10m,relative_humidity_2m,surface_pressure' +
|
||||
'&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m,wind_direction_10m,precipitation,surface_pressure,wind_gusts_10m' +
|
||||
'&forecast_days=3' +
|
||||
'&timezone=auto';
|
||||
|
||||
fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(function(response) {
|
||||
if (!response.ok) {
|
||||
throw new Error('网络响应状态: ' + response.status);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
weatherData = transformOpenMeteoData(data);
|
||||
|
||||
var currentTime = new Date().getTime();
|
||||
var closestIndex = 0;
|
||||
var futureIndex = -1;
|
||||
|
||||
if (weatherData.ts && weatherData.ts.length > 0) {
|
||||
for (var i = 0; i < weatherData.ts.length; i++) {
|
||||
if (weatherData.ts[i] > currentTime) {
|
||||
futureIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (futureIndex >= 0) {
|
||||
closestIndex = futureIndex;
|
||||
} else {
|
||||
var smallestDiff = Number.MAX_VALUE;
|
||||
for (var i = 0; i < weatherData.ts.length; i++) {
|
||||
var diff = Math.abs(weatherData.ts[i] - currentTime);
|
||||
if (diff < smallestDiff) {
|
||||
smallestDiff = diff;
|
||||
closestIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentForecastIndex = closestIndex;
|
||||
displayCurrentForecast();
|
||||
updateForecastNavigation();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('天气数据获取失败:', error);
|
||||
displayWeatherError('获取天气数据失败: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function transformOpenMeteoData(data) {
|
||||
var transformed = {
|
||||
ts: [],
|
||||
'temp-surface': [],
|
||||
'wind-speed': [],
|
||||
'wind-direction': [],
|
||||
'precipitation': [],
|
||||
'rh-surface': [],
|
||||
'pressure-surface': [],
|
||||
'gust-surface': []
|
||||
};
|
||||
|
||||
if (!data.hourly || !data.hourly.time) {
|
||||
return transformed;
|
||||
}
|
||||
|
||||
for (var i = 0; i < data.hourly.time.length; i++) {
|
||||
transformed.ts.push(new Date(data.hourly.time[i]).getTime());
|
||||
|
||||
transformed['temp-surface'].push(data.hourly.temperature_2m[i]);
|
||||
|
||||
var windSpeedMs = data.hourly.wind_speed_10m[i] ? data.hourly.wind_speed_10m[i] / 3.6 : null;
|
||||
transformed['wind-speed'].push(windSpeedMs);
|
||||
transformed['wind-direction'].push(data.hourly.wind_direction_10m[i]);
|
||||
transformed['precipitation'].push(data.hourly.precipitation[i]);
|
||||
transformed['rh-surface'].push(data.hourly.relative_humidity_2m[i]);
|
||||
transformed['pressure-surface'].push(data.hourly.surface_pressure[i]);
|
||||
var gustMs = data.hourly.wind_gusts_10m[i] ? data.hourly.wind_gusts_10m[i] / 3.6 : null;
|
||||
transformed['gust-surface'].push(gustMs);
|
||||
}
|
||||
|
||||
return transformed;
|
||||
}
|
||||
|
||||
function displayCurrentForecast() {
|
||||
if (!weatherData || !weatherData.ts || weatherData.ts.length === 0) {
|
||||
displayWeatherError('无可用的天气预测数据');
|
||||
return;
|
||||
}
|
||||
|
||||
var i = currentForecastIndex;
|
||||
var forecastHtml = '<div class="weather-forecast-item"><div class="weather-param-grid">';
|
||||
|
||||
if (weatherData['temp-surface'] && weatherData['temp-surface'][i] !== null) {
|
||||
var temp = weatherData['temp-surface'][i].toFixed(1);
|
||||
forecastHtml += createWeatherParam('温度', temp + '°C');
|
||||
}
|
||||
|
||||
if (weatherData['wind-speed'] && weatherData['wind-speed'][i] !== null) {
|
||||
var windSpeed = weatherData['wind-speed'][i].toFixed(1);
|
||||
forecastHtml += createWeatherParam('风速', windSpeed + ' m/s');
|
||||
}
|
||||
|
||||
if (weatherData['wind-direction'] && weatherData['wind-direction'][i] !== null) {
|
||||
var windDir = getWindDirectionFromDegrees(weatherData['wind-direction'][i]);
|
||||
forecastHtml += createWeatherParam('风向', windDir);
|
||||
}
|
||||
|
||||
if (weatherData['precipitation'] && weatherData['precipitation'][i] !== null) {
|
||||
var precip = weatherData['precipitation'][i].toFixed(1);
|
||||
forecastHtml += createWeatherParam('降水', precip + ' mm');
|
||||
}
|
||||
|
||||
if (weatherData['rh-surface'] && weatherData['rh-surface'][i] !== null) {
|
||||
var humidity = weatherData['rh-surface'][i].toFixed(0);
|
||||
forecastHtml += createWeatherParam('湿度', humidity + '%');
|
||||
}
|
||||
|
||||
if (weatherData['pressure-surface'] && weatherData['pressure-surface'][i] !== null) {
|
||||
var pressure = weatherData['pressure-surface'][i].toFixed(0);
|
||||
forecastHtml += createWeatherParam('气压', pressure + ' hPa');
|
||||
}
|
||||
|
||||
if (weatherData['gust-surface'] && weatherData['gust-surface'][i] !== null) {
|
||||
var gust = weatherData['gust-surface'][i].toFixed(1);
|
||||
forecastHtml += createWeatherParam('阵风', gust + ' m/s');
|
||||
}
|
||||
|
||||
forecastHtml += '</div></div>';
|
||||
|
||||
var contentElement = document.getElementById('weatherForecastContent');
|
||||
if (contentElement) {
|
||||
contentElement.innerHTML = forecastHtml;
|
||||
}
|
||||
}
|
||||
|
||||
function displayWeatherError(message) {
|
||||
var contentElement = document.getElementById('weatherForecastContent');
|
||||
if (contentElement) {
|
||||
contentElement.innerHTML =
|
||||
'<div class="weather-error">' +
|
||||
'<i class="layui-icon layui-icon-close"></i>' +
|
||||
'<p>' + message + '</p>' +
|
||||
'</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function createWeatherParam(label, value) {
|
||||
return '<div class="weather-param">' +
|
||||
'<span class="weather-param-label">' + label + '</span>' +
|
||||
'<span class="weather-param-value">' + value + '</span>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function formatDateTime(date) {
|
||||
var month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
var day = date.getDate().toString().padStart(2, '0');
|
||||
var hours = date.getHours().toString().padStart(2, '0');
|
||||
var minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
return month + '-' + day + ' ' + hours + ':' + minutes;
|
||||
}
|
||||
|
||||
function getWindDirection(u, v) {
|
||||
var angle = Math.atan2(-u, -v) * 180 / Math.PI;
|
||||
angle = (angle + 360) % 360;
|
||||
|
||||
var directions = ['北', '东北', '东', '东南', '南', '西南', '西', '西北'];
|
||||
var index = Math.round(angle / 45) % 8;
|
||||
return directions[index];
|
||||
}
|
||||
|
||||
function getWindDirectionFromDegrees(degrees) {
|
||||
if (degrees === null || degrees === undefined) return '无风';
|
||||
|
||||
var directions = ['北', '东北', '东', '东南', '南', '西南', '西', '西北'];
|
||||
var index = Math.round(degrees / 45) % 8;
|
||||
return directions[index];
|
||||
}
|
||||
|
||||
function isEnabled() {
|
||||
return weatherEnabled;
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
toggleWeatherForecast: toggleWeatherForecast,
|
||||
showWeatherForecast: showWeatherForecast,
|
||||
closeWeatherCard: closeWeatherCard,
|
||||
showPrevForecast: showPrevForecast,
|
||||
showNextForecast: showNextForecast,
|
||||
isEnabled: isEnabled
|
||||
};
|
||||
})();
|
||||
File diff suppressed because it is too large
Load Diff
@ -67,9 +67,9 @@
|
||||
var cfg_cols = [
|
||||
{field: 'deviceid', title: '设备号', width: 100},
|
||||
{field: 'createtime', title: '上报时间', templet: "<div>{{layui.util.toDateString(d.createtime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'roll', title: 'roll', templet: "<div>{{d.roll.toFixed(2)}}</div>"},
|
||||
{field: 'pitch', title: 'pitch', templet: "<div>{{d.pitch.toFixed(2)}}</div>"},
|
||||
{field: 'yaw', title: 'yaw', templet: "<div>{{d.yaw.toFixed(2)}}</div>"},
|
||||
{field: 'roll', title: 'roll', templet: "<div>{{d.roll.toFixed(3)}}</div>"},
|
||||
{field: 'pitch', title: 'pitch', templet: "<div>{{d.pitch.toFixed(3)}}</div>"},
|
||||
{field: 'yaw', title: 'yaw', templet: "<div>{{d.yaw.toFixed(3)}}</div>"},
|
||||
{field: 'rssi', title: '信号强度'},
|
||||
{field: 'voltage', title: '电压(mV)'},
|
||||
{field: 'solarvoltage', title: '光伏电压(mV)'},
|
||||
|
||||
@ -53,16 +53,38 @@
|
||||
</fieldset>
|
||||
|
||||
<table class="layui-hide" id="forwardParaTableId" lay-filter="forwardParaTableFilter"></table>
|
||||
<script type="text/html" id="currentTableBar">
|
||||
<a class="layui-btn layui-btn-xs layui-btn-danger data-count-delete" lay-event="delete">删除</a>
|
||||
</script>
|
||||
<script type="text/html" id="toolbarTop">
|
||||
<div class="layui-btn-container" th:if="${role=='SUPER_ADMIN'}">
|
||||
<button class="layui-btn layui-btn-normal layui-btn-sm data-add-btn" lay-event="add">添加</button>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script src="../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
|
||||
<script th:inline="none">
|
||||
<script th:inline="javascript">
|
||||
layui.use(['form', 'table'], function () {
|
||||
var $ = layui.$,
|
||||
form = layui.form,
|
||||
table = layui.table,
|
||||
laydate = layui.laydate;
|
||||
var cfg_cols = [
|
||||
{field: 'projectid', title: '项目号', sort: true},
|
||||
{field: 'deviceid', title: '设备号'},
|
||||
{field: 'createtime', title: '推送时间', templet: "<div>{{layui.util.toDateString(d.createtime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'starttime', title: '数据开始时间', templet: "<div>{{layui.util.toDateString(d.starttime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'endtime', title: '数据结束时间', templet: "<div>{{layui.util.toDateString(d.endtime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'fwd_group_id', title: '推送组'},
|
||||
{field: 'state', title: '状态',templet: '#stateTrans'},
|
||||
{title: '操作', toolbar: '#currentTableBar', fixed: "right", width: 120}
|
||||
];
|
||||
if([[${role}]] != "SUPER_ADMIN") {
|
||||
cfg_cols[7].hide = true;
|
||||
}
|
||||
|
||||
laydate.render({
|
||||
elem: '#ID-laydate-start-date1',
|
||||
type: 'datetime'
|
||||
@ -85,17 +107,11 @@
|
||||
table.render({
|
||||
elem: '#forwardParaTableId',
|
||||
url: '/fwd/resend_records',
|
||||
toolbar: '#toolbarTable',
|
||||
toolbar: '#toolbarTop',
|
||||
defaultToolbar: ['filter'],
|
||||
cols: [[
|
||||
{field: 'projectid', title: '项目号', sort: true},
|
||||
{field: 'deviceid', title: '设备号'},
|
||||
{field: 'createtime', title: '推送时间', templet: "<div>{{layui.util.toDateString(d.createtime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'starttime', title: '数据开始时间', templet: "<div>{{layui.util.toDateString(d.starttime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'endtime', title: '数据结束时间', templet: "<div>{{layui.util.toDateString(d.endtime, 'yyyy-MM-dd HH:mm:ss')}}</div>"},
|
||||
{field: 'fwd_group_id', title: '推送组'},
|
||||
{field: 'state', title: '状态',templet: '#stateTrans'}
|
||||
]],
|
||||
cols: [
|
||||
cfg_cols
|
||||
],
|
||||
limits: [10, 20, 50, 100, 150],
|
||||
limit: 10,
|
||||
page: true,
|
||||
@ -119,8 +135,50 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
table.on('toolbar(forwardParaTableFilter)', function (obj) {
|
||||
if (obj.event === 'add') { // 监听添加操作
|
||||
var index = layer.open({
|
||||
title: '添加补传记录',
|
||||
type: 2,
|
||||
shade: 0.2,
|
||||
maxmin:true,
|
||||
shadeClose: true,
|
||||
area: ['100%', '100%'],
|
||||
content: '../page/table/resend_record_add'
|
||||
});
|
||||
$(window).on("resize", function () {
|
||||
layer.full(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
table.on('tool(forwardParaTableFilter)', function (obj) {
|
||||
var data = obj.data;
|
||||
if (obj.event === 'delete') {
|
||||
layer.confirm('确定删除?', function(index){
|
||||
$.ajax({
|
||||
type:"POST",
|
||||
url:"/fwd/resend_records/delete",
|
||||
data:{
|
||||
'del_id':data.id
|
||||
},
|
||||
success: function (data) {
|
||||
table.reload('forwardParaTableId');
|
||||
},
|
||||
error: function () {
|
||||
console.log("ajax error");
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function onUpdated(){
|
||||
layui.table.reload('forwardParaTableId');
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="stateTrans">
|
||||
@ -129,9 +187,9 @@
|
||||
{{# } else if(d.state == 1){ }}
|
||||
<span class="layui-badge layui-bg-red">推送失败</span>
|
||||
{{# } else if(d.state == 2){ }}
|
||||
<span class="layui-badge layui-bg-red">断点补传</span>
|
||||
<span class="layui-badge layui-bg-orange">断点补传</span>
|
||||
{{# } else { }}
|
||||
<span class="layui-badge layui-bg-orange">推送中</span>
|
||||
<span class="layui-badge layui-bg-blue">推送中</span>
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
|
||||
@ -230,8 +230,9 @@
|
||||
<hr th:if="${role=='SUPER_ADMIN'}">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block" style="float:right" >
|
||||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">确认保存</button>
|
||||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">保存</button>
|
||||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="initLocBtn">取初值</button>
|
||||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="contBtn">续数据</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -286,6 +287,47 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
form.on('submit(contBtn)', function (data) {
|
||||
// 弹出输入框
|
||||
layer.prompt({
|
||||
title: '请输入接续的设备编号和日期时间,用逗号隔开', // 弹窗标题
|
||||
formType: 0, // 0-文本输入框
|
||||
maxlength: 50, // 最大输入长度
|
||||
value: '', // 初始值
|
||||
btn: ['确定', '取消'] // 按钮组
|
||||
}, function(value, index) { // 确定按钮回调
|
||||
// 处理输入结果
|
||||
if(value) {
|
||||
$.ajax({
|
||||
type:"POST",
|
||||
url:"/gnss/device/cont_loc",
|
||||
data:{
|
||||
'deviceid':$('#deviceid').val(),
|
||||
'createtime':value
|
||||
},
|
||||
success: function (result) {
|
||||
if(result.code == 0) {
|
||||
$('#ipose').val(result.ipose);
|
||||
$('#iposn').val(result.iposn);
|
||||
$('#iposd').val(result.iposd);
|
||||
}
|
||||
else{
|
||||
layer.alert(result.msg);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
console.log("ajax error");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
layer.msg('输入内容不能为空!', {icon: 2});
|
||||
}
|
||||
// 关闭当前弹窗
|
||||
layer.close(index);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
form.on('select(device_type)', function (data) {
|
||||
setEcefEditor();
|
||||
});
|
||||
|
||||
@ -0,0 +1,136 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>补传数据</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link rel="stylesheet" href="../../lib/layui-v2.6.3/css/layui.css" media="all">
|
||||
<link rel="stylesheet" href="../../css/public.css" media="all">
|
||||
<style>
|
||||
body {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="layui-form layuimini-form">
|
||||
<input type="hidden" name="id" id="id">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">所属部门</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="tenantid" id="tenantid" lay-search="" lay-filter="tenant">
|
||||
<option value="">全部</option>
|
||||
<option th:each="item : ${tenant_list}" th:text="${item.name}" th:value="${item.id}"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">所属项目</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="project_id" id="projectid" lay-search="" lay-filter="project">
|
||||
<option value="">全部</option>
|
||||
<option th:each="item : ${project_list}" th:text="${item.project_id}" th:value="${item.project_id}"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">设备编号</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="deviceid" id="deviceid" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">推送组</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="fwd_group_id" id="fwd_group_id" lay-verify="required" lay-reqtext="不能为空" lay-search="">
|
||||
<option th:each="item : ${gnss_group_fwd_list}" th:text="${item.name}" th:value="${item.name}"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">时间段</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="starttime" autocomplete="off" lay-verify="required" lay-reqtext="不能为空" id="ID-laydate-start-date" class="layui-input" placeholder="开始日期">
|
||||
</div>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="endtime" autocomplete="off" lay-verify="required" lay-reqtext="不能为空" id="ID-laydate-end-date" class="layui-input" placeholder="结束日期">
|
||||
</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.$,
|
||||
laydate = layui.laydate;
|
||||
var iframeIndex = parent.layer.getFrameIndex(window.name);
|
||||
|
||||
laydate.render({
|
||||
elem: '#ID-laydate-start-date',
|
||||
type: 'datetime'
|
||||
});
|
||||
laydate.render({
|
||||
elem: '#ID-laydate-end-date',
|
||||
type: 'datetime'
|
||||
});
|
||||
// 所属部门下拉框改变,修改项目下拉框
|
||||
form.on('select(tenant)', function (data) {
|
||||
console.log(data.value);
|
||||
$.ajax({
|
||||
type:"GET",
|
||||
url:"/gnss/device/q_project",
|
||||
data:{
|
||||
'tenantid':data.value
|
||||
},
|
||||
success: function(result) {
|
||||
$('#projectid').empty();
|
||||
$('#projectid').append(new Option("全部", ""));
|
||||
$('#deviceid').empty();
|
||||
//console.log(result);
|
||||
$.each(result.data, function (index, item) {
|
||||
$('#projectid')
|
||||
.append(new Option(item.project_id, item.project_id));
|
||||
});
|
||||
layui.form.render("select");
|
||||
},
|
||||
error: function () {
|
||||
console.log("ajax error");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//监听提交
|
||||
form.on('submit(saveBtn)', function (data) {
|
||||
$.ajax({
|
||||
type:"POST",
|
||||
url:"/fwd/resend_records/add",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
data: JSON.stringify(data.field),
|
||||
success: function (result) {
|
||||
parent.onUpdated();
|
||||
parent.layer.close(iframeIndex);
|
||||
},
|
||||
error: function () {
|
||||
console.log("ajax error");
|
||||
parent.layer.close(iframeIndex);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -3,7 +3,9 @@ package com.imdroid.ntripproxy.service;
|
||||
public class Ntrip2Channels {
|
||||
final private String localHost="127.0.0.1";
|
||||
final private int localPort=9903;
|
||||
final private String remoteHost="47.107.50.52";
|
||||
final private String remoteHost="8.134.185.53";
|
||||
//final private String remoteHost="100.91.37.6";
|
||||
//final private String remoteHost="47.107.50.52";
|
||||
final private int remotePort=9903;
|
||||
|
||||
public static final Ntrip2Channels INSTANCE = new Ntrip2Channels();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user