Hallo, Ich baue gerade an einer GUI für eine Kommunikation über die serielle Schnittstelle zum schnellen konfigurieren und debuggen. Da ich mich zwar mit Java nicht besonders gut auskenne das Programm aber auch auf dem MAC laufen soll hab ich mich mal dran gesetzt Java einzusetzen. Die Kommunikation habe ich mit der RXTX Lib realisiert was soweit auch funktioniert. Ich kann rausschreiben und lesen. Das haut hin. Auch meine GUI funktioniert soweit, lesen, schreiben Jlists kopieren usw. alles kein Problem. Wie verbinde ich jetzt meine zwei Klassen? SwingWorker ist meiner Meinung nach der falsche Ansatz, so wie ich das sehe ist der für Backgroundaufgaben mit hoher Rechenlast ausgelegt die irgendwann mal ein Ergebniss liefern. Jetzt funktioniert meine Kommunikation natürlich eventbasierend, das heißt doBackground... funktioniert so nicht wirklich. Ideal wäre meiner Meinung nach eine Mailboxklasse mit einem einfachen Lock-Mechanismus. In der GUI hohle ich mir (falls Daten anliegen) jede Sekunde oder sowas meine aktuellen Daten mit getXXX(), der COMPort schreibt mit setXXX(), gelockt wird über ein boolean mittels "newdatardy" oder sowas. Nur wie gebe ich Java bekannt das die beiden Threads auf die selbe Instanz von meiner Mailbox zugreifen können? Oder anders gefragt. Wie synchronisiere ich Threads soweit? Schonmal Danke Michael
Am besten löst du das über Events. Ich wollte es erst beschreiben, bau jetzt aber doch schnell was zusammen ;) So:
1 | package test; |
2 | public interface MailBoxListener { |
3 | public void newMessageArived(String message); |
4 | }
|
Deine "Mailbox":
1 | import java.util.ArrayList; |
2 | import java.util.List; |
3 | import javax.swing.SwingUtilities; |
4 | |
5 | public class Mailbox { |
6 | private List<MailBoxListener> listener = new ArrayList<MailBoxListener>(); |
7 | |
8 | /**
|
9 | * @param listener
|
10 | * Listener welcher benachrichtigt werden soll wen eine neue
|
11 | * Nachricht eintrifft.
|
12 | */
|
13 | public void addListener(MailBoxListener listener) { |
14 | this.listener.add(listener); |
15 | }
|
16 | |
17 | /**
|
18 | * benachrichtigt alle Listener über eine neue Nachricht
|
19 | *
|
20 | * @param message
|
21 | */
|
22 | protected void fireNewMessageArived(final String message) { |
23 | if (SwingUtilities.isEventDispatchThread()) { |
24 | //Wir sind schon im Event Thread
|
25 | notifyAllListeners(message); |
26 | } else { |
27 | //wir führen die Aktion im Event Thread aus
|
28 | SwingUtilities.invokeLater(new Runnable() { |
29 | |
30 | @Override |
31 | public void run() { |
32 | notifyAllListeners(message); |
33 | }
|
34 | });
|
35 | }
|
36 | }
|
37 | |
38 | private void notifyAllListeners(String message) { |
39 | for (MailBoxListener l : listener) { |
40 | l.newMessageArived(message); |
41 | }
|
42 | }
|
43 | }
|
Deine GUI muss jetzt nur noch das Interface implementieren und sich bei
der Mailbox registrieren.
Natürlich sollte man sinnvollerweise nicht "String" als Nachrichtentyp
nehmen sondern ein eigenes Objekt auch könnte man natürlich noch andere
mögliche Events dem Interface hinzufügen. Senden kannst du einfach ohne
spezielle Vorkehrungen.
> Wie synchronisiere ich Threads soweit?
Ansonsten läuft sowas über das syncronized nur bei der GUI muß man
etwas vorsichtig sein, da (einige) Veränderungen an GUI-Objekten immer
im Event-Thread geschehen sollen.
Vielen Dank für die ausführliche Antwort. Ich versuche gerade mal meine Gedankengänge zusammenzufassen bitte korrigiere mich, bin wie gesagt nicht fit mit Java... Meine GUI Klasse muss newMessageArived(String message); Dazu muss ich noch das Event dafür registrieren. Wie mache ich das speziell für dieses Event? Jetzt muss ich noch dem Thread eine Referenz auf die Mailboxklasse geben, damit der das Event auch auslösen kann, kan ich der run() Methode auch Referenzen übergeben? also run(Mailbox mail)? Angenommen das funktioniert, wie würde das in die andere Richtung ausschauen? Im Moment sende ich nichts aus der GUI Anwendung raus, nur innerhalb des Threads...das ganze in die andere Richtung implementieren? Danke nochmal Michael
Michael B. schrieb: > Meine GUI Klasse muss newMessageArived(String message); > Dazu muss ich noch das Event dafür registrieren. Wie mache ich das > speziell für dieses Event? z.B. so:
1 | import javax.swing.SwingUtilities; |
2 | public class GUIClass implements MailBoxListener { |
3 | |
4 | public static void main(String[] args) { |
5 | SwingUtilities.invokeLater(new Runnable() { |
6 | |
7 | @Override |
8 | public void run() { |
9 | GUIClass gclass = new GUIClass(); |
10 | gclass.createGUI(); |
11 | }
|
12 | });
|
13 | }
|
14 | |
15 | protected void createGUI() { |
16 | //Do somethin usefull
|
17 | Mailbox mailnew = new Mailbox(); |
18 | mailnew.addListener(this); |
19 | }
|
20 | |
21 | @Override |
22 | public void newMessageArived(String message) { |
23 | //do something with the Message
|
24 | }
|
25 | }
|
Michael B. schrieb: > Jetzt muss ich noch dem Thread eine Referenz auf die Mailboxklasse > geben, damit der das Event auch auslösen kann, kan ich der run() Methode > auch Referenzen übergeben? also run(Mailbox mail)? Am besten lässt du deine Run Klasse (man erweitert normalerweise nicht Thread direkt) die Mailbox erweitern:
1 | import java.util.Date; |
2 | public class RS232MailBox extends Mailbox implements Runnable { |
3 | |
4 | public RS232MailBox() { |
5 | //einen Thread für uns starten
|
6 | new Thread(this).start(); |
7 | }
|
8 | |
9 | @Override |
10 | public void run() { |
11 | while (true) { |
12 | String message = String.valueOf(System.currentTimeMillis() + " " + new Date()); |
13 | fireNewMessageArived(message); |
14 | try { |
15 | Thread.sleep(2000); |
16 | } catch (InterruptedException e) { |
17 | }
|
18 | }
|
19 | }
|
20 | public synchronized void sendMessage(String messageToSend) { |
21 | //Message über RS232 senden
|
22 | }
|
23 | }
|
Dann halt im ersten Beispiel einfach die RS232MailBox nutzen. Michael B. schrieb: > Angenommen das funktioniert, wie würde das in die andere Richtung > ausschauen? Im Moment sende ich nichts aus der GUI Anwendung raus, nur > innerhalb des Threads...
1 | protected void createGUI() { |
2 | //Do somethin usefull
|
3 | RS232MailBox mailnew = new RS232MailBox(); |
4 | mailnew.addListener(this); |
5 | mailnew.sendMessage("Hallo Ihr da draussen!"); |
6 | }
|
Viel Dank! Funktioniert bestens...! extends und Runnable waren die fehlenden Schlüsselwörter ;) Und ich habs sogar kapiert :D Vielen vielen Dank... :) Jetzt zerhaut mir dafür aber NetBeans mein SwingLayout :( verstehe wer will...muss unbewusst etwas verändert haben.. Obwohl es in der "Preview" richtig aussieht Michael
Da kann ich dir leider nicht weiterhelfen, ich bau meine GUIs immer per Hand das weis ich wenigstens was warum geht (oder auch nicht) welchen Layoutmanager verwendest du für die Buttons?
Kein Problem habe das Layout sowieso umgebastelt jetzt passts wieder. Dafür ist mir aufgefallen das ich scheinbar ab und an events verliere... Also Daten kommen an, werden aber nicht verarbeitet... Passiert aber schon im Serialport Eventlistener... die Daten kommen nachweislich an, ich sehe die Antwort vom Modem im SerialPort Sniffer...Kann das was mit den Threads zu tun haben? Hab jetzt mal testweise alles synchronized gemacht und send und read gelocket.
Michael B schrieb: > Hab jetzt mal testweise alles synchronized > gemacht und send und read gelocket. Ah... Also das hilft nix und ist eher kontraproduktiv. > Passiert aber > schon im Serialport Eventlistener Eventuell USB/SerialWandler? hast du mal versucht die Streams manuell zu lesen? > events verliere... Wenn du Events 'verlierst' ist der PC kaputt ;) Java ist kein µC welcher nur maximal einen Interupt zur Zeit speicher kann. Wenn etwas verloren geht dann eventuell schon auf Basis der RX/TX oder Betriebssystemebene wegen Pufferüberlauf. Aber ich hab bisher keine Probleme gehabt mit verlorenen Bytes hab aber auch eine Hardware RS232.
Ja, ist richtig^^ Es ist auch bei mir direkt RS232 Trotzdem sehe ich im Terminal das Daten ankommen, aber kein Event ausgelöst wird... merkwürdig merkwürdig :( Naja ich debuge mal weiter..mal schaun ob ich noch wo was rausfinden kann Trtozdem danke :)
Also etwa so:
1 | CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName); |
2 | |
3 | |
4 | |
5 | if (portIdentifier.isCurrentlyOwned()) { |
6 | |
7 | throw new RuntimeException("Port in use!"); |
8 | |
9 | } else { |
10 | |
11 | // points who owns the port and connection timeout
|
12 | |
13 | SerialPort serialPort = (SerialPort) portIdentifier.open("RS232Communicator", 2000); |
14 | |
15 | // setup connection parameters
|
16 | |
17 | serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); |
18 | |
19 | // setup serial port writer
|
20 | |
21 | out=serialPort.getOutputStream(); |
22 | |
23 | serialPort.disableReceiveTimeout(); |
24 | |
25 | in = serialPort.getInputStream(); |
26 | |
27 | }
|
und dann einfach in einer Schleife vom InputStream lesen.
Mittlerweile funktionierts, meine Statemaschine war schuld und ließ sich scheinbar mit Netbeans nicht sauber debuggen (oder was wahrscheinlicher ist, ich kanns einfach nicht :) ) Danke nochmal!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.