Most recent edit on 2007-03-28 09:20:00 by KogAdmin [updated disclaimer]
Additions:
This page is obsolete as of 0.3 and 1.0 releases, and only covers the "Legacy" release
Deletions:
This is, or will be out of date! I'll update on release 0.2!
Edited on 2006-10-14 09:18:39 by KogAdmin [note]
Additions:
This is, or will be out of date! I'll update on release 0.2!
Edited on 2005-07-27 16:15:53 by KogAdmin [windows porting -> win32]
Additions:
~- New Windows porting page @ HOGWin32
NOTE: Working with named pipes on NT based systems proves complicated, Please see
HOGWin32 for details
Deletions:
~- New Windows porting page @ HOGWindowsPorting
NOTE: Working with named pipes on NT based systems proves complicated, Please see
HOGWindowsPorting for details
Edited on 2005-07-27 16:14:27 by KogAdmin [added windows porting link]
Additions:
~- Want to know more about the spark controller? Check out our new page HOGSparkController
NOTE: Working with named pipes on NT based systems proves complicated, Please see
HOGWindowsPorting for details
Deletions:
Want to know more about the spark controller? Check out our new page HOGSparkController
Edited on 2005-06-26 01:52:35 by KogAdmin
Additions:
Deletions:
Edited on 2005-06-26 01:52:01 by KogAdmin
Additions:
Deletions:
Edited on 2005-06-26 01:50:30 by KogAdmin
Additions:
Edited on 2005-06-25 23:37:54 by KogAdmin [added code samples and more detailed tracing of observable/observer route]
Additions:
Model_HOG_Component has the following bits of code in it:
import java.util.Observer;
import java.util.Observable;
public abstract class Model_HOG_Component implements Observer type sig for the class
and
public void update(Observable sender, Object m)
pre: sender is the messenger, m is the message
post: if the message's ID matches ours it's for us and we call parseMessage()
{
if(id.equals(((HOG_Message)m).getID())) {parseMessage((HOG_Messenger)sender,(HOG_Message)m);}
and we can see that the HOG_Messenger has:
public void getMessage(byte[] bytes, int numBytes)throws java.io.IOException{
HOG_Message ms;
HOG_Messenger m = HOG_Messenger.getMessenger();
HOG_NMEA n = HOG_NMEA.getNMEA();
Model_HOG_Formatter mf = HOG_MegaFormatter.getMega();
/*if(numBytes > 5){
if((char)bytes[bytes.length - 5]
'*'){mf = n;}
}
*/
mf = n;
ms = mf.parseBytes(bytes, numBytes);
if(ms != null) {
m.setChanged();
m.notifyObservers(ms);}Should we put out an error if null?
return;
%%
Where it creates a messenger, makes an NMEA packet, creates a Mega Squirt packet, sets changed and then tells the observers that the changed object is the HOG_Message base class. I think that Ryan commented out some code in the main source branch to get it to send all messages straight through to the rest of the system - I'm not sure because he wasn't particularly vocal at that point - there was very visible tension in the group.
Originally he wrote the stuff to go straight to the Mega Squirt component and then realized it didn't work. I'm assuming the MS device spits out some really funky syntax that isn't compatible with the rest of what we were trying to do. Anyway, the crux of the mattter is that the message object is then sent out to any observers (registered in HOG_Main class) that then, in turn, call update() - which should be invisible unless you need to override it. update() will tell your class if the message is for you, then call parseMessage
This code listens for messages that fit your object's ID. If you respond to more than one message, you'll need to override this code. The parent class requires that you override parseMessage, but I guess you don't really have to use it. I would keep it, and I'd make update check the ID and see if it's in a list of IDs and then call parseMessage() on it, doing all the logic for the message understanding. Please make sure to read the notes on the observer/observable pattern!
Deletions:
This code listens for messages that fit your object's ID. If you respond to more than one message, you'll need to override this code. The parent class requires that you override parseMessage, but I guess you don't really have to use it. I would keep it, and I'd make update check the ID and see if it's in a list of IDs and then call parseMessage() on it, doing all the logic for the message understanding.
Edited on 2005-06-25 23:19:16 by KogAdmin [observer/observable, update()]
Additions:
Observer/Observable pattern in Java
If you're familiar with design patterns, you'll note that this is pretty simple. If you're familiar with .NET you'll notice that this is akin to an event. Basically, there's an observable object (in this case, the messanger). You subscribe observers to the observable object, and it notifies any watchers that it's updated by sending around an object. The observer must implement the observable interface and must override the update() method, but we've done that in Model_HOG_Component. The parent class has code that checks the ID of the message, sees if it's the same as the current module and if it is calls parseMessage() (this is all listed below, as well as how to add multiple messages to listen for).
The main class calls:
Add each of the HOG_Components as Observers of theMessenger
for(int i = 0; i < components.length; i){
theMessenger.addObserver(components[i]);
}
on the component list, adding all of the components as observers of the Messanger object that's been instantiated a few lines above. So, by implementing parseMessage and providing an ID attribute you can easily hook into our system. All the components are grabbed out of hog.conf and are lines with the syntax similar to panel:classname where classname is something like model_HOG_Test - the main class instantiates classes by strings. This is why it's imperative that you don't write a bad configuration file, or HOG won't work!
- All models need the following methods parseMessage(HOG_Messenger sender, HOG_Message m), a constructor
If you want to update the "hook" - that is, the messages the component listens to, you'll need to override the default code in the parent class (Model_HOG_Component) which is as follows:
This code listens for messages that fit your object's ID. If you respond to more than one message, you'll need to override this code. The parent class requires that you override parseMessage, but I guess you don't really have to use it. I would keep it, and I'd make update check the ID and see if it's in a list of IDs and then call parseMessage() on it, doing all the logic for the message understanding.
Deletions:
~- All models need the following methods parseMessage(HOG_Messenger sender, HOG_Message m), a constructor, and update(HOG_Messenger sender, HOG_Message m) by default
Edited on 2005-06-24 15:33:13 by KogAdmin
Additions:
Currently the configuration is ONLY for panels. The original serial port server has a variable in heartofgold.c line 79 where it says openSerial(0) - that sets the TTyS number. In the Windows version you can pass an argument with the COM file in the form of COM1 or other such COM files as valid. If none is specified, it'll use COM1 by default. I didn't add any error checking on this - I figure if you want to mess around and specify COM0 or CM1 that's your issue.
Deletions:
Currently the configuration is ONLY for panels. The original serial port server has a variable in heartofgold.c line 79 where it says openSerial(0) - that sets the TTyS number. In the Windows version you can pass an argument with the COM file in the form of COM1 or other such COM files as valid. If none is specified, it'll use COM1 by default. I didn't add any error checking on this - I figure if you want to mess around and specify COM0 or CM1 that's your issue.
Edited on 2005-06-24 15:32:43 by KogAdmin [removed wikinames that didn't need to be there (html markup)]
Additions:
Currently the configuration is ONLY for panels. The original serial port server has a variable in heartofgold.c line 79 where it says openSerial(0) - that sets the TTyS number. In the Windows version you can pass an argument with the COM file in the form of COM1 or other such COM files as valid. If none is specified, it'll use COM1 by default. I didn't add any error checking on this - I figure if you want to mess around and specify COM0 or CM1 that's your issue.
- message.AddArg("arg"); Where arg is the argument to attach. There is a limit in the NMEA-0183 specification for message length, but I don't believe we follow it
- java.awt.event.ActionEvent
- java.awt.event.ActionListener
- Every UI object should extend JPanel and implement ActionListener
- You'll also need to implement actionPerformed, it's signature is public void actionPerformed(ActionEvent arg)
Deletions:
Currently the configuration is ONLY for panels. The original serial port server has a variable in heartofgold.c line 79 where it says openSerial(0) - that sets the TTyS number. In the Windows version you can pass an argument with the COM file in the form of COM1 or other such COM files as valid. If none is specified, it'll use COM1 by default. I didn't add any error checking on this - I figure if you want to mess around and specify COM0 or CM1 that's your issue.
message.AddArg("arg"); Where arg is the argument to attach. There is a limit in the NMEA-0183 specification for message length, but I don't believe we follow it
Every UI object should extend JPanel and implement ActionListener
You'll also need to implement actionPerformed, it's signature is public void actionPerformed(ActionEvent arg)
Edited on 2005-06-24 14:23:49 by KogAdmin [added links under nmea0183]
Additions:
You can view a FAQ on NMEA-0183∞ or check out William Dillon's page on the spark controller and misc hardware devices from the project
Edited on 2005-06-24 14:05:46 by KogAdmin
Additions:
Want to know more about the spark controller? Check out our new page HOGSparkController
Edited on 2005-06-24 03:17:01 by KogAdmin [added walkthrough]
Additions:
Walkthrough of developing a new module. First you might want to take care of some business. See the installation guide and make sure you meet the requirements. If you don't, go grab the appropriate packages - you should only need Java (and .NET2 if you're Windows). Please ignore some of the Wiki pages - Camel Case is similar to what Wikka deems Wiki pages.
The idea
So, my wonderful idea was to make it so you could swap out components of the system. It's OO and damnit, you should be able to do that. Ryan wanted to "get 'er done" and fought me tooth-and-nail every step of the way. To his credit, he eventually agreed that my design efforts would be the easiest way to design said system and we went at it. You should be able to add a component easily - just have a message parsing method, have a UI and do whatever it is you think you need to. If you don't like NMEA-0183 messages you can swap them out, if you don't like X component - swap it out.
We didn't have enough time to polish the code, and I'd like to go back and work on that quite a bit. Still, if you want something feel free to send me code. I'm taking over this project by either SF.NET or by my own. It's obvious the project isn't being worked on by Ryan (the so-called administrator). Can you tell I'm angry?
Housekeeping
Setting the serial port
Currently the configuration is ONLY for panels. The original serial port server has a variable in heartofgold.c line 79 where it says openSerial(0) - that sets the TTyS number. In the Windows version you can pass an argument with the COM file in the form of COM1 or other such COM files as valid. If none is specified, it'll use COM1 by default. I didn't add any error checking on this - I figure if you want to mess around and specify COM0 or CM1 that's your issue.
SUGGESTION: Different people have a multitude of ideas on using IDEs. I've used Eclipse on this project because at the time it was the thing to do, and Eclipse is a really nice piece of software. If you're "too manly," Eclipse is too heavy, you like Brand X better or just don't care I won't push it on you. It comes in handy, and if you're curious you can find a copy at
Eclipse.org∞
Named Pipes
Your OS MUST support named pipes. That means no Win ME (and possibly nothing that isn't NT based - NT/XP/2003/Long Horn/Black Comb et al). If you're curious, both the Windows and the original Unix create named pipes named Hog_In (Windows: \\.\pipe\Hog_In) and Hog_Out (Windows: \\.\.pipe\Hog_Out).
Why named pipes? You need something to watch the serial port, and it's OS specific so we opted to use IPC instead of either JNI or Javax.comm (javax.comm wasn't stable at the time, there were rumors of it being discontinued/didn't work with 1.4 or 1.5 and it's not truly cross-platform). We could use either networking and sockets or named pipes. We figured named pipes are files and should be easy to work with.
If you don't like named pipes you can definately swap out the communications code to use something like sockets or message passing or shared memory segments or (insert IPC method here). It's open source - do whatever you feel like, but please tell me because I'm curious. Later versions will have more modular code (I apologize for the messiness, but getting the project delivered was hell and involved me yelling at Ryan - and vice versa - plenty, he's not into design work...) where you could potentially swap out modules.
NMEA-0183
Also known as the GPS message standard. It consists of $command,arg,arg,arg*chksum Where command is no more than 5 characters and the checksum is an optional 2 digit hex XOR. We use the prefix "P" for proprietary, the next letter of the command is for the module (the spark controller uses "S") and the last two letters are the command ("ST" is for Spark Table, "GT" is for Get Table). This can be expanded, or you can use a different message type. William Dillon suggested we use this, and Ryan wrote a message object (extending my base class of the default message object).
Developing
Framework and associated naming conventions
The Main block calls the configuration file, which loads the panels based on strings. It does some minor checking, but will throw an exception. For this reason DO NOT give it bogus panels or it won't work. Naming: everything has a model_HOG_ object and a ui_HOG_ object. The model does all the stuff with the system and contains the hookins, the UI is how to visualize it and to capture the action handlers (think of it as MVC of a sorts).
We make use of the Observer/Observable pattern because Java doesn't (or didn't at the time) have any event model like .NET does. You'll need to include the update method, and I think that can actually be moved into the abstract, and overriden if you so desire. I never did so, but I'm contemplating it and putting it on my TODO list.
When we receive a message from the serial server, it throws the update() method and all modules do a check to see if the message is for them (using their ID data member) and then if it is, they call parseMessage() on it. All messages must be created like so:
- HOG_Message message = new HOG_Message("ID"); Where ID is a proper 4 character ID
- message.AddArg("arg"); Where arg is the argument to attach. There is a limit in the NMEA-0183 specification for message length, but I don't believe we follow it
- Call messenger.sendMessage(HOG_Message); Where HOG_Message is an object of type, or descended from, HOG_Message.
Every model object contains a UI object to display itself, a messanger to send messages and hook into our message system, a JPanel called panelLabel which is a string for the button that will view your panel in the main menu and lastly a data member called "id" which is the message command (see above) that is for your particular model.
On the id member: If you need to respond to more than one type of message, you should override the update() method accordingly. By default, our modules just execute the following code:
public void update(HOG_Messenger sender, HOG_Message m) {
if(id.equals(m.getID())) {parseMessage(sender, m);}
return;
}
Which, as you can see just compares the ID. It needs to be overriden, but you don't need to use it - it's just for ease of use.
Adding the module to the system
Either use the ConfTool or edit hog.conf and add the following line: panel99:model_HOG_Test
Creating a model_HOG_Test object
Now that we said we want to create one, we should do that.
Neccessary imports/objects
- All models are in package COM.HOG
- All models need the following methods parseMessage(HOG_Messenger sender, HOG_Message m), a constructor, and update(HOG_Messenger sender, HOG_Message m) by default
- All models must contain a HOG_Messenger object to pass messages around
- All models must extend Model_HOG_Component
Overrides
- All models must define the member id - the type is String
- All models must define the member panelLabel - the type is String
- All models must define the member panel - the type is a Jpanel, but should be a ui_HOG_ type, it's for displaying the model
A simple test module:
package COM.
HOG;
import java.io.IOException;
public class model_HOG_Test
extends Model_HOG_Component
{
private HOG_Messenger messenger;
public void parseMessage
(HOG_Messenger sender, HOG_Message m
) {
}
public model_HOG_Test
() throws IOException {
id =
"PSST";
panelLabel =
"Test Module";
panel =
new ui_HOG_Test
(this);
messenger = HOG_Messenger.
getMessenger();
}
public void update
(HOG_Messenger sender, HOG_Message m
) {
if(id.
equals(m.
getID())) {parseMessage
(sender, m
);
}
return;
}
}
Creating a ui_HOG_Test object
Neccessary imports/objects
- Every UI object must be in the package COM.HOG
- Every UI object should import the following:
- Every UI object should extend JPanel and implement ActionListener
- Every UI object should contain a Model_HOG_Component (private)
Overrides
- You'll need a constructor that takes a Model_HOG_Component for an argument. While you can add other parameters, I wouldn't see why you'd want to - but then I haven't tested it, so maybe you can't.
- You'll also need to implement actionPerformed, it's signature is public void actionPerformed(ActionEvent arg)
A simple UI module is:
package COM.
HOG;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
public class ui_HOG_Test
extends JPanel implements ActionListener {
public ui_HOG_Test
(model_HOG_Test modelTest
) {
}
private model_HOG_Test test;
public void actionPerformed
(ActionEvent arg
) {
}
}
What now?
Now you can keep adding stuff to your UI object, maybe even make more than one UI called from within that (see the MegaSquirt code Ryan wrote for examples). You shouldn't need to do anything else to the framework once you're satisfied, but you can feel free to make improvements (and hopefully send them to me). My priorities lie mainly on work and other projects - testing with actual devices is hard to do, especially when your laptop has no serial device.
However! in future releases the communication will be modular - you should be able to use USB, Serial, Networking etc. You can keep adding panels and using the system for whatever you want. I request all interesting research be described to me (optional) and I can even put a reference up somewhere (optional). You can even extend the software for usage with all KINDS of physical devices: GPS, astronomy (that might be complicated though), home brew devices... you name it. This is actually virtual instrumentation software, or writing software to control physical devices.
Thanks for your interest in (my/our) virtual instrumentation software!
Deletions:
Sweet sweet developer documentation. Eventually I'll add how to write a module for HOG in here.
Edited on 2005-06-24 01:33:17 by KogAdmin
Additions:
Heart of Gold developer documents
Deletions:
Heart of Gold developer documents
Oldest known version of this page was edited on 2005-06-24 01:11:28 by KogAdmin []
Page view:
Heart of Gold developer documents
Sweet sweet developer documentation. Eventually I'll add how to write a module for HOG in here.
CategoryHeartOfGold