Contact Craig Contact Craig
Show Changes Show Changes
Edit Edit
Print Print
Recent Changes Recent Changes
Subscriptions Subscriptions
Lost and Found Lost and Found
Find References Find References
Rename Rename
Search

History

6/3/2005 11:10:46 AM
CraigAndera-68.10...
6/3/2005 1:25:43 AM
-67.82.29.165
1/26/2005 8:48:43 AM
CraigAndera
List all versions List all versions
Build Lights Code
.

I wrote a blog post about how I set up a series of colored lights to turn on and off based on the status of a build at a client of mine. It looks like this:

A few people asked me for the code I wrote. As it was pretty simple, and since I got permission to post it, you can find it below. A few notes:

  1. I wrote this code in something like two hours. Therefore, it's nowhere near perfect. Don't take it as an indication of my best work.
  2. If I let the service run for long enough, it eventually stops updating the lights. I'm not sure why that is. My fix was to use Scheduled Tasks to stop the service every night and start it every morning. Good enough: we're not controlling the Space Shuttle here, just turning pretty lights on and off.
  3. It looks for a config file called settings.xml in the same directory as the service executable. If it doesn't find one there, it writes one with some defaults. I realize that writing to the program directory isn't necessarily a great idea, but see #1.
  4. You can install the service by running sc create BuildLights binPath= \path\to\the\exe. Notice the space after binPath=.
  5. You'll need the appropriate X10 hardware and ActiveHome SDK, which you can get here.com/sdk/.
  6. You'll need references to ThoughtWorks.CruiseControl.CCTrayLib.dll and ThoughtWorks.CruiseControl.Remote.dll.
  7. I hardcoded my X10 device addresses to a1 (yellow), a2 (green), and a3 (red). You'll need to change these as appropriate, or make them configurable.
  8. I set it up so it can run in either console mode or as a service. To run in console mode, just run it with the /console switch. It'll run until you hit enter.

The Code

 using System;
 using System.Collections;
 using System.ComponentModel;
 using System.Data;
 using System.Diagnostics;
 using System.IO;
 using System.ServiceProcess;
 using System.Threading;
 using System.Xml; 
 using System.Xml.Serialization; 


 using ActiveHomeScriptLib; 


 using ThoughtWorks.CruiseControl.CCTrayLib;
 using ThoughtWorks.CruiseControl.Remote;


 namespace Wangdera.BuildLightsSvc
 {
     public class App 
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting..."); 


            // If the user specifies /console, run interactively until they hit enter 
            if (args.Length > 0 && args[0].ToLower() == "/console")
            {
                BuildLightsService service = new BuildLightsService(); 
                service.Start(); 
                Console.WriteLine("Hit enter to terminate");
                Console.ReadLine();
                service.Stop(); 
            }
           // Otherwise, run as a service
            else
            {
                System.ServiceProcess.ServiceBase[] ServicesToRun;
                ServicesToRun = new System.ServiceProcess.ServiceBase[] { new BuildLightsService() };
                System.ServiceProcess.ServiceBase.Run(ServicesToRun);
            }
        }


    }


    public class BuildLightsService : ServiceBase
    {
        private ActiveHome activeHome; 
        private Thread thread; 


       // Here's where we actually do the monitoring and turn lights on and off.
        public void Run()
        {
            // The ActiveHome object is Apartment model, so this seems like a good idea. 
            Thread.CurrentThread.ApartmentState = ApartmentState.STA; 


            activeHome = new ActiveHomeClass(); 


            // Turn everything off until we figure out where we're at
            SetRedState(false);
            SetGreenState(false);
            SetYellowState(false); 


            Settings settings = ReadSettings(); 
            StatusMonitor monitor = new StatusMonitor(settings); 


            try
            {
                // Loop forever, asking the project its status periodically. I could also have
                // used monitor.StartPolling(), but I had problems with it. They're probably not
               // related to StartPolling(), but I was too lazy to switch back. 
                while (true)
                {
                    monitor.Poll(); 
                    SetLightsForStatus(monitor.ProjectStatus); 
                    Thread.Sleep(settings.PollingIntervalSeconds * 1000);
                }
            }
            // The service stops work here by causing a ThreadInterruptException to be thrown, so 
            // we do our cleanup in a finally block. 
            finally
            {
                SetRedState(false);
                SetGreenState(false);
                SetYellowState(false); 
            }
        }


        protected override void Dispose( bool disposing )
        {
            base.Dispose( disposing );
        }


        // This is called when the service receives a start command from the system
        protected override void OnStart(string[] args)
        {
            Start(); 
        }


        // Kick off a new thread to do the monitoring
        public void Start()
        {
            thread = new Thread(new ThreadStart(Run)); 
            thread.Start(); 
        }


        // This is called when the service receives a stop command from the system.  
        protected override void OnStop()
        {
            Stop();
        }


        public void Stop()
        {
           // Cause an exception in the thread that's doing the monitoring
            thread.Interrupt(); 
           // and wait for it to finish what it's doing
            thread.Join(); 
        }


        // Here's where we figure out what lights should be on
        private void SetLightsForStatus(ProjectStatus status)
        {
            if (status.Status == ProjectIntegratorState.Running)
            {
                // The yellow light goes on whenever a build is in progress.
                if (status.Activity == ProjectActivity.Building)
                {
                    SetYellowState(true);
                }
                else
                {
                    SetYellowState(false);
                }


                // Turn on the red light when the last build failed
                if (status.BuildStatus == IntegrationStatus.Failure)
                {
                    SetRedState(true);
                    SetGreenState(false); 
                }
                // Turn on the green light when the last build succeeded
                else if (status.BuildStatus == IntegrationStatus.Success)
                {
                    SetRedState(false);
                    SetGreenState(true); 
                }
                // Turn off the red and green lights when we don't know what happened with the last build
                else
                {
                    SetRedState(false);
                    SetGreenState(false); 
                }
            }
           // If the build service isn't running or we can't communicate with it, turn off all the lights
            else
            {
                SetRedState(false);
                SetGreenState(false);
                SetYellowState(false); 
            }
        }


        // Read a file called settings.xml from the same directory as the EXE. If it's not there, create a new
        // one with some defaults. 
        private Settings ReadSettings()
        {
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.xml");


            Settings settings = null; 
            if (File.Exists(path))
            {
                XmlSerializer ser = new XmlSerializer(typeof(Settings)); 
                XmlTextReader reader = new XmlTextReader(path); 
                try
                {
                    settings = (Settings) ser.Deserialize(reader); 
                }
                finally
                {
                    reader.Close(); 
                }
            }
            else
            {
                settings = new Settings(); 
                settings.RemoteServerUrl = "tcp://your-build-machine-name-here:21234/CruiseManager.rem";
                settings.PollingIntervalSeconds = 5; 


                XmlSerializer ser = new XmlSerializer(typeof(Settings));
                XmlTextWriter writer = new XmlTextWriter(path, System.Text.Encoding.UTF8); 
                try
                {
                    writer.Formatting = Formatting.Indented; 
                    ser.Serialize(writer, settings); 
                }
                finally
                {
                    writer.Close(); 
                }
            }


            return settings;


        }


        private void SetRedState(bool state)
        {
            SetItemState("a3", state);
        }


        private void SetYellowState(bool state)
        {
            SetItemState("a1", state);
        }


        private void SetGreenState(bool state)
        {
            SetItemState("a2", state);
        }


        private void SetItemState(string item, bool state)
        {
            string action = null; 
            if (state)
            {
                action = " on";
            }
            else
            {
                action = " off";
            }
            // Turning a light on or off is as simple as sending a "sendplc" command with an
            // address of something like "a1 on" or "a3 off"
            activeHome.SendAction("sendplc", item + action, null, null); 
        }
    }
 }
This is CraigAndera's wiki. Visit the HomePage for more info. If you have any feedback, please contact Craig .

About FlexWiki.

Recent Topics