[Java] Problem z obsługą portu com


(Marcins2009) #1

Witam !

Chciałbym zrobić program który odbierał by dane z portu com i wyświetlał je w JTextField, a po naciśnięciu przycisku wysyłał odpowiednie dane.

Stworzyłem już klase ramki z przyciskami i polem tekstowym.

import gnu.io.SerialPortEvent;


import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.util.Date;


import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JScrollPane;

import javax.swing.JTextArea;



public class OknoProgramu extends JFrame implements ActionListener

{

	public static int EXIT1 = EXIT_ON_CLOSE;

	JButton bOne, bTwo, bThree;

	public JTextArea poleOutPut;


	public OknoProgramu()

	{

		setSize(500, 700);

		setTitle("ArduinoControll");

		setLayout(null);




		bOne = new JButton("One");

		bOne.setBounds(110, 20, 70, 40);

		add(bOne);

		bTwo = new JButton("Two");

		bTwo.setBounds(200, 20, 70, 40);

		add(bTwo);

		bThree = new JButton("Three");

		bThree.setBounds(290, 20, 70, 40);

		add(bThree);


		poleOutPut = new JTextArea();

		JScrollPane suwak = new JScrollPane(poleOutPut);

		suwak.setBounds(40, 100, 400, 200);

		add(suwak);



	}



	public static void main(String[] args) {

		OknoProgramu okno = new OknoProgramu();

		okno.setDefaultCloseOperation(EXIT_ON_CLOSE);

		okno.setVisible(true);


	}


	@Override

	public void actionPerformed(ActionEvent e) 

	{

		Object źródło = e.getSource();


	}


}

I znalazłem jakiś gotowy skrypt bazujący na biblotece RXTX który odbiera i wyświetla dane z portu com w konsoli.

import java.io.InputStream;

import java.io.OutputStream;

import gnu.io.CommPortIdentifier; 

import gnu.io.SerialPort;

import gnu.io.SerialPortEvent; 

import gnu.io.SerialPortEventListener; 

import java.util.Enumeration;


public class SerialTest implements SerialPortEventListener {

	SerialPort serialPort;

        /** The port we're normally going to use. */

	private static final String PORT_NAMES[] = { 

			"/dev/tty.usbserial-A9007UX1", // Mac OS X

			"/dev/ttyUSB0", // Linux

			"COM5", // Windows

			};

	/** Buffered input stream from the port */

	private InputStream input;

	/** The output stream to the port */

	private OutputStream output;

	/** Milliseconds to block while waiting for port open */

	private static final int TIME_OUT = 2000;

	/** Default bits per second for COM port. */

	private static final int DATA_RATE = 9600;


	public void initialize() {

		CommPortIdentifier portId = null;

		Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();


		// iterate through, looking for the port

		while (portEnum.hasMoreElements()) {

			CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();

			for (String portName : PORT_NAMES) {

				if (currPortId.getName().equals(portName)) {

					portId = currPortId;

					break;

				}

			}

		}


		if (portId == null) {

			System.out.println("Could not find COM port.");

			return;

		}


		try {

			// open serial port, and use class name for the appName.

			serialPort = (SerialPort) portId.open(this.getClass().getName(),

					TIME_OUT);


			// set port parameters

			serialPort.setSerialPortParams(DATA_RATE,

					SerialPort.DATABITS_8,

					SerialPort.STOPBITS_1,

					SerialPort.PARITY_NONE);


			// open the streams

			input = serialPort.getInputStream();

			output = serialPort.getOutputStream();


			// add event listeners

			serialPort.addEventListener(this);

			serialPort.notifyOnDataAvailable(true);

		} catch (Exception e) {

			System.err.println(e.toString());

		}

	}


	/**

	 * This should be called when you stop using the port.

	 * This will prevent port locking on platforms like Linux.

	 */

	public synchronized void close() {

		if (serialPort != null) {

			serialPort.removeEventListener();

			serialPort.close();

		}

	}


	/**

	 * Handle an event on the serial port. Read the data and print it.

	 */

	public synchronized void serialEvent(SerialPortEvent oEvent) {

		if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

			try {

				int available = input.available();

				byte chunk[] = new byte[available];

				input.read(chunk, 0, available);


				// Displayed results are codepage dependent

				System.out.print(new String(chunk));

			} catch (Exception e) {

				System.err.println(e.toString());

			}

		}

		// Ignore all the other eventTypes, but you should consider the other ones.

	}


	public static void main(String[] args) throws Exception {

		SerialTest main = new SerialTest();

		main.initialize();

		System.out.println("Started");

	}

}

Że, javy dopiero się uczę, nie potrafię sprawić by obie klasy potrafiły ze sobą "rozmawiać' tzn. żeby klasa OknoProgramu mogło odczytywać dane z portu com jak i je wysyłać.

Z góry dziękuję za pomoc :slight_smile:

System Win7 x64, IDE Eclipse, JavaSE-1.7.


(Grzegorz Olszewski) #2

Użycie kodu biblioteki wygląda dość prosto. Masz gotową metodę odczytującą tekst z portu - public synchronized void serialEvent(SerialPortEvent). Możesz zaimplementować interfejs SerialPortEventListener w swojej klasie OknoProgramu i robić w tej metodzie to samo, tylko zamiast wypisywać Stringa na standardowe wyjście, wpisać go do pola tekstowego:

// tak jest w kodzie testowym

//System.out.print(new String(chunk));


// tak Ty robisz

poleOutPut.setText(new String(chunk));

W drugą stronę jest trochę trudniej, ale niewiele. Masz gotowy OutputStream, do którego możesz pisać. Do przycisku dodajesz zdarzenie na kliknięcie, w którym dowolny tekst wpisujesz do obiektu OutputStream. Oczywiście nie bezpośrednio, tylko za pomocą PrintWritera.

Oczywiście pomijając sens wypisywania tekstu na port COM...

Ale jeśli dopiero zaczynasz naukę programowania, to komunikacja z portem COM wydaje mi się wyjątkowo złym pomysłem na początek.


(Marcins2009) #3

Ok implementuje SerialPortEventListener w OknoProgramu i tworzę w nim metodę serialEvent. Jak zrobić by ta metoda miała identyczne działanie jak w SerialTest bo tak po prostu niechce sobie działać. Tzn. jak zrobić bym tą metodą mógł tak samo odczytywać dane z portu jak w klasie SerialTest. Gdy wkleję całą metodę serialEvent z pliku SerialTest do OknoProgramu to eclipse wyświetla błąd "imput cannot be resolved" przy linijce

input.read(chunk, 0, available);

. Pomocy !


(Grzegorz Olszewski) #4

Błąd "input cannot be resolved" to błąd kompilacji - odwołujesz się do pola input, którego nie ma w klasie OknoProgramu. Mógłbym napisać za Ciebie ten kod, tylko po co? Niczego się w ten sposób nie nauczysz.

Z tego co piszesz wynika, że w ogóle nie masz pojęcia co robisz. Daruj sobie ten port COM i zacznij od podstaw, bo bez tego nie masz szans sobie poradzić. Ja wiem, że każdy chciałby od razu pisać ciekawe programy, ale niestety tak się nie da. Bez podstaw nie napiszesz niczego sensownego.


(Marcins2009) #5

Aha czyli to forum jest dla tych którzy są mistrzami w każdym języku ;/ Powiedz co robię źle ? Nie każę ci pisać całego programu za mnie. Z javą mam styczność ok. tygodnia w ciągu którego przerobiłem jeden kurs na YT i napisałem parę różnych aplikacji, a obsługa portów com mi jest akurat mocno potrzebna. Weź pod uwagę to że, java jest dość specyficznym językiem i dla początkujących wcale nie jest taka oczywista jak prawdopodobnie dla ciebie. Jeszcze raz proszę !


(Grzegorz Olszewski) #6

Robisz źle to, że przekopiowałeś metodę serialEvent, która odpowiada za odczytywanie danych z portu (kiedy się pojawią), ale nie skopiowałeś kodu odpowiadającego za nawiązanie połączenia z tym portem.

Krótko mówiąc - potrzebny Ci jest prawie cały kod klasy SerialTest, bo w tej klasie są pola i metody potrzebne do połączenia z portem. Ja bym zrobił tak:

  • kopiujesz kod klasy SerialTest do jakiejś nowej klasy, nazwijmy ją ComPortConnection (bez metod main i serialEvent), i wywalasz implementację interfejsu SerialPortEventListener

  • implementujesz interfejs SerialPortEventListener w swojej klasie OknoProgramu i kopiujesz do niej metodę serialEvent z klasy SerialTest, z taką różnicą, że zamiast wypisywać Stringa na standardowe wyjście, wpisujesz go do textArea

  • do klasy ComPortConnection dodajesz metodę która zwraca pole z OutputStreamem (getter)

  • w klasie ComPortConnection zmieniasz metodę initialize tak, żeby przyjmowała argument typu SerialPortEventListener, i dodawała go jako listenera do serialPort zamiast this (linijka serialPort.addEventListener(this):wink:

  • tworzysz obiekt tej klasy w swoim oknie - ComPortConnection connection = new ComPortConnection()

  • do jakiegoś przycisku dodajesz akcję na kliknięcie - w tej akcji pobierasz OutputStream z connection i wpisujesz coś do niego (co tam chcesz) - oczywiście to tylko przykład

  • wywołujesz metodę connection.initialize(this)

I to z grubsza by było na tyle, pomijając fakt, że SerialTest próbuje łączyć się z adresami portów typowimi dla maxos, unix, i windows, więc dwie próby z trzech są niepotrzebne. Ale będzie działać. A przynajmniej powinno, ale nie jestem w stanie sprawdzić.

Oprócz tego, operacje na strumieniach wejścia/wyjścia są blokujące, więc powinny być wykonywane w innym wątku niż główny wątek aplikacji, żeby nie blokowały GUI. Ale to chyba na razie przekracza Twoje możliwości.