【问题标题】:node serialport module not working with child process fork()节点串行端口模块不适用于子进程 fork()
【发布时间】:2018-08-20 16:20:43
【问题描述】:

我正在尝试使用电子中 child_process 模块的 fork() 在 usb 中进行异步读取。也就是说,当我单击一个 div (id="read") 时,一条消息从渲染器进程发送到主进程,然后主进程作为响应调用 fork() 并从 USB 异步读取数据。为了处理 USB 通信,我使用了node-serialport library

index.html

<html>

    <head>
        <link rel="stylesheet" href="style.css">
        <link rel="stylesheet" href="scroll.css">
        <link href="https://fonts.googleapis.com/css?family=Questrial" rel="stylesheet">
    </head>

    <body>

        <div id="ui-container">


            <div id="top-bar" class="shadowed">
                <div id="top-bar-left-decoration"></div>
                <div id="close-app" class="top-bar-button"><div class="unselectable top-button-text">x</div></div> 
                <div id="maximize-app" class="top-bar-button"><div class="unselectable top-button-text">&#9633</div></div> 
                <div id="minimize-app" class="top-bar-button"><div class="unselectable top-button-text">-</div></div>
            </div>

            <div id="navigation-bar"> 
                <div id="deviceSelection" class ="unselectable navigation-button">

                    <img id="deviceSelection-image" class="navigation-image" src="Icons/selectDevice2.png">
                </div>

                <div id="info" class="unselectable navigation-button">

                    <img id="info-image" class="navigation-image" src="Icons/DeviceInfoNoBorder.png">
                </div>

                <div id="config" class=" unselectable navigation-button">

                    <img id="config-image" class="navigation-image" src="Icons/DeviceConfigNoBorder.png">
                </div>

                <div id="graph" class="unselectable navigation-button">

                    <img id="graph-image" class="navigation-image" src="Icons/DataViewNoBorder.png">
                </div>

                <div id="export" class="unselectable navigation-button">

                    <img id="export-image" class="navigation-image" src="Icons/Export.png">
                </div>

                <div id="options-container"><img id="options" src="Icons/options.png"></div> 

            </div>

            <div id="information-data-view" class="unselectable">
                <div id="data">
                        <div id="push-to-read"></div>
                </div>
            </div>

        </div>


    </body>

    <script src="ipcRenderer.js" type="text/javascript"></script>
</html>

这是我的index.js,负责创建浏览器窗口

const electron = require('electron')
const ipcMain = require('electron').ipcMain
const url = require('url')
const path = require('path')
const child_process = require('child_process')
const app = electron.app


var windowFullScreen = false;

const BrowserWindow = electron.BrowserWindow



var MainWindow;
app.on('ready', function()
{
    MainWindow = new BrowserWindow({
        width: 1024, 
        height: 768, 
        backgroundColor : '123355',
        frame: false,
        resizable: true,
        movable: true,
        show: false
    })

MainWindow.on('ready-to-show', () => {
    MainWindow.show()
    console.log('Ready to go!')
})
MainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
}))
ipcMain.on("minimize", (event, arg) => {
    MainWindow.minimize()
    console.log("minimize")
})
ipcMain.on("maximize", (event, arg) => {
    if (!windowFullScreen) {
        MainWindow.maximize()
        windowFullScreen = true;
    }
    else {
        MainWindow.setSize(800, 600)
        windowFullScreen = false;
    }

    console.log("maximize")
})
ipcMain.on("close", (event, arg) => {
    MainWindow.close()
    console.log("close")
})

ipcMain.on("read", (event, arg) => {
    setInterval(()=>{console.log(usbcomm.read(1))}, 1)
    const process = child_process.fork('./readUSB.js')
})
})

ipcRenderer.js

这是我为 DOM 设置 onclick 事件处理程序的地方。这里的相关部分是 read.onclick() ,它向主进程发送“读取”消息,告诉它分叉进程并运行我在下面包含的 readUSB.js。

const electron = require('electron');
const ipcRenderer = electron.ipcRenderer;


var minimize = document.getElementById("minimize-app");
var maximize = document.getElementById("maximize-app");
var read = document.getElementById("push-to-read");
var close = document.getElementById("close-app");

minimize.onclick = (event) => {
    ipcRenderer.send("minimize", "an-argument")
    console.log("minimize");    
} 

maximize.onclick = (event) => {
    ipcRenderer.send("maximize", "an-argument")
    console.log("maximize")
} 

close.onclick = (event) => {
    ipcRenderer.send("close", "an-argument")
    console.log("close")
}

read.onclick = (event) => {
    ipcRenderer.send("read", "an-argument")
    console.log("read")
}

读取USB.js

这段代码应该简单地从 USB 读取一个字节并将其输出到控制台,它所做的是什么都不读取,并且输出 null(如果它没有读取任何内容,usbcomm.read() 返回 null)。这只是一个概念证明,以确保我可以异步 read(),但事情显然无法正常工作。

const SerialPort = require('serialport')
const usbcomm = new SerialPort("COM4")
usbcomm.setEncoding('utf-8')


usb_rx();


function usb_rx()
{

    console.log("running in child process!")
    console.log(usbcomm.read(1))

}

如果我将此代码作为事件处理程序运行以响应来自渲染器进程的“读取”消息,那么一切都会正常运行,并且我会从 USB 读取正确的响应。我已经确认这不是我的 USB 设备的问题,并且在简单的用例中 usbcomm.read() 的行为符合预期。

我现在想知道 node-serialport 是否因任何原因无法与在 fork() 内运行的代码一起使用。

【问题讨论】:

  • 为什么不直接从渲染进程调用node-serialport
  • 这会很好,因为我只需要执行一次 read(),但我必须重复读取才能发送消息的全部内容。这引入的问题是渲染器线程的阻塞,以及 GUI 的扩展。为了进一步澄清(我确定这是否是有用的信息),如果我尝试将其放入条件或循环中,它看起来像 usbcomm.read() 始终为空。例如 while(1){usbcomm.read()} 立即返回 null。这也发生在 if(true){usbcomm.read()} 中。相反,如果我将 usbcomm.read() 插入到 setInterval 中,它会正常读取。

标签: javascript node.js electron node-serialport


【解决方案1】:

您可以从渲染器进行阻塞调用。只要您将它们保持在 20-30 毫秒以下,并且您不会用阻塞调用淹没进程,您就可以了。我们在渲染器进程中广泛使用了node-serialportnode-ffi,每秒进行数十次调用,传输数兆字节的数据,同时保持 UI 的响应速度,没有抖动。

也许你不能让阻塞调用足够短?在主进程中做呢?这也是一个坏主意。阻塞主进程会阻塞渲染器和 GPU 进程之间的 IPC,因此您很可能也会以这种方式挂起 UI。

如果您确实需要进行长时间阻塞同步调用,请将它们置于隐藏的渲染器进程中。您可以随意阻止它,因为它不显示任何 UI。为了简化此操作,您可以使用electron-remote - renderer-taskpool 功能将长时间运行的任务推送到另一个渲染器进程。

【讨论】:

  • 我已经尝试过你的方法,它确实工作得相对好,但根据我的经验(这可能是因为我的实现方式),就实际阅读而言,它最终变得相对较慢。我发现当我将 usbcomm.read() 放入任何类型的循环或条件中时,它总是返回 null。当我把它放在计时器里时,虽然一切都很好。我目前的方法是在我收到数据时使用“数据”事件将数据附加到字符串上,例如buf += 数据。即使您知道有数据要读取,您是否有任何 read() 返回 null 的经验?
  • 老实说,我只是通过port.on('data', (data) =&gt; { });真正使用过它,它似乎运作良好
猜你喜欢
  • 1970-01-01
  • 2022-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-10
相关资源
最近更新 更多