www.mikrocontroller.net

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


Autor: Michael B. (bubi)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Am besten löst du das über Events. Ich wollte es erst beschreiben, bau 
jetzt aber doch schnell was zusammen ;)

So:
package test;
public interface MailBoxListener {
    public void newMessageArived(String message);
}
Deine "Mailbox":
import java.util.ArrayList;
import java.util.List;
import javax.swing.SwingUtilities;

public class Mailbox {
    private List<MailBoxListener> listener = new ArrayList<MailBoxListener>();

    /**
     * @param listener
     *            Listener welcher benachrichtigt werden soll wen eine neue
     *            Nachricht eintrifft.
     */
    public void addListener(MailBoxListener listener) {
        this.listener.add(listener);
    }

    /**
     * benachrichtigt alle Listener über eine neue Nachricht
     * 
     * @param message
     */
    protected void fireNewMessageArived(final String message) {
        if (SwingUtilities.isEventDispatchThread()) {
            //Wir sind schon im Event Thread
            notifyAllListeners(message);
        } else {
            //wir führen die Aktion im Event Thread aus
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    notifyAllListeners(message);
                }
            });
        }
    }

    private void notifyAllListeners(String message) {
        for (MailBoxListener l : listener) {
            l.newMessageArived(message);
        }
    }
}
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.

Autor: Michael B. (bubi)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
import javax.swing.SwingUtilities;
public class GUIClass implements MailBoxListener {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                GUIClass gclass = new GUIClass();
                gclass.createGUI();
            }
        });
    }

    protected void createGUI() {
        //Do somethin usefull
        Mailbox mailnew = new Mailbox();
        mailnew.addListener(this);
    }

    @Override
    public void newMessageArived(String message) {
        //do something with the Message
    }
}

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:
import java.util.Date;
public class RS232MailBox extends Mailbox implements Runnable {

    public RS232MailBox() {
        //einen Thread für uns starten
        new Thread(this).start();
    }

    @Override
    public void run() {
        while (true) {
            String message = String.valueOf(System.currentTimeMillis() + " " + new Date());
            fireNewMessageArived(message);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
        }
    }
    public synchronized void sendMessage(String messageToSend) {
        //Message über RS232 senden
    }
}
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...
protected void createGUI() {
    //Do somethin usefull
    RS232MailBox mailnew = new RS232MailBox();
    mailnew.addListener(this);
    mailnew.sendMessage("Hallo Ihr da draussen!");
}

Autor: Michael B (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Michael B (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Michael B. (bubi)
Datum:

Bewertung
0 lesenswert
nicht 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 :)

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also etwa so:
        CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);

 

        if (portIdentifier.isCurrentlyOwned()) {

            throw new RuntimeException("Port in use!");

        } else {

            // points who owns the port and connection timeout

            SerialPort serialPort = (SerialPort) portIdentifier.open("RS232Communicator", 2000);

            // setup connection parameters

            serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

            // setup serial port writer

            out=serialPort.getOutputStream();

            serialPort.disableReceiveTimeout();

            in = serialPort.getInputStream();

        }
und dann einfach in einer Schleife vom InputStream lesen.

Autor: Michael B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.