18-649 Simulator Development Overview
Last modified May 9, 2010
Thispage give an overview of the network interfaces in the simulator. You should read this document carefully. Make sure you havedownloaded the newest release from the codebasedownloadpage.
If you are not familiar with object oriented code, or the Java objectmodel, you may want to read about it first:
Simulator Javadoc DocumentationTo build the javadoc API documentation for the simulator, run make inthe root of thesimulator directory (not the code/ subfolder). This will buildthe javadoc API in the doc/ subdirectory. We have madesignificant effort to improve the javadoc. If you find anyerrors, please send mail to the course staff with an explanation of theerror. Be sure to let us know what file or module the error islocated in.
Coding and commenting styleWhen you write code for the simulator, your goal should be to make thecode easy to read and understand. At a minimum, you must includecomments for traceability. However, additional commentsdescribing your code will help both you and the TAs as the projectprogresses. The list below contains some guidelines for writingcode. We recognize that it is not always possible (or reasonable)to follow every guideline in every circumstance, so use your bestjudgment. As with the rest of the project, we are expecting agood-faith effort. If you would like to discuss details with theTAs, please come to office hours.
- For traceability comments, be sure to follow the requirementsoutlined in the assignments. Be sure that the comment isunambiguous (i.e. that it's clear which line of code it refers to).
- For other comments, we recommend javadoc style comments. Many IDEs will generate the comment structures automatically. You can read more about javadoc here.
- Try to keep your code at 80 characters or less per line. Again, we know this is not always feasible.
- Avoid "clever" code. It's okay to do things in severalsimple lines of code instead of one long, complicated line. It'salso easier to document.
- Use descriptive names for variables. Some single-charactervariable names (such as for loop variables) are okay, although youshould be careful with nested loops.
- Use descriptive names for methods.
- Do NOT use "magic numbers" in your code. If you use aconstant literal, the best practice is to defineaconstant (using the "static final" keywords) with a descriptivename, and use that constant throughout your code. If you want tocreate a set of related constants, such as state machine states, youshould use Javaenums.
Modifications to the simulator frameworkWe have provided access to the source files for the entire simulationframework, with the exception of the reference implementation controlclasses (for which we only provide the compiled class files). Feel free to browse the code as much as you like.
You will be required to turn in the contents of thesimulator.elevatorcontrol package and onlythis package. You are free to modify the rest of thesimulator framework as much as you like for debugging purposes, but youshould ensure that your code compilesand runs correctly with the simulator framework that we have released.
codethat will not compile or for code thatcrashes or throws an exception when run.
Controller Implementation The simulator.framework.Controllerclass provides basic functionality such as network and physicalinterfaces and logging capabilities. You should create a subclassof Controller for each of your controller objects. Refer back tothe Testlight example from Project 1for a complete example.
The constructor for Controller includes a "name" stringparameter. Because of the way controllers are handled by the simulator, eachinstance of a controller object should have a unique name. Forreplicated controllers, the recommended format is something likeHallButtonControl[FRONT][UP]. You will use this name to refertothe controller if you use state assertions during unit and integrationtests. The ReplicationComputerclass provides helper methods for generating a formatted string fromthe replication parameters.
Constructor Signatures for Controllers
For unit and integration tests, controllers are instantiated throughreflection based on the parameters given in the .cf configurationfile. For acceptance tests, controllers are instantiated throughreflection by the simulator.framework.ControllerBuilder.makeAll()method. Your controllers must follow the naming conventions andparameter lists given in this file. If your controller does notprovide a signature that matches those in makeAll(), a runtime errorwill occur when you try to run an acceptance test.
Logging / Debug Messages
If you want to generate debugging output from your controllers, youmust use the logging infrastructure provided by the simulator. Thecontroller must not print anything to stdout if the verbose flag in theconstructor is set to false. For more details on logging(including how to call the log() method correctly), see the Logging framework and simulatorperformance considerations section of the Debugging tips page.
Reporting Controller State
If you intend to use state assertions in testing, you can use thesetState(String key, String value) method to update the controllersstate in a way that can be checked by the testing framework. Inunit and integration, state values are requested by key, and the valuesare compared using case-sensitive string comparison. Even if youdo nothing else, it is a good idea to at least report the current statemachine state. A string key, Controller.STATE_KEYhas been provided for this purpose.
Network and Physical InterfacesThe TestLightclass demonstrates the methods for connecting your controller to thephysical simulation and the simulated CAN network. You shouldconsult the comments in that file and look at the examples. Butbefore we talk about interfaces, a word on Readable and Writeablesemantics.
Readable and Writeable Payload Objects
Payload objects are used internally to represent physical state andnetwork messages. You do not handle the payload objectsdirectly. Rather, you handle wrapper classes that are descendedfrom simulator.payloads.ReadablePayload andsimulator.payloads.WriteablePayload. These wrapper objectsenforce the readable/writeable semantics of the networkinterfaces. You cannot send a WriteablePayload, and you cannotreceive a ReadablePayload.
Readable and Writeable wrappers are instantiated through static factorymethods. PhysicalPayload objects provide getReadablePayload() andgetWriteablePayload() methods. For network payloads, useCanMailbox.getReadableCanMailbox() and getWriteableCanMailbox(). Network translators (which are discussed in more detail below), shouldaccept both ReadableCanMailbox and WriteableCanMailbox objects.
Physical InterfacesTo interact with the physicalInterface, you use Payload objects whichare descendants of PhysicalPayload. There are different payloadobjects for different types of physical state (e.g car callbuttons, car call lights, motors, door commands, etc.). For moreinformation, consult the javadoc on simulator.framework.PhysicalNetwork.
Network InterfacesTo send and receive messages on the CAN network, you must create andregister a CanMailbox object for each message. Messages areprioritized on the network using 29-bit CAN message IDs, sentMSB-first. Because the 0 bit is dominant, lower numbered CAN IDshave higher priority. CAN ID constants are defined insimulator.elevatorcontrol.MessageDictionary
The CanMailbox object contains a raw, bit-level representation of theCAN message payload. In order to provide a get-set interface fornetwork messages, you need a translator class that understands themessage formatting. The translator's get methods should read thebit level message and return a result of the desired type (boolean,int, double, etc). The translator's set methods should take avalue of of a type like boolean, int, double, etc. format itappropriately, and copy it into the bit level message in theCanMailbox.
We have provided two example translators, BooleanCanPayloadTranslatorand IntegerCanPayloadTranslator. These are located in simulator.payloads.translators.
formost single-value network messages. For messages that havemultiple values, we have provided two exampletranslators. For the mDrive message, use DriveCommandCanPayloadTranslator.
othertranslators, you should write your own. Forexample,you will need a translator for the mDriveSpeed message. Anytranslators you write shall belocated in simulator.elevatorcontrol.
In the early part of the course,performance (including network bandwidth) is not important, but whenperformance becomes an issue later on, you may find that you need moreefficient translators to make smaller payloads. Thereare several utility methods implemented in the base class CanPayloadTranslator that may helpyou pack message values into a binary format. Note that the getmethods in the DesiredFloorCanPayloadTranslator must remain the samefor your code to be compiler compatible with the simulation framework.
Any translator you implement will provide both get() and setmethods. These methods will have to call protected methodsgetMessage() and setMessage() (provided by the CanPayloadTranslatorclass) which handle the transfer of BitSet objects to the mailbox whichis passed to the CanPayloadTranslator constructor. If you pass aReadableCanMailbox object to the constructor and then callsetMessage(), you will get a runtime error as this violates thesemantics of the ReadableCanMailbox object. See the javadoc inCanPayloadTranslator and the example translators for more details.
The simulator.elevatormodulespackagecontains translator objects (all ending in CanPayloadTranslator) forall the environmental and smart sensor objects. You mayinstantiate these translator objects in your controllers to decode themessages sent by the smart sensor objects. Since you cannotmodify them, these translators are designed to make efficient use ofnetwork bandwidth, and you can expect to use them throughout theproject for decoding smart sensor network messages.
CAN Message IDsWe have developed a standard format for CAN message IDs similar tothose used by DeviceNet. The message ID format given in thistable:
|Message ID Breakdown ||Size (bits) |
|Criticality (always 01) ||2 |
|Message ID ||11 |
|Sender Node ID ||8 |
|Replication ID ||8 |
|Total # of bits ||29 |
The first dominant bit ensures that the control system messages willalways have priority (for example, over diagnostic messages). Thefirst bit meets the requirement that there be one recessive bit thefirst 7 bits of the ID.
The messages are prioritized first on message ID, then on Node ID, thenon Replication ID. The table below gives the Message IDbreakdowns used by the naive implementation we have provided.
Note: These tables are providedto give you a basic idea of the network messages present in thesystem. The values listed here may be slightly different fromthose in the code, and you may end up changing them throughout thesemester anyway. You should always refer to the codebase foractual CAN ID values.
|Sender Node Name |
|MessageName ||Deadline(ms) ||MessageID ||SenderNodeType ||ReplicationID ||BaseCanID |
|Door reversal Sensor ||DoorReversal ||10 ||1400 ||100 ||hall,side ||0x0D786400 |
|Safety Sensor ||EmergencyBrake ||50 ||1000 ||20 ||none ||0x0BE81400 |
|At Floor Sensor ||AtFloor ||50 ||1100 ||40 ||floor,hall ||0x0C4C2800 |
|Car Level Position Sensor ||CarLevelPosition ||50 ||1200 ||60 ||none ||0x0CB03C00 |
|Door Closed Sensor ||DoorClosed ||100 ||1300 ||80 ||hall,side ||0x0D145000 |
|Weight Sensor ||CarWeight ||200 ||1500 ||120 ||none ||0x0DDC7800 |
|Weight Sensor ||CarWeightAlarm ||200 ||1600 ||140 || ||0x0E408C00 |
|Door Opened Sensor ||DoorOpened ||200 ||1700 ||160 ||hall,side ||0x0EA4A000 |
|Hoistway Limit Sensor ||HoistwayLimit ||200 ||1800 ||180 ||direction ||0x0F08B400 |
|Sender Node Name |
|MessageName ||Deadline(ms) ||MessageID ||SenderNodeType ||ReplicationID ||BaseCanID |
|Drive Control ||DriveSpeed ||10 ||1801 ||181 ||none ||0x0F09B500 |
|Drive Control ||DriveCommand ||10 ||1802 ||181 ||none ||0x0F0AB500 |
|Dispatcher ||DesiredDwell ||50 ||1803 ||182 ||hallway ||0x0F0BB600 |
|Dispatcher ||DesiredFloor ||50 ||1804 ||182 ||none ||0x0F0CB600 |
|Door Control ||DoorMotorCommand ||10 ||1805 ||183 ||hall,side ||0x0F0DB700 |
|Hall Button ||HallCall ||100 ||1806 ||184 ||floor,hall,side ||0x0F0EB800 |
|Hall Button ||HallLight ||100 ||1807 ||184 ||floor,hall,side ||0x0F0FB800 |
|Car Button ||CarCall ||100 ||1808 ||185 ||floor,hall ||0x0F10B900 |
|Car Button ||CarLight ||100 ||1809 ||185 ||floor,hall ||0x0F11B900 |
The constant definitions for the CAN ID's are defined in simulator.elevatorcontrol.MessageDictionary(which you may edit).
We have provided a class called ReplicationComputerthatprovides uniform methods for computing the various replicationIDs. You are not required to
translators(although we recommend that you do), butyou will need touse it (or emulate it) to interfacewith the translators provided for environment objects and smart sensors.
The commandline flag -pd will cause the simulator to print a complete of #DEFINEdirectives for all network message IDs and controller periods. Example output looks like:
;The following lines are automatically generated and designed to be;compatible with the .mf #DEFINE directive.;controller periods#DEFINE HALL_BUTTON_CONTROL_PERIOD 100.0ms;Controller CAN Ids;NOTE: These IDs assume you use the ReplicationComputer offsets and;MessageDictionary base values#DEFINE DRIVE_SPEED_CAN_ID 0xF09B500#DEFINE DOOR_MOTOR_COMMAND_[FRONT][LEFT]_CAN_ID 0xF0EB800#DEFINE DOOR_MOTOR_COMMAND_[FRONT][RIGHT]_CAN_ID 0xF0EB801#DEFINE DOOR_MOTOR_COMMAND_[BACK][LEFT]_CAN_ID 0xF0EB802#DEFINE DOOR_MOTOR_COMMAND_[BACK][RIGHT]_CAN_ID 0xF0EB803
It is recommended that you save this output to a file and use the #INCLUDE directive to include these definitions in your test files. Then use the defined macros in your unit tests. Thismakes it very easy to update your CAN message ID values (by updating the include file) wheneveryou change the values in your codebase.
*Please submit allproject-related questions to-- thanks!
Back to the codebase