previous chapter table of contents next chapter

Higher-Level Mechanisms: Not Quite C

"Not Quite C" (nqc) is a language and a compiler from David Baum, designed for the RCX. It defines a language with C-like syntax that defines tasks that can be executed concurrently. The RCX API also defines a number of constants, functions, and macros targeted specifically to the RCX. These include constants such as OUT_A (for output A) and functions such as OnFwd to turn a motor on forwards.

The following is a trivial nqc program to turn motor A on for 1 second (units are 1/100th of a second):

 task main() {         OnFwd(OUT_A);         Wait(100);         Off(OUT_A); } 

Writing programs using a higher-level language such as this is clearly preferable to writing in Assembler!

nqc is not the only higher-level language for programming the RCX. There are links to many others on the alternative MINDSTORMS site ( http://www.crynwr.com/LEGO- robotics / ). It is one of the earliest and more popular ones, though, and it is a typical example of a standalone, non-GUI program written in a language other than Java that can still be used as a Jini service.

The nqc compiler is written in C++ and needs to be compiled for each platform that it will run on. Precompiled versions are available for a number of systems, such as Windows and Linux. Once compiled, it is tied to a particular computer (at least, to computers with a particular OS and shared library configuration). It is software, not hardware like the MINDSTORMS robots, but it is nevertheless not mobile. It cannot be moved around like Java code can. However, it can be turned into a Jini service in exactly the same way as MINDSTORMS, by wrapping it in a Java class that can be exported as a Jini service. This also fits the RMI proxy model, with the client side using a thin proxy that makes calls to a service that invokes the nqc compiler.

The class diagram follows other RMI proxy diagrams and is shown in Figure 17-3.

click to expand
Figure 17-3: Class diagram for nqc with RMI proxy

The NotQuiteC and RemoteNotQuiteC interfaces are defined by

 /**  * NotQuiteC.java  */ package rcx.jini; import java.rmi.RemoteException; import java.io.Serializable; public interface NotQuiteC extends Serializable {     public byte[] compile(String program)         throws RemoteException, CompileException; } // NotQuiteC 

and by

 /**  * RemoteNotQuiteC.java  */ package rcx.jini; import java.rmi.Remote; public interface RemoteNotQuiteC extends NotQuiteC, Remote { } // RemoteNotQuiteC 

The compile exception is thrown when things go wrong:

 /**  * CompileException.java  */ package rcx.jini; public class CompileException extends Exception {     protected String error;     public CompileException(String err) {         error = err;     }     public String toString() {         return error;     } } // CompileException 

An implementation of the RemoteNotQuiteC interface needs to encapsulate a traditional application running in an environment of just reading and writing files. GUI applications, or those nuisance Unix ones that insist on using an interactive terminal (such as telnet ), will need more complex encapsulation methods . The nqc type of application will read from standard input or from a file, often depending on command line flags. Similarly, it will write to a file or to standard output, again depending on command line flags. Applications either succeed or fail in their task; this should be indicated by what is known as an exit code, which by convention is 0 for success and some other integer value for failure. If a failure occurs, an application will usually write diagnostic output to the standard error channel.

The current version of nqc (version 2.0.2) is badly behaved for reading from standard input (it crashes) and writing to standard output (no way of doing this). So we can't create a Process to run nqc and feed into its input and output. Instead, we need to create temporary files and write to and read from these files so that the Jini wrapper can communicate with nqc . These files also need to be cleaned up on termination, whether the normal or exception routes are followed. On the other hand, if errors occur, they will be reported on the error channel of the process, and this needs to be captured in some way ”in this example, we will do it via an exception constructor.

The hard part in this example is plowing your way through the Java I/O maze, and deciding exactly how to hook up I/O streams and/or files to the external process. The following code uses temporary files for ordinary I/O with the process (the current version I have of nqc has a bug with pipelines) and the standard error stream for compile errors.

 /**  * NotQuiteCImpl.java  */ package rcx.jini; import java.rmi.server.UnicastRemoteObject; import java.rmi.RemoteException; import java.io.*; public class NotQuiteCImpl extends UnicastRemoteObject     implements RemoteNotQuiteC {     protected final int SIZE = 1<<15; // 32k - the size of the RCX memory       public NotQuiteCImpl() throws RemoteException {     }       public byte[] compile(String program)         throws CompileException {         // This is the input file we read from - it is the output from nqc         File inFile = null;         // This is the output file that we write to - it is the input to nqc         File outFile = null;         byte[] buff = new byte[SIZE];         try {             outFile = File.createTempFile("jini", ".nqc");             inFile = File.createTempFile("jini", ".rcx");             OutputStreamWriter out = new OutputStreamWriter(                                          new FileOutputStream(                                              outFile));             out.write(program);             out.close();             Process p = Runtime.getRuntime().exec("nqc -O" +                                                   inFile.getAbsolutePath() +                                                   " " + outFile.getAbsolutePath());             int status = p.waitFor();             if (status != 0) {                 BufferedReader err = new BufferedReader(                                          new InputStreamReader(p.                                               getErrorStream()));                 StringBuffer errBuff = new StringBuffer();                 String line;                 while ((line = err.readLine()) != null) {                     errBuff.append(line + '\n');                 }                 throw new CompileException(errBuff.toString());             }             DataInputStream compiled = new DataInputStream(new                                                 FileInputStream(outFile));             int nread = compiled.read(buff);             byte[] result = new byte[nread];             System.arraycopy(buff, 0, result, 0, nread);             return result;         } catch(IOException e) {             throw new CompileException(e.toString());         } catch(InterruptedException e) {             throw new CompileException(e.toString());         } finally {             // clean up files even if exceptions thrown             if (inFile != null) {                 inFile.delete();             }             if (outFile != null) {                 outFile.delete();             }         }     }       public static void main(String[] argv) {         String program = "task main() {\n" +                          "     OnFwd(OUT_A);\n" +                          "     Wait(100);\n" +                          "     Off(OUT_A);\n" +                          "}";         NotQuiteCImpl compiler = null;         try {             compiler = new NotQuiteCImpl();             byte[] bytes = compiler.compile(program);         } catch(Exception e) {             e.printStackTrace();         }     } } // NotQuiteCImpl 

This section does not give server and client implementations ”the server is the same as servers delivering other RMI services. A client will make a call on this service, specifying the program to be compiled. It can then write the byte stream to the RCX using the classes given earlier.


A Programmer[ap]s Guide to Jini Technology
A Programmer[ap]s Guide to Jini Technology
ISBN: 1893115801
Year: 2000
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net