electron+node+node-serialport nodeJs实现串口通信+electron-builder打包完整流程

Posted MmM豆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了electron+node+node-serialport nodeJs实现串口通信+electron-builder打包完整流程相关的知识,希望对你有一定的参考价值。

electron+node+node-serialport 实现串口通信+electron-builder打包完整流程

最近有个项目需要连接电子秤到收银系统,然后需要调用硬件串口,因为收银系统,本来是用vue写好的网页,后面突然加了个需求,所以笔者思考了一下实现思路

  1. 用node js 操作串口 (serialport )
  2. 用electron,建立webSocet 和网页建立通信(用的ws模块)

下面是笔者花了几天时间,排坑查资料书理的心得
过程中遇到问题,请查看文章末尾的问题集合,帮助你解决问题,有时候网速问题,electron丢包,需要耐心卸载再安装(删除nodemodle后最好用cnpm i 安装)
有问题,和更正确的使用请在评论区留言,笔者会及时回复

文章目录

一、 创建一个electron应用

首先安装 一个官方例子

git clone https://github.com/electron/electron-quick-start.git

二、安装依赖

进入 electron-quick-start 到package.json中把electron版本改成7.1.9不然后面打包会有问题

npm 有点慢(容易丢包),推荐使用cnpm i 使用淘宝镜像 下载

安装好依赖后,启动项目 npm start

三、配置环境

配置npm(解决打包和electron下载慢的问题)

全局设置下载源:

npm config set registry https://registry.npm.taobao.org/

下载node源码加速:

npm config set disturl https://npm.taobao.org/mirrors/node 

然后将electron包下载地址注册位淘宝的镜像:

npm config set ELECTRON_MIRROR https://npm.taobao.org/mirrors/electron/

或者 找到c盘中的.npmrc 直接写

registry=https://registry.npm.taobao.org/
disturl=https://npm.taobao.org/mirrors/node
ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/

参考文档

electron使用原生模块需要再编译, 文档

我们还需要使用node-gyp,node-gyp,是由于node程序中需要调用一些其他语言编写的 工具 甚至是dll,需要先编译一下,否则就会有跨平台的问题,

npm install --global --production windows-build-tools
安装内容:
           1、python(v2.73.x不支持);
      2、visual C++ Build Tools,或者 (vs2015以上(包15))
      3.net framework 4.5.1
如果电脑中存在python,最好先卸载掉不然会冲突,或者set一下

打开命令窗口 输入python 查看一下是否存在python, 如果找不到命令 找到python安装目录

把路径加到环境变量中

再次执行python

安装完成后,安装node-gyp

npm install -g node-gyp

输入 node-gyp list 命令 查看是否安装成功

参考文档

安装 ws模块(用于开启服务,无法使用node-webSocket模块编译后依旧无效,ws不需要编译且可以使用)

cnpm i ws -S

安装 serialport

  1. 英文文档
  2. 翻译的文档 (翻译中有许多使用有误,不过可以帮助阅读文档,)
cnpm install serialport -S

安装 electron-rebuild(electron用于编译node原生模块)

cnpm install electron-rebuild -D

前置工作做完,输入命令开始重新编译(第一次编译需要点时间,耐心等待git下是这样,cmd下面是反斜杠,有些也是反斜杠,那个能用用那个)

 ./node_modules/.bin/electron-rebuild.cmd

没有报错看到rebiuld Complete那么就表示ok了,那前面几步就没问题,报错,就看看前面那些有问题

这个时候在启动一下 npm start 编译成功,就ok

四、编写electron

重新编写mian.js

const 
  app,
  BrowserWindow,
  Menu,
  MenuItem,
  globalShortcut
 = require('electron')
const SerialPort = require('serialport');
const Delimiter = require('@serialport/parser-delimiter')
const WebSocket = require('ws');
// 引用Server类:
const WebSocketServer = WebSocket.Server;
var portName = 'COM3'; //定义默认串口名
var serialPort;
var strs = ''
var wss    //webSocet Server
var wsSend 
var COMarr = [] //查询到的该电脑在使用的串口

//获取正在使用的串口集合,用于生成切换端口
function getPortArr() 
  return new Promise((res, rej) => 
    SerialPort.list().then((ports) => 
      ports.forEach(function (port) 
        COMarr.push(port.comName)
        console.log(port.comName);
        
      console.log(port.pnpId);
  
      console.log(port.manufacturer);
        res()
      );
    );
  )


function startPort() 
  if (serialPort) 
    try 
      serialPort.close();
     catch (err) 

    
  
  serialPort = new SerialPort( //设置串口属性

    portName, 

      baudRate: 9600, //波特率

      dataBits: 8, //数据位

      parity: 'none', //奇偶校验

      stopBits: 1, //停止位

      flowControl: false,

      autoOpen: false //不自动打开

    , false);

  const parser = serialPort.pipe(new Delimiter(
    delimiter: '\\n'
  )) //当端口读取到换行符时,才发送到程序,这是因为电子秤发送的数据是间断的原因
  
  serialPort.on('error', (error) => //捕获错误
    console.log('Error: ', error.message);
  )
  serialPort.open(function (error)  //手动打开串口
    if (error) 
      console.log("打开端口" + portName + "错误:" + error);
     else 
      if (wss) 
        wss.close()
      
      // 实例化: 监听本机的27611端口 127.0.0.1:27611
      wss = new WebSocketServer(
        port: 27611
      );

      wss.on('connection', function (ws) 
        console.log("开启连结")
        ws.on("message", function (message) 
          console.log('接受到的信息' + message);
          if (message === "HeartBeat") 
            ws.send("01 收到开始操作")
             wsSend = ws
          
        )
        ws.on("close", function () 
          console.log("关闭服务");
        )
      )
      console.log("打开端口成功,正在监听数据中");
	//串口设备传来的数据 是buffer对象,用toString转一下码
      parser.on('data', function (data) 
        console.log(data.toString());
        strs = data.toString()
          if(wsSend)
           	     wsSend.send(strs)   
          
     
      )
    

  );


startPort() //调用开启串口
 app.on('ready', () => 
    let win = new BrowserWindow(
      width: 800,
      height: 600,
      title:"webSocet sever",
      webPreferences: 
        nodeIntegration: true
      
    )
    // 加载页面
     win.loadFile('./index.html')
    // 类似浏览器的window 与窗口有关的浏览器内容都是通过下面的属性
    // 配置esc退出全屏
    globalShortcut.register('ESC', () => 
      win.setFullScreen(false);
  )
     //是否默认打开就全屏显示
    // win.setFullScreen(true);
    //默认打开开发者工具(调试使用)
    win.webContents.openDevTools();
getPortArr().then(() => 
    // 创建菜单对象
    let menu = new Menu();
    // 创建菜单项
          let submenu = []
      COMarr.map(item => 
        submenu.push(
          type: "normal",
          label: item,
          click() 
            portName = item
            strs = ''
            startPort()
          
        )
        submenu.push(   
               type: "separator", //菜单分割符
             ,)
      )
    let mil = new MenuItem(
      type: "submenu",
      label: '切换端口',
      submenu: submenu
    )
    let mil2 = new MenuItem(

      type: "submenu",
      label: '功能',
      submenu: [
          role: "forcereload",
          label: "刷新",

        ,
        
          type: "separator", //菜单分割符
        ,

        
          role: "togglefullscreen",
          label: "全屏",

        ,
        
          type: "separator", //菜单分割符
        ,
        
          role: "minimize",
          label: "最小化",

        ,
        
          type: "separator", //菜单分割符
        ,
        
          type: "normal",
          label: "开发者工具",
          click: function(item, focusedWindow) 
         	 if (focusedWindow)
            focusedWindow.toggleDevTools();
        

        ,
        
          type: "separator", //菜单分割符
        ,
        
          role: "quit ",
          label: "退出",
        ,
      ]
    )
    //把菜单添加到指定的菜单对象
    menu.append(mil)
    menu.append(mil2)
    // 菜单位置: 1, 应用程序窗口顶层,
    Menu.setApplicationMenu(menu)

)

  )

启动一下看看能否开启服务

五、编辑client客户端

打开index.html 复制下列

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        #messtext-align: center
    </style>
</head>
<body>
    <div id="mess">正在连接...</div>
    <div id="socket">
      
    </div>

    <script>
        var mess = document.getElementById("mess");
        var socket = document.getElementById("socket");
        if(window.WebSocket)
            var ws = new WebSocket('ws://127.0.0.1:27611');

          
            ws.onopen = function(e)
                console.log("连接服务器成功");
                ws.send("HeartBeat");
            
            ws.onclose = function(e)
                console.log("服务器关闭");
            
            ws.onerror = function()
                console.log("连接出错");
            

            ws.onmessage = function(e)
              console.log(e.data);
              socket.innerHTML ="获取到的数据"+ e.data
              mess.innerHTML = "连接成功"
              
            
        
    </script>
</body>
</html>

ok启动一下咯, 发现启动成功,服务器却压根没连上 ,这时请先看看 默认串口是否是你连接的,然后把默认串口改成你需要的串口(也可以点击切换端口=>然后点击功能 刷新)如果切换端口为空,那么爱莫能助,你想测试是否能调通只能把和端口有关的都注释了

查看方式(有连接设备的请选择连接的串口)

桌面 => 计算机 =>右键选者管理


到这你就成功一半了,接下来就是打包环节

六、打包 electron-builder

​ 安装

npm install electron-builder -D

打包配置

  1. 基础配置
"build":   // 这里是electron-builder的配置
    "productName":"xxxx",//项目名 这也是生成的exe文件的前缀名
    "appId": "com.xxx.xxxxx",//包名  
    "copyright":"xxxx",//版权  信息
    "directories":  // 输出文件夹
      "output": "build"
    , 
    // windows相关的配置
    "win":   
      "icon": "xxx/icon.ico"//图标路径 
      
  

在配置文件中加入以上的文件之后就可以打包出来简单的文件夹,文件夹肯定不是我们想要的东西。下一步我们来继续讲别的配置。

  1. 打包目标配置

要打包成安装程序的话我们有两种方式,

  1. 使用NSIS工具对我们的文件夹再进行一次打包,打包成exe
  2. 通过electron-builder的nsis直接打包成exe,配置如下
"win":   // 更改build下选项
    "icon": "build/icons/aims.ico",
    "target": [
      
        "target": "nsis" // 我们要的目标安装包
      
    ]
  ,
  1. 其他平台配置
  "dmg":  // macOSdmg
    "contents": [
      ...
    ]
    ,
    "mac":   // mac
      "icon": "build/icons/icon.icns"
    ,
    "linux":  // linux
      "icon": "build/icons"
    
  1. nsis配置

这个要详细的讲一下,这个nsis的配置指的是安装过程的配置,其实还是很重要的,如果不配置nsis那么应用程序就会自动的安装在C盘。没有用户选择的余地,这样肯定是不行的

关于nsis的配置是在build中nsis这个选项中进行配置,下面是部分nsis配置

"nsis": 
  "oneClick": false, // 是否一键安装
  "allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
  "allowToChangeInstallationDirectory": true, // 允许修改安装目录
  "installerIcon": "./build/icons/aaa.ico",// 安装图标
  "uninstallerIcon": "./build/icons/bbb.ico",//卸载图标
  "installerHeaderIcon": "./build/icons/aaa.ico", // 安装时头部图标
  "createDesktopShortcut": true, // 创建桌面图标
  "createStartMenuShortcut": true,// 创建开始菜单图标
  "shortcutName": "xxxx", // 图标名称
  "include": "build/script/installer.nsh", // 包含的自定义nsis脚本 这个对于构建需求严格得安装过程相当有用。
,

参考资料 文档

编写nsis脚本新建一个installer.nsh(提供一个简单的)需要有自定义界面要求的 提供一个脚本文档 中文文档

!macro customInstall
  WriteRegStr HKCR "CenDC" "URL Protocol" ""
  WriteRegStr HKCR "CenDC" "" "URL:CenDC Protocol Handler"
  WriteRegStr HKCR "CenDC\\shell\\open\\command" "" '"$INSTDIR\\CenDC.exe" "%1"'
!macroend

下面是笔者使用的,可以用于参考


  "name": "electron",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": 
    "rebuild": "./node_modules/.bin/electron-rebuild.cmd",
    "test": "echo \\"Error: no test specified\\" && exit 1",
    "start": ".\\\\node_modules\\\\.bin\\\\electron .",
    "build": "electron-builder",
    "packager": "electron-packager . wbx --platform=win32 --arch=x64 --out ./dist --app-version=1.0.0"
  ,
  
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": 
    "electron": "^7.1.9",
    "electron-builder": "^22.2.0",
    "electron-rebuild": "^1.10.0"
  ,
  "dependencies": 
    "node-uuid": "^1.4.8",
    "node-websocket": "^0.1.6",
    "serialport": "^8.0.8",
    "ws": "^7.2.1"
  ,
  "build": 
    "appId": "mmm",
    "productName": "系统",
    "copyright": "Copyright",
    "directories": 
      "output": "./dist"
    ,
    "win": 
      "target": [
        
          "target": "nsis",
          "arch": [
            "ia32"
          ]
        
      ],
      "icon": "./img/logo.png"
    ,
    "nsis": 
      "oneClick": false,
      "allowElevation": true,
      "allowToChangeInstallationDirectory": true,
      "installerIcon": "./img/in.ico",
      "uninstallerIcon": "./img/in.ico",
      "installerHeaderIcon": "./img/in.ico",
      "createDesktopShortcut": true,
      "createStartMenuShortcut": true,
      "include": "./installer.nsh"
    
  


如果打包报错,先看看你的文件路径上是否有中文命名,这个报错不会有提示(这个是笔者写demo时,创建在了我的笔记文件夹里,然后0-0)

第二是下载还是慢,卡住,这个网速硬伤还是有解决方法,那就是手动下载

electron-builder只有第一次打包需要下载打包依赖,就很棒,所以我们只需要,手动把依赖下好放在对应的文件就ok了

参考资料

七、项目中遇到的坑

  1. NODE_MODULE_VERSION?

    每一个node版本 都有对应的 NODE_MODULE_VERSION

    文档

可以在 mian.js 打印当前项目需要的对应版本 如果报错安装对应的node就ok


//获取对应的elctron版本和对应的node版本、模块版本,
console.log("node:",process.versions.node)
console.log("electron:",process.versions.electron)
console.log("modules:",process.versions.modules)

如果找不到对应版本, 那么运行一下这个命令也是可以的
./node_modules/.bin/electron-rebuild.cmd

最好安装一个nvm用于切换版本,

  1. python环境问题?


    如果启动报错第一个,就是编译失败,都是和环境有关,配置环境变量即可

  2. 打包时没把dist中的东西删除因为文件被程序占用中删除不掉(打开任务管理器找到占用的进程清空dist就好了)

  3. 这是builder貌似不能打包最新的electron版本,这里吧electron版本改为7.1.9,再下载一次就好了,


  1. 找不到@serialport/parser-delimiter 模块

在node-module中找到这个包,然后复制一份,粘贴到node-module中

手动在package.json 的dependencies 中添加 “parser-delimiter”:"^8.0.6" ,原理是webpack打包只会打dependencies 中的包,其他项目遇到也可以这样操作,然后在main.js中将引用改成 const Delimiter = require(’./node_modules/parser-delimiter’) 就行了

  1. 报错不是一个win32程序?

    以上是关于electron+node+node-serialport nodeJs实现串口通信+electron-builder打包完整流程的主要内容,如果未能解决你的问题,请参考以下文章

    electron之node原生模块ffi的使用

    使用electron构建跨平台Node.js桌面应用

    Node-Webkit vs Electron [关闭]

    如何在 Electron 中使用 node_modules?

    如何在 Electron 中使用 node_modules?

    利用 Node.js 反序列化漏洞远程执行代码