马上注册,享受更多特权
您需要 登录 才可以下载或查看,没有帐号?立即注册 ![](source/plugin/zhanmishu_wechat/template/static/img/wechat_login.png)
x
本帖最后由 mingiii 于 2022-8-14 12:02 编辑
1 创建 Electron 项目
上一章我们创建了能与 H5U 进行 Modbus TCP 通讯的程序,这一章将尝试在 Electron 内嵌的 Node.js 中实现与可视化界面的通讯。
注意:汇川论坛的markdown解析器有时候会把小于号 < 解析成 < 大于号 > 解析成 > 复制代码的话要记得替换
1.1 初始化项目
首先我们和上一章一样,创建一个项目文件夹,在终端中进入该文件夹,然后输入 npm init,随后按照下图配置项目。(# 后为注释)
> npm init
Debugger attached.
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help init` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (3-electron) h5u-modbus-tcp-client # 项目名
version: (1.0.0) # 版本
description: This is a modbus client for inovance h5u plc # 描述
entry point: (main.js) # 入口文件
test command: electron . # 测试命令
git repository: # git仓库
keywords:
author: mingiii # 作者
license: (ISC)
About to write to package.json:
{
"name": "h5u-modbus-tcp-client",
"version": "1.0.0",
"description": "This is a modbus client for inovance h5u plc",
"main": "main.js",
"scripts": {
"test": "electron ."
},
"author": "mingiii",
"license": "ISC"
}
Is this OK? (yes)
自此项目初始化完成
1.2 下载 Electron
这里我们下载依赖与我们系统中 Node.js 版本相同的 Electron 14.2.9 版本,首先我们需要切换至国内镜像,在终端中输入
npm config set ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
然后输入 npm install --save-dev electron@14.2.9 完成下载
> npm config set ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
> npm install --save-dev electron@14.2.9
+ electron@14.2.9
added 91 packages from 98 contributors in 48.163s
10 packages are looking for funding
run `npm fund` for details
1.3 运行 Electron
接下来我们新建一个 main.js 作为 Electron 主入口:
console.log(`mingiii: hello`)
然后我们在终端运行 npm test , 得到如下输出,Electron 成功运行
> electron .
mingiii: hello
2 将网页装载至窗体
在 Electron 中,每个窗口展示一个页面,该页面可以来自本地的 HTML,也可以来自远程 URL。 在本例中将会装载本地的文件。 接下来在项目的根目录中创建一个 index.html 文件,并写入下面的内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>一起摸鱼</title>
</head>
<body>
<h1>【一起摸鱼】Node.js上位机应用开发</h1>
<p> --mingiii</p>
</body>
</html>
现在我们得到了一个网页,可以将它装载到 Electron 的窗体上了。 然后我们需要将 main.js 中的内容替换成下列代码。
const { app, BrowserWindow } = require('electron')
// createWindow() 函数将页面加载到新的 BrowserWindow 实例中:
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
})
// 窗体载入网页
win.loadFile('index.html')
}
// 在应用准备就绪时调用函数
app.whenReady().then(() => {
createWindow()
// 如果没有窗口打开则打开一个窗口 (macOS)
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// 关闭所有窗口时退出应用 (Windows & Linux)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
});
然后我们再次在终端运行 npm test , 得到如下图包含着我们网页的窗体
![](data/attachment/album/202208/14/114708gdwd3qdbn3pzbbde.png)
3 Node.js 与网页通讯
Electron 的 main 和 renderer 进程有不同的职责并且不可互换。这意味着无法直接从渲染器进程(网页)访问 Node.js API,也无法从主进程( Node.js )访问 HTML 文档对象模型 (DOM)。这个问题的解决方案是使用 ElectronipcMain 和ipcRenderer 模块进行进程间通信(IPC),详细内容请参考 Electron文档。
接下来我们尝试结合上一章讲到的 Modbus TCP 通讯使用 ipcMain 和ipcRenderer 模块实现窗体与 H5U 之间的数据读写。
在这之前,为了将 Electron 的不同进程类型连接在一起,我们需要使用一个称为preload的特殊脚本。
3.1 预加载脚本
我们新建一个preload.js 文件,将我们将要使用的读写 API 挂载至渲染进程的 window.electronAPI 下
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
// 写寄存器
setRegisters: () => ipcRenderer.send('set-registers'),
// 读寄存器
handleData: (callback) => ipcRenderer.on('send-data', callback)
})
然后在main.js 中新增代码
/* 新增 引入path
__dirname 字符串指向当前正在执行脚本的路径 (在本例中,它指向你的项目的根文件夹)。
path.join API 将多个路径联结在一起,创建一个跨平台的路径字符串。 */
const path = require('path')
const win = new BrowserWindow({
width: 800,
height: 600,
// 新增:挂载预加载脚本
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
这样渲染进程的 window 对象下就成功挂载了读写用的 API
3.2 写入
为了在网页上发出写入信息的命令,我们需要在index.html 文件中增加一个按钮并挂载脚本
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>一起摸鱼</title>
</head>
<body>
<h1>【一起摸鱼】Node.js上位机应用开发</h1>
<!--一会读取用到的p标签 -->
D0 - D4:<p id="data"> --mingiii</p>
<!--增加按钮 -->
<button id="btn" type="button">Set 1 - 5</button>
<!--挂载脚本 -->
<script src="./renderer.js"></script>
</body>
</html>
然后我们新建renderer.js ,为按钮的click 事件增加监听器
const setButton = document.getElementById('btn')
setButton.addEventListener('click', () => {
window.electronAPI.setRegisters()
})
最后我们参照上一章的通讯例程,修改一下main.js
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
// 引入 Modbus
const ModbusRTU = require("modbus-serial")
const client = new ModbusRTU()
// 窗体移到外部作用域方便调用
let win
// createWindow() 函数将页面加载到新的 BrowserWindow 实例中:
const createWindow = () => {
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// 窗体载入网页
win.loadFile('index.html')
// 连接 Modbus TCP 客户端
client.connectTCP("127.0.0.1", { port: 502 })
client.setID(1)
}
// 在应用准备就绪时调用函数
app.whenReady().then(() => {
ipcMain.on('set-registers', handleSetRegisters)
createWindow()
// 如果没有窗口打开则打开一个窗口 (macOS)
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// 关闭所有窗口时退出应用 (Windows & Linux)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// 将1、2、3、4、5分别写入D0到D4
function handleSetRegisters () {
client.writeRegisters(0, [1, 2, 3, 4, 5])
}
此时我们使用 npm test 运行程序,然后点击按钮,可以看到 PLC D0 - D4 的监控值已经分别变成 1, 2, 3, 4, 5
3.3 读取
实现读取只需要在renderer.js 中增加一个回调函数,将回调值写入DOM
const data = document.getElementById('data')
window.electronAPI.handleData((event, value) => {
data.innerText = value
})
然后在 main.js 中增加方法read,并在 createWindow 中增加一个计时器
// 每秒读取一次,追加至 createWindow 函数中的最后
setInterval(read, 1000)
// 读取D0到D4,追加至 main.js 文件的最后
function read () {
client.readHoldingRegisters(0, 5)
.then((data) => {
win.webContents.send('send-data', data.data)
})
}
此时我们再次使用 npm test 运行程序,可以看到如下窗体
![](data/attachment/album/202208/14/114717gzsuesqx6xlq2om7.png)
然后我们点击按钮,寄存器中的值改变,界面值也随之改变
![](data/attachment/album/202208/14/114722aba0spc1xjsj1xb8.png)
自此读取、写入功能实现成功。
4 参考文献
- electronjs.org / OpenJS 基金会和 Electron 贡献者们, 2021
5 讨饭用附件
附件不包含依赖,使用前请输入npm i安装
给点给点
3-electron.7z
(14.47 KB, 下载次数: 3, 售价: 3 )
|