加折线图前
This commit is contained in:
parent
f437f7dc77
commit
fbdce2a6fb
24
package-lock.json
generated
24
package-lock.json
generated
@ -12,7 +12,8 @@
|
||||
"@electron-toolkit/preload": "^3.0.1",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"element-plus": "^2.9.2",
|
||||
"modbus-serial": "^8.0.17"
|
||||
"modbus-serial": "^8.0.17",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/eslint-config": "^1.0.2",
|
||||
@ -2296,6 +2297,12 @@
|
||||
"@vue/shared": "3.5.13"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-api": {
|
||||
"version": "6.6.4",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/eslint-config-prettier": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz",
|
||||
@ -7093,6 +7100,21 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.0.tgz",
|
||||
"integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.6.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
|
||||
|
@ -21,7 +21,8 @@
|
||||
"@electron-toolkit/preload": "^3.0.1",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"element-plus": "^2.9.2",
|
||||
"modbus-serial": "^8.0.17"
|
||||
"modbus-serial": "^8.0.17",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/eslint-config": "^1.0.2",
|
||||
|
@ -3,39 +3,103 @@ import { join } from 'path'
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||
import icon from '../../resources/icon.png?asset'
|
||||
|
||||
// 模拟配置的modbus配置单
|
||||
const modbusConfig = {
|
||||
name: [
|
||||
'风速',
|
||||
'风向',
|
||||
'温度',
|
||||
'湿度',
|
||||
'气压',
|
||||
'光照',
|
||||
'雨量',
|
||||
'PM2.5',
|
||||
'PM10',
|
||||
'噪音',
|
||||
], // 名称
|
||||
}
|
||||
var ModbusRTU = require('modbus-serial')
|
||||
var client = new ModbusRTU()
|
||||
var time
|
||||
// 寄存器循环
|
||||
var time = null
|
||||
// 主窗口
|
||||
var mainWindow
|
||||
// 登录窗口
|
||||
var loginWindow
|
||||
// 连接后读取modbus从机的连接
|
||||
var readClient
|
||||
|
||||
// IPC test
|
||||
// 读取寄存器
|
||||
ipcMain.on('read', (event, message) => {
|
||||
read(event, message)
|
||||
console.log('pong')
|
||||
})
|
||||
|
||||
// 通过功能码03读取寄存器
|
||||
function read(event, message) {
|
||||
// 登录后开启modbus连接
|
||||
ipcMain.on('login', (event, message) => {
|
||||
// login(event, message)
|
||||
mainWindow.webContents.send('loginReply', 1)
|
||||
loginWindow.close()
|
||||
})
|
||||
|
||||
// 登录
|
||||
// function login(event, message) {
|
||||
// // 登录业务代码
|
||||
// console.log('login', message, event)
|
||||
// }
|
||||
|
||||
// 接收渲染进程发送的link请求
|
||||
ipcMain.on('link', (event, message) => {
|
||||
link(event, message)
|
||||
})
|
||||
|
||||
// 连接modbus
|
||||
function link(event, message) {
|
||||
// 连接modbus
|
||||
client.connectTCP(message.ip, { port: 502 })
|
||||
// client.connectTCP('192.168.151.59', { port: 502 })
|
||||
console.log(message.ip)
|
||||
// 循环读取寄存器
|
||||
time = setInterval(() => {
|
||||
// 连接成功后读取寄存器方法
|
||||
readClient = (event, message) => {
|
||||
client.readHoldingRegisters(message.startAddress, message.quantity, function (err, data) {
|
||||
if (err) {
|
||||
event.reply('readReply', err)
|
||||
clearInterval(time)
|
||||
return
|
||||
}
|
||||
// 返回数据给渲染进程
|
||||
event.reply('readReply', data.data)
|
||||
event.reply('readReply', dataHandle(data.data))
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
read(event, message)
|
||||
}
|
||||
|
||||
// 通过功能码03读取寄存器
|
||||
function read(event, message) {
|
||||
if (!(time == null)) {
|
||||
console.log('重复读取数据', time)
|
||||
return
|
||||
}
|
||||
// 循环读取寄存器
|
||||
time = setInterval(() => {
|
||||
readClient(event, message)
|
||||
}, 500)
|
||||
}
|
||||
|
||||
// 数据处理
|
||||
function dataHandle(data) {
|
||||
let result = []
|
||||
for (let i = 0; i < modbusConfig.name.length; i++) {
|
||||
result[i] = {
|
||||
name: modbusConfig.name[i],
|
||||
data: data[i]
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 停止modbus读取
|
||||
ipcMain.on('stop', (event) => {
|
||||
console.log('stop')
|
||||
clearInterval(time)
|
||||
time = null
|
||||
console.log('stop')
|
||||
// 返回数据给渲染进程
|
||||
event.reply('readReply', [])
|
||||
})
|
||||
@ -43,7 +107,7 @@ ipcMain.on('stop', (event) => {
|
||||
// 创建主窗口
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 670,
|
||||
show: false,
|
||||
@ -54,7 +118,6 @@ function createWindow() {
|
||||
sandbox: false
|
||||
}
|
||||
})
|
||||
|
||||
mainWindow.on('ready-to-show', () => {
|
||||
mainWindow.show()
|
||||
})
|
||||
@ -75,9 +138,10 @@ function createWindow() {
|
||||
|
||||
// 创建子窗口
|
||||
function createChildWindow() {
|
||||
let childWindow = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 670,
|
||||
loginWindow = new BrowserWindow({
|
||||
parent: mainWindow,
|
||||
width: 500,
|
||||
height: 400,
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
...(process.platform === 'linux' ? { icon } : {}),
|
||||
@ -87,11 +151,12 @@ function createChildWindow() {
|
||||
}
|
||||
})
|
||||
|
||||
childWindow.on('ready-to-show', () => {
|
||||
childWindow.show()
|
||||
loginWindow.on('ready-to-show', () => {
|
||||
loginWindow.show()
|
||||
})
|
||||
|
||||
childWindow.webContents.setWindowOpenHandler((details) => {
|
||||
loginWindow.webContents.setWindowOpenHandler((details) => {
|
||||
// console.log('details.url', details.url)
|
||||
shell.openExternal(details.url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
@ -99,9 +164,10 @@ function createChildWindow() {
|
||||
// HMR for renderer base on electron-vite cli.
|
||||
// Load the remote URL for development or the local html file for production.
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
childWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
||||
// console.log(process.env['ELECTRON_RENDERER_URL'] + '/#/login')
|
||||
loginWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/#/login')
|
||||
} else {
|
||||
childWindow.loadFile(join(__dirname, '../renderer/login.html'))
|
||||
// loginWindow.loadURL(join(__dirname, '../renderer/login.html'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +186,7 @@ app.whenReady().then(() => {
|
||||
})
|
||||
|
||||
createWindow()
|
||||
|
||||
createChildWindow()
|
||||
app.on('activate', function () {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
|
@ -1,72 +1,11 @@
|
||||
<script setup>
|
||||
import { reactive, ref, toRaw } from 'vue'
|
||||
|
||||
let modbusData = ref([])
|
||||
let parameters = reactive({
|
||||
ip: '192.168.151.59',
|
||||
slaveId: 1,
|
||||
startAddress: 600,
|
||||
quantity: 10,
|
||||
// unitId: 1,
|
||||
functionCode: 3,
|
||||
})
|
||||
|
||||
const ipcHandle = () => window.electron.ipcRenderer.send('read', toRaw(parameters))
|
||||
const ipcModbus = () => window.electron.ipcRenderer.send('stop')
|
||||
window.electron.ipcRenderer.on('readReply', (event, message) => {
|
||||
modbusData.value = message
|
||||
})
|
||||
|
||||
// const handleClick = (tab, event) => {
|
||||
// console.log(tab, event)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <img alt="logo" class="logo" src="./assets/electron.svg" /> -->
|
||||
<!-- <div class="creator">Powered by electron-vite</div> -->
|
||||
<!-- <div class="text">
|
||||
Build an Electron app with
|
||||
<span class="vue">Vue</span>
|
||||
</div> -->
|
||||
<!-- <div class="tabs">
|
||||
<el-tabs v-model="activeName" type="border-card" @tab-click="handleClick">
|
||||
<el-tab-pane label="Tab 1" name="first">Tab 1</el-tab-pane>
|
||||
<el-tab-pane label="Tab 2" name="second">Tab 2</el-tab-pane>
|
||||
<el-tab-pane label="Tab 3" name="third">Tab 3</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div> -->
|
||||
|
||||
<p class="tip">modbus测试</p>
|
||||
<div class="fromDiv">
|
||||
<p>ip地址</p> <input type="text" v-model="parameters.ip" /> <br />
|
||||
<p>id</p> <input type="text" v-model="parameters.slaveId" /> <br />
|
||||
<p>起始地址</p> <input type="text" v-model="parameters.startAddress" /> <br />
|
||||
<p>读取长度</p> <input type="text" v-model="parameters.quantity" /> <br />
|
||||
<!-- <p></p> <input type="text" v-model="parameters.unitId" /> -->
|
||||
<p>功能码</p> <input type="text" v-model="parameters.functionCode" />
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<div class="action">
|
||||
<a target="_blank" rel="noreferrer" @click="ipcHandle">read</a>
|
||||
<a target="_blank" rel="noreferrer" @click="ipcModbus">stop</a>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{ modbusData }}</p>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tabs {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.fromDiv > p {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
.fromDiv > input {
|
||||
/* width: 200px; */
|
||||
}
|
||||
|
||||
</style>
|
||||
|
20
src/renderer/src/component/topBar.vue
Normal file
20
src/renderer/src/component/topBar.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="topBar">
|
||||
<el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect">
|
||||
<el-menu-item index="1">modbus测试</el-menu-item>
|
||||
<el-menu-item index="2">可视化图表</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeIndex = ref('1')
|
||||
|
||||
const handleSelect = (key, heyPath) => {
|
||||
console.log(key, heyPath)
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,21 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form>
|
||||
<el-form-item label="IP">
|
||||
<el-input v-model="param.ip"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input v-model="param.password" type="password"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -5,7 +5,9 @@ import App from './App.vue'
|
||||
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import router from './route'
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
app.use(ElementPlus)
|
||||
app.mount('#app')
|
||||
|
14
src/renderer/src/route/index.js
Normal file
14
src/renderer/src/route/index.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
const routes = [
|
||||
{ path: '/', component: () => import('../view/index.vue') },
|
||||
{ path: '/login', component: () => import('../view/login.vue') }
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
mode: 'hash',
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
76
src/renderer/src/view/index.vue
Normal file
76
src/renderer/src/view/index.vue
Normal file
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<TopBar />
|
||||
<div>
|
||||
<p class="tip">modbus测试</p>
|
||||
<el-table :data="modbusData" style="width: 100%">
|
||||
<el-table-column prop="name" label="变量名"></el-table-column>
|
||||
<el-table-column prop="data" label="数值"></el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="actions">
|
||||
<div class="action">
|
||||
<a target="_blank" rel="noreferrer" @click="ipcHandle">继续</a>
|
||||
<a target="_blank" rel="noreferrer" @click="ipcModbus">停止</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <p>{{ modbusData }}</p> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, toRaw } from 'vue'
|
||||
import TopBar from '../component/topBar.vue'
|
||||
|
||||
let modbusData = reactive([])
|
||||
let parameters = reactive({
|
||||
ip: '192.168.151.59',
|
||||
slaveId: 1,
|
||||
startAddress: 600,
|
||||
quantity: 10,
|
||||
// unitId: 1,
|
||||
functionCode: 3,
|
||||
})
|
||||
|
||||
const ipcHandle = () => window.electron.ipcRenderer.send('read', toRaw(parameters))
|
||||
const ipcModbus = () => window.electron.ipcRenderer.send('stop')
|
||||
// 从主进程接收登录成功的消息
|
||||
window.electron.ipcRenderer.on('loginReply', (event, message) => {
|
||||
if (message) {
|
||||
// 发送连接modbus的请求
|
||||
window.electron.ipcRenderer.send('link', toRaw(parameters))
|
||||
} else {
|
||||
console.log('连接失败')
|
||||
}
|
||||
})
|
||||
|
||||
// 接收主进程返回的modbus数据
|
||||
window.electron.ipcRenderer.on('readReply', (event, message) => {
|
||||
for (let i = 0; i < message.length; i++) {
|
||||
modbusData[i] = {
|
||||
name: message[i].name,
|
||||
data: message[i].data
|
||||
}
|
||||
}
|
||||
})
|
||||
// const handleClick = (tab, event) => {
|
||||
// console.log(tab, event)
|
||||
// }
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.tabs {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.fromDiv>p {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.fromDiv>input {
|
||||
/* width: 200px; */
|
||||
}
|
||||
</style>
|
58
src/renderer/src/view/login.vue
Normal file
58
src/renderer/src/view/login.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="fromDiv">
|
||||
<div class="fromLable">
|
||||
<p>ip地址</p> <input v-model="parameters.ip" type="text" /> <br />
|
||||
</div>
|
||||
<div class="fromLable">
|
||||
<button class="linkBtn" @click="login">连接</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, toRaw } from 'vue'
|
||||
let parameters = reactive({
|
||||
ip: '192.168.151.59'
|
||||
})
|
||||
|
||||
function login() {
|
||||
window.electron.ipcRenderer.send('login', toRaw(parameters))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fromLable {
|
||||
margin-top: 20%;
|
||||
}
|
||||
|
||||
p {
|
||||
display: inline-block;
|
||||
/* width: 100px; */
|
||||
}
|
||||
|
||||
.fromDiv {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.linkBtn {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
background-color: #1c2834;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
font-size: 16px;
|
||||
transition: 3s;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user