A Cheap Agile Lamp on Windows 7

At my previous company (FireID) we had an awesome CI queue. One of the components was a dedicated lamp server, showing a status page and three lava lamps. The colour of the active lamp reflecting the build status. Unfortunately the controller board controlling the lamps had to be custom made and the lamps themselves were also not cheap.

I found this extremely cheap usb controlled lamp on the Deal Extreme site. This light is very cheap and I can plug it directly into my development PC. I do not have to solder a pc-board together and worldwide delivery is free!

Here is photo of this Setup

When you download and install the custom email notification software you’ll notice a MailNotifierLib.dll in the program’s directory. I wrote a small C# class to use this dll to change the light’s colour.

using MailNotifierLib;

namespace emailnotifier
{

	class BuildStatusLamp
	{
		public static void Main(string[] args)
		{
			eColors lampColour = eColors.NoColor;

			if (args.Length > 0) {

				int colour;
				int.TryParse(args[0], out colour);

				switch (colour)
				{
					case 1:
						lampColour = eColors.Lime;
						break;
					case 2:
						lampColour = eColors.Red;
						break;
					case 3:
						lampColour = eColors.Blue;
						break;
					case 4:
						lampColour = eColors.Aqua;
						break;
					case 5:
						lampColour = eColors.Yellow;
						break;
					case 6:
						lampColour = eColors.Fuchsia;
						break;
					case 7:
						lampColour = eColors.White;
						break;
					default:
						lampColour = eColors.NoColor;
						break;
				}
			}

			MailNotifier[] notifiers = MailNotifier.GetNotifiers();
			notifiers[0].SetColor(lampColour);
		}
	}
}

I’m a Java developer, but writing this class in C# with the free SharpDevelop IDE was a breeze. I also used the .NET reflector tool. It was a lot simpler than trying to do it with Java via JNI or trying to traverse the dependency nightmare in Python or Ruby.

I briefly started to do this project in Python, as there was a nice example here. I quickly remembered why I hate most scripting languages… Managing and installing libraries is a nightmare. This is a far cry from the comfortable experience I have with maven, gradle or ivy in my Java environment.

Now that I have an easy way to change the lamp’s colours, I wrote a simple Groovy script to monitor our Jenkins server. I used the Status Monitor plugin in Jenkins to simplify the status gathering. I am most comfortable in Java and Groovy, it probably would have been a cleaner solution if I made the effort to write this script in C# too. Maybe someone else can contribute the C# code to make this setup even simpler? Here is the Groovy script:

public interface NotifierColours {
	public static int OFF     = 0
	public static int LIME    = 1
	public static int RED     = 2
	public static int BLUE    = 3
	public static int AQUA    = 4
	public static int YELLOW  = 5
	public static int FUCHSIA = 6
	public static int WHITE   = 7
}


class StatusChecker2 implements NotifierColours {

	private static String MONITOR_PAGE = "http://someurl:someport/monitor/"
	private static String SERVER_PAGE = "http://ahost:aport/somePage/"
	private static String SERVER_TEST = "xxxxx?option=controllername"

	private static String ERROR_STRING = "<a class=\"FAILURE\" href=\"/job/aJenkinsJob"
	private static String SUCCESS_STRING = "<a class=\"SUCCESS\" href=\"/job/aJenkinsJob"
	private static String BUSY_STRING = "/plugin/statusmonitor/images/ajax-loader.gif"

	private long blinkDelay
	private long updateDelay

	private int buildColour;
	private boolean isBlinking = false
	private boolean panic = false

	// The unit tests will override these closures with test code
	private def execute = { command ->  command.execute() }
	private def getPage = { url -> new URL(url).getText() }

	public StatusChecker2(long blinkDelay, long updateDelay) {
		this.blinkDelay = blinkDelay
		this.updateDelay = updateDelay
	}

	class Blinker implements Runnable {
		private int colour1, colour2

		public Blinker (int colour1, int colour2) {
			this.colour1 = colour1
			this.colour2 = colour2
		}

		public void run() {
			while (isBlinking) {
				execute("emailnotifier.exe ${colour1}")
				Thread.sleep(blinkDelay)
				execute("emailnotifier.exe ${colour2}")
				Thread.sleep(blinkDelay)
			}
		}
	}

	boolean serverIsRunning() {
		boolean siteDeployed = false
		try {
			String loginPage = getPage(SERVER_PAGE)
			siteDeployed = loginPage.indexOf(SERVER_TEST) > 0
		} catch (Exception e) {
		}

		siteDeployed
	}

	void stopBlinker() {
		Thread.sleep(blinkDelay*3) 
		isBlinking = false
	}
	
	void startBlinker(int colour1, int colour2) {
		if (isBlinking) {
			throw new RuntimeException("Cannot start a new blinker while an instance is still running..")
		}
		
		isBlinking = true
		new Thread(new Blinker(colour1, colour2)).start()
	}
	
	void run () {
		while (true) {
			updateBuildStatus()
		}
	}

	void updateBuildStatus() {
		String statusPage = getPage(MONITOR_PAGE)
		
		boolean isBuilding = statusPage.indexOf(BUSY_STRING) > 0

		if (statusPage.indexOf(SUCCESS_STRING) > 0) {
			buildColour = LIME
		} else if (statusPage.indexOf(ERROR_STRING) > 0) {
			buildColour = RED
		} else {
			// Unexpected behaviour
			buildColour = YELLOW
		}

		println "colour is ${buildColour}, ${isBuilding}\t ${new Date()}"

		if (isBuilding) {
			if (panic && serverIsRunning()) { // Reset panic state
				panic = false
				stopBlinker()
			}

			if (!isBlinking) {
				startBlinker(buildColour, OFF)
			}
		} else if (buildColour == RED && !serverIsRunning()) {
			// Our server reset after a successful build (GREEN), we don't want to go into panic state unnecessary
			println "SHOCK! HORROR! Server is down!"
			if (isBlinking && !panic) {
				stopBlinker()
			}

			if (!panic /* YET */) {
				startBlinker(RED, FUCHSIA)
				panic = true
			}
		} else {
			panic = false
			if (isBlinking) {
				stopBlinker()
			}
			
			execute("emailnotifier.exe ${buildColour}")
		}
		
		Thread.sleep(updateDelay)
	}

	public static void main(String[] args) {
		new StatusChecker(1000, 10000).run();
	}
}


Note, this is a simple script. No error handling, etc. I haven’t played with the light on Linux yet, maybe I’ll have better luck with Python there :).

Install Groovy and change the script to work with your Continious Integration setup. Now, all you have to do is copy the MailNotifierLib.dll, emailnotifier.exe, NotifierColours.groovy and StatusChecker.groovy into a directory and run groovy StatusChecker.groovy.
Enjoy!

Tagged , , , ,
Follow

Get every new post delivered to your Inbox.