Electron與工業CCD通過TCP Server協議連接通訊


Electron與工業CCD通過TCP Server協議連接通訊

CCD可以理解為是一個相機,在工業上可以通過CCD給物體拍照,然后識別出物體表面激光刻印的信息,大概如下圖,下圖是我公司研發的軟件一張截圖,軟件是使用C#實現的,這個工業軟件還是很厲害的,里面涉及到了許多與工業傳感器的數據交互。現在我的工作主要就是使用前端知識重構這個軟件,要與硬件打交道了,當然要選擇ElectronNode技術棧了。

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作為一個客戶端,可以向CCDTCP服務器發送消息,並且可以接收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 });

文章會首發於我的微信公眾號:小笑殘虹,大家可以關注我,一起交流進步。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM