1. Trang chủ
  2. » Công Nghệ Thông Tin

programming LEGO MINDSTORMS phần 9 doc

47 149 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 47
Dung lượng 473,57 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Interfacing with the RCX Java APIThere are multiple mechanisms by which the Jini proxy could interface with theRCX—in our example, we shall use the RCX Java API.We will create a servicep

Trang 1

method calls; it knows how to communicate back to the original serviceobject, which may well be running on another machine, and that orig-inal object does all of the processing.

This flow is shown in Figure 9.8

Now that would make sense; if the service object was expecting to nicate with an RCX via a serial port, then it still could because the object would

commu-be invoked in the original JVM that created it And if that object held references

to other objects, these references would still be valid for the same reasons

The only problem is that we have to write an absolutely enormous amount

of code to achieve this complex interaction between objects across the network,right? Wrong! Fortunately, Jini’s use of RMI means that this can be achieved withessentially no coding whatsoever! There are several ways to implement this, butthe one we will use in the examples is by extending a class known as

java.rmi.server.UnicastRemoteObject Basically, the steps required are as follows:

1 Make sure that the class of the object that is to be registered as a service

extends UnicastRemoteObject (and also ensure that it still implements

Serializable).

2 When you compile your service’s class (for example, MyCoolService.class),

run the RMI compiler against it to produce a stub class

1 Service object created

2 Stub object registered with lookup service

Stub object

Stub object (serialized)

3 Stub object reconstructed in client JVM

5 Stub object communicates back to service object

4 Methods called against stub object locally

Trang 2

HTTP server.

That’s it! That’s all you have to do; the RMI compiler will generate all of thecode required for the stub so that the stub and the original object know how tocommunicate across JVMs and even across a network.You still specify the originalclass name in your code; Jini knows to instantiate the stub and register that with

reggie instead of your local object.

Generating the stub class using the RMI compiler is no trouble at all.The

compiler executable is rmic, and the argument is the class file that the stub is

being produced for.The output is the class name followed by an underscore,

fol-lowed by Stub, folfol-lowed by the class extension So, if the original class was called

MyCoolServiceClass.class, the generated stub would automatically be called MyCoolServiceClass_Stub.class.

The most commonly used options for rmic in this context are:

actually generates a java source file on its way to generating the classfile, and this option stops the source file from being deleted; handy ifyou want to understand a little more about what’s going on behind thescenes

files, with “_stub” and “_skel” suffixes.The skeleton files are no longerrequired, so this option suppresses their creation

So, running the command:

rmic -v1.2 mindstorm.MindstormProxy

would create a single stub file called MindstormProxy_Stub.class in the mindstorm

directory

Proxies and Service Architectures

Having now covered some of the basics of registering and using a Jini service, wealso need to consider how to decide on the correct architecture for a specific sit-uation, and most appropriately in this context, how to select the right architec-ture for a situation involving LEGO MINDSTORMS and Jini

www.syngress.com

Trang 3

Selecting the Right Architecture

As with most things, there are good and bad ways to approach the architecture of

a Jini federation, but often no “right” or “wrong” way.The following sections willserve as a guide to how your particular solutions may be designed

Using Proxies

As we have already covered, Jini is often considered appropriate as a technologyfor allowing the services of a wide range of devices to be readily discovered andutilized in a distributed environment If you think about it, it makes perfect sensethat a large device such as a household air conditioning unit may contain enoughcomputing power to run Java, (including RMI), a TCP/IP stack, and Jini, so that

it can register its own services.What then if the device contained a much lesspowerful embedded system (perhaps an 8- or 16-bit microcontroller), with only aprimitive communications facility (perhaps RS-232) and limited RAM (measured

in Kbytes rather than Mbytes or Gbytes)? That’s when you would need a proxy

A proxy in the Jini context is a service that runs on a more powerful puting platform (such as a PC or perhaps a home control system) and whichstands between the client and something like an embedded device.The thingabout the proxy is that to the client, the proxy appears to actually be the serviceitself However, the reality is that the proxy is a layer of abstraction; it handles all

com-of the communication with the device that is physically performing the service(over RS-232 in our previous example), and it handles such tasks as service regis-tration and lease renewal Figure 9.9 shows the basic architecture of a systemwhere a single object acts as a proxy, and the physical service is carried out by aLEGO MINDSTORMS device

A RCX Jini Proxy Service

In this section, we will delve into building an example Jini service and client thatwill allow multiple LEGO MINDSTORMS to interact with each other.Thiswill follow a design similar to that shown in Figure 9.9, where a proxy architec-ture is used

Why a Proxy?

If you were thinking that our discussions about embedded devices with limitedcomputing powers and primitive communications abilities could apply to the

www.syngress.com

Trang 4

implement a form of Java Virtual Machine within the RCX, it really doesn’tcome close to having the computing power required to run Jini.

That said, the Jini proxy architecture makes perfect sense in the case of theRCX Since the only way it has of connecting to a network is via an infraredlink connected to some form of more powerful computer, that computer is theideal candidate to act as proxy So, a Jini federation of MINDSTORMS is a veryreal possibility; one or many machines connected to infrared towers register proxyservices.To the client or clients, these proxy services appear as the services them-

selves—the client calls a method DoWhateverRobotsDo(), and the robot responds

by doing it.What has really taken place though is that the proxy has interpretedthe method call and executed it by sending and receiving a set of RCX opcodesover the infrared connection

1 Proxy object created 2 Stub object registered with lookup service

Stub object

Stub object (serialized)

3 Stub object reconstructed in client JVM

5 Stub object communicates back to proxy object

4 Methods called against stub object locally

Embedded device (e.g., LEGO MINDSTORMS) Proprietary protocol (e.g., RCX opcodes over infrared link)

Trang 5

Interfacing with the RCX Java API

There are multiple mechanisms by which the Jini proxy could interface with theRCX—in our example, we shall use the RCX Java API.We will create a serviceprogram that will instantiate a proxy object (and its corresponding stub), and reg-ister this with the lookup service, and we will run more than one instance of thisprogram (using more than one RCX, each connected to its own machine).When

a client invokes methods against these proxy objects, the proxies will actuallymake use of the RCX Java API to send and receive the RCX opcodes to com-mand the RCX to behave a certain way and to receive feedback from the RCX

Using the RCX Jini Service:

Example Server and Client

Now we will cover a more complex example scenario that involves one or moreLEGO MINDSTORMS In this example, the MINDSTORMS will effectivelycommunicate with each other (via a central client program) to perform a kind ofsynchronized dance

The desired outcome is this: for a pair of MINDSTORMS, the first will form a certain “dance step” (actually one of eight predefined movements); thesecond will imitate the exact movement, wait several seconds, and then performits own randomly chosen movement.This will then be imitated by the first, and

per-so on until the program is terminated or the batteries run out!

If there is only one MINDSTORMS robot in the Jini Federation, it will justhave to be content with imitating its own movements Likewise, if there are morethan two robots, they will form a “daisy chain,” with each one copying the moves

of the previous one in the chain before deciding on its own original step

Figure 9.10 shows the basic scenario for this example, assuming two robots

As you can see in this diagram, the PCs which are connected to the infraredtowers will act as proxies for RCX units themselves Also, because some of thecomponents of this system are likely to reside on different machines (or at least indifferent JVMs in the case where one machine may control multiple RCXs viamultiple serial ports), the proxy objects must be executed in the JVM of the ser-vice that registers them.Therefore, it will actually be stub objects that will be reg-

istered with reggie and these stub objects will communicate via RMI with the

proxies

www.syngress.com

Trang 6

A RCX Jini Server

The first step is to create an interface that can be known to both client and vice Again, as in the simple Jini example shown previously, we will create this

ser-interface in a package called shared (see Figure 9.11).The ser-interface defines some

methods to allow certain attributes to be retrieved, as well as the method

imitateThis(), which is a command to a MINDSTORMS robot to imitate a

cer-tain dance step

Infrared

PC and Infrared Tower

MINDSTORMS robot 1

Locator Service (reggie)

Client Register service proxy Register service proxy

Lookup both service proxies

Send commands

Receive messages

Send commands Receive

messages

Continued

Trang 7

// Define the two possible message types:

// EVENT_RCX_MSG is a message from a remote RCX.

// EVENT_DANCE_STEP is a notification that a Mindstorm // has completed a dance step.

public final int EVENT_RCX_MSG = 1;

public final int EVENT_DANCE_STEP = 2;

// Method to retrieve the ID of a Mindstorm.

public String getRcxId() throws RemoteException;

// Tell a Mindstorm to imitate a dance step

public void imitateThis(int danceStep) throws RemoteException;

// Retrieve the most recent RCX Message received Should // be called in response to receiving an EVENT_RCX_MSG.

public byte[] GetLastRCXMessage() throws RemoteException;

// Retrieve the most recent dance step performed // by an RCX Should be called in response to // receiving an EVENT_DANCE_STEP.

public int GetLastStepPerformed() throws RemoteException;

// Allow the client to register a listener // to receive events from the service

public void RegisterRemoteListener(RemoteEventListener listener)

www.syngress.com

Continued

Trang 8

throws RemoteException;

}

The proxy class will be called mindstorm.MindstormProxy It will make extensive

use of the RCX Java classes, so make sure you are familiar with the RCX Javamechanisms before you embark on this one.This proxy will also make use of mul-tithreading; it will spawn a new thread of execution and return, before proceedingthrough the cycle of imitating a dance step, pausing for 10 seconds, and thendeciding on another dance step to perform.This is so that the client program isnot held up for the entire 10-plus seconds, and it is a very common technique touse for this kind of scenario in Java In this case, the class that will process the

additional thread (ImitationThread) has been implemented as an inner class It is a

class declared within another class, which has implicit access to the members ofthe enclosing class and is not visible outside of that class Just remember that when

MindstormProxy.java is compiled, two class files will be produced—MindstormProxy class and MindstormProxy$ImitationThread.class—and that both these files must be

in the Java runtime’s CLASSPATH for the example to work

The other thing to note about this class is that instances of it will be required

to execute on the machine that they were created on (because they will be municating out a physical serial port to an RCX device), so we will be required

com-to register an instance of a proxy that will be capable of communicating backwith the equivalent object of this class from a remote client As already discussed,

we are very fortunate that RMI can provide this functionality with very littleeffort on our part As can be seen from the following code, the proxy class will

extend UnicastRemoteObject.The only other thing you must remember to do is

run the RMI compiler against the compiled class file to produce a stub class.Youcan invoke the compiler with a command such as:

rmic -v1.2 mindstorm.MindstormProxy

After running rmic, you should now have produced a file called

MindstormProxy_Stub.class in the /mindstorm subdirectory; this file is the

proxy code (see Figure 9.12).That certainly beats coding it by hand!

www.syngress.com

Trang 9

// Make sure to generate a stub class using the // RMI compiler since this class extends // UnicastRemoteObject.

protected static final byte FORWARDS = (byte)0x80;

protected static final byte BACKWARDS = (byte)0x00;

protected static final byte ON = (byte)0x80;

protected static final byte OFF = (byte)0x40;

protected static final byte MOTOR_A = (byte)0x01;

protected static final byte MOTOR_C = (byte)0x04;

protected static final byte MOTOR_DIR = (byte)0xe1;

protected static final byte MOTOR_ON_OFF = (byte)0x21;

// Store our unique ID so that the client

www.syngress.com

Continued

Trang 10

// can differentiate between robots.

protected String rcxId = null;

protected RCXPort rcxPort = null;

protected RemoteEventListener remoteListener = null;

// Store any message received from the RCX // as a raw byte array for retrieval by the client.

// Note that any subsequent message will overwrite // the existing one and if messages are retireved // in quick succession, data may be lost A more // robust implementation could be employed for a // production system

protected byte[] lastRCXEvent = null;

// Similarly for the most recent dance step performed

protected int lastDanceStep = 0;

protected long seqNo = 0;

public MindstormProxy() throws RemoteException { }

public String getRcxId() { return rcxId;

Trang 11

// Register as a listener with the RCX rcxPort.addRCXListener(this);

}

protected void executeMovement(int movementId) { // Execute one of our robot's 8

// spectacular dance steps.

System.out.println("Executing step: " + movementId);

switch(movementId) { case 0:

// Directly forward setMotorDir(FORWARDS, (byte)(MOTOR_A | MOTOR_C));

motorOn((byte)(MOTOR_A | MOTOR_C));

break;

case 1:

// Directly back setMotorDir(BACKWARDS, (byte)(MOTOR_A | MOTOR_C));

motorOn((byte)(MOTOR_A | MOTOR_C));

break;

case 2:

// Rotate right setMotorDir(FORWARDS, MOTOR_A);

Trang 12

break;

case 6:

// Reverse right setMotorDir(BACKWARDS, MOTOR_A);

motorOn(MOTOR_A);

break;

case 7:

// Reverse left setMotorDir(BACKWARDS, MOTOR_C);

motorOn(MOTOR_C);

break;

} // Each dance step is of a 0.3 second duration try {

Thread.sleep(300);

} catch(InterruptedException e) { //

} motorsOff();

}

protected void motorOn(byte motors) { // Turn one or both motors on byte[] msg = new byte[] {};

sendToRcx(

new byte[] { MOTOR_ON_OFF, (byte)(ON | motors)});

www.syngress.com

Continued

Trang 13

protected void motorsOff() { // Turn both motors off sendToRcx(

new byte[] { MOTOR_ON_OFF, (byte)(OFF | MOTOR_A | MOTOR_C)});

}

protected void sendToRcx(byte[] msg) {

System.out.println(

"Sending to port: " + byteArrayToString(msg));

if (!rcxPort.write(msg)) { System.err.println("Error writing to port");

} }

public void imitateThis(int danceStep) throws RemoteException {

// This should be the dance step that the other // robot has just performed We will attempt to // imitate it Handle this from a new thread.

new ImitationThread(danceStep, this).start();

www.syngress.com

Continued

Trang 14

public void receivedMessage(byte[] msg) { // Receive messages from the RCX

if (null != msg) { System.out.println(

"RCX message: " + byteArrayToString(msg));

if (null != remoteListener) { // Store the event contents // for retrieval by the listener lastRCXEvent = msg;

// Notify the listener of the event.

// Listener will have to call back to // obtain the details.

RemoteEvent evt = new RemoteEvent(

this, EVENT_RCX_MSG, seqNo++, null);

try { remoteListener.notify(evt);

} catch(UnknownEventException e) { System.err.println("Event exception");

} catch(java.rmi.RemoteException e) { System.err.println("Remote exception");

}

} } }

www.syngress.com

Continued

Trang 15

protected String byteArrayToString(byte[] msg) {

// Convert an array of bytes to a human-readable // string

StringBuffer sBuf = new StringBuffer();

for(int ix = 0; ix < msg.length; ix++) { int dm =

msg[ix] >= 0 ? (int)msg[ix] : ((int)msg[ix]) + 256;

}

public byte[] GetLastRCXMessage() { // This will be called by the client // to retrieve details of the last message // after we have sent a notification.

return lastRCXEvent;

}

public int GetLastStepPerformed() { // This will be called by the client // to retrieve details of the last step // after we have sent a notification.

return lastDanceStep;

www.syngress.com

Continued

Trang 16

public void RegisterRemoteListener(

RemoteEventListener listener) { // Called by the client to register its // listener object (which should also extend // UnicastRemoteObject so that we can notify it // remotely).

remoteListener = listener;

}

// Declare this thread class as an inner class class ImitationThread extends Thread {

protected int step;

protected MindstormProxy proxy = null;

public ImitationThread(

int danceStep, MindstormProxy proxy) {

// Store the reference to the // object that created us as well // as the dance step to execute.

this.proxy = proxy;

step = danceStep;

}

public void run() {

// Firstly execute the move in // imitation of the other robot.

Trang 17

} catch(InterruptedException e) { // Do nothing

}

// Now randomly pick a new movement // (between 0 and 7 inclusive).

int newMovement;

newMovement = (int)(8 * (Math.random()));

// Perform our randomly-selected movement executeMovement(newMovement);

// Now let the remote client // know that we just performed it.

new RemoteEvent(proxy, EVENT_DANCE_STEP, seqNo++, null);

try { remoteListener.notify(evt);

} catch(UnknownEventException e) { System.err.println("Event exception"); } catch(java.rmi.RemoteException e) { System.err.println(e.toString()); }

} } } }

www.syngress.com

Trang 18

server that will instantiate a proxy object, register its stub with any lookup vices on the network, and keep it alive and keep the lease renewed.

ser-There are a number of differences between this service and the one welooked at in the preceding simple example One of these differences is that we

will be using multicast instead of unicast lookups to find instances of reggie (the

lookup service).Whereas, in the checksum example, we specified the IP address

of a specific lookup service instance, here we will use multicast to find anynumber of lookup services that may exist on our network Once we find them,

we will register the proxy object (actually its stub) with each of them

Another difference between this and our simple example is that in this nario we are expecting multiple services on the network to register objectsimplementing the same common interface.This is because this scenario involvesmultiple MINDSTORMS robots, and we need some way to differentiatebetween them Because there is no distinguishing identifier inherent in the basicRCX firmware, we will assign an identifying name to each proxy object instead

sce-This will be passed in as a command line parameter to the service as a string; itcan be any name at all as long as you use a different name for any other robotsyou register

Along with the robot’s identifier, we will expect another command lineparameter: the port identifier.This is the actual port name to be used by RCXJava, such as “COM1” for a Windows machine

It is important that this service keep itself alive after it has done the job ofregistering the proxy with the lookup service.This is not just for the purpose oflease renewal, but also because we are really only registering a stub for remoteuse, and the proxy object itself will run in the JVM of this service and willreceive calls remotely from the client via RMI If this JVM goes away, so does theproxy, and the client will start to see all kinds of remote exceptions and noresponses from the robot

The code for the service is as shown in Figure 9.13

Trang 19

// This service will be responsible for creating // an instance of the proxy and registering it // with the Jini locator service.

protected MindstormProxy proxy = null;

// The LeaseRenewalManager will ensure that the // lease with the locator service is regularly // renewed.

protected LeaseRenewalManager leaseManager

Trang 20

// The name of the serial port.

static protected String portId = null;

static public void main(String args[]) {

// Since reggie is running with a security policy, // we will have to as well This assumes that // the policy file is located at

// c:\jini1_2\policy\policy.all // Adjust this for specific installations

keepAlive.wait();

} catch(java.lang.InterruptedException e) { // do nothing

www.syngress.com

Continued

Trang 21

} } }

public MindstormService() {

try { // Lookup using Multicast (i.e., look for any lookup // services on the network) We will receive a // callback to the discovered() method with a // list of all lookup services found.

LookupDiscovery lookupDiscovery = new LookupDiscovery(

LookupDiscovery.ALL_GROUPS);

lookupDiscovery.addDiscoveryListener(this);

} catch(IOException e) { System.err.println(e.toString());

System.exit(1);

} }

public void discarded(DiscoveryEvent event) { // Must be implemented from the

// DiscoveryListener interface.

}

public void discovered(DiscoveryEvent event) {

//try { // Must be implemented from the // DiscoveryListener interface.

// This method will be called with a list

www.syngress.com

Continued

Trang 22

// of all lookup services found // (actually their registrar proxies).

ServiceRegistrar[] regArray = event.getRegistrars();

try { proxy = new MindstormProxy();

} catch (RemoteException e) { System.err.println(e.toString());

SecurityManager sm = System.getSecurityManager();

for (int ix = 0; ix < regArray.length; ix++) { ServiceRegistrar svcRegistrar = regArray[ix];

www.syngress.com

Continued

Trang 23

// register ourselves as a service

ServiceItem serviceItem = new ServiceItem(

null, proxy, null);

// Request a 60 second lease duration This // means that the lease will require renewing // at least once every 10 seconds.

ServiceRegistration serviceRegistration = null;

try { serviceRegistration = svcRegistrar.register(

serviceItem, Lease.FOREVER);

} catch (RemoteException e) { // If the service registration // fails, we can still try with // any other lookup services // on the network.

leaseManager.renewUntil(

serviceRegistration.getLease(), Lease.FOREVER,

Lease.ANY, this);

Ngày đăng: 13/08/2014, 15:21

TỪ KHÓA LIÊN QUAN