Forum: PC-Programmierung Java COM Port, Threads und Swing


von Michael B. (bubi)


Lesenswert?

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

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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.

von Michael B. (bubi)


Lesenswert?

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

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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
}

von Michael B (Gast)


Angehängte Dateien:

Lesenswert?

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

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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?

von Michael B (Gast)


Lesenswert?

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.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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.

von Michael B. (bubi)


Lesenswert?

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 :)

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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.

von Michael B. (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.