北斗卫星数据监测展示互动系统

This commit is contained in:
2026-03-18 16:40:36 +08:00
parent 7bc9959465
commit 70792767d4
17 changed files with 429 additions and 330 deletions

View File

@@ -1,13 +1,59 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="w-h-full"> <html class="w-h-full">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
/* 当屏幕宽度大于或等于3840px时 */
@media (min-width: 3840px) {
html {
font-size: 40px;
}
}
/* 当屏幕宽度在2560px到3839px之间时 */
@media (min-width: 2560px) and (max-width: 3839px) {
html {
font-size: 26px;
}
}
/* 当屏幕宽度在1920px到2559px之间时 */
@media (min-width: 1920px) and (max-width: 2559px) {
html {
font-size: 20px;
}
}
/* 当屏幕宽度在750px到1919px之间时 */
@media (min-width: 750px) and (max-width: 1919px) {
html {
font-size: 14px;
}
}
/* 当屏幕宽度在600px到749px之间时 */
@media (min-width: 600px) and (max-width: 749px) {
html {
font-size: 14px;
}
}
/* 当屏幕宽度小于600px时 */
@media (max-width: 599px) {
html {
font-size: 12px;
}
}
</style>
<title>北斗卫星数据监测展示软件</title> <title>北斗卫星数据监测展示软件</title>
</head> </head>
<body class="w-h-full"> <body class="w-h-full">
<div id="app" class="w-h-full"></div> <div id="app" class="w-h-full"></div>
<script type="module" src="/src/render/main.ts"></script> <script type="module" src="/src/render/main.ts"></script>
</body> </body>
</html> </html>

View File

@@ -19,6 +19,8 @@
"echarts": "^5.1.0", "echarts": "^5.1.0",
"electron-store": "^8.0.0", "electron-store": "^8.0.0",
"nmea": "http://git.hwasmart.com/bd_group/node-nmea.git", "nmea": "http://git.hwasmart.com/bd_group/node-nmea.git",
"node-gyp": "^10.0.1",
"node-pre-gyp": "^0.17.0",
"serialport": "^12.0.0", "serialport": "^12.0.0",
"through": "^2.3.8", "through": "^2.3.8",
"vue": "^3.2.26", "vue": "^3.2.26",
@@ -31,9 +33,9 @@
"@vue/compiler-sfc": "^3.2.26", "@vue/compiler-sfc": "^3.2.26",
"autoprefixer": "^10.2.5", "autoprefixer": "^10.2.5",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"electron": "^19.0.3", "electron": "^22.0.0",
"electron-builder": "^25.0.5", "electron-builder": "^25.0.5",
"electron-rebuild": "2.3.5", "electron-rebuild": "^2.3.5",
"postcss": "^8.2.10", "postcss": "^8.2.10",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",

View File

@@ -1,3 +1,3 @@
// 加密关键字 // 加密关键字
const keyword = 'BD' const keyword = 'BD';
export { keyword } export { keyword };

View File

@@ -1,89 +1,89 @@
import { parse, Helpers } from 'nmea' import { parse, Helpers } from 'nmea';
let events = {} let events = {};
const on = (eventName, callback) => { const on = (eventName, callback) => {
if (!(eventName in events)) { if (!(eventName in events)) {
events[eventName] = [] events[eventName] = [];
} }
events[eventName].push(callback) events[eventName].push(callback);
} };
const emit = (eventName, ...args) => { const emit = (eventName, ...args) => {
if (!(eventName in events)) { if (!(eventName in events)) {
return return;
} }
const cbs = events[eventName] const cbs = events[eventName];
cbs.forEach(cb => { cbs.forEach(cb => {
cb.call(this, ...args) cb.call(this, ...args);
}); });
} };
const handle = (value) => { const handle = (value) => {
if (value.length <= 0) return if (value.length <= 0) return;
emit('received', value) emit('received', value);
nmeaObj = resetNMEAObj() nmeaObj = resetNMEAObj();
const strArr = value.split('\r\n') const strArr = value.split('\r\n');
for (let i = 0; i < strArr.length; i++) { for (let i = 0; i < strArr.length; i++) {
const element = strArr[i]; const element = strArr[i];
doHandle(element) doHandle(element);
} }
emit('handled', nmeaObj) emit('handled', nmeaObj);
} };
const doHandle = (value) => { const doHandle = (value) => {
if (!verify(value)) { if (!verify(value)) {
return return;
} }
const result = parse(value) const result = parse(value);
switch (result.sentence) { switch (result.sentence) {
case 'GGA': case 'GGA':
nmeaObj.GGA = result nmeaObj.GGA = result;
break; break;
case 'GLL': case 'GLL':
nmeaObj.GLL = result nmeaObj.GLL = result;
break; break;
case 'RMC': case 'RMC':
nmeaObj.RMC = result nmeaObj.RMC = result;
break; break;
case 'VTG': case 'VTG':
nmeaObj.VTG = result nmeaObj.VTG = result;
break; break;
case 'ZDA': case 'ZDA':
nmeaObj.ZDA = result nmeaObj.ZDA = result;
break; break;
case 'TXT': case 'TXT':
nmeaObj.TXT = result nmeaObj.TXT = result;
break; break;
case 'GSA': case 'GSA':
nmeaObj.GSA.push(result) nmeaObj.GSA.push(result);
break; break;
case 'GSV': case 'GSV':
nmeaObj.GSV.push(result) nmeaObj.GSV.push(result);
break; break;
} }
} };
const verify = (value) => { const verify = (value) => {
if (!value) { if (!value) {
return false return false;
} }
const dollar_index = value.indexOf('$') const dollar_index = value.indexOf('$');
const star_index = value.indexOf('*') const star_index = value.indexOf('*');
if (value.indexOf('$') !== 0 || star_index <= dollar_index) { if (value.indexOf('$') !== 0 || star_index <= dollar_index) {
return false return false;
} }
const [sentence, checksum, ] = value.split('*') const [sentence, checksum, ] = value.split('*');
return Helpers.verifyChecksum(sentence, checksum) return Helpers.verifyChecksum(sentence, checksum);
} };
const resetNMEAObj = () => { const resetNMEAObj = () => {
return { return {
@@ -95,9 +95,9 @@ const resetNMEAObj = () => {
TXT: null, TXT: null,
GSA: [], GSA: [],
GSV: [] GSV: []
} };
} };
let nmeaObj = resetNMEAObj() let nmeaObj = resetNMEAObj();
export default { on, handle } export default { on, handle };

View File

@@ -1,80 +1,80 @@
import { app, screen, globalShortcut, BrowserWindow, ipcMain, dialog } from 'electron' import { app, screen, globalShortcut, BrowserWindow, ipcMain, dialog } from 'electron';
import { join } from "path" import { join } from "path";
import config from './config.js' import config from './config.js';
import './load-serialport' import './load-serialport';
import { keyword } from './KeyWord' import { keyword } from './KeyWord';
import child_process from 'child_process' import child_process from 'child_process';
import crypto from 'crypto' import crypto from 'crypto';
function queryPass(passPath: string, passValue: string) { function queryPass(passPath: string, passValue: string) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
try { try {
child_process.exec(`reg query ${passPath} /v ${passValue}`, (error: Error | null, stdout: string, stderr: string) => { child_process.exec(`reg query ${passPath} /v ${passValue}`, (error: Error | null, stdout: string, stderr: string) => {
if (error) { if (error) {
reject(error) reject(error);
return return;
} }
resolve({stdout, stderr}) resolve({stdout, stderr});
}) })
} catch (error) { } catch (error) {
reject(error) reject(error);
} }
}) })
} };
function queryKey(keyPath: string, keyValue: string) { function queryKey(keyPath: string, keyValue: string) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
try { try {
child_process.exec(`reg query ${keyPath} /v ${keyValue}`, (error: Error | null, stdout: string, stderr: string) => { child_process.exec(`reg query ${keyPath} /v ${keyValue}`, (error: Error | null, stdout: string, stderr: string) => {
if (error) { if (error) {
reject(error) reject(error);
return return;
} }
resolve({stdout, stderr}) resolve({stdout, stderr});
}) })
} catch (error) { } catch (error) {
reject(error) reject(error);
} }
}) })
} };
function cryptMD5(GUID: string) { function cryptMD5(GUID: string) {
let md5 = crypto.createHash('md5') let md5 = crypto.createHash('md5');
let ciphertext = md5.update(GUID).digest('hex') let ciphertext = md5.update(GUID).digest('hex');
return ciphertext.slice(0,8)+'-'+ciphertext.slice(8,12)+'-'+ciphertext.slice(12,16)+'-'+ciphertext.slice(16,20)+'-'+ciphertext.slice(20,32) return ciphertext.slice(0,8)+'-'+ciphertext.slice(8,12)+'-'+ciphertext.slice(12,16)+'-'+ciphertext.slice(16,20)+'-'+ciphertext.slice(20,32);
} };
const passPath = 'HKEY_CURRENT_USER\\SOFTWARE\\HwaSmart' const passPath = 'HKEY_CURRENT_USER\\SOFTWARE\\HwaSmart';
const passValue = 'BDAuthorization' const passValue = 'BDAuthorization';
const keyPath = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography' const keyPath = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography';
const keyValue = 'MachineGuid' const keyValue = 'MachineGuid';
async function checkLaunchEnv() { async function checkLaunchEnv() {
try { try {
const passResult: any = await queryPass(passPath, passValue) const passResult: any = await queryPass(passPath, passValue);
const keyResult: any = await queryKey(keyPath, keyValue) const keyResult: any = await queryKey(keyPath, keyValue);
if(cryptMD5(keyResult.stdout.slice(83,119) + keyword) == passResult.stdout.slice(72,108)){ if(cryptMD5(keyResult.stdout.slice(83,119) + keyword) == passResult.stdout.slice(72,108)){
return true return true;
}else{ }else{
return false return false;
} }
// 成功 // 成功
// 查询到 有这个app启动项 // 查询到 有这个app启动项
} catch (error) { } catch (error) {
// 没有查询到该app启动项目 // 没有查询到该app启动项目
return false return false;
}
} }
};
async function main() { async function main() {
// 异步代码 // 异步代码
const checkReault: any = await checkLaunchEnv() const checkReault: any = await checkLaunchEnv();
console.log('env right:', checkReault) console.log('env right:', checkReault);
// 异步代码执行完毕后执行的代码 // 异步代码执行完毕后执行的代码
if (checkReault) { if (checkReault) {
const URl_REGEX = /[a-zA-z]+:\/\/[^\s]*/ const URl_REGEX = /[a-zA-z]+:\/\/[^\s]*/;
const DEFAULT_OPTION = { const DEFAULT_OPTION = {
fullscreen: true, fullscreen: true,
frame: false, frame: false,
@@ -82,57 +82,75 @@ const DEFAULT_OPTION = {
nodeIntegration: true, nodeIntegration: true,
preload: join(__dirname, 'preload.js') preload: join(__dirname, 'preload.js')
} }
} };
const launch = () => { const launch = () => {
const extend_tdt_window = config.get('extend_tdt_window') const showMode = config.get('showMode');
const displays = screen.getAllDisplays() const extend_tdt_window = config.get('extend_tdt_window');
const displays = screen.getAllDisplays();
const { option, url } = getMonitorWindowArguments(displays[0]) if (showMode == "showPositive") {
createWindow(option, url) const { option, url } = getMonitorWindowArguments(displays[0]);
createWindow(option, url);
if (extend_tdt_window) { if (extend_tdt_window) {
const { option, url } = getTDTWindowArguments(displays[displays.length - 1]) const { option, url } = getTDTWindowArguments(displays[displays.length - 1]);
createWindow(option, url) createWindow(option, url);
}
} else if (showMode == "showReverse") {
const { option, url } = getTDTWindowArguments(displays[0]);
createWindow(option, url);
if (extend_tdt_window) {
const { option, url } = getMonitorWindowArguments(displays[displays.length - 1]);
createWindow(option, url);
} }
} }
};
const getMonitorWindowArguments = (display: any) => { const getMonitorWindowArguments = (display: any) => {
const option = getOption(display) const option = getOption(display);
const url = process.env.NODE_ENV === 'development' ? 'http://localhost:3000/' : 'dist/render/index.html' const url = process.env.NODE_ENV === 'development' ? 'http://localhost:3000/' : 'dist/render/index.html';
return { option, url };
};
return { option, url }
}
const getTDTWindowArguments = (display: any) => { const getTDTWindowArguments = (display: any) => {
const option = getOption(display) const option = getOption(display);
const url = 'http://gd.map.hwasmart.com/' const url = 'http://gd.map.hwasmart.com/';
return { option, url } return { option, url };
} };
const getOption = (display: any) => { const getOption = (display: any) => {
const origin = getDisplayOrigin(display) const origin = getDisplayOrigin(display);
const option = Object.assign({}, DEFAULT_OPTION, origin) const option = Object.assign({}, DEFAULT_OPTION, origin);
return option return option;
} };
const getDisplayOrigin = (display: any) => { const getDisplayOrigin = (display: any) => {
const origin = !display ? {x: 0, y: 0} : {x: display.bounds.x, y: display.bounds.y} const origin = !display ? {x: 0, y: 0} : {x: display.bounds.x, y: display.bounds.y};
return origin return origin;
} };
var windows:Array<BrowserWindow> = []; var windows:Array<BrowserWindow> = [];
function createWindow(option: any, url: any) { function createWindow(option: any, url: any) {
const win = new BrowserWindow(option) const win = new BrowserWindow(option);
if (URl_REGEX.test(url)) { if (URl_REGEX.test(url)) {
win.loadURL(url) win.loadURL(url);
} else { } else {
win.loadFile(url) win.loadFile(url);
} }
if(process.env.NODE_ENV === 'development') win.webContents.openDevTools() if(process.env.NODE_ENV === 'development') win.webContents.openDevTools();
windows.push(win); windows.push(win);
} };
ipcMain.on("APP_MOUNTED", (event) => {
const { sender } = event;
let softwareName = config.get('softwareName');
sender.send('softwareName', softwareName);
});
ipcMain.on('CLOSE', (event) => { ipcMain.on('CLOSE', (event) => {
const res = dialog.showMessageBox({ const res = dialog.showMessageBox({
@@ -143,7 +161,7 @@ ipcMain.on('CLOSE', (event) => {
cancelId: 1, // 按esc默认点击索引按钮 cancelId: 1, // 按esc默认点击索引按钮
defaultId: 0, // 默认高亮的按钮下标 defaultId: 0, // 默认高亮的按钮下标
buttons: ['确认', '取消'], // 按钮按索引从右往左排序 buttons: ['确认', '取消'], // 按钮按索引从右往左排序
}) });
res.then((data)=>{ res.then((data)=>{
if(data.response == 0){ if(data.response == 0){
@@ -152,33 +170,33 @@ ipcMain.on('CLOSE', (event) => {
}); });
windows = []; windows = [];
}else{ }else{
console.log('not close software') console.log('not close software');
} }
}) });
}) });
app.whenReady().then(() => { app.whenReady().then(() => {
launch() launch();
// 屏蔽 F11 进入/退出全屏功能 // 屏蔽 F11 进入/退出全屏功能
globalShortcut.register('F11', () => {return}) globalShortcut.register('F11', () => {return});
}) });
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit();
} }
}) });
app.on('activate', () => { app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) { if (BrowserWindow.getAllWindows().length === 0) {
launch() launch();
} }
}) });
} else { } else {
dialog.showErrorBox('系统提示', '软件启动出错,请联系售后技术支持人员') dialog.showErrorBox('系统提示', '软件启动出错,请联系售后技术支持人员');
process.exit(1) process.exit(1);
}
} }
};
main() main();

View File

@@ -1,10 +1,10 @@
// import Store from 'electron-store' // import Store from 'electron-store'
const Store = require('electron-store') const Store = require('electron-store');
const store = new Store() const store = new Store();
const set = (key, val) => store.set(key, val) const set = (key, val) => store.set(key, val);
const get = (key) => store.get(key) const get = (key) => store.get(key);
// export default {set, get} // export default {set, get}
module.exports = { set, get } module.exports = { set, get };

View File

@@ -1,26 +1,26 @@
import { ipcMain } from 'electron' import { ipcMain } from 'electron';
const senders = [] const senders = [];
ipcMain.on("APP_MOUNTED", (event) => { ipcMain.on("APP_MOUNTED", (event) => {
const { sender } = event const { sender } = event;
senders.push(sender) senders.push(sender);
sender.once('destroyed', () => { sender.once('destroyed', () => {
const index = senders.indexOf(sender) const index = senders.indexOf(sender);
if (index >= 0) senders.splice(index, 1) if (index >= 0) senders.splice(index, 1);
})
}) })
});
const sendMsg = (channel, msg) => { const sendMsg = (channel, msg) => {
if (senders.length <= 0) return if (senders.length <= 0) return;
try { try {
senders.forEach((sender) => { senders.forEach((sender) => {
sender.send(channel, msg) sender.send(channel, msg);
}) })
} catch (error) { } catch (error) {
console.error(error) console.error(error);
}
} }
};
export default sendMsg export default sendMsg;

View File

@@ -1,49 +1,49 @@
import { SerialPort } from 'serialport' import { SerialPort } from 'serialport';
import { InterByteTimeoutParser } from '@serialport/parser-inter-byte-timeout' import { InterByteTimeoutParser } from '@serialport/parser-inter-byte-timeout';
import config from './config.js' import config from './config.js';
import sendMsg from './ipcRendererManager' import sendMsg from './ipcRendererManager';
import NMEAHandler from './api/NMEAHandler' import NMEAHandler from './api/NMEAHandler';
const DEFAULT_BAUDRATE = 115200 const DEFAULT_BAUDRATE = 115200;
const getSerialPortConstructorArguments = (ports) => { const getSerialPortConstructorArguments = (ports) => {
// 很多电脑自带COM1接口,过滤掉COM1口 // 很多电脑自带COM1接口,过滤掉COM1口
ports = ports.filter((port) => port.path != 'COM1') ports = ports.filter((port) => port.path != 'COM1');
if (ports.length == 0) return if (ports.length == 0) return;
const serialport = config.get('serialport') const serialport = config.get('serialport');
const path = ( serialport && serialport.path ) ? serialport.path : ports[0].path const path = ( serialport && serialport.path ) ? serialport.path : ports[0].path;
const baudRate = ( serialport && serialport.baudRate ) ? serialport.baudRate : DEFAULT_BAUDRATE const baudRate = ( serialport && serialport.baudRate ) ? serialport.baudRate : DEFAULT_BAUDRATE;
return { path, baudRate } return { path, baudRate };
} };
const openSerialport = (ports) => { const openSerialport = (ports) => {
try { try {
const constructorArguments = getSerialPortConstructorArguments(ports) const constructorArguments = getSerialPortConstructorArguments(ports);
if (!constructorArguments) { if (!constructorArguments) {
console.error('设备未连接') console.error('设备未连接');
return return;
} }
const { path, baudRate } = constructorArguments const { path, baudRate } = constructorArguments;
const serialPort = new SerialPort({ path, baudRate }) const serialPort = new SerialPort({ path, baudRate });
const parser = serialPort.pipe(new InterByteTimeoutParser({interval: 300})) const parser = serialPort.pipe(new InterByteTimeoutParser({interval: 300}));
NMEAHandler.on('received' , (nmeaStr) => { NMEAHandler.on('received' , (nmeaStr) => {
sendMsg('NMEA_RECEIVED', nmeaStr) sendMsg('NMEA_RECEIVED', nmeaStr);
}) });
NMEAHandler.on('handled' , (nmeaObj) => { NMEAHandler.on('handled' , (nmeaObj) => {
sendMsg('NMEA_HANDLED', JSON.stringify(nmeaObj)) sendMsg('NMEA_HANDLED', JSON.stringify(nmeaObj));
}) });
parser.on('data', (data) => NMEAHandler.handle(data.toString())) parser.on('data', (data) => NMEAHandler.handle(data.toString()));
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
} };
SerialPort.list().then( SerialPort.list().then(
ports => openSerialport(ports), ports => openSerialport(ports),
err => console.error(err) err => console.error(err)
) );

View File

@@ -1,20 +1,20 @@
const { ipcRenderer, contextBridge } = require('electron') const { ipcRenderer, contextBridge } = require('electron');
contextBridge.exposeInMainWorld('ipcRenderer', { contextBridge.exposeInMainWorld('ipcRenderer', {
send: (channel, data) => { send: (channel, data) => {
// whitelist channels // whitelist channels
let validChannels = ['toMain'] let validChannels = ['toMain'];
if (validChannels.includes(channel)) { if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data) ipcRenderer.send(channel, data);
} }
ipcRenderer.send(channel, data) ipcRenderer.send(channel, data);
}, },
receive: (channel, func) => { receive: (channel, func) => {
let validChannels = ['fromMain'] let validChannels = ['fromMain'];
if (validChannels.includes(channel)) { if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender` // Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args)) ipcRenderer.on(channel, (event, ...args) => func(...args));
} }
ipcRenderer.on(channel, (event, ...args) => func(...args)) ipcRenderer.on(channel, (event, ...args) => func(...args));
} }
}) });

View File

@@ -16,43 +16,46 @@
<BaseInfoView ref="baseInfoViewInstance" /> <BaseInfoView ref="baseInfoViewInstance" />
</div> </div>
<div class="text-white fixed bottom-0 right-0"> <div class="text-white fixed bottom-0 right-0">
北斗卫星数据监测展示互动系统V1.0 {{ softwareName }}
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue';
import SNRView from './components/SNRView.vue' import SNRView from './components/SNRView.vue';
import NMEAView from './components/NMEAView.vue' import NMEAView from './components/NMEAView.vue';
import EarthView from './components/EarthView.vue' import EarthView from './components/EarthView.vue';
import BaseInfoView from './components/BaseInfoView.vue' import BaseInfoView from './components/BaseInfoView.vue';
import PlanisphereView from './components/PlanisphereView.vue' import PlanisphereView from './components/PlanisphereView.vue';
const nmeaViewInstance = ref(null);
const snrViewInstance = ref(null);
const earthViewInstance = ref(null);
const baseInfoViewInstance = ref(null);
const planisphereViewInstance = ref(null);
const softwareName = ref("北斗卫星数据监测展示互动系统V1.0");
const nmeaViewInstance = ref(null) const changeName = (newName) => {
const snrViewInstance = ref(null) if (newName !== undefined) {
const earthViewInstance = ref(null) softwareName.value = newName;
const baseInfoViewInstance = ref(null) }
const planisphereViewInstance = ref(null) };
onMounted(() => { onMounted(() => {
if ('ipcRenderer' in window) { if ('ipcRenderer' in window) {
ipcRenderer.receive('NMEA_RECEIVED', nmeaViewInstance.value.update) ipcRenderer.receive('NMEA_RECEIVED', nmeaViewInstance.value.update);
ipcRenderer.receive('NMEA_HANDLED' , (nmeaStr: string) => { ipcRenderer.receive('NMEA_HANDLED' , (nmeaStr: string) => {
const nmea = JSON.parse(nmeaStr) const nmea = JSON.parse(nmeaStr);
snrViewInstance.value.update(nmea);
snrViewInstance.value.update(nmea) earthViewInstance.value.update(nmea);
earthViewInstance.value.update(nmea) baseInfoViewInstance.value.update(nmea);
baseInfoViewInstance.value.update(nmea) planisphereViewInstance.value.update(nmea);
planisphereViewInstance.value.update(nmea) });
}) ipcRenderer.receive("softwareName", changeName);
ipcRenderer.send('APP_MOUNTED');
ipcRenderer.send('APP_MOUNTED')
} }
}) });
</script> </script>
<style scoped> <style scoped>

View File

@@ -1,58 +1,67 @@
import { Helpers } from 'nmea' import { Helpers } from 'nmea';
const getPosition = (nmea) => { const getPosition = (nmea) => {
if (!nmea) return if (!nmea) return;
const { RMC } = nmea const { RMC } = nmea;
const position = { lng: Helpers.parseLongitude(RMC.lon, RMC.lonPole), lat: Helpers.parseLatitude(RMC.lat, RMC.latPole) } const position = { lng: Helpers.parseLongitude(RMC.lon, RMC.lonPole), lat: Helpers.parseLatitude(RMC.lat, RMC.latPole) };
return position return position;
} };
const getBaseInfo = (nmea) => { const getBaseInfo = (nmea) => {
if (!nmea) return if (!nmea) return;
const { RMC, GGA } = nmea const { RMC, GGA } = nmea;
try { try {
const longitude = (!RMC.lon || !RMC.lonPole) ? '' : Helpers.parseLongitude(RMC.lon, RMC.lonPole) const longitude = (!RMC.lon || !RMC.lonPole) ? '' : Helpers.parseLongitude(RMC.lon, RMC.lonPole);
const latitude = (!RMC.lat || !RMC.latPole) ? '' : Helpers.parseLatitude(RMC.lat, RMC.latPole) const latitude = (!RMC.lat || !RMC.latPole) ? '' : Helpers.parseLatitude(RMC.lat, RMC.latPole);
const altitude = GGA.alt + GGA.altUnit const altitude = GGA.alt + GGA.altUnit;
const date = (!RMC.date) ? '' : '20' + RMC.date.slice(4, 6) + '-' + RMC.date.slice(2, 4) + '-' + RMC.date.slice(0, 2) const date = (!RMC.date) ? '' : '20' + RMC.date.slice(4, 6) + '-' + RMC.date.slice(2, 4) + '-' + RMC.date.slice(0, 2);
let UTCTime = '', BJTime = '' let UTCTime = '', BJTime = '';
if (RMC.timestamp) { if (RMC.timestamp) {
const hour = parseInt(RMC.timestamp.slice(0, 2)) const hour = parseInt(RMC.timestamp.slice(0, 2));
const bjHour = (hour + 8) % 24 const bjHour = (hour + 8) % 24;
UTCTime = hour.toString().padStart(2, '0') + ':' + RMC.timestamp.slice(2, 4) + ':' + RMC.timestamp.slice(4, 6) UTCTime = hour.toString().padStart(2, '0') + ':' + RMC.timestamp.slice(2, 4) + ':' + RMC.timestamp.slice(4, 6);
BJTime = bjHour.toString().padStart(2, '0') + ':' + RMC.timestamp.slice(2, 4) + ':' + RMC.timestamp.slice(4, 6) BJTime = bjHour.toString().padStart(2, '0') + ':' + RMC.timestamp.slice(2, 4) + ':' + RMC.timestamp.slice(4, 6);
} }
const baseInfo = { longitude, latitude, altitude, date, UTCTime, BJTime } const baseInfo = { longitude, latitude, altitude, date, UTCTime, BJTime };
return baseInfo return baseInfo;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
} };
const getSNROption = (nmea) => { const getSNROption = (nmea) => {
if (!nmea) return if (!nmea) return;
const { GSV, GSA } = nmea const { GSV, GSA } = nmea;
const GSVArr = GSV.filter((gsv) => gsv.talker_id === 'BD') const GSVArr = GSV.filter((gsv) => gsv.talker_id === 'BD' || gsv.talker_id === 'GB');
const GSAArr = GSA.filter((gsa) => gsa.talker_id === 'BD') const GSAArr = GSA.filter((gsa) => gsa.talker_id === 'BD' || gsa.talker_id === 'GB');
try { try {
let yAxis_data = [], series_data = [] let yAxis_data = [], series_data = [];
const in_positioning_id = get_in_positioning_id(GSAArr) const in_positioning_id = get_in_positioning_id(GSAArr);
GSVArr.forEach(({ satellites }) => { GSVArr.forEach(({ satellites }) => {
let compatibleVersion = false;
if (satellites[satellites.length-1].elevationDeg == null && satellites[satellites.length-1].azimuthTrue == null && satellites[satellites.length-1].SNRdB == null) {
compatibleVersion = true;
let type = satellites.pop();
if (type.id == 0 || type.id == 1) {
compatibleVersion = false;
}
}
if (!compatibleVersion) {
satellites.forEach(({ id, SNRdB }) => { satellites.forEach(({ id, SNRdB }) => {
yAxis_data.push(id) yAxis_data.push(id);
if (in_positioning_id.indexOf(parseInt(id)) < 0) { if (in_positioning_id.indexOf(parseInt(id)) < 0) {
series_data.push([SNRdB, id, false]) series_data.push([SNRdB, id, false]);
} else { } else {
series_data.push([SNRdB, id, true]) series_data.push([SNRdB, id, true]);
} }
}); });
}
}); });
const SNROption = { const SNROption = {
@@ -62,52 +71,52 @@ const getSNROption = (nmea) => {
series: [{ series: [{
data: series_data data: series_data
}] }]
} };
return SNROption return SNROption;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
} };
const getPlanisphereOption = (nmea) => { const getPlanisphereOption = (nmea) => {
if (!nmea) return if (!nmea) return;
const { GSV, GSA } = nmea const { GSV, GSA } = nmea;
const GSVArr = GSV.filter((gsv) => gsv.talker_id === 'BD') const GSVArr = GSV.filter((gsv) => gsv.talker_id === 'BD' || gsv.talker_id === 'GB');
const GSAArr = GSA.filter((gsa) => gsa.talker_id === 'BD') const GSAArr = GSA.filter((gsa) => gsa.talker_id === 'BD' || gsa.talker_id === 'GB');
try { try {
let data = [] let data = [];
const in_positioning_id = get_in_positioning_id(GSAArr) const in_positioning_id = get_in_positioning_id(GSAArr);
GSVArr.forEach(({ satellites }) => { GSVArr.forEach(({ satellites }) => {
satellites.forEach(({ id, elevationDeg, azimuthTrue }) => { satellites.forEach(({ id, elevationDeg, azimuthTrue }) => {
if (in_positioning_id.indexOf(parseInt(id)) < 0) { if (in_positioning_id.indexOf(parseInt(id)) < 0) {
data.push([parseInt(elevationDeg), azimuthTrue, id, false]) data.push([parseInt(elevationDeg), azimuthTrue, id, false]);
} else { } else {
data.push([parseInt(elevationDeg), azimuthTrue, id, true]) data.push([parseInt(elevationDeg), azimuthTrue, id, true]);
} }
}); });
}); });
const planisphereOption = { const planisphereOption = {
series: [{ data }] series: [{ data }]
} };
return planisphereOption return planisphereOption;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
} };
const get_in_positioning_id = (GSAArr) => { const get_in_positioning_id = (GSAArr) => {
let in_positioning_id = [] let in_positioning_id = [];
GSAArr.forEach(({ satellites }) => { GSAArr.forEach(({ satellites }) => {
in_positioning_id.push(...satellites) in_positioning_id.push(...satellites);
}); });
return in_positioning_id return in_positioning_id;
} };
export { getPosition, getBaseInfo, getSNROption, getPlanisphereOption } export { getPosition, getBaseInfo, getSNROption, getPlanisphereOption };

View File

@@ -1,28 +1,28 @@
<template> <template>
<div> <div>
<SubTitleView :title="'基础信息'" /> <SubTitleView :title="'基础信息'" />
<div class="text-center text-2xl leading-loose text-blue-100"> <div class="text-center text-2xl leading-8 text-blue-100">
<div> <div class="box">
<div class="label">经  度:</div> <div class="label">经  度:</div>
<div class="content">{{ baseInfo.longitude ? baseInfo.longitude : '' }}</div> <div class="content">{{ baseInfo.longitude ? baseInfo.longitude : '' }}</div>
</div> </div>
<div> <div class="box">
<div class="label">纬  度:</div> <div class="label">纬  度:</div>
<div class="content">{{ baseInfo.latitude ? baseInfo.latitude : '' }}</div> <div class="content">{{ baseInfo.latitude ? baseInfo.latitude : '' }}</div>
</div> </div>
<div> <div class="box">
<div class="label">海  拔:</div> <div class="label">海  拔:</div>
<div class="content">{{ baseInfo.altitude }}</div> <div class="content">{{ baseInfo.altitude }}</div>
</div> </div>
<div> <div class="box">
<div class="label">日  期:</div> <div class="label">日  期:</div>
<div class="content">{{ baseInfo.date }}</div> <div class="content">{{ baseInfo.date }}</div>
</div> </div>
<div> <div class="box">
<div class="label">UTC时间:</div> <div class="label">UTC时间:</div>
<div class="content">{{ baseInfo.UTCTime }}</div> <div class="content">{{ baseInfo.UTCTime }}</div>
</div> </div>
<div> <div class="box">
<div class="label">北京时间:</div> <div class="label">北京时间:</div>
<div class="content">{{ baseInfo.BJTime }}</div> <div class="content">{{ baseInfo.BJTime }}</div>
</div> </div>
@@ -31,9 +31,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue';
import SubTitleView from './SubTitleView.vue' import SubTitleView from './SubTitleView.vue';
import { getBaseInfo } from '../api/util' import { getBaseInfo } from '../api/util';
let baseInfo = reactive({ let baseInfo = reactive({
@@ -43,49 +43,55 @@ let baseInfo = reactive({
date : '', date : '',
UTCTime : '', UTCTime : '',
BJTime : '', BJTime : '',
}) });
let longitudeD = ref() let longitudeD = ref();
let longitudeM = ref() let longitudeM = ref();
let longitudeS = ref() let longitudeS = ref();
let latitudeD = ref() let latitudeD = ref();
let latitudeM = ref() let latitudeM = ref();
let latitudeS = ref() let latitudeS = ref();
const update = (nmea) => { const update = (nmea) => {
const newBaseInfo = getBaseInfo(nmea) const newBaseInfo = getBaseInfo(nmea);
if (!newBaseInfo) return
Object.assign(baseInfo, newBaseInfo) if (!newBaseInfo) return;
longitudeD.value = baseInfo.longitude.substring(0,baseInfo.longitude.indexOf("."))+'°' Object.assign(baseInfo, newBaseInfo);
longitudeM.value = String((Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10)).substring(0,String((Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10)).indexOf("."))+'′'
longitudeS.value = (Number('0.'+(Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10).substring(String((Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10)).indexOf(".")+1,((Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10)).length))*60).toFixed(6)+'″'
latitudeD.value = baseInfo.latitude.substring(0,baseInfo.latitude.indexOf("."))+'°' longitudeD.value = baseInfo.longitude.substring(0,baseInfo.longitude.indexOf("."))+'°';
latitudeM.value = String((Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10)).substring(0,String((Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10)).indexOf("."))+'′' longitudeM.value = String((Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10)).substring(0,String((Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10)).indexOf("."))+'′';
latitudeS.value = (Number('0.'+(Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10).substring(String((Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10)).indexOf(".")+1,((Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10)).length))*60).toFixed(6)+'″' longitudeS.value = (Number('0.'+(Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10).substring(String((Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10)).indexOf(".")+1,((Number('0.'+baseInfo.longitude.substring(baseInfo.longitude.indexOf(".")+1,baseInfo.longitude.length))*60).toFixed(10)).length))*60).toFixed(6)+'″';
}
defineExpose({ update }) latitudeD.value = baseInfo.latitude.substring(0,baseInfo.latitude.indexOf("."))+'°';
latitudeM.value = String((Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10)).substring(0,String((Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10)).indexOf("."))+'′';
latitudeS.value = (Number('0.'+(Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10).substring(String((Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10)).indexOf(".")+1,((Number('0.'+baseInfo.latitude.substring(baseInfo.latitude.indexOf(".")+1,baseInfo.latitude.length))*60).toFixed(10)).length))*60).toFixed(6)+'″';
};
defineExpose({ update });
</script> </script>
<style scoped> <style scoped>
@layer components { @layer components {
.box {
height: 2rem;
margin-bottom: 0.6rem;
}
.label { .label {
@apply float-left text-right w-2/5; @apply float-left text-right w-2/5;
min-width: 120px; min-width: 6rem;
height: 36px; height: 2rem;
} }
} }
@layer components { @layer components {
.content { .content {
@apply float-left text-left w-3/5; @apply float-left text-left w-3/5;
min-width: 165px; min-width: 6rem;
height: 36px; height: 2rem;
/* min-height: 48px; */ /* min-height: 2.4rem; */
} }
} }
</style> </style>

View File

@@ -10,11 +10,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import { ref } from 'vue';
import SubTitleView from './SubTitleView.vue' import SubTitleView from './SubTitleView.vue';
const nmeaStr = ref('') const nmeaStr = ref('');
const update = (value) => nmeaStr.value = value const update = (value) => nmeaStr.value = value;
defineExpose({ update }) defineExpose({ update });
</script> </script>

View File

@@ -6,12 +6,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, onMounted } from "vue";
import { use } from "echarts/core"; import { use } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers"; import { CanvasRenderer } from "echarts/renderers";
import { ScatterChart } from "echarts/charts"; import { ScatterChart } from "echarts/charts";
import { PolarComponent } from "echarts/components"; import { PolarComponent } from "echarts/components";
import VChart from "vue-echarts"; import VChart from "vue-echarts";
import { reactive } from "vue";
import SubTitleView from './SubTitleView.vue' import SubTitleView from './SubTitleView.vue'
import { getPlanisphereOption } from '../api/util' import { getPlanisphereOption } from '../api/util'
@@ -21,8 +21,8 @@ use([
PolarComponent PolarComponent
]); ]);
const LOCATED_COLOR = '#e5323e' const LOCATED_COLOR = '#e5323e';
const UNLOCATED_COLOR = '#9ca3af' const UNLOCATED_COLOR = '#9ca3af';
const option = reactive({ const option = reactive({
@@ -61,7 +61,8 @@ const option = reactive({
symbolSize: 32, symbolSize: 32,
label: { label: {
show: true, show: true,
formatter: '{@[2]}' formatter: '{@[2]}',
fontSize: '14'
}, },
itemStyle: { itemStyle: {
color: ({ value }) => { color: ({ value }) => {
@@ -69,18 +70,25 @@ const option = reactive({
} }
} }
}] }]
}) });
const update = (nmea) => { const update = (nmea) => {
const newOption = getPlanisphereOption(nmea) const newOption = getPlanisphereOption(nmea);
if (!newOption) return
Object.assign(option, newOption) if (!newOption) return;
}
Object.assign(option, newOption);
};
const close = () => { const close = () => {
window.ipcRenderer.send('CLOSE') window.ipcRenderer.send('CLOSE');
} };
defineExpose({ update }) defineExpose({ update });
onMounted(() => {
// 获取屏幕高度
const screenHeight = window.screen.height;
option.series[0].label.fontSize = `${screenHeight / 100}`;
});
</script> </script>

View File

@@ -6,11 +6,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, onMounted } from "vue";
import { use, graphic } from "echarts/core"; import { use, graphic } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers"; import { CanvasRenderer } from "echarts/renderers";
import { BarChart } from "echarts/charts"; import { BarChart } from "echarts/charts";
import VChart from "vue-echarts"; import VChart from "vue-echarts";
import { reactive } from "vue";
import SubTitleView from './SubTitleView.vue' import SubTitleView from './SubTitleView.vue'
import { getSNROption } from '../api/util' import { getSNROption } from '../api/util'
@@ -25,8 +25,8 @@ const LOCATED_COLOR = new graphic.LinearGradient(
{offset: 0.3, color: '#83bff6'}, {offset: 0.3, color: '#83bff6'},
{offset: 1, color: '#188df0'} {offset: 1, color: '#188df0'}
] ]
) );
const UNLOCATED_COLOR = '#9ca3af' const UNLOCATED_COLOR = '#9ca3af';
const option = reactive({ const option = reactive({
grid: { grid: {
@@ -47,7 +47,7 @@ const option = reactive({
inverse: true, inverse: true,
axisLine: {show: false}, axisLine: {show: false},
axisTick: {show: false}, axisTick: {show: false},
axisLabel: {color: 'white', fontSize: '12'}, axisLabel: {color: 'white', fontSize: '14'},
}, },
series: [{ series: [{
type: 'bar', type: 'bar',
@@ -65,14 +65,21 @@ const option = reactive({
}, },
barMaxWidth: 24 barMaxWidth: 24
}] }]
}) });
const update = (nmea) => { const update = (nmea) => {
const newOption = getSNROption(nmea) const newOption = getSNROption(nmea);
if (!newOption) return
Object.assign(option, newOption) if (!newOption) return;
}
defineExpose({ update }) Object.assign(option, newOption);
};
defineExpose({ update });
onMounted(() => {
// 获取屏幕高度
const screenHeight = window.screen.height;
option.yAxis.axisLabel.fontSize = `${screenHeight / 100}`;
});
</script> </script>

View File

@@ -5,5 +5,5 @@
<script lang="ts" setup> <script lang="ts" setup>
defineProps({ defineProps({
title: String title: String
}) });
</script> </script>

View File

@@ -1,11 +1,11 @@
import { createApp } from 'vue' import { createApp } from 'vue';
import App from './App.vue' import App from './App.vue';
import 'vue-cesium/dist/index.css' import 'vue-cesium/dist/index.css';
import { VcConfigProvider, VcViewer, VcEntity, VcLayerImagery, VcImageryProviderSingletile, VcGraphicsBillboard } from 'vue-cesium' import { VcConfigProvider, VcViewer, VcEntity, VcLayerImagery, VcImageryProviderSingletile, VcGraphicsBillboard } from 'vue-cesium';
import './main.css' import './main.css';
const app = createApp(App) const app = createApp(App);
app.use(VcConfigProvider).use(VcViewer).use(VcEntity).use(VcLayerImagery).use(VcImageryProviderSingletile).use(VcGraphicsBillboard) app.use(VcConfigProvider).use(VcViewer).use(VcEntity).use(VcLayerImagery).use(VcImageryProviderSingletile).use(VcGraphicsBillboard);
app.mount('#app') app.mount('#app');