div.main {margin-left: 20pt; margin-right: 20pt} Доступ к COM порту из Java приложения
Эта статья посвящена обзору пакета javax.comm,
позволяющего получить доступ с COM порту компьютера. Данный пакет не входит в
стандартный набор JDK.
Итак, сформулируем задачу. Для примера возьмем обычный модем, который
проинсталлирован на стандартный COM порт, нам необходимо, что бы он откликался
на терминальные команды (AT, ATDP и т.д.), посылаемые ему из java приложения.
Для того, чтобы мы могли написать такое приложение, нам необходимо скачать
пакет классов javax.com (см.[2]). После чего, необходимо совершить ряд
манипуляций, чтобы написанные нами приложения могли корректно работать.
Инсталляция Java Communications API для JDK 1.2.
(Windows).<jdk> корневой каталог JDK. Если вы
проинсталлировали JDK в каталог c:jdk1.2тогда замените все ссылки
<jdk> на c:jdk1.2.
Положите файл win32com.dll в директорию
<jdk>jrebin .
Положите архив comm.jar в директорию
<jdk>jrelibext.
Положите файл javax.comm.properties в
<jdk>jrelib .
Не изменяйте переменную CLASSPATH.
Примечание: в настоящее время существуют реализации пакета для
следующих платформ Windows, Solaris, Linux. (см. [2]).
Только после проделанной работы команды javac и java, смогут правильно
работать.
Примечание: если вы используете версию JDK ниже, чем 1.2 то вам
придется по другому проинсталлировать данный пакет (см. [2])
Рассмотрим следующий фрагмент кода: import javax.comm.*;
import java.util.*;
. . .
public static void main(String[] args) {
Enumeration portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
CommPortIdentifier portId =
(CommPortIdentifier) portList.nextElement();
if (portId.getPortType() ==
CommPortIdentifier.PORT_SERIAL) {
if (portId.getName().equals("COM2")) {
Terminal terminal = new Termianl(portID);
}
}
}
}
Приведем описание переменных и методов класса CommPortIdentifier:
Переменные класса:
PORT_SERIAL public static final int
PORT_SERIAL
RS-232 последовательный порт
PORT_PARALLEL public static final int
PORT_PARALLEL
IEEE 1284 паралельный порт
Методы Класса:public static
Enumeration getPortIdentifiers()
Метод возвращает объект типа enumeration, который содержит объекты типа
CommPortIdentifier для каждого порта системы.
public String getName()
Возвращает имя порта . Например, "COM1" и "COM2" на PC;
public int
getPortType()
Метод возвращает тип порта PORT_SERIAL или PORT_PARALLEL.
public synchronized CommPort open(String
appname, int timeout) throws PortInUseException
Открывает порт. Возвращает объект типа CommPort.
параметры: appname - Имя приложения
вызывающего данный метод. (произвольная строка) timeout - время в
милисекундах, в течение которого, блокируется доступ к порту для его открытия.
Throws: PortInUseException если порт используется другим приложением.
Таким образом, в методе main, мы получили полный перечень идентификаторов
портов нашего компьютера, после чего отобрали идентификаторы последовательных
портов. Ну, и наконец, выбрали идентификатор COM2 порта (предположим что модем
находится именно на нем) и передали его конструктору класса Terminal. Следующим
шагом будет написание класса Terminal, который бы смог отправлять сообщения в
COM порт, и прослушивал бы сообщения поступающие из этого порта. import javax.comm.*;
import java.io.*;
public class Terminal implements Runnable,
SerialPortEventListener {
InputStream inputStream;
OutputStream outputStream;
SerialPort serialPort;
Thread readThread;
String[] messageString = {"ATn","ATI1n","ATI3n"};
public Terminal() {
try {
serialPort = (SerialPort) portId.open("TerminalApp", 2000);
} catch (PortInUseException e) {}
try {
outputStream = serialPort.getOutputStream();
inputStream = serialPort.getInputStream();
} catch (IOException e) {}
try {
serialPort.addEventListener(this);
} catch (TooManyListenersException e) {}
serialPort.notifyOnDataAvailable(true);
try {
// устанавливаем параметры порта
serialPort.setSerialPortParams(9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {}
readThread = new Thread(this);
readThread.start();
}
public void run() {
for(int i=0;i<3;i++){
try {
outputStream.write(messageString[i].getBytes());
}catch (IOException e) {}
try{
Thread.sleep(5000);
} catch (InteruptedException e) {}
}
System.exit(1); // выход из программы
}
public void serialEvent(SerialPortEvent event) {
switch(event.getEventType()) {
case SerialPortEvent.BI:
case SerialPortEvent.OE:
case SerialPortEvent.FE:
case SerialPortEvent.PE:
case SerialPortEvent.CD:
case SerialPortEvent.CTS:
case SerialPortEvent.DSR:
case SerialPortEvent.RI:
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
break;
case SerialPortEvent.DATA_AVAILABLE:
byte[] readBuffer = new byte[20];
try {
while (inputStream.available() > 0) {
int numBytes = inputStream.read(readBuffer);
}
System.out.print(new String(readBuffer));
} catch (IOException e) {}
break;
}
}
}
Класс Terminal реализует интерфейс SerialPortEventListener это подразумевает
включение одного единственного метода public void
serialEvent(SerialPortEvent event){}. Рассмотрим используемые
нами методы класса SerialPort:
public abstract InputStream getInputStream()
throws IOException
Возвращает поток InputStream. Это один единственный способ
получить данные из порта. Если порт не поддерживает получение данных то данный
метод вернет null. Метод наследуется от класса CommPort. public abstract OutputStream getOutputStream() throws
IOException
Возвращает поток OutputStream. Это один единственный способ
послать данные в порт. Если порт не поддерживает посылку данных то данный
метод вернет null. Метод наследуется от класса CommPort. public void close()
Метод закрывает порт. Приложение может вызвать метод close когда оно закончило работу с портом.
Метод наследуется от класса CommPort. public abstract void notifyOnDataAvailable(boolean
enable)
Когда данные будут доступны в входном буфере, будут послано оповещение
listener который зарегестрирован методом addEventListener. Сообщение будет
сгенерировано один раз, когда новые данные поступят в последовательный порт.
Параметры: enable - true: оповещение включено.
false: оповещение выключено. public
abstract void addEventListener(SerialPortEventListener lsnr) throws
TooManyListenersException
Регестрирует объект SerialPortEventListener для прослушивания
SerialEvent-s.
Итак, разберем по шагам, что происходит в конструкторе класса Terminal. Получив объект CommPortIdentifier, мы открываем порт, и
преобразуем его к типу SerialPort: serialPort = (SerialPort)
portId.open("TerminalApp", 2000);
после чего открываем входной и выходной потоки: outputStream = serialPort.getOutputStream();
inputStream = serialPort.getInputStream();
регестрируем объект SerialPortEventListener и устанавливаем
параметры порта: serialPort.addEventListener(this);
serialPort.setSerialPortParams(9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
Ну, и наконец, создаем новый поток, который в цикле, с интервалом в пять
секунд, начинает писать в порт сообщения из массива messageString ={"ATI3n","ATI1n","ATn"}; outputStream.write(messageString[i].getBytes());
Сообщения поступающие из COM порта на поможет прослушать метод serialEvent, который при наступлении события
SerialPortEvent.DATA_AVAILABLE
читает данные из входного потока.
Вот казалось бы и все, только осталась маленькая деталь. Заменим строку в
конструкторе класса Terminal: outputStream = serialPort.getOutputStream();
на
outputStream = new ConvertedOutputStream
(serialPort.getOutputStream());
где класс ConvertedOutputStream выглядит следующим
образом. import java.io.OutputStream;
import java.io.IOException;
class ConvertedOutputStream extends OutputStream {
OutputStream outstream;
ConvertedOutputStream(OutputStream outstream) {
this.outstream = outstream;
}
public void flush() throws IOException {
outstream.flush();
}
private int prev = 0;
public void write(int b) throws IOException {
if (b == 'n') {
if (prev != 'r')
outstream.write('r');
} else if (b == 'r') {
if (prev != 'n')
outstream.write('n');
}
prev = b;
outstream.write(b);
}
}
Данный класс расширяет OutputStream, перегружая основной метод
write(int b) и его понимание я
оставляю на откуп читателям.
Таким образом, наше приложение вполне сформировано, если его запустить на
выполнение, то мы увидим примерно следующее:
ATI3 Sportster 33600/Fax
V10.0.23 OK ATI1 7C60 OK AT OK
Модем корректно откликнулся на команды, следовательно, написанное нами
приложение, работает правильно. Так что теперь, вооружившись полученными
знаниями вы можете спокойно приступать к написанию аналога HyperTerminal :)). За
дополнительными сведениями обращайтесь к литературе (см [1]).
Ссылки:
http://java.sun.com/products/javacomm/javadocs/packages.htmlЭто
полное описание API функций пакета javax.com
http://java.sun.com/products/javacomm/index.htmlЗдесь
находится пакет javax.comm и инструкции по его установке. ООО "Кубикс"
(www.qbix.ru), Санкт-Петербург.
Дата последнего обновления: 6 августа 2000 г.
Автор Владислав
Каменский
|