Voor een hobby projectje ben ik bezig met een logger op basis van een java ServerSocket. Ik heb hier een Siemens PLC die TCP berichtjes stuurt en wacht op antwoord. In het voorbeeld hieronder heb ik de code uitgekleed en stuur ik het bericht zoals hij ontvangen wordt weer terug. Op zich heb ik het redelijk draaiend maar ik liep tegen het probleem aan dat alles in een "while(true)" loop wel erg veel CPU belasting geeft. Een zoektocht leverde een "Runnable Thread" op. Dit geeft aanzienlijk minder CPU belasting maar de helft van de berichten worden laat of niet afgehandeld. Dit is in de "while (true)" situatie niet het geval. Ik heb zelf het vermoeden dat door de code uit te voeren als een Thread the priortijd relatief laag is en op die manier hij berichten "mist".
Mijn vraag:
Mijn vraag is dus eigenlijk hoe kan ik mijn java code zo inrichten dat het zowel niet te veel resources gebruikt en tegelijkertijd goed bereikbaar is?
Relevante software en hardware die ik gebruik
Voor het programmeren/debuggen gebruik ik IntellJ community 2019.2. Als java versie draait hier OpenJDK 11.0.4 in een Ubuntu-Mate 19.04 desktop. De server moet op een Pi3+ gaan draaien. Om de TCP berichten te "simuleren" gebruik ik Packet Sender om het TCP verkeer te debuggen gebruik ik Wireshark
Wat ik al gevonden of geprobeerd heb
Ik ben ook aan het hobbyen geweest met synchronized Threads (Thread.wait() en Thread.notify()) maar dit leide tot nog veel meer fouten in de TCP afhandeling en zelfs disconnect.
Hieronder vind je een omschrijving van hoe de PLC handelt en de 2 stukjes "werkende" code die ik nu heb. Ook een de verschillen laat ik zien tussen de 2 zowel het data verkeer als de CPU belasting op de Pi (op de desktop is het net zo gek alleen kan ik daar het data verkeer niet mooi in beeld krijgen) Voor beide java classen is de zelfde PLC met de zelfde code gebruikt. Ik verwacht dat er maximaal 5 berichten per seconde verstuurt moeten kunnen worden. De PLC kan dit op de slofjes dus Java moet dit ook kunnen
maar hoe?
De PLC handelt de berichten als volgt af:


Met de "Runnable Thread":


Mijn vraag:
Mijn vraag is dus eigenlijk hoe kan ik mijn java code zo inrichten dat het zowel niet te veel resources gebruikt en tegelijkertijd goed bereikbaar is?
Relevante software en hardware die ik gebruik
Voor het programmeren/debuggen gebruik ik IntellJ community 2019.2. Als java versie draait hier OpenJDK 11.0.4 in een Ubuntu-Mate 19.04 desktop. De server moet op een Pi3+ gaan draaien. Om de TCP berichten te "simuleren" gebruik ik Packet Sender om het TCP verkeer te debuggen gebruik ik Wireshark
Wat ik al gevonden of geprobeerd heb
Ik ben ook aan het hobbyen geweest met synchronized Threads (Thread.wait() en Thread.notify()) maar dit leide tot nog veel meer fouten in de TCP afhandeling en zelfs disconnect.
Hieronder vind je een omschrijving van hoe de PLC handelt en de 2 stukjes "werkende" code die ik nu heb. Ook een de verschillen laat ik zien tussen de 2 zowel het data verkeer als de CPU belasting op de Pi (op de desktop is het net zo gek alleen kan ik daar het data verkeer niet mooi in beeld krijgen) Voor beide java classen is de zelfde PLC met de zelfde code gebruikt. Ik verwacht dat er maximaal 5 berichten per seconde verstuurt moeten kunnen worden. De PLC kan dit op de slofjes dus Java moet dit ook kunnen
De PLC handelt de berichten als volgt af:
- Met een "trigger" wordt er een bericht in een FIFO datablock geplaatst.
- Als er 1 of meerdere berichten in de FIFO staan verstuurt hij het eerste bericht met TCP
- als dit bericht succesvol verzonden is (ik geloof dat de trigger hiervoor een ACK berichtje i, maar ik ben hier niet 100% zeker van)
- De PLC gaat nu wachten op antwoord "confirm" van de TCP server (in dit geval is dit het zelfde berichtje dat terug gestuurd wordt)
- Als deze is ontvangen is kijk de PLC of het bericht de zelfde Header en ID heeft
- Als deze goed zijn kan het het eventuele volgende bericht verstuurd worden


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
| import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; public class TcpServerOLD { private ServerSocket ss; public static void main(String[] args) { new TcpServerOLD(); // call the constructor } public TcpServerOLD() { try { this.ss = new ServerSocket(4444); (new Thread(() -> { try { this.startAccept(); } catch (IOException ignored) { } })).start(); System.out.println(InetAddress.getLocalHost().getHostAddress()); // print the IP-Address System.out.println("4444"); // print the Port } catch (IOException ignored) { } } public void startAccept() throws IOException { while(true) { try { Socket so = this.ss.accept(); DataInputStream readBuffer = new DataInputStream(so.getInputStream()); DataOutputStream writeBuffer = new DataOutputStream(so.getOutputStream()); (new Thread(() -> { while(true) { try { if (readBuffer.available() > 0) { byte[] message = new byte[64]; int len = readBuffer.read(message, 0, 64); if (len > 0) { byte[] received = new byte[len]; for(int i = 0; i < len; ++i) { received[i] = message[i]; } byte[] returnMessage = received; writeBuffer.write(returnMessage); writeBuffer.flush(); } } } catch (IOException ignored) {} } })).start(); } catch (IOException ignored) { } } } } |
Met de "Runnable Thread":


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
| import java.io.*; import java.net.ServerSocket; import java.net.InetAddress; import java.net.Socket; /** * This class is for setting up a TCP server and wait for incoming messages from their clients */ public class TcpServerNEW { private ServerSocket ss; private DataInputStream readBuffer; private DataOutputStream writeBuffer; /** * main, the initializer */ public static void main(String[] args) { new TcpServerNEW(); // call the constructor } /** * TcpServerNEW Constructor */ public TcpServerNEW() { try { ss = new ServerSocket(4444); // create the ServerSocket new Thread(new Runnable() { // create new Thread for listening to new clients public void run() { startAccept(); // run the accept-clients method } }).start(); // don't forget to start the thread System.out.println(InetAddress.getLocalHost().getHostAddress()); // print the IP-Address System.out.println("4444"); // print the Port } catch (Exception ignored) { } } /** * startAccept wil start the TCP socket server and does the reading and writing of the buffers. */ public void startAccept() { while (true) { // listen to new clients try { Socket so = ss.accept(); // accept the clients and create reader/writer readBuffer = new DataInputStream(so.getInputStream()); writeBuffer = new DataOutputStream(so.getOutputStream()); new Thread(new Runnable() { // start new thread for reading from the client public void run() { try { if (readBuffer.available() > 0) { byte[] message = new byte[64]; int len = readBuffer.read(message, 0, 64); // read receive buffer if (len > 0) { byte[] received = new byte[len]; for (int i = 0; i < len; i++) { // temporary the rebuild reply (just send the same message back) received[i] = message[i]; System.out.print(String.format("%02X", message[i]) + " "); // show message bytes in hex format } System.out.println(" end"); byte[] confirm = received; //handleMessage(received,len); writeBuffer.write(confirm); // write the message to send writeBuffer.flush(); } } } catch (IOException ignored) { } } }).start(); // don't forget to start the thread } catch (Exception ignored) { } } } } |