Category: java

GitBlit on the Raspberry Pi

I’ve used GitHub, BitBucket and Unfuddle for hosted Git repositories – and they’re good at what they do – but until recently hadn’t found a good solution for a locally hosted Git repository server. I stumbled across GitBlit, which fits the bill perfectly, and even runs acceptably on a Raspberry Pi. It’s written in Java, so you’ll need to install Oracle JDK 8. For the record, I used the Raspbian OS and installed the Linux Gitblit GO distribution. GitBlit includes basic issue tracking and repository documentation (via Markdown).

Further XPDisplay improvements

XPDisplay now has a ‘Data’ tab, which (currently) displays charts with approach glideslope and landing gear forces, for post-landing analysis:

Glideslope

Glideslope - showing a late approach and landing!

Landing Gear Forces

Landing Gear Forces

Charts are updated in real time and can be zoomed, printed or exported as PNG images (thanks to the excellent JFreeChart library).

Caveat: these features are not in the current released version of XPDisplay yet.

XPDisplay improvements in the pipeline

After a couple of months with hardly any time to work on my personal projects, it feels great to be developing again! This is a screenshot of XPDisplay, showing the new airport information display on the map tab:

The airport information is read from the same apt.dat file that XPlane uses, so everything is kept nice and consistent.

MudPi

About 10 years ago, I wrote Alternate Universe MUD, a free text-based online adventure game, with a space-theme and an emphasis on exploration and discovery.

Note: for younger/less geeky readers, a MUD is a Multi User Dungeon, i.e. a game that you could play (with other people, no less!), which often revolved around dungeons, monsters and combat. Massively Multiplayer Online Role-Playing Games (MMORPGs) like World of Warcraft can trace their history back to these kinds of games.

The game features spaceships, several worlds, an astronomical observatory, mudmail and mud-wide-web terminals, a stock market, various robots and even an auditorium where Shakespeare’s plays are performed by bots every few hours.

Over 2000 players discovered Alternate Universe over the first few years (not bad for something that started out just as an experiment). Most players just played, but others contributed code, some helped build the world from the inside and one even ran an in-game monthly newspaper. I ended up meeting some of them in real life too, as a result of all this.

The game server itself was written from scratch in Java and has had many homes, but thanks to Oracle’s release of Java 8 for ARM in December, it now runs on a Raspberry Pi (and it runs happily too, using less than a third of the 512MB of memory – AU was mostly written on a 400MHz HP laptop with half the CPU speed that the Pi has now, so I’m not that surprised).


(the black thing poking out by the yellow S-Video socket is an external temperature sensor – I hope to wire this up to the MUD, so that the external temperature drives some behaviour or description inside the game)

Play!
To play Alternate Universe, go to the command-line and type
telnet alternateuniverse.dyndns.org 1063
If you are playing from another Pi, you may first need to install the ‘telnet’ program:
sudo apt-get install telnet

I hope you have fun playing. Drop me a MUD-mail if you enjoy the game!

Low-cost XPlane Control Panel

This is a little project that I’ve been working on over the last month or so. (I completed it several weeks back, but I’ve been having too much fun with it to write up this blog post) It is a physical control panel for the XPlane flight simulator – i.e. when I move a slider (in the real world), the flaps begin to deploy (in the simulator). I flick another switch, some lights start to flash (in the real world) and in the simulator the landing gear start to rumble their way down into place. What the switches/sliders do is arbitrary – they can be reprogrammed whenever I feel like it.

There are three components: 1) the x-plane simulator 2) the physical box itself, with switches and LEDs wired to inputs/outputs on an Arduino Uno 3) some software running on the PC, which talks to the Arduino (over serial / USB) and to the simulator (sending and receiving UDP packets).

Building the box

The box is made from offcuts of MDF (about 3mm thick), laboriously measured, cut with a saw and drilled with a Dremel. (To cut the slots, I drilled lots of holes in a row and used the Dremel’s circular saw attachment).

The sides of the box were epoxied together (with a little help from some scrap blocks of wood in the corners) so that the back section could be removed, and the gaps filled with ordinary DIY filler. I sanded the whole thing and painted it with a clear primer (to stop the MDF from soaking up the paint and getting a bit soggy) then painted with two coats of a satin black paint (in hindsight, I wish I’d used matt rather than satin as it shows up the imperfections more than I’d like). The labels were printed on my ordinary inkjet printer, white text on a black background & attached with a little spot of glue so that I can rip them off and attach new labels if I reprogram the switches to do something different.

Electronics

The panel has six switches, two linear potentiometers (I accidentally ordered audio ones, which are logarithmic, and had to work around this in the Arduino sketch code to convert to near-linear values), three 5mm red LEDs and three 5mm green LEDs (plus a resistor for each set of LEDs). Inside the box there is an Arduino Uno, a breadboard and quite a few jumper wires.

The USB port of the Arduino sticks out through a square hold in the side of the box. The Arduino is screwed to some chipboard (with some bubblewrap between the PCB and the wood, just to cushion the circuit a little.

The inside is rather messy. With hindsight I would’ve spent longer finding a way to make this a lot tidier and probably not needed the breadboard. In most cases, I ended up snipping a jumper wire in half, soldering one end to a component and pressing the pointy end into the breadboard.

I didn’t use all the ports on the Arduino Uno – if I made a version 2, I could probably have 6 more lights or switches and 4 more sliders, or possibly include a buzzer to serve as stall / warning indicator.

Arduino sketch code

This is the Arduino sketch that is currently running inside the control panel. In a nutshell, in a loop, it checks the state of each switch and writes a string to the serial port (really it’s serial over USB), which looks something like this:

A^ Bv Cv Dv Ev F^ P1022 Q1019

This indicates that switch A is up, B is down, etc. P and Q are the values of the sliders (from 0 to 1023).

The code also reads from the serial port to receive one of the letters U, D, R, F or N to indicate the current state of the landing gear in the simulator (Up, Down, Rising, Falling or No idea!). If the gear is up, the red LEDs go on, if the gear is down, the green LEDs go on. If it’s rising, the red LEDs are flashed, falling = green LEDs are flashed.

const int led_red_pin = 3;
const int led_green_pin = 2;

const int switch_1_pin = 6;
const int switch_2_pin = 5;
const int switch_3_pin = 4;
const int switch_4_pin = 9;
const int switch_5_pin = 8;
const int switch_6_pin = 7;

const int slider_1_pin = A0;
const int slider_2_pin = A1;

int switch_1_state = 0;
int switch_2_state = 0;
int switch_3_state = 0;
int switch_4_state = 0;
int switch_5_state = 0;
int switch_6_state = 0;

int slider_1_state = 0;
int slider_2_state = 0;

int inByte = 0;

const int GEAR_NO_CONNECTION = 0;  // alternate flashing both sets of LEDs
const int GEAR_DOWN = 1; // solid green 'D'
const int GEAR_UP = 2;  // solid red 'U'
const int GEAR_TRANSITIONING_DOWN = 3;  // flash green 'F'
const int GEAR_TRANSITIONING_UP = 4;  // flash red 'R'

int gearState = GEAR_NO_CONNECTION;

unsigned long time;
unsigned long lastReceivedTime;

void setup() {
    pinMode(switch_1_pin, INPUT_PULLUP);
    pinMode(switch_2_pin, INPUT_PULLUP);
    pinMode(switch_3_pin, INPUT_PULLUP);
    pinMode(switch_4_pin, INPUT_PULLUP);
    pinMode(switch_5_pin, INPUT_PULLUP);
    pinMode(switch_6_pin, INPUT_PULLUP);

    pinMode(led_red_pin, OUTPUT);
    pinMode(led_green_pin, OUTPUT);    

    // no pinMode required for analog inputs

    Serial.begin(9600);

    time = 0;
}

void loop() {
    if (Serial.available() > 0) {
        inByte = Serial.read();
        delay(20);    

        if( inByte == 'U' ) {
            gearState = GEAR_UP;
        } else
        if( inByte == 'D' ) {
            gearState = GEAR_DOWN;
        } else
        if( inByte == 'R' ) {
            gearState = GEAR_TRANSITIONING_UP;
        } else
        if( inByte == 'F' ) {
            gearState = GEAR_TRANSITIONING_DOWN;
        } else
        if( inByte == 'N' ) {
            gearState = GEAR_NO_CONNECTION;
        }

        lastReceivedTime = millis();
    }

    if( (millis() - lastReceivedTime) > 2000 ) {
        gearState = GEAR_NO_CONNECTION;
    }

    // note: read delays required due to issue with Arduino ADC timing.
    delay(20);
    slider_1_state = logToLin(analogRead(slider_1_pin));
    delay(50);
    slider_2_state = logToLin(analogRead(slider_2_pin));

    switch_1_state = digitalRead(switch_1_pin);
    switch_2_state = digitalRead(switch_2_pin);
    switch_3_state = digitalRead(switch_3_pin);
    switch_4_state = digitalRead(switch_4_pin);
    switch_5_state = digitalRead(switch_5_pin);
    switch_6_state = digitalRead(switch_6_pin);

    write('A', switch_1_state);
    write('B', switch_2_state);
    write('C', switch_3_state);
    write('D', switch_4_state);
    write('E', switch_5_state);
    write('F', switch_6_state);

    Serial.write('P');
    //Serial.println(slider_1_state, DEC);
    Serial.print(slider_1_state, DEC);
    Serial.write(' ');
    Serial.write('Q');
    Serial.print(slider_2_state, DEC);

    // switch_1 is avionics on/off and so is also an on/off switch for all the LEDs
    if( switch_1_state == LOW ) {
        if( gearState == GEAR_UP ) {
            digitalWrite(led_red_pin, HIGH);
            digitalWrite(led_green_pin, LOW);
        } else
        if( gearState == GEAR_DOWN ) {
            digitalWrite(led_red_pin, LOW);
            digitalWrite(led_green_pin, HIGH);
        } else
        if( gearState == GEAR_TRANSITIONING_UP ) {
            // time since last change..
            unsigned long since = millis() - time;
            if( since > 1000 ) {
                digitalWrite(led_red_pin, HIGH);
                digitalWrite(led_green_pin, LOW);
                time = millis();
            } else
            if( since > 500 ) {
                digitalWrite(led_red_pin, LOW);
                digitalWrite(led_green_pin, LOW);
            }
        } else
        if( gearState == GEAR_TRANSITIONING_DOWN ) {
            // time since last change..
            unsigned long since = millis() - time;
            if( since > 1000 ) {
                digitalWrite(led_red_pin, LOW);
                digitalWrite(led_green_pin, HIGH);
                time = millis();
            } else
            if( since > 500 ) {
                digitalWrite(led_red_pin, LOW);
                digitalWrite(led_green_pin, LOW);
            }
        } else
        if( gearState == GEAR_NO_CONNECTION ) {
            digitalWrite(led_red_pin, LOW);
            digitalWrite(led_green_pin, LOW);
        }

    } else {
        digitalWrite(led_red_pin, LOW);
        digitalWrite(led_green_pin, LOW);
    }

    Serial.write('\n');  

    delay(150);
}

void write(char c, int state) {
    Serial.write(c);
    if( state == HIGH ) {
        Serial.write('v');
    } else
    if( state == LOW ) {
        Serial.write('^');
    } else {
        Serial.write('x');
    }
    Serial.write(' ');
}

int logToLin(int a) {
    double x = (double) a / 1024.0;
    double y = pow(20.0, x);
    double z = y - 1.0;
    int b = (int)(z * 54.0);
    return b;
}

Note the logToLin function which I needed because I’d accidentally ordered logarithmic scale potentiometers rather than linear ones. The magic numbers you see in that function came from experimentation.

I found the Arduino serial monitor very helpful to watch what the box was sending over the serial port.

PC software

The code to send/receive UDP packets to/from XPlane is written in Java and based on XPDisplay but using the RXTX library for serial port communication. If I’m honest it’s a bit of a shambles at the moment and I’m hoping to rewrite it, so samples are available on request. :-)

Conclusions

All in all, this was an easy project to pick up for an hour, do a little bit, then put down again (which is important when you’ve got young kids). Although it’s definitely amateurish, the end result is better than I hoped for at the beginning, and as this was my first arduino project it was very much a case of discovering the right way to do things as I went along.

The total cost of materials & electronics was about £30 (from RS, Farnell, Oomlout):

  • MDF wood – scraps I had lying around, probably worth no more than £3
  • slide potentiometers – £1 each, so £2 – get linear ones, I got logarithmic (audio) ones
  • knobs for slide potentiometers – £1
  • switches – £1 each, so £6
  • LEDs – about 40p in total
  • Arduino Uno – £15
  • Epoxy glue – £2
  • Labels – nothing, printed on my inkjet printer
  • Paint – £3 for the primer & for the paint, probably used 50p’s worth.

XPDisplay for X-Plane

I’ve been enjoying X-Plane for several months now and figured that since I’m a programmer, it’s time to give something back. I’ve been messing around and have come up with an information display – useful if you have a second computer sitting around (maybe a laptop) that isn’t powerful enough to run X-Plane. It basically displays information useful for a fly-in:

* Time in GMT
* Altitude (relative to ground), speed, heading, etc.
* Moving map display (so you can get an idea of what you’re flying over)
* Basic radar (showing other aircraft)

It’s by no means perfect, but I’ve used it for the last few fly-ins and found it surprisingly helpful.

www.duncanjauncey.com/xplane/xpdisplay

JDarkRoom 14

The beginning of this month saw the release of version 14 of JDarkRoom, the full-screen text editor that has developed a loyal following since its first release over two years ago.

The new version is the first for almost a year and brings some much-needed bug fixes, new features and a complete rewrite of the settings screen. The main new feature is the ability to export your file via a converter – for example the supplied ‘Markdown‘ converter will generate a snippet of HTML suitable for posting into a web page, using the Markdown syntax rules for formatting. Suggestions for other converters are welcome.

Future development plans include a plugin system, so that other friendly Java developers can get in on the act to add features they’d like to see in JDarkRoom.

Finally, a big thank you to everyone who requested features, submitted bug reports and gave feedback on JDarkRoom – your input is always very much appreciated.

Java Swing UIManager Look and Feel Defaults

While working with Java Swing UIs, I’ve occasionally found it difficult to locate a complete list of all the tweakable colours, borders, fonts etc that form part of a Swing Look and Feel. Here’s my solution to that problem: www.duncanjauncey.com/java/ui

NextDesk

Nearly three years after I first started the project, the first public beta of NextDesk is now online.

Put simply, NextDesk lets you easily drag and drop files from your computer to another computer. If you paste text to it from the clipboard, the pasted text will appear on the clipboard of the other computer.

Have fun!

XFire Custom Types

XFire Custom Types – 25th February 2008 – XFire 1.2.6

  • Custom Types are types registered with XFire that perform their own mapping to/from XML.
  • "complex types" are your own beans/objects which don’t have a custom type mapping, therefore XFire will attempt to map to/from XML on its own.
  • They *do* work, but you have to be careful that you set everything up right, otherwise you’ll see a StackOverflowError and many other exceptions.
  • XFire sometimes gets terribly confused if one of your own (complex) types is put inside a map (e.g. of <String,Object>) – unless you create a custom type mapping as described below. (see bug XFIRE-831)
  • I couldn’t find a proper example of this on the web & spent several days discovering the correct way to do this.

Example: java.util.Date
Date objects are automatically converted to XMLGregorianCalendar by XFire. This behaviour may not be appropriate since converting back from an XMLGregorianCalendar to a Date is very slow. This example overrides the normal mapping for java.util.Date so that only a long value is passed across the wire, then reparsed back into a java.util.Date again.

  1. Define your type mapping class. The below defines a mapping for java.util.Date, so that it will be read and written as a String containing a long value (the number of milliseconds from the Date object). Note that this class must be available on the classpath for both client and server.
    import java.util.Date;
     
    import javax.xml.namespace.QName;
     
    import org.codehaus.xfire.MessageContext;
    import org.codehaus.xfire.aegis.MessageReader;
    import org.codehaus.xfire.aegis.MessageWriter;
    import org.codehaus.xfire.aegis.type.basic.ObjectType;
    import org.codehaus.xfire.aegis.type.java5.Java5TypeCreator;
    import org.codehaus.xfire.fault.XFireFault;
     
    public class DateType extends ObjectType {
    	private static final Class&lt;java.util.Date&gt; componentClass = java.util.Date.class;
    	protected QName qname = new Java5TypeCreator().createQName(componentClass);
     
    	public DateType()
    	{
    		setNillable(true);
    		setTypeClass(java.util.Date.class);
    		setSchemaType(qname);
    		System.out.println(&quot;Created DateType&quot;);
    	}
     
    	public Object readObject(MessageReader reader, MessageContext context) throws XFireFault
    	{	
    		String value = reader.getValue();
    		System.out.println(&quot;Reading DateType from '&quot;+value+&quot;'&quot;);
     
    		if (value == null)
    			return null;
     
    		try {
        		Long l = Long.parseLong(value);
        		return new Date(l.longValue());
    		} catch( NumberFormatException e ) {
    			throw new XFireFault(&quot;Could not parse Date: &quot; + e.getMessage(), e, XFireFault.SENDER);
    		}
    	}
     
    	public void writeObject(Object object, MessageWriter writer, MessageContext context)
    	{
    		System.out.println(&quot;Writing DateType..&quot;);
     
    		assert( object instanceof Date );
     
    		Date d = (Date) object;
    		writer.writeValue(&quot;&quot;+d.getTime());
    	}
    }
  2. Register the type mapping with both the client and the server

    Registering with the client is easy. I dare say you already have a convenience method that allows you to get hold of a Service. The important parts from mine look something like this:

    	String serviceURL = ... // replace with your service URL
    	Class serviceClass = ... // replace with the class object for your service (e.g. MyService.class)
    	Service service = new ObjectServiceFactory().create(serviceClass);
     
    	// register our new type mapping class
    	TypeMapping tm = ((AegisBindingProvider)service.getBindingProvider()).getTypeMapping(serviceModel);
    	tm.register(new DateType());	
     
    	serviceObject = new XFireProxyFactory().create(service, serviceURL);
    	System.out.println(&quot;Created service object.&quot;);
     
    	// then cast your serviceObject to the correct type (e.g. MyService) and use it.

    Registering with the server is somewhat more confusing. I tried several things and ended up extending XFireConfigurableServlet so that the type mappings were registered inside the init() method:

    public class MyXFireServlet extends XFireConfigurableServlet {
    	public MyXFireServlet() {
    		super();
    		System.out.println(&quot;Creating MyXFireServlet.&quot;);
    	}
     
    	public void init() throws ServletException {
    		super.init();
     
    		System.out.println(&quot;Initializing My XFire Servlet..&quot;);
     
    		ServiceRegistry serviceRegistry = getXFire().getServiceRegistry();
    		for( Service service : (Collection&lt;Service&gt;) serviceRegistry.getServices() ) {
    			TypeMapping tm = ((AegisBindingProvider) service.getBindingProvider()).getTypeMapping(service);
    			tm.register(new DateType());
    			System.out.println(&quot;Registering DateType for service &quot;+service.getName());
    		}
     
    		System.out.println(&quot;Initialization complete.&quot;);
    	}
    }

    If you do this, you should also update your web.xml file so that your servlet definition for XFire uses your customized servlet, like this:

    	&lt;!-- XFire --&gt;
    	&lt;servlet&gt;
    		&lt;servlet-name&gt;XFireServlet&lt;/servlet-name&gt;
    		&lt;display-name&gt;XFire Servlet&lt;/display-name&gt;
    		&lt;servlet-class&gt;my.package.name.MyXFireServlet&lt;/servlet-class&gt;
    	&lt;/servlet&gt;

    Now your type mapping will be used whenever XFire encounters an object of the type registered by setTypeClass().

Note: if sending Date objects created by Hibernate, they may be of type java.sql.Date – in which case XFire will still convert them to XMLGregorianCalendar unless you add a type mapping (as above) for java.sql.Date. Depending on your database vendor, you may also have to register mappings for vendor-specific types (e.g. com.sybase.jdbc2.tds.SybTimestamp).

You can define type mappings for any of your own objects – just substitute your fully-qualified class name where java.util.Date is used in the example above, and customize the readObject() and writeObject() methods to read and write a representation of your object.

For this example, I used the 3.2.4 version of the woodstox library. If you don’t specify woodstox as a dependency, you may get a cryptic com.bea.xml.stream.MXParserFactory not found error.

Hints:

If you see the following then your client is almost certainly not aware of a custom type mapping that it needs in order to parse a type that it has encountered (possibly in a Map or List). Check that you’ve registered it with the appropriate code that creates your service object.

java.lang.StackOverflowError
	at com.ctc.wstx.util.StringVector.findLastNonInterned(StringVector.java:194)
	at com.ctc.wstx.sr.NsInputElementStack.getNamespaceURI(NsInputElementStack.java:503)
	at com.ctc.wstx.sr.BasicStreamReader.getNamespaceURI(BasicStreamReader.java:775)
	at org.codehaus.xfire.util.stax.DepthXMLStreamReader.getNamespaceURI(DepthXMLStreamReader.java:142)
	at org.codehaus.xfire.util.stax.DepthXMLStreamReader.getNamespaceURI(DepthXMLStreamReader.java:142)
	at org.codehaus.xfire.aegis.stax.ElementReader.getNamespaceForPrefix(ElementReader.java:271)
	at org.codehaus.xfire.aegis.type.basic.ObjectType.extractQName(ObjectType.java:146)
	at org.codehaus.xfire.aegis.type.basic.ObjectType.readObject(ObjectType.java:97)
	at org.codehaus.xfire.aegis.type.basic.ObjectType.readObject(ObjectType.java:133)
	at org.codehaus.xfire.aegis.type.basic.ObjectType.readObject(ObjectType.java:133)
	at org.codehaus.xfire.aegis.type.basic.ObjectType.readObject(ObjectType.java:133)
	at org.codehaus.xfire.aegis.type.basic.ObjectType.readObject(ObjectType.java:133)
	at org.codehaus.xfire.aegis.type.basic.ObjectType.readObject(ObjectType.java:133)
	at org.codehaus.xfire.aegis.type.basic.ObjectType.readObject(ObjectType.java:133)

JDarkRoom 12

JDarkRoom version 12 is now available including the following new features:

  • Auto-save backups
  • Adjustable screen margins for smaller displays
  • Text search

As I write this, JDarkRoom has now been downloaded over 20,000 times.

Java font metrics

I’m always having to remind myself how Java’s font metrics work. As an aide-memoire (mainly for my own use, but someone may find it useful), I’ve created this diagram:

Java Font Metrics

Dansette