Otwarcie nowych portów na Javę za pomocą javax.comm

Z pakietem klas javax.comm zapoznałem się, gdy odkryłem, że są one używane w zestawie deweloperskim dla pierścienia Java Ring. (Szczegółowe informacje na temat javax.comm można znaleźć w kolumnie Java Developer Rinaldo Di Giorgio w majowym wydaniu JavaWorld: „Java otrzymuje obsługę portów szeregowych dzięki nowemu pakietowi javax.comm.”) Podczas mojego szalonego pośpiechu w JavaOne, aby wprowadzić program do mojego pierścienia, napotkałem wiele problemów, z których nie najmniejszy dotyczył komunikacji z pierścieniem. Pobrałem dystrybucję z Java Developer Connection i bezskutecznie próbowałem użyć jej do komunikacji z Java Ring. Później odkryłem problem z moim pierścieniem: nie miałem poprawnie zainstalowanych starszych interfejsów API Dallas Semiconductor. Gdy pierścień działał, w zasadzie zapomniałem o pakiecie komunikacyjnym. To znaczy do jednego weekendu około miesiąca temu, który jest punktem wyjścia dla tej historii.

Z wielu różnych powodów (głównie związanych z wysoce interaktywnymi symulowanymi środowiskami - na przykład grami), na głównym komputerze w moim „laboratorium” działa system Windows 95. Jednak w ten weekend bardziej interesował mnie inny komputer, który w na wiele sposobów był tak potężny jak pierścień Java: a Digital Equipment Corporation PDP-8 / e.

PDP-8 był prawdopodobnie pierwszym prawdziwym komputerem osobistym. Zaprojektowany pod koniec lat 60. i wyprodukowany w stosunkowo dużych ilościach w latach 70. PDP-8 mógł być podnoszony przez jedną osobę, był zasilany napięciem sieciowym 120 V i kosztował mniej niż 0,000. Większość z tych komputerów jest dostarczana z jednym urządzeniem peryferyjnym: terminalem Teletype Model ASR-33 - oryginalnym „TTY” w języku komputerowym.

Teletyp ASR-33 był terminalem drukującym z czytnikiem taśmy papierowej i dziurkaczem. Tak, to była taśma papierowa, papier o szerokości 1 cala z wyciętymi w niej dziurkami, który był głównym nośnikiem przechowywania programów na PDP-8.

PDP-8 był pierwszym komputerem, jaki kiedykolwiek zaprogramowałem, dlatego zajmuje szczególne miejsce w moim sercu. Co więcej, z powodu pewnych przypadkowych okoliczności znalazłem się we właściwym miejscu we właściwym czasie i udało mi się uratować PDP-8, który miał zostać złomowany jako śmieci. Zdjęcie mojej nagrody jest pokazane poniżej.

W ten wyjątkowy weekend nie tak dawno postanowiłem przywrócić PDP-8 do życia, choćby po to, by ponownie przeżyć te cenne wczesne wspomnienia i pokazać mojej córce, jak dobrze sobie radzi ze swoim „starym, marnym Pentium 133 MHz”. "

Ożywienie jednego klasyka poprzez symulację innego

Aby rozpocząć mój wysiłek przebudzenia, musiałem wprowadzić program do PDP-8. W PDP-8 osiąga się to poprzez trzyetapowy proces:

  1. Używając przełączników na przednim panelu, użytkownik „wpisuje” krótki program do pamięci rdzenia magnetycznego. Ten program nazywa się RIM Loader i jego celem jest ładowanie innego programu z taśmy papierowej w formacie Read-in-Mode lub RIM.

  2. RIM Loader ładuje papierową taśmę w formacie RIM. Ta taśma zawiera program o nazwie BIN Loader, który może ładować programy z taśmy papierowej w formacie binarnym (BIN).

  3. Na koniec uruchamiasz BIN Loader, aby załadować program, którego naprawdę potrzebujesz, który znajduje się na taśmie papierowej w formacie BIN. Uff!

Po wykonaniu tych trzech kroków program, który chcesz uruchomić, jest przechowywany w pamięci podstawowej. Wszystko, co użytkownik musi wtedy zrobić, to ustawić adres początkowy i nakazać maszynie „start”.

Podczas moich wysiłków, aby ożywić maszynę, krok 1 nie był problemem, ale krok 2 obejmował użycie czytnika taśmy papierowej w teletype - a ja nie miałem teletype. Oczywiście, ja nie mam komputera stacjonarnego, więc logicznym krokiem było symulować czytnik taśmy papierowej na moim pulpicie.

Z logicznego i programistycznego punktu widzenia symulacja czytnika taśmy papierowej jest banalna. Po prostu czyta się plik, który zawiera dane z „taśmy”, wysyła go do portu szeregowego z prędkością 110 bodów (tak, tylko 10 znaków na sekundę), aż do wyczerpania pliku. Mógłbym napisać program w C na moim systemie Solaris lub we FreeBSD w około 10 minut, który mógłby to zrobić - ale pamiętaj, że pracowałem na systemie Windows 95, a nie na systemie Unix.

Od złego do brzydkiego iz powrotem

Wiedziałem, że mogę łatwo napisać ten program w C, więc to był mój wybrany język. Zły wybór. Wywołałem moją kopię Visual C ++ 5.0 i uruchomiłem prosty program o nazwie sendtape.c, który wywoływał open()port komunikacyjny. Próbowałem ustawić go w trybie RAW (tryb w Uniksie, w którym system operacyjny nie próbuje interpretować niczego na porcie szeregowym jako dane wejściowe użytkownika), a następnie próbowałem to skompilować. Ups, brak ioctl()funkcji lub ttyfunkcji - nada, zip, zilch!

Nie ma problemu, pomyślałem sobie: „Mam całą bibliotekę sieciową Microsoft Software Developer's Network na CD z moim kompilatorem C; szybko wyszukam słowa kluczowe 'port COM'.”

Wyszukano wiele odniesień do Microsoft Component Object Model (zwanego także COM), a także odniesień do MSComm. MSComm to klasa C ++ dostarczana przez firmę Microsoft do komunikacji z portami szeregowymi. Spojrzałem na przykłady i byłem zbulwersowany, ile kodu potrzeba, aby zrobić tak prostą rzecz, jak zapisanie bajtów do portu szeregowego z prędkością 110 bodów. Chciałem tylko otworzyć cholerny port szeregowy, ustawić jego szybkość transmisji i wrzucić kilka bajtów w dół - a nie tworzyć nowej klasy aplikacji wspomagających komunikację szeregową!

Siedząc przed moim monitorem, znajdował się receptor Blue Dot dla mojego pierścienia Java i pomyślałem: „Aha! Ludzie z Dallas Semiconductor wymyślili, jak rozmawiać z portem szeregowym w komputerze PC. Zobaczmy, co robią. " Po przejrzeniu firmowego kodu źródłowego Win32 było jasne, że rozmowa z portami szeregowymi nie będzie prostym zadaniem.

Java na ratunek

W tym momencie mojego weekendu myślałem, że może przeciągnę jedną z moich maszyn Unix do laboratorium, aby zakodować na nim program , zamiast używać tego, co już miałem. Wtedy przypomniałem sobie moje doświadczenia z pierścieniem Java i pakietem java.comm firmy Sun. Zamiast tego postanowiłem podążać tą drogą.

Co zapewnia java.comm?

Java Communications API - lub java.comm - zapewnia niezależną od platformy metodę uzyskiwania dostępu do portów szeregowych i równoległych w języku Java. Podobnie jak w przypadku innych interfejsów API języka Java, takich jak JFC, JDBC i Java 3D, programiści zmuszeni są do wyodrębnienia koncepcji platformy dotyczącej tego, „czym jest port szeregowy”, od modelu programowania. W przypadku projektu javax.comm elementy takie jak nazwy urządzeń, które różnią się w zależności od platformy, nigdy nie są używane bezpośrednio. Trzy interfejsy API zapewniają niezależny od platformy dostęp do portów szeregowych i równoległych. Te interfejsy zapewniają wywołania metod w celu wyświetlenia listy dostępnych portów komunikacyjnych, kontroli współużytkowanego i wyłącznego dostępu do portów oraz sterowania określonymi funkcjami portów, takimi jak szybkość transmisji, generowanie parzystości i kontrola przepływu.

Kiedy zobaczyłem w dokumentacji przykład SimpleWrite.java i porównałem jego 40 linii kodu z 150 do 200 liniami kodu, na które patrzyłem pisząc w C, wiedziałem, że rozwiązanie jest pod ręką.

Abstrakcją wysokiego poziomu dla tego pakietu jest klasa javax.comm.CommPort. CommPortKlasa definiuje rodzaje rzeczy, które zazwyczaj zrobić z portu, który obejmuje coraz InputStreami OutputStreamobiekty, które są kanałami I / O dla portu. PlikCommPortzawiera również metody kontrolowania rozmiarów buforów i dostosowywania sposobu obsługi danych wejściowych. Ponieważ wiedziałem, że te klasy obsługują protokół Dallas Semiconductor One-Wire (protokół obejmujący dynamiczne zmiany szybkości transmisji i pełną przejrzystość przesyłanych bajtów), wiedziałem, że interfejs API javax.comm musi być elastyczny. Miłym zaskoczeniem było to, jak ciasne były zajęcia: miały wystarczającą elastyczność, aby wykonać zadanie i nic więcej. Niewiele było niepotrzebnych nadużyć w postaci „wygodnych metod” lub obsługi protokołów modemowych, takich jak Kermit czy 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) { ... } 

Po inicjalizacji otrzymujesz kod metody newStatepokazanej powyżej, która śledzi zawartość taśmy papierowej (niezależnie od tego, czy jest to informacja adresowa, czy informacja programowa). Powyższa metoda również drukuje komunikat dla każdej lokalizacji pamięci na PDP-8, która jest inicjalizowana.

Następnie masz mainmetodę, która jest pokazana poniżej; otwiera plik i wczytuje go. Następnie kod otwiera port szeregowy i ustawia jego parametry komunikacyjne.