Отваряне на нови портове за Java с javax.comm

Запознах се с пакета от класове javax.comm, когато открих, че те се използват в комплекта за разработка на Java Ring. (За подробности относно javax.comm, вижте колоната Java Developer на Rinaldo Di Giorgio в майския брой на JavaWorld: „Java получава серийна поддръжка с новия пакет javax.comm.“) По време на лудото ми бързане в JavaOne да вкарам програма в ринга си, се сблъсках с различни проблеми, не на последно място при комуникацията с пръстена. Изтеглих дистрибуцията от Java Developer Connection и се опитах неуспешно да я използвам, за да говоря с Java Ring. По-късно открих проблема с моя пръстен: не бях инсталирал правилно приложимите API на Dallas Semiconductor. Когато пръстенът работи, аз всъщност забравих за комуникационния пакет. Тоест до един уикенд преди около месец, което е отправна точка за тази история.

По много различни причини (най-вече свързани с силно интерактивна симулирана среда - например игри), основният компютър в моята "лаборатория" работи с Windows 95. В този конкретен уикенд обаче бях по-загрижен за друг компютър, който в по много начини, беше почти толкова мощен, колкото Java Ring: Digital Equipment Corporation PDP-8 / e.

PDP-8 е може би първият истински персонален компютър. Проектиран в края на 60-те години на миналия век и произведен в относително големи количества през 70-те години, PDP-8 може да бъде повдигнат от един човек, захранван е от 120-волтов линеен ток и е струвал по-малко от 0 000. Повечето от тези компютри се доставят с една периферия: терминал Teletype Model ASR-33 - оригиналният „TTY“ в компютърна реч.

Телетайпът ASR-33 беше терминал за печат, който се доставяше с четец на хартиени ленти и перфоратор. Да, именно хартиена лента, 1 "широка хартия с пробити дупки в нея, беше основният носител за съхранение на програми на PDP-8.

PDP-8 беше първият компютър, който някога съм програмирал и затова има специално място в сърцето ми. Освен това, поради някои случайни обстоятелства, бях на правилното място в точното време и успях да спася PDP-8, който ще бъде бракуван като боклук. Снимка на моята награда е показана по-долу.

В този специален уикенд, не много отдавна, реших да възвърна PDP-8, макар и само за да преживея тези скъпоценни ранни спомени и да покажа на дъщеря ми колко добре се справя с нейния „стар червен 133-MHz Pentium. "

Съживяване на една класика чрез симулиране на друга

За да започна усилията си за възраждане, трябваше да вкарам програма в PDP-8. На PDP-8 това се постига чрез следване на тристепенен процес:

  1. Използвайки превключвателите на предния панел, потребителят "включва" кратка програма в паметта на магнитното ядро. Тази програма се нарича RIM Loader и нейната цел е да зареди друга програма от хартиена лента, която е във формат Read-in-Mode или RIM.

  2. RIM Loader зарежда хартиената лента във формат RIM. Тази лента съдържа програма, наречена BIN Loader, която може да зарежда програми от хартиена лента в двоичен (BIN) формат.

  3. И накрая, стартирате BIN Loader, за да заредите програмата, която наистина искате, която е на хартиена лента във формат BIN. Ууу!

След като преминете през тези три стъпки, програмата, която искате да стартирате, се съхранява в основната памет. Всичко, което потребителят трябва да направи, е да зададе началния адрес и да каже на машината да "тръгне".

В усилията ми да съживя машината, стъпка 1 не представляваше проблем, но стъпка 2 включваше използването на четеца на хартиени касети в телетайпа - а аз нямах телетайп. Разбира се, аз съм имам настолен компютър, така че логична стъпка е да се симулира лента четец книга на компютъра си.

От логическа и програмна гледна точка симулирането на четец на хартиени касети е тривиално. Просто четете файл, който съдържа данните от "лентата", изпращате го на сериен порт със скорост 110 бода (да, само 10 знака в секунда), докато не изчерпате файла. Бих могъл да напиша програма в C на моята система Solaris или системата ми FreeBSD за около 10 минути, която би могла да направи това - но не забравяйте, че бях на Windows 95, а не на Unix система.

От лошо на грозно и обратно

Знаех, че мога лесно да напиша тази програма на C, така че това беше моят избор. Лош избор. Изкарах моето копие на Visual C ++ 5.0 и извадих проста програма, наречена sendtape.c, която извикваше open()комуникационния порт. Опитах се да го настроя в режим RAW (режимът в Unix, където операционната система не се опитва да интерпретира нищо на серийния порт като потребителски вход) и след това се опитах да го компилирам. Ами сега, няма ioctl()функция или ttyфункции - nada, zip, zilch!

Няма проблем, помислих си, „Имам цялата библиотека на мрежата на Microsoft Software Developer на CD с моя компилатор C; Ще направя бързо търсене на ключовите думи„ COM порт “.“

При търсенето се откриха много препратки към Microsoft Component Object Model (наричан още COM), както и препратки към MSComm. MSComm е клас C ++, който Microsoft доставя, за да говори със серийните портове. Разгледах примерите и се ужасих колко код ще отнеме, за да се направи такова просто нещо като писане на байтове в серийния порт на 110 бода. Всичко, което исках да направя, беше да отворя потъналия сериен порт, да задам скоростта на предаване и да натъпча няколко байта надолу - да не създавам нов клас приложения, подобрени от серийни комуникации!

Пред монитора ми седеше рецепторът Blue Dot за моя Java Ring и си помислих: „Аха! Хората от Dallas Semiconductor са измислили как да говорят със сериен порт на компютъра. Да видим какво правят. " След като разгледахме изходния код на компанията за Win32, беше ясно, че разговорът със серийни портове няма да бъде проста задача.

Java на помощ

В този момент през уикенда си мислех, че може би ще влача една от моите Unix машини в лабораторията, за да кодирам програмата в нея, вместо да използвам това, което вече имах. Тогава си спомних опита си с Java Ring и пакета java.comm от Sun. Вместо това реших да продължа по този път.

Какво предоставя java.comm?

API за комуникация на Java - или java.comm - предоставя независим от платформа метод за достъп до серийни и паралелни портове от Java. Както при другите Java API, като JFC, JDBC и Java 3D, определено ниво на непрякост е принудено на програмиста да изолира идеята на платформата за „какво е сериен порт“ от програмния модел. В случая с дизайна на javax.comm елементи като имена на устройства, които варират от платформа до платформа, никога не се използват директно. Трите интерфейса на API осигуряват независим от платформа достъп до серийни и паралелни портове. Тези интерфейси предоставят извиквания на методи за изброяване на наличните комуникационни портове, контрол на споделен и изключителен достъп до портове и контрол на специфични характеристики на портове като скорост на предаване, генериране на паритет и контрол на потока.

Когато видях примера SimpleWrite.java в документацията и сравних неговите 40 реда код с 150 до 200 реда код, който гледах да пиша на C, разбрах, че решението е под ръка.

Абстракцията на високо ниво за този пакет е класът javax.comm.CommPort. В CommPortклас определя вида на неща, които обикновено биха направили с пристанище, което включва и получаване InputStreamи OutputStreamобекти, които са I / O канали за пристанището на. TheCommPortclass включва също методи за контрол на размерите на буфера и регулиране на начина, по който се обработва въвеждането. Тъй като знаех, че тези класове поддържат протокола Dallas Semiconductor One-Wire (протокол, който включва динамични промени в скоростта на предаване и пълна прозрачност на прехвърляните байтове), знаех, че API на javax.comm трябва да бъде гъвкав. Това, което дойде като приятна изненада, беше колко стегнати бяха класовете: те имаха достатъчно гъвкавост, за да свършат работата и не повече. Нямаше почти никакъв излишен софтуер под формата на "методи за удобство" или поддръжка на модемни протоколи като Kermit или xmodem.

A companion class to CommPort is the javax.comm.CommPortIdentifier class. This class abstracts the relationship between how a port is named on a particular system (that is, "/dev/ttya" on Unix systems, and "COM1" on Windows systems) and how ports are discovered. The static method getCommPortIdentifiers will list all known communication ports on the system; furthermore, you can add your own port names for pseudo communication ports using the addPortName method.

The CommPort class is actually abstract, and what you get back from an invocation of openPort in the CommPortIdentifier is a subclass of CommPort that is either ParallelPort or SerialPort. These two subclasses each have additional methods that let you control the port itself.

The power of Java

You can argue about the reality of "write once, run anywhere" all you want, but I will tell you from experience that for single- threaded or even simple multithreaded non-GUI applications, Java is there. Specifically, if you want to write a program that runs on Unix systems, Win32, and Mac systems, and can access the serial port, then Java is the only solution today.

The benefit here is that fewer resources are required to maintain code that runs on a large number of platforms -- and this reduces cost.

A number of applications share a requirement to have pretty low-level access to the serial port. The term low-level in this context means that a program has access to interfaces that allow it to change modes on-the-fly and directly sample and change the states of the hardware flow-control pins. Besides my PDP-8 project, Dallas Semiconductor needed to use its Blue Dot interfaces on serial ports to talk to the iButton with Java. In addition, the makers of microprocessors have evaluation boards that use a serial port for communications and program loading. All of these applications can now be completely, and portably, written in Java -- a pretty powerful statement.

All of this power to control the parallel and serial ports of the host machine comes from the javax.comm library. Giving Java programmers access to the ports opens up an entirely new set of applications that target embedded systems. In my case, it gave me the ability to write my TTY paper-tape reader emulator completely in Java.

How do you get to play with this stuff?

To get a copy of the latest javax.comm distribution, first you need to sign up as a developer on the Java Developer Connection (JDC) if you haven't done so already. (See Resources.) JDC is free, and as a member you will get early access to Java classes that will eventually be part of the final product.

Go to the Java Communications API section and download the latest javax.comm archive file. Unpack the file and install the shared libraries (yes, the Java virtual machine needs native code to talk to the ports -- fortunately for you, you don't have to write it), and install the comm.jar file. Finally, add the comm.jar file to your CLASSPATH variable.

Once the comm.jar file is stored in the lib directory of your Java installation, and the win32comm.dll is stored in the bin directory of your Java installation, you can compile and run all the examples that come with the download. I encourage you to look them over as there is lots of good information nestled in with the source code.

Where does this leave the PDP-8?

So, what's happened with the PDP-8? I thought you'd never ask! After reading the README document that came with the javax.comm distribution, then scanning the JavaDocs for the javax.comm package, I put together an application class called SendTape. This class simulates a paper-tape reader by opening the serial port and stuffing bytes over it at 110 baud. The code for this class is shown here:

import javax.comm.*; import java.io.*; public class SendTape { static final int LEADER = 0; static final int COLLECT_ADDR = 1; static final int COLLECT_DATA = 2; static final int COLLECT_DATA2 = 3; /* This array holds a copy of the BIN format loader */ static byte binloader[] = { (byte) 0x80,(byte) 0x80,(byte) 0x80,(byte) 0x80, ... (byte) 0x80,(byte) 0x80, }; 

The code fragment above is the first part of the SendTape class. This class begins by implicitly importing all classes in the javax.comm package and the java.io packages. The SendTape class then defines some constants and pre-initializes a byte array to contain the BIN Loader program I mentioned earlier. I included the BIN Loader because it is always needed when initializing the memory of the PDP-8 and I kept losing track of where I had last stored the file containing its image in RIM format. With this crucial paper tape image embedded in the class in this way, I always have the ability to load it with this class.

 /** * This method runs a mini-state machine that gives * a useful human readable output of what is happening * with the download. */ static int newState(int oldState, byte b) { ... } 

След инициализацията имате кода за метода newState, показан по-горе, който проследява съдържанието на хартиената лента (независимо дали е информация за адрес или информация за програмиране). Горният метод също отпечатва съобщение за всяко място на паметта на PDP-8, което е инициализирано.

След това имате mainметода, който е показан по-долу; той отваря файла и го чете. След това кодът отваря серийния порт и задава параметрите му за комуникация.