Electron与工业CCD通过TCP Server协议连接通讯
Electron与工业CCD通过TCP Server协议连接通讯
CCD
可以理解为是一个相机,在工业上可以通过CCD
给物体拍照,然后识别出物体表面激光刻印的信息,大概如下图,下图是我公司研发的软件一张截图,软件是使用C#
实现的,这个工业软件还是很厉害的,里面涉及到了许多与工业传感器的数据交互。现在我的工作主要就是使用前端知识重构这个软件,要与硬件打交道了,当然要选择Electron
和Node
技术栈了。
CCD
底层是通过TCP Server
协议进行通讯的,所以要想与CCD
进行数据对接,就要在Electron
中实现TCP Server
服务了。
这个知识我是不会的,最先是读C#
的代码,看之前人家是怎么实现的,6000
行的代码,看的我也是头晕脑胀的。再后来就是在网上找资料了,可是发现网上关于Electron
连接TCP Server
的资料真不多,但还是有的,下面就开始实现吧。
参考资料一:electron测试TCP通信,这篇文章写的估计也就只有博主自己能看懂吧!没办法参考着他的代码,我居然还悟出来点东西。
首先声明我的Electron
是通过Vue CLI Plugin Electron Builder
创建出来的。在主进程(background.js
)中添加下面这段代码:
1 // TCP/IP通讯 2 3 // 试了半天,才发现这是创建一个 TCP Server 的 4 function createServer(port) { 5 const HOST = '127.0.0.1'; 6 // const port = 7899; 7 if (server) { 8 server.close(); 9 } 10 11 server = net.createServer(); 12 13 server.listen(port, HOST, function () { 14 console.log('Server listen on port:' + server.address().address); 15 console.log('--------------------------------------服务正在监听中---------------------------------------'); 16 sendServerData('start-server', '服务正在监听中,server is listening...'); 17 }); 18 19 20 server.on('connection', socket => { 21 sendServerData('connect-server', 'Get conneciton from:' + socket.remoteAddress); 22 23 socket.on('data', data => { 24 sendServerData('data-server', 'Get data from socket:' + socket.remoteAddress + '. The data:' + data); 25 socket.write('you said:' + data); 26 }); 27 28 socket.on('close', () => { 29 sendServerData('close-server', 'Socket:' + socket.remoteAddress + " closed"); 30 }) 31 }); 32 33 } 34 35 let server; 36 let client; 37 let serverEvent, clientEvent; 38 // 通过这个方法就可以建立一个TCP服务器,当收到前端发送的event,就可以创建了,这里前端发送的消息是 start-server 39 ipcMain.on('start-server', (event, arg) => { 40 serverEvent = event; 41 // event.sender.send() 42 console.log('+++++++++++++++++++++++++++ event ++++++++++++++++++++++++++++++++++++++++++++++') 43 console.log(event) 44 console.log('=================================== arg =============================================') 45 console.log(arg) 46 createServer(arg); 47 }) 48 49 // 将消息返回给前端 50 function sendServerData(channel, msg) { 51 try { 52 console.log(`server send event ${channel}, ${msg}`); 53 if (serverEvent) { 54 serverEvent.sender.send(channel, msg); 55 } 56 } catch (error) { 57 console.error('gt error:' + error); 58 } 59 }
然后在前端页面上新增两个输入框和一个按钮,如下图:
Home.vue
代码:
1 <template> 2 <div class="home"> 3 <img alt="Vue logo" src="../assets/logo.png" /> 4 <div> 5 发送的消息:<input type="text" v-model="msg" /> 6 </div> 7 <div> 8 接收的消息:<input type="text" v-model="msg2"> 9 </div> 10 <div> 11 <button @click="tcpServer">TCP通信</button> 12 </div> 13 <!-- <HelloWorld msg="Welcome to Your Vue.js App" /> --> 14 </div> 15 </template> 16 17 <script> 18 const ipc = window.require("electron").ipcRenderer; 19 ipc.on("start-server", (evnet, args) => { 20 console.log(evnet,args) 21 }); 22 ipc.on("data-server", (evnet, args) => { 23 console.log(evnet,args) 24 }); 25 26 ipc.on("close-server", (evnet, args) => { 27 console.log(evnet,args) 28 }); 29 30 // @ is an alias to /src 31 import HelloWorld from "@/components/HelloWorld.vue"; 32 33 export default { 34 name: "Home", 35 components: { 36 HelloWorld, 37 }, 38 data() { 39 return { 40 msg: "", 41 msg2: "", 42 }; 43 }, 44 methods: { 45 tcpServer() { 46 console.log(this.msg); 47 ipc.send("start-server", this.msg); 48 }, 49 addText(msg) { 50 this.msg2 += msg + "\n"; 51 }, 52 }, 53 }; 54 </script> 55 56 <style lang="css" scoped> 57 img { 58 -webkit-app-region: drag; 59 } 60 </style>
下面这张图是主进程在控制台输出的信息,从中可以看出event
参数是这么一大坨东西,arg
参数就是我们输入框输入的111。
如果我们先通过网络调试助手,创建一个ip
为 127.0.0.1 端口号为 7899 的 TCP Server
,然后再回到页面上在输入框内也输入7899,然后点击按钮,就发现报错了,报错说127.0.0.1:7899已经被创建了。
可以看出在主进程里面加的这段代码其实是通过渲染进程来创建一个TCP Server
的,可这并不是我的需求,我的需求是将Electron
作为一个客户端,可以向CCD
的TCP
服务器发送消息,并且可以接收CCD
返回给我的信息,下面实现一下。
参考资料二、electron 使用tcp套接字(一)
参考资料三、Node.js Net 模块
在主进程(background.js
)中添加下面这段代码:
1 // 作为客户端 2 var net = require('net'); 3 var HOST = '127.0.0.1'; 4 var PORT = 7899; 5 6 var client = new net.Socket(); 7 client.connect(PORT, HOST, function() { 8 console.log('CONNECTED TO: ' + HOST + ':' + PORT); 9 // 建立连接后立即向服务器发送数据,服务器将收到这些数据 10 client.write('Hello TCP/IP! 老子终于通过 Electron 实现和你通信了!!!'); 11 12 }); 13 14 // 为客户端添加“data”事件处理函数 15 // data是服务器发回的数据 16 client.on('data', function(data) { 17 console.log('DATA: ' + data); 18 // 完全关闭连接 19 client.destroy(); 20 }); 21 22 // 为客户端添加“close”事件处理函数 23 client.on('close', function() { 24 console.log('Connection closed'); 25 });
然后通过网络调试助手打开一个127.0.0.1:7899的TCP Server
服务,我们通过Electron
向网络调试助手发送一条消息。
再次启动我们的Electron
项目,项目一启动就会自动向网络调试助手发送一条信息,在网络调试助手中我们就可以看到这条消息了
然后再通过网络调试助手给我们的客户端Electron
发送一条数据:
然后我们的Electron
也就可以收到来自TCP Server
服务端的消息了,至此,大功告成。
最后贴出我主进程background.js
的完整代码:
1 'use strict' 2 3 import { 4 app, 5 protocol, 6 BrowserWindow, 7 ipcMain, 8 } from 'electron' 9 import { 10 createProtocol 11 } from 'vue-cli-plugin-electron-builder/lib' 12 import installExtension, { 13 VUEJS_DEVTOOLS 14 } from 'electron-devtools-installer' 15 const isDevelopment = process.env.NODE_ENV !== 'production' 16 17 const net = require('net'); 18 // Keep a global reference of the window object, if you don't, the window will 19 // be closed automatically when the JavaScript object is garbage collected. 20 let win 21 22 // Scheme must be registered before the app is ready 23 protocol.registerSchemesAsPrivileged([{ 24 scheme: 'app', 25 privileges: { 26 secure: true, 27 standard: true 28 } 29 }]) 30 31 function createWindow() { 32 // Create the browser window. 33 win = new BrowserWindow({ 34 width: 800, 35 height: 700, 36 // 设置窗口的透明属性为true 37 // transparent:true, 38 // 禁用默认边框,无法拖拽移动窗口,也无法最大化、最小化、关闭窗口 39 frame: false, 40 // 透明的窗口不可调整大小,所以将resizable属性也设置为false 41 // resizable:false, 42 // 为了防止双击窗口可拖拽区域触发最大化事件,将maximizable属性也设置为false 43 // maximizable:false, 44 webPreferences: { 45 // nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION, 46 // 注意devTools可能会影响到transparent 47 // devTools: false, 48 nodeIntegration: true, 49 // enableRemoteModule:true 50 } 51 }) 52 53 if (process.env.WEBPACK_DEV_SERVER_URL) { 54 // Load the url of the dev server if in development mode 55 win.loadURL(process.env.WEBPACK_DEV_SERVER_URL) 56 if (!process.env.IS_TEST) win.webContents.openDevTools() 57 } else { 58 createProtocol('app') 59 // Load the index.html when not in development 60 win.loadURL('app://./index.html') 61 } 62 63 win.on('closed', () => { 64 win = null 65 }) 66 } 67 68 // Quit when all windows are closed. 69 app.on('window-all-closed', () => { 70 // On macOS it is common for applications and their menu bar 71 // to stay active until the user quits explicitly with Cmd + Q 72 if (process.platform !== 'darwin') { 73 app.quit() 74 } 75 }) 76 77 app.on('activate', () => { 78 // On macOS it's common to re-create a window in the app when the 79 // dock icon is clicked and there are no other windows open. 80 if (win === null) { 81 createWindow() 82 } 83 }) 84 85 // This method will be called when Electron has finished 86 // initialization and is ready to create browser windows. 87 // Some APIs can only be used after this event occurs. 88 app.on('ready', async () => { 89 if (isDevelopment && !process.env.IS_TEST) { 90 // Install Vue Devtools 91 try { 92 await installExtension(VUEJS_DEVTOOLS) 93 } catch (e) { 94 console.error('Vue Devtools failed to install:', e.toString()) 95 } 96 } 97 98 // 最小化窗体 99 ipcMain.on('minWindow', () => { 100 win.minimize() 101 }) 102 103 // 关闭窗体 104 ipcMain.on('closeWindow', () => { 105 win.close() 106 }) 107 108 // 最大化窗体 109 ipcMain.on('maxWindow', () => { 110 win.isMaximized() ? win.unmaximize() : win.maximize() 111 }) 112 113 createWindow() 114 }) 115 116 // Exit cleanly on request from parent process in development mode. 117 if (isDevelopment) { 118 if (process.platform === 'win32') { 119 process.on('message', (data) => { 120 if (data === 'graceful-exit') { 121 app.quit() 122 } 123 }) 124 } else { 125 process.on('SIGTERM', () => { 126 app.quit() 127 }) 128 } 129 } 130 131 132 // TCP/IP通讯 133 134 // 试了半天,才发现这是创建一个 TCP Server 的 135 // function createServer(port) { 136 // const HOST = '127.0.0.1'; 137 // // const port = 7899; 138 // if (server) { 139 // server.close(); 140 // } 141 142 // server = net.createServer(); 143 144 // server.listen(port, HOST, function () { 145 // console.log('Server listen on port:' + server.address().address); 146 // console.log('--------------------------------------服务正在监听中---------------------------------------'); 147 // sendServerData('start-server', '服务正在监听中,server is listening...'); 148 // }); 149 150 151 // server.on('connection', socket => { 152 // sendServerData('connect-server', 'Get conneciton from:' + socket.remoteAddress); 153 154 // socket.on('data', data => { 155 // sendServerData('data-server', 'Get data from socket:' + socket.remoteAddress + '. The data:' + data); 156 // socket.write('you said:' + data); 157 // }); 158 159 // socket.on('close', () => { 160 // sendServerData('close-server', 'Socket:' + socket.remoteAddress + " closed"); 161 // }) 162 // }); 163 164 // } 165 166 // let server; 167 // let client; 168 // let serverEvent, clientEvent; 169 // // 通过这个方法就可以建立一个TCP服务器,当收到前端发送的event,就可以创建了,这里前端发送的消息是 start-server 170 // ipcMain.on('start-server', (event, arg) => { 171 // serverEvent = event; 172 // // event.sender.send() 173 // console.log('+++++++++++++++++++++++++++ event ++++++++++++++++++++++++++++++++++++++++++++++') 174 // console.log(event) 175 // console.log('=================================== arg =============================================') 176 // console.log(arg) 177 // createServer(arg); 178 // }) 179 180 // 将消息返回给前端 181 // function sendServerData(channel, msg) { 182 // try { 183 // console.log(`server send event ${channel}, ${msg}`); 184 // if (serverEvent) { 185 // serverEvent.sender.send(channel, msg); 186 // } 187 // } catch (error) { 188 // console.error('gt error:' + error); 189 // } 190 // } 191 192 193 // 作为客户端 194 var HOST = '127.0.0.1'; 195 var PORT = 7899; 196 197 var client = new net.Socket(); 198 client.connect(PORT, HOST, function() { 199 console.log('CONNECTED TO: ' + HOST + ':' + PORT); 200 // 建立连接后立即向服务器发送数据,服务器将收到这些数据 201 client.write('Hello TCP/IP! 老子终于通过 Electron 实现和你通信了!!!'); 202 }); 203 204 // 为客户端添加“data”事件处理函数 205 // data是服务器发回的数据 206 client.on('data', function(data) { 207 console.log('DATA: ' + data); 208 // 完全关闭连接 209 client.destroy(); 210 }); 211 212 // 为客户端添加“close”事件处理函数 213 client.on('close', function() { 214 console.log('Connection closed'); 215 });
文章会首发于我的微信公众号:小笑残虹,大家可以关注我,一起交流进步。