Commits

Joel Ross committed 642fc74

Initial support for multiple build indicators and adding support for the BusyLight UC

Comments (0)

Files changed (35)

Add a comment to this file

lib/busylight/BusyLightHIDCommunications.dll

Binary file added.

Add a comment to this file

lib/busylight/LinkLampConfiguration.dll

Binary file added.

src/Core/Adapters/BusyLightAdapter.cs

+using System;
+using BusyLightHIDCommunications;
+using RossCode.TrafficLight.Core.Utilities;
+
+namespace RossCode.TrafficLight.Core.Adapters
+{
+    public interface IBusylightAdapter : IDisposable
+    {
+        void TurnGreen();
+        void TurnRed();
+        void TurnYellow();
+        void TurnOff();
+    }
+
+    public class BusylightAdapter : IBusylightAdapter
+    {
+        private UsbDevice busylight;
+        private bool isConnected;
+        private LinkLampConfiguration.Color currentColor;
+        private ITimer timer;
+
+        private bool Connect()
+        {
+            busylight = new UsbDevice(1240, 63560);
+            busylight.FindTargetDevice();
+            isConnected = true;
+
+            StartTimer(25);
+
+            return busylight.IsDeviceAttached;
+        }
+
+        private void StartTimer(int intervalInSeconds)
+        {
+            timer = new Timer();
+            timer.IntervalInSeconds = intervalInSeconds;
+            timer.TimerElapsed += (sender, args) => ChangeColor();
+            timer.Start();
+        }
+
+        private void ChangeColor()
+        {
+            if (!isConnected) { if (!Connect()) return; }
+            if (currentColor != null)
+            {
+                busylight.Light(currentColor);
+            }
+        }
+
+        public void TurnOff()
+        {
+            if (!isConnected) { if (!Connect()) return; }
+            currentColor = new LinkLampConfiguration.Color
+                {
+                    Red = 0, 
+                    Blue = 0, 
+                    Green = 0
+                };
+            ChangeColor();
+        }
+
+        public void TurnGreen()
+        {
+            if (!isConnected) { if (!Connect()) return; }
+            currentColor = new LinkLampConfiguration.Color
+                {
+                    Red = 0, 
+                    Blue = 0, 
+                    Green = 255
+                };
+            ChangeColor();
+        }
+
+        public void TurnYellow()
+        {
+            if (!isConnected) { if (!Connect()) return; }
+            currentColor = new LinkLampConfiguration.Color
+                {
+                    Red = 255, 
+                    Blue = 0, 
+                    Green = 255
+                };
+            busylight.Light(currentColor);
+        }
+
+        public void TurnRed()
+        {
+            if (!isConnected) { if (!Connect()) return; }
+            currentColor = new LinkLampConfiguration.Color
+                {
+                    Red = 255, 
+                    Blue = 0, 
+                    Green = 0
+                };
+            ChangeColor();
+        }
+
+        public void Dispose()
+        {
+            timer.Stop();
+            timer = null;
+
+            if (isConnected)
+            {
+                TurnOff();
+                busylight = null;
+            }
+            isConnected = false;
+        }
+    }
+}

src/Core/Data/Migrations/IndicatorsTable.cs

+using System.Data;
+using Migrator.Framework;
+
+namespace RossCode.TrafficLight.Core.Data.Migrations
+{
+    [Migration(201210282154)]
+    public class IndicatorsTable_201210282154 : Migration
+    {
+        public override void Up()
+        {
+            Database.AddTable("Indicators", new Column("Id", DbType.Int32, ColumnProperty.PrimaryKey));
+            Database.ExecuteNonQuery("insert into Indicators (Id) values (1);");
+        }
+
+        public override void Down()
+        {
+            Database.RemoveTable("Indicators");
+        }
+    }
+}

src/Core/Domain/IndicatorType.cs

+namespace RossCode.TrafficLight.Core.Domain
+{
+    public enum IndicatorType
+    {
+        Delcom = 1,
+        BusyLight = 2,
+    }
+}

src/Core/Domain/Repositories/IIndicatorTypeRepository.cs

+using System.Collections.Generic;
+
+namespace RossCode.TrafficLight.Core.Domain.Repositories
+{
+    public interface IIndicatorTypeRepository
+    {
+        IEnumerable<IndicatorType> GetAll();
+        void EnableIndicators(IEnumerable<IndicatorType> indicators);
+
+    }
+}
 using Ninject.Modules;
 using RossCode.TrafficLight.Core.Modules;
 using RossCode.TrafficLight.Core.Services;
+using RossCode.TrafficLight.Core.Services.Indicators;
 
 namespace RossCode.TrafficLight.Core
 {
 
         public static void Unload()
         {
-            Resolve<DelcomTrafficLightProjectStatusIndicatorService>().Dispose();
+            Resolve<DelcomTrafficLightBuildStatusIndicatorService>().Dispose();
         }
     }
 }

src/Core/Modules/PersistenceModule.cs

     {
         public override void Load()
         {
-            Kernel.Bind<IDatabaseCreator>().To<SqliteDatabaseCreator>();
-            Kernel.Bind<IDatabaseMigrator>().To<DatabaseMigrator>();
+            Bind<IDatabaseCreator>().To<SqliteDatabaseCreator>();
+            Bind<IDatabaseMigrator>().To<DatabaseMigrator>();
 
-            Kernel.Bind<IDatabaseConnection>().To<SqliteDatabaseConnection>();
-            Kernel.Bind<IDatabaseGateway>().To<SqliteDatabaseGateway>();
-            Kernel.Bind<IDatabaseConnectionFactory>().To<SqliteDatabaseConnectionFactory>();
+            Bind<IDatabaseConnection>().To<SqliteDatabaseConnection>();
+            Bind<IDatabaseGateway>().To<SqliteDatabaseGateway>();
+            Bind<IDatabaseConnectionFactory>().To<SqliteDatabaseConnectionFactory>();
 
             Bind<IDataMapper<Project>>().To<ProjectDataMapper>();
+            Bind<IDataMapper<IndicatorType>>().To<IndicatorTypeDataMapper>();
 
-            Kernel.Bind<IProjectRepository>().To<ProjectRepository>().InSingletonScope();
+            Bind<IProjectRepository>().To<ProjectRepository>();
+            Bind<IIndicatorTypeRepository>().To<IndicatorTypeRepository>();
         }
     }
 }

src/Core/Modules/ServiceModule.cs

 using Ninject.Modules;
 using RossCode.TrafficLight.Core.Factories;
 using RossCode.TrafficLight.Core.Services;
+using RossCode.TrafficLight.Core.Services.Indicators;
 
 namespace RossCode.TrafficLight.Core.Modules
 {
     {
         public override void Load()
         {
-            Kernel.Bind<IDatabaseService>().To<DatabaseService>();
-            Kernel.Bind<IProjectService>().To<ProjectService>();
-            Kernel.Bind<IApplicationService>().To<ApplicationService>();
-            Kernel.Bind<DelcomTrafficLightProjectStatusIndicatorService>().ToSelf().InSingletonScope();
-            Kernel.Bind<IProjectResolverFactory>().To<ProjectResolverFactory>();
+            Bind<IDatabaseService>().To<DatabaseService>();
+            Bind<IProjectService>().To<ProjectService>();
+            Bind<IProjectMonitorService>().To<ProjectMonitorService>();
+            Bind<IBuildIndicatorService>().To<BuildIndicatorService>();
+            Bind<DelcomTrafficLightBuildStatusIndicatorService>().ToSelf().InSingletonScope();
+            Bind<IProjectResolverFactory>().To<ProjectResolverFactory>();
         }
     }
 }

src/Core/Modules/UtilityModule.cs

 using Ninject.Modules;
+using RossCode.TrafficLight.Core.Adapters;
 using RossCode.TrafficLight.Core.Utilities;
 
 namespace RossCode.TrafficLight.Core.Modules
     {
         public override void Load()
         {
-            Kernel.Bind<IFileSystem>().To<FileSystem>();
-            Kernel.Bind<IWebClient>().To<WebClient>();
-            Kernel.Bind<ITimer>().To<Timer>();
+            Bind<IFileSystem>().To<FileSystem>();
+            Bind<IWebClient>().To<WebClient>();
+            Bind<ITimer>().To<Timer>();
+            Bind<IBusylightAdapter>().To<BusylightAdapter>();
         }
     }
 }

src/Core/Persistence/Mappings/IndicatorTypeDataMapper.cs

+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using RossCode.TrafficLight.Core.Domain;
+
+namespace RossCode.TrafficLight.Core.Persistence.Mappings
+{
+    public class IndicatorTypeDataMapper : IDataMapper<IndicatorType>
+    {
+        public IEnumerable<IndicatorType> MapAllFrom(IEnumerable<DataRow> dataRows)
+        {
+            return dataRows.Select(MapOneFrom).ToList();
+        }
+
+        public IndicatorType MapOneFrom(DataRow dataRow)
+        {
+            return (IndicatorType) int.Parse(dataRow["Id"].ToString());
+        }
+    }
+}

src/Core/Persistence/Queries/AllIndicatorTypesQuery.cs

+using System.Data;
+
+namespace RossCode.TrafficLight.Core.Persistence.Queries
+{
+    public class AllIndicatorTypesQuery : IQuery
+    {
+        public void Prepare(IDbCommand command)
+        {
+            command.CommandText = "select Id from Indicators";
+        }
+    }
+}

src/Core/Persistence/Queries/DeleteAllIndicatorsQuery.cs

+using System.Data;
+
+namespace RossCode.TrafficLight.Core.Persistence.Queries
+{
+    public class DeleteAllIndicatorsQuery : IQuery
+    {
+        public void Prepare(IDbCommand command)
+        {
+            command.CommandText = "delete from Indicators";
+        }
+    }
+}

src/Core/Persistence/Queries/InsertIndicatorQuery.cs

+using System.Data;
+using RossCode.TrafficLight.Core.Domain;
+using RossCode.TrafficLight.Core.Utilities.Extensions;
+
+namespace RossCode.TrafficLight.Core.Persistence.Queries
+{
+    public class InsertIndicatorQuery : IQuery
+    {
+        private readonly IndicatorType indicator;
+
+        public InsertIndicatorQuery(IndicatorType indicator)
+        {
+            this.indicator = indicator;
+        }
+
+        public void Prepare(IDbCommand command)
+        {
+            command.CommandText = "insert into Indicators (Id) values (@id);";
+            command.AddParameter("@id", (int)indicator);
+        }
+    }
+}

src/Core/Persistence/Repositories/IndicatorTypeRepository.cs

+using System.Collections.Generic;
+using RossCode.TrafficLight.Core.Domain;
+using RossCode.TrafficLight.Core.Domain.Repositories;
+using RossCode.TrafficLight.Core.Persistence.Framework;
+using RossCode.TrafficLight.Core.Persistence.Mappings;
+using RossCode.TrafficLight.Core.Persistence.Queries;
+
+namespace RossCode.TrafficLight.Core.Persistence.Repositories
+{
+    public class IndicatorTypeRepository : IIndicatorTypeRepository
+    {
+        private readonly IDatabaseGateway databaseGateway;
+        private readonly IDataMapper<IndicatorType> dataMapper;
+
+        public IndicatorTypeRepository(IDatabaseGateway databaseGateway, IDataMapper<IndicatorType> dataMapper)
+        {
+            this.databaseGateway = databaseGateway;
+            this.dataMapper = dataMapper;
+        }
+
+        public IEnumerable<IndicatorType> GetAll()
+        {
+            return dataMapper.MapAllFrom(databaseGateway.GetResultsFrom(new AllIndicatorTypesQuery()));
+        }
+
+        public void EnableIndicators(IEnumerable<IndicatorType> indicators)
+        {
+            databaseGateway.Execute(new DeleteAllIndicatorsQuery());
+
+            foreach (var indicator in indicators)
+            {
+                databaseGateway.Execute(new InsertIndicatorQuery(indicator));
+            }
+        }
+    }
+}

src/Core/Presenters/SettingsPresenter.cs

+using RossCode.TrafficLight.Core.Domain.Repositories;
+using RossCode.TrafficLight.Core.Presenters.Views;
+
+namespace RossCode.TrafficLight.Core.Presenters
+{
+    public class SettingsPresenter : PresenterBase<ISettingsView>
+    {
+        private readonly IIndicatorTypeRepository indicatorTypeRepository;
+
+        public SettingsPresenter(ISettingsView view, IIndicatorTypeRepository indicatorTypeRepository) : base(view)
+        {
+            this.indicatorTypeRepository = indicatorTypeRepository;
+            View.Load += (sender, args) => LoadConfiguration();
+            view.SaveClicked += SaveConfiguration;
+        }
+
+        private void SaveConfiguration()
+        {
+            indicatorTypeRepository.EnableIndicators(View.GetIndicatoryTypes());
+            View.Close();
+        }
+
+        private void LoadConfiguration()
+        {
+            View.SetSelectedProjectsTo(indicatorTypeRepository.GetAll());
+        }
+    }
+}

src/Core/Presenters/Views/ISettingsView.cs

+using System.Collections.Generic;
+using RossCode.TrafficLight.Core.Domain;
+using RossCode.TrafficLight.Core.Utilities;
+
+namespace RossCode.TrafficLight.Core.Presenters.Views
+{
+    public interface ISettingsView : IView
+    {
+        void SetSelectedProjectsTo(IEnumerable<IndicatorType> indicatorTypes);
+        event VoidHandler SaveClicked;
+        IList<IndicatorType> GetIndicatoryTypes();
+    }
+}

src/Core/RossCode.TrafficLight.Core.csproj

     <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="BusyLightHIDCommunications">
+      <HintPath>..\..\lib\busylight\BusyLightHIDCommunications.dll</HintPath>
+    </Reference>
     <Reference Include="CookComputing.XmlRpcV2">
       <HintPath>..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll</HintPath>
     </Reference>
+    <Reference Include="LinkLampConfiguration">
+      <HintPath>..\..\lib\busylight\LinkLampConfiguration.dll</HintPath>
+    </Reference>
     <Reference Include="Migrator, Version=0.9.0.33276, Culture=neutral, PublicKeyToken=3b3586e9632ecfce, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\MigratorDotNet.0.9.0.33276\lib\NET40\Migrator.dll</HintPath>
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Adapters\BusyLightAdapter.cs" />
     <Compile Include="Data\DataMigrator.cs" />
     <Compile Include="Data\IDatabaseCreator.cs" />
     <Compile Include="Data\IDatabaseMigrator.cs" />
+    <Compile Include="Data\Migrations\IndicatorsTable.cs" />
     <Compile Include="Data\Migrations\ProjectsTable.cs" />
     <Compile Include="Data\SqliteDatabaseCreator.cs" />
+    <Compile Include="Domain\IndicatorType.cs" />
     <Compile Include="Domain\Project.cs" />
+    <Compile Include="Domain\Repositories\IIndicatorTypeRepository.cs" />
     <Compile Include="Domain\Repositories\IProjectRepository.cs" />
     <Compile Include="Eventing\DomainEvent.cs" />
     <Compile Include="Eventing\Events\BuildStatusChanged.cs" />
     <Compile Include="Persistence\Framework\SqliteDatabaseConnectionFactory.cs" />
     <Compile Include="Persistence\Mappings\IDataMapper.cs" />
     <Compile Include="Persistence\Framework\SqliteDatabaseGateway.cs" />
+    <Compile Include="Persistence\Mappings\IndicatorTypeDataMapper.cs" />
     <Compile Include="Persistence\Mappings\ProjectDataMapper.cs" />
+    <Compile Include="Persistence\Queries\AllIndicatorTypesQuery.cs" />
     <Compile Include="Persistence\Queries\AllProjectsQuery.cs" />
+    <Compile Include="Persistence\Queries\DeleteAllIndicatorsQuery.cs" />
     <Compile Include="Persistence\Queries\DeleteProjectQuery.cs" />
+    <Compile Include="Persistence\Queries\InsertIndicatorQuery.cs" />
     <Compile Include="Persistence\Queries\IQuery.cs" />
     <Compile Include="Persistence\Queries\InsertProjectQuery.cs" />
+    <Compile Include="Persistence\Repositories\IndicatorTypeRepository.cs" />
     <Compile Include="Persistence\Repositories\ProjectRepository.cs" />
     <Compile Include="Persistence\Queries\UpdateProjectQuery.cs" />
     <Compile Include="Presenters\BuildStatusPresenter.cs" />
     <Compile Include="Presenters\EditProjectPresenter.cs" />
+    <Compile Include="Presenters\SettingsPresenter.cs" />
     <Compile Include="Presenters\Views\IBuildStatusView.cs" />
     <Compile Include="Presenters\Views\IEditProjectView.cs" />
     <Compile Include="Presenters\Views\IProjectsView.cs" />
+    <Compile Include="Presenters\Views\ISettingsView.cs" />
     <Compile Include="Presenters\Views\IView.cs" />
     <Compile Include="Presenters\PresenterBase.cs" />
     <Compile Include="Presenters\ProjectsPresenter.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Services\ApplicationService.cs" />
+    <Compile Include="Services\Indicators\BuildIndicatorService.cs" />
+    <Compile Include="Services\Indicators\BusyLightBuildStatusIndicatorService.cs" />
+    <Compile Include="Services\Indicators\DelcomTrafficLightBuildStatusIndicatorService.cs" />
+    <Compile Include="Services\Indicators\IndicatorTypeMaps.cs" />
+    <Compile Include="Services\ProjectMonitorService.cs" />
     <Compile Include="Services\DatabaseService.cs" />
-    <Compile Include="Services\DelcomTrafficLightProjectStatusIndicatorService.cs" />
     <Compile Include="Services\IDatabaseService.cs" />
     <Compile Include="Services\IProjectService.cs" />
     <Compile Include="Services\ProjectStatusResolvers\CruiseControlProjectStatusResolverService.cs" />

src/Core/Services/ApplicationService.cs

-using System;
-using System.Linq;
-using RossCode.TrafficLight.Core.Domain;
-using RossCode.TrafficLight.Core.Domain.Repositories;
-using RossCode.TrafficLight.Core.Eventing;
-using RossCode.TrafficLight.Core.Eventing.Events;
-using RossCode.TrafficLight.Core.Utilities;
-
-namespace RossCode.TrafficLight.Core.Services
-{
-    public interface IApplicationService
-    {
-        void StartMonitoring();
-    }
-
-    public class ApplicationService : IApplicationService
-    {
-        private readonly IProjectRepository projectRepository;
-        private readonly IProjectService projectService;
-        private readonly ITimer timer;
-
-        public ApplicationService(IProjectRepository projectRepository, IProjectService projectService, ITimer timer)
-        {
-            this.projectRepository = projectRepository;
-            this.projectService = projectService;
-            this.timer = timer;
-        }
-
-        public void StartMonitoring() 
-        {
-            var projects = projectRepository.GetAll().ToList();
-            if (!projects.Any())
-            {
-                DomainEvents.Raise(new EditProjectRequested(new Project()));
-            }
-            projectService.CheckAndSetStatusesFor(projects);
-
-            timer.TimerElapsed += (sender, args) => TimerElapsed();
-            timer.IntervalInSeconds = 60;
-            timer.Start();
-
-            DomainEvents.Register<ProjectAdded>(args => TimerElapsed());
-        }
-
-        private void TimerElapsed()
-        {
-            timer.Stop();
-            projectService.CheckAndSetStatusesFor(projectRepository.GetAll().ToList());
-            timer.Start();
-        }
-    }
-}

src/Core/Services/DelcomTrafficLightProjectStatusIndicatorService.cs

-using System;
-using System.Text;
-using Microsoft.Win32;
-using RossCode.TrafficLight.Core.Domain;
-using RossCode.TrafficLight.Core.Eventing;
-using RossCode.TrafficLight.Core.Eventing.Events;
-
-namespace RossCode.TrafficLight.Core.Services
-{
-    public class DelcomTrafficLightProjectStatusIndicatorService  : IDisposable
-    {
-        private readonly uint deviceHandle;
-        private BuildStatus currentBuildStatus;
-
-        public DelcomTrafficLightProjectStatusIndicatorService()
-        {
-            var deviceName = new StringBuilder(Delcom.MAXDEVICENAMELEN);
-
-            try
-            {
-                // Search for the first match USB device, For USB IO Chips use Delcom.USBIODS
-                // With Generation 2 HID devices, you can pass a TypeId of 0 to open any Delcom device.
-                var result = Delcom.DelcomGetNthDevice(0, 0, deviceName);
-
-                if (result == 0) return;
-
-                deviceHandle = Delcom.DelcomOpenDevice(deviceName, 0);
-                SetBuildStatus(BuildStatus.Unknown);
-                DomainEvents.Register<BuildStatusChanged>(Handle);
-
-                SystemEvents.PowerModeChanged += PowerModeChanged;
-            } 
-            catch { }
-        }
-
-        private void PowerModeChanged(object sender, PowerModeChangedEventArgs e)
-        {
-            if (e.Mode == PowerModes.Suspend)
-            {
-                ClearDisplayState();
-            }
-        }
-
-        public void SetBuildStatus(BuildStatus buildStatus)
-        {
-            if (currentBuildStatus != buildStatus)
-            {
-                ClearDisplayState();
-                currentBuildStatus = buildStatus;
-            }
-            ClearDisplayState();
-            switch (buildStatus)
-            {
-                case BuildStatus.Building:
-                    Delcom.DelcomLEDControl(deviceHandle, Delcom.YELLOWLED, Delcom.LEDON);
-                    break;
-
-                case BuildStatus.Failing:
-                    Delcom.DelcomLEDControl(deviceHandle, Delcom.REDLED, Delcom.LEDON);
-                    break;
-
-                case BuildStatus.Success:
-                    Delcom.DelcomLEDControl(deviceHandle, Delcom.GREENLED, Delcom.LEDON);
-                    break;
-            }
-        }
-
-        private void ClearDisplayState()
-        {
-            Delcom.DelcomLEDControl(deviceHandle, Delcom.GREENLED, Delcom.LEDOFF);
-            Delcom.DelcomLEDControl(deviceHandle, Delcom.YELLOWLED, Delcom.LEDOFF);
-            Delcom.DelcomLEDControl(deviceHandle, Delcom.REDLED, Delcom.LEDOFF);
-        }
-
-        public void Dispose()
-        {
-            try
-            {
-                ClearDisplayState();
-                Delcom.DelcomCloseDevice(deviceHandle);
-            }
-            catch { }
-        }
-
-        public void Handle(BuildStatusChanged arg)
-        {
-            SetBuildStatus(arg.Status);
-        }
-    }
-}

src/Core/Services/Indicators/BuildIndicatorService.cs

+using System.Collections.Generic;
+using RossCode.TrafficLight.Core.Domain.Repositories;
+
+namespace RossCode.TrafficLight.Core.Services.Indicators
+{
+    public interface IBuildIndicatorService
+    {
+        void StartIndicators();
+        void ShutDownIndicators();
+    }
+
+    public class BuildIndicatorService : IBuildIndicatorService
+    {
+        private readonly IIndicatorTypeRepository indicatorTypeRepository;
+        private readonly IList<IBuildStatusIndicatorService> services = new List<IBuildStatusIndicatorService>();
+
+        public BuildIndicatorService(IIndicatorTypeRepository indicatorTypeRepository)
+        {
+            this.indicatorTypeRepository = indicatorTypeRepository;
+        }
+
+        public void StartIndicators()
+        {
+            var indicatorTypes = indicatorTypeRepository.GetAll();
+
+            foreach (var indicatorType in indicatorTypes)
+            {
+                services.Add(IndicatorTypeMaps.GetIndicatorServiceFor[indicatorType]());
+            }
+            foreach (var service in services)
+            {
+                service.Start();
+            }
+        }
+
+        public void ShutDownIndicators()
+        {
+            foreach (var service in services)
+            {
+                service.Stop();
+            }
+        }
+    }
+}

src/Core/Services/Indicators/BusyLightBuildStatusIndicatorService.cs

+using System;
+using System.Collections.Generic;
+using RossCode.TrafficLight.Core.Adapters;
+using RossCode.TrafficLight.Core.Domain;
+using RossCode.TrafficLight.Core.Eventing;
+using RossCode.TrafficLight.Core.Eventing.Events;
+
+namespace RossCode.TrafficLight.Core.Services.Indicators
+{
+    public class BusyLightBuildStatusIndicatorService : IBuildStatusIndicatorService, IDisposable
+    {
+        private readonly IBusylightAdapter busylightAdapter;
+        private readonly IDictionary<BuildStatus, Action<IBusylightAdapter>> setBusylightStatus = new Dictionary<BuildStatus, Action<IBusylightAdapter>>
+            {
+                { BuildStatus.Success, adapter => adapter.TurnGreen() },
+                { BuildStatus.Building, adapter => adapter.TurnYellow() },
+                { BuildStatus.Failing, adapter => adapter.TurnRed() }
+            };
+
+        public BusyLightBuildStatusIndicatorService(IBusylightAdapter busylightAdapter)
+        {
+            this.busylightAdapter = busylightAdapter;
+        }
+
+        public void Start()
+        {
+            DomainEvents.Register<BuildStatusChanged>(Handle);
+            busylightAdapter.TurnOff();
+        }
+
+        private void Handle(BuildStatusChanged args)
+        {
+            setBusylightStatus[args.Status](busylightAdapter);
+        }
+
+        public void Stop()
+        {
+            busylightAdapter.TurnOff();
+        }
+
+        public void Dispose()
+        {
+            Stop();
+        }
+    }
+}

src/Core/Services/Indicators/DelcomTrafficLightBuildStatusIndicatorService.cs

+using System;
+using System.Text;
+using Microsoft.Win32;
+using RossCode.TrafficLight.Core.Domain;
+using RossCode.TrafficLight.Core.Eventing;
+using RossCode.TrafficLight.Core.Eventing.Events;
+
+namespace RossCode.TrafficLight.Core.Services.Indicators
+{
+    public class DelcomTrafficLightBuildStatusIndicatorService  : IBuildStatusIndicatorService, IDisposable
+    {
+        private uint deviceHandle;
+        private BuildStatus currentBuildStatus;
+
+        public void Stop()
+        {
+            try
+            {
+                ClearDisplayState();
+                Delcom.DelcomCloseDevice(deviceHandle);
+            }
+            catch { }
+        }
+
+        public void Start()
+        {
+            var deviceName = new StringBuilder(Delcom.MAXDEVICENAMELEN);
+
+            try
+            {
+                // Search for the first match USB device, For USB IO Chips use Delcom.USBIODS
+                // With Generation 2 HID devices, you can pass a TypeId of 0 to open any Delcom device.
+                var result = Delcom.DelcomGetNthDevice(0, 0, deviceName);
+
+                if (result == 0) return;
+
+                deviceHandle = Delcom.DelcomOpenDevice(deviceName, 0);
+                SetBuildStatus(BuildStatus.Unknown);
+                DomainEvents.Register<BuildStatusChanged>(Handle);
+
+                SystemEvents.PowerModeChanged += PowerModeChanged;
+            } 
+            catch { }
+        }
+
+        private void PowerModeChanged(object sender, PowerModeChangedEventArgs e)
+        {
+            if (e.Mode == PowerModes.Suspend)
+            {
+                ClearDisplayState();
+            }
+        }
+
+        public void SetBuildStatus(BuildStatus buildStatus)
+        {
+            if (currentBuildStatus != buildStatus)
+            {
+                ClearDisplayState();
+                currentBuildStatus = buildStatus;
+            }
+            ClearDisplayState();
+            switch (buildStatus)
+            {
+                case BuildStatus.Building:
+                    Delcom.DelcomLEDControl(deviceHandle, Delcom.YELLOWLED, Delcom.LEDON);
+                    break;
+
+                case BuildStatus.Failing:
+                    Delcom.DelcomLEDControl(deviceHandle, Delcom.REDLED, Delcom.LEDON);
+                    break;
+
+                case BuildStatus.Success:
+                    Delcom.DelcomLEDControl(deviceHandle, Delcom.GREENLED, Delcom.LEDON);
+                    break;
+            }
+        }
+
+        private void ClearDisplayState()
+        {
+            Delcom.DelcomLEDControl(deviceHandle, Delcom.GREENLED, Delcom.LEDOFF);
+            Delcom.DelcomLEDControl(deviceHandle, Delcom.YELLOWLED, Delcom.LEDOFF);
+            Delcom.DelcomLEDControl(deviceHandle, Delcom.REDLED, Delcom.LEDOFF);
+        }
+
+        public void Dispose()
+        {
+            Stop();
+        }
+
+        public void Handle(BuildStatusChanged arg)
+        {
+            SetBuildStatus(arg.Status);
+        }
+    }
+
+    public interface IBuildStatusIndicatorService
+    {
+        void Start();
+        void Stop();
+    }
+}

src/Core/Services/Indicators/IndicatorTypeMaps.cs

+using System;
+using System.Collections.Generic;
+using RossCode.TrafficLight.Core.Domain;
+
+namespace RossCode.TrafficLight.Core.Services.Indicators
+{
+    public static class IndicatorTypeMaps
+    {
+        public static readonly IDictionary<IndicatorType, Func<IBuildStatusIndicatorService>> GetIndicatorServiceFor = new Dictionary<IndicatorType, Func<IBuildStatusIndicatorService>>
+            {
+                { IndicatorType.Delcom, IoC.Resolve<DelcomTrafficLightBuildStatusIndicatorService> },
+                { IndicatorType.BusyLight, IoC.Resolve<BusyLightBuildStatusIndicatorService> }
+            };
+    }
+}

src/Core/Services/ProjectMonitorService.cs

+using System;
+using System.Linq;
+using RossCode.TrafficLight.Core.Domain;
+using RossCode.TrafficLight.Core.Domain.Repositories;
+using RossCode.TrafficLight.Core.Eventing;
+using RossCode.TrafficLight.Core.Eventing.Events;
+using RossCode.TrafficLight.Core.Utilities;
+
+namespace RossCode.TrafficLight.Core.Services
+{
+    public interface IProjectMonitorService
+    {
+        void StartMonitoring();
+    }
+
+    public class ProjectMonitorService : IProjectMonitorService
+    {
+        private readonly IProjectRepository projectRepository;
+        private readonly IProjectService projectService;
+        private readonly ITimer timer;
+
+        public ProjectMonitorService(IProjectRepository projectRepository, IProjectService projectService, ITimer timer)
+        {
+            this.projectRepository = projectRepository;
+            this.projectService = projectService;
+            this.timer = timer;
+        }
+
+        public void StartMonitoring() 
+        {
+            var projects = projectRepository.GetAll().ToList();
+            if (!projects.Any())
+            {
+                DomainEvents.Raise(new EditProjectRequested(new Project()));
+            }
+            projectService.CheckAndSetStatusesFor(projects);
+
+            timer.TimerElapsed += (sender, args) => TimerElapsed();
+            timer.IntervalInSeconds = 60;
+            timer.Start();
+
+            DomainEvents.Register<ProjectAdded>(args => TimerElapsed());
+        }
+
+        private void TimerElapsed()
+        {
+            timer.Stop();
+            projectService.CheckAndSetStatusesFor(projectRepository.GetAll().ToList());
+            timer.Start();
+        }
+    }
+}

src/Tests/Integration/Services/DelcomTrafficLightProjectStatusIndicatorServiceSpecs.cs

 using NUnit.Framework;
 using RossCode.TrafficLight.Core.Domain;
 using RossCode.TrafficLight.Core.Services;
+using RossCode.TrafficLight.Core.Services.Indicators;
 
 namespace RossCode.TrafficLight.Tests.Integration.Services
 {
         [Test]
         public void it_should_turn_the_light_green()
         {
-            using (var indicatorService = new DelcomTrafficLightProjectStatusIndicatorService())
+            using (var indicatorService = new DelcomTrafficLightBuildStatusIndicatorService())
             {
                 indicatorService.SetBuildStatus(BuildStatus.Success);
             }
         [Test]
         public void it_should_turn_the_light_red()
         {
-            using (var indicatorService = new DelcomTrafficLightProjectStatusIndicatorService())
+            using (var indicatorService = new DelcomTrafficLightBuildStatusIndicatorService())
             {
                 indicatorService.SetBuildStatus(BuildStatus.Failing);
             }
         [Test]
         public void it_should_turn_the_light_yellow()
         {
-            using (var indicatorService = new DelcomTrafficLightProjectStatusIndicatorService())
+            using (var indicatorService = new DelcomTrafficLightBuildStatusIndicatorService())
             {
                 indicatorService.SetBuildStatus(BuildStatus.Building);
             }

src/Tests/Presenters/SettingsPresenterSpecs.cs

+using System;
+using System.Collections.Generic;
+using Moq;
+using NUnit.Framework;
+using RossCode.TrafficLight.Core.Domain;
+using RossCode.TrafficLight.Core.Domain.Repositories;
+using RossCode.TrafficLight.Core.Presenters;
+using RossCode.TrafficLight.Core.Presenters.Views;
+using RossCode.TrafficLight.Tests.Utilties;
+
+namespace RossCode.TrafficLight.Tests.Presenters
+{
+    public static class SettingsPresenterSpecs
+    {
+        public class when_loading_the_view : under.the_default_context
+        {
+            protected override void When()
+            {
+                View.Raise(v => v.Load += null, this, EventArgs.Empty);
+            }
+
+            [Test]
+            public void it_should_set_the_selected_indicators_on_the_view()
+            {
+                View.Setup(v => v.SetSelectedProjectsTo(It.IsAny<IEnumerable<IndicatorType>>()));
+            }
+        }
+
+         public static class under
+         {
+             public abstract class the_default_context : ContextSpecification
+             {
+                 protected SettingsPresenter SUT;
+                 protected Mock<ISettingsView> View;
+                 protected Mock<IIndicatorTypeRepository> IndicatorTypeRepository;
+
+                 protected override void EstablishContext()
+                 {
+                     base.EstablishContext();
+
+                     View = new Mock<ISettingsView>();
+
+                     IndicatorTypeRepository = new Mock<IIndicatorTypeRepository>();
+
+                     SUT = new SettingsPresenter(View.Object, IndicatorTypeRepository.Object);
+                 }
+             }
+         }
+    }
+}

src/Tests/RossCode.TrafficLight.Tests.csproj

     <Compile Include="Persistence\Repositories\ProjectRepositorySpecs.cs" />
     <Compile Include="Presenters\EditProjectPresenterSpecs.cs" />
     <Compile Include="Presenters\ProjectsPresenterSpecs.cs" />
+    <Compile Include="Presenters\SettingsPresenterSpecs.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Services\ApplicationServiceSpecs.cs" />
     <Compile Include="Services\CruiseControlProjectStatusResolverServiceSpecs.cs" />

src/Tests/Services/ApplicationServiceSpecs.cs

                 protected Mock<ITimer> Timer;
                 protected IList<Project> Projects;
 
-                protected IApplicationService SUT;
+                protected IProjectMonitorService SUT;
 
                 protected override void EstablishContext()
                 {
                     ProjectService = new Mock<IProjectService>();
                     Timer = new Mock<ITimer>();
 
-                    SUT = new ApplicationService(ProjectRepository.Object, ProjectService.Object, Timer.Object);
+                    SUT = new ProjectMonitorService(ProjectRepository.Object, ProjectService.Object, Timer.Object);
                 }
             }
         }

src/UI/Modules/UIModule.cs

             Bind<IProjectsView>().To<ProjectsView>();
             Bind<IBuildStatusView>().To<BuildStatusView>();
             Bind<IEditProjectView>().To<EditProjectView>();
+            Bind<ISettingsView>().To<SettingsView>();
             
             Bind<TrafficLightApplicationContext>().ToSelf().InSingletonScope();
         }

src/UI/RossCode.TrafficLight.UI.csproj

       <DependentUpon>Resources.resx</DependentUpon>
       <DesignTime>True</DesignTime>
     </Compile>
+    <EmbeddedResource Include="SettingsView.resx">
+      <DependentUpon>SettingsView.cs</DependentUpon>
+    </EmbeddedResource>
     <None Include="app.config" />
     <None Include="packages.config" />
     <None Include="Properties\Settings.settings">
       <DependentUpon>Settings.settings</DependentUpon>
       <DesignTimeSharedInput>True</DesignTimeSharedInput>
     </Compile>
+    <Compile Include="SettingsView.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="SettingsView.Designer.cs">
+      <DependentUpon>SettingsView.cs</DependentUpon>
+    </Compile>
     <Compile Include="TrafficLightApplicationContext.cs" />
   </ItemGroup>
   <ItemGroup>

src/UI/SettingsView.Designer.cs

+namespace RossCode.TrafficLight.UI
+{
+    partial class SettingsView
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.chkBuildIndicatorDelcom = new System.Windows.Forms.CheckBox();
+            this.btnSave = new System.Windows.Forms.Button();
+            this.lblInstructions = new System.Windows.Forms.Label();
+            this.chkBuildIndicatorBusyLight = new System.Windows.Forms.CheckBox();
+            this.SuspendLayout();
+            // 
+            // chkBuildIndicatorDelcom
+            // 
+            this.chkBuildIndicatorDelcom.AutoSize = true;
+            this.chkBuildIndicatorDelcom.Location = new System.Drawing.Point(15, 25);
+            this.chkBuildIndicatorDelcom.Name = "chkBuildIndicatorDelcom";
+            this.chkBuildIndicatorDelcom.Size = new System.Drawing.Size(121, 17);
+            this.chkBuildIndicatorDelcom.TabIndex = 0;
+            this.chkBuildIndicatorDelcom.Text = "Delcom Traffic Light";
+            this.chkBuildIndicatorDelcom.UseVisualStyleBackColor = true;
+            // 
+            // btnSave
+            // 
+            this.btnSave.Location = new System.Drawing.Point(110, 71);
+            this.btnSave.Name = "btnSave";
+            this.btnSave.Size = new System.Drawing.Size(75, 23);
+            this.btnSave.TabIndex = 1;
+            this.btnSave.Text = "Save";
+            this.btnSave.UseVisualStyleBackColor = true;
+            // 
+            // lblInstructions
+            // 
+            this.lblInstructions.AutoSize = true;
+            this.lblInstructions.Location = new System.Drawing.Point(12, 9);
+            this.lblInstructions.Name = "lblInstructions";
+            this.lblInstructions.Size = new System.Drawing.Size(245, 13);
+            this.lblInstructions.TabIndex = 2;
+            this.lblInstructions.Text = "Select the build indicators you would like to enable";
+            // 
+            // chkBuildIndicatorBusyLight
+            // 
+            this.chkBuildIndicatorBusyLight.AutoSize = true;
+            this.chkBuildIndicatorBusyLight.Location = new System.Drawing.Point(15, 48);
+            this.chkBuildIndicatorBusyLight.Name = "chkBuildIndicatorBusyLight";
+            this.chkBuildIndicatorBusyLight.Size = new System.Drawing.Size(90, 17);
+            this.chkBuildIndicatorBusyLight.TabIndex = 3;
+            this.chkBuildIndicatorBusyLight.Text = "BusyLight UC";
+            this.chkBuildIndicatorBusyLight.UseVisualStyleBackColor = true;
+            // 
+            // SettingsView
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(289, 100);
+            this.Controls.Add(this.chkBuildIndicatorBusyLight);
+            this.Controls.Add(this.lblInstructions);
+            this.Controls.Add(this.btnSave);
+            this.Controls.Add(this.chkBuildIndicatorDelcom);
+            this.Name = "SettingsView";
+            this.Text = "SettingsView";
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.CheckBox chkBuildIndicatorDelcom;
+        private System.Windows.Forms.Button btnSave;
+        private System.Windows.Forms.Label lblInstructions;
+        private System.Windows.Forms.CheckBox chkBuildIndicatorBusyLight;
+    }
+}

src/UI/SettingsView.cs

+using System.Collections.Generic;
+using RossCode.TrafficLight.Core.Domain;
+using RossCode.TrafficLight.Core.Presenters.Views;
+using System.Linq;
+using RossCode.TrafficLight.Core.Utilities;
+
+namespace RossCode.TrafficLight.UI
+{
+    public partial class SettingsView : BaseForm, ISettingsView
+    {
+        public SettingsView()
+        {
+            InitializeComponent();
+            btnSave.Click += (sender, args) => SaveClicked();
+
+        }
+
+        public void SetSelectedProjectsTo(IEnumerable<IndicatorType> indicatorTypes)
+        {
+            var indicators = indicatorTypes as IList<IndicatorType> ?? indicatorTypes.ToList();
+            chkBuildIndicatorDelcom.Checked = (indicators.Contains(IndicatorType.Delcom));
+            chkBuildIndicatorBusyLight.Checked = (indicators.Contains(IndicatorType.BusyLight)); 
+        }
+
+        public IList<IndicatorType> GetIndicatoryTypes()
+        {
+            var indicators = new List<IndicatorType>();
+
+            if (chkBuildIndicatorDelcom.Checked) indicators.Add(IndicatorType.Delcom);
+            if (chkBuildIndicatorBusyLight.Checked) indicators.Add(IndicatorType.BusyLight);
+
+            return indicators;
+        } 
+
+        public event VoidHandler SaveClicked = delegate { };
+    }
+}

src/UI/SettingsView.resx

+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

src/UI/TrafficLightApplicationContext.cs

 using RossCode.TrafficLight.Core.Presenters;
 using RossCode.TrafficLight.Core.Presenters.Views;
 using RossCode.TrafficLight.Core.Services;
+using RossCode.TrafficLight.Core.Services.Indicators;
 using RossCode.TrafficLight.UI.Properties;
 
 namespace RossCode.TrafficLight.UI
 {
     public class TrafficLightApplicationContext : ApplicationContext
     {
-        private readonly IApplicationService applicationService;
+        private readonly IProjectMonitorService projectMonitorService;
+        private readonly IBuildIndicatorService buildIndicatorService;
 
         private System.ComponentModel.IContainer components;
 		private NotifyIcon trafficLightNotifyIcon;
 		private MenuItem exitContextMenuItem;
 		private MenuItem showProjectsContextMenuItem;
 		private MenuItem showBuildStatusMenuItem;
+		private MenuItem showSettingsMenuItem;
 
         private readonly IDictionary<BuildStatus, Action> setBuildIcon;
         private readonly IDictionary<BuildStatus, Action> showToolTip;
         private BuildStatus currentBuildStatus;
 
-        public TrafficLightApplicationContext(IApplicationService applicationService) 
+        public TrafficLightApplicationContext(IProjectMonitorService projectMonitorService, IBuildIndicatorService buildIndicatorService) 
 		{
 			InitializeContext();
-            this.applicationService = applicationService;
-            InitializeBuildMonitors();
+            this.projectMonitorService = projectMonitorService;
+            this.buildIndicatorService = buildIndicatorService;
+
+            buildIndicatorService.StartIndicators();
 
             setBuildIcon = new Dictionary<BuildStatus, Action>
                 {
             DomainEvents.Register<BuildStatusChanged>(ChangeBuildStatus);
             DomainEvents.Register<EditProjectRequested>(EditProject);
 
-            this.applicationService.StartMonitoring();
+            this.projectMonitorService.StartMonitoring();
 		}
 
         private void EditProject(EditProjectRequested args)
 		    InitializeNotifyIconContextMenu();
 		}
 
-        private static void InitializeBuildMonitors()
-        {
-            IoC.Resolve<DelcomTrafficLightProjectStatusIndicatorService>();
-        }
-
         private void InitializeNotifyIconContextMenu()
         {
             showProjectsContextMenuItem = new MenuItem();
             showBuildStatusMenuItem = new MenuItem();
             exitContextMenuItem = new MenuItem();
+            showSettingsMenuItem = new MenuItem();
 
-            trafficLightNotifyIconContextMenu.MenuItems.AddRange(new[] { showBuildStatusMenuItem, showProjectsContextMenuItem, exitContextMenuItem });
+            trafficLightNotifyIconContextMenu.MenuItems.AddRange(new[] { showBuildStatusMenuItem, showProjectsContextMenuItem, showSettingsMenuItem, exitContextMenuItem });
 
             var menuIndex = 0;
             showBuildStatusMenuItem.Index = menuIndex;
             showProjectsContextMenuItem.Click += (sender, e) => ActivateForm<ProjectsPresenter, IProjectsView>();
             menuIndex++;
 
+            showSettingsMenuItem.Index = menuIndex;
+            showSettingsMenuItem.Text = "Show Se&showBuildStatusMenuItemttings";
+            showSettingsMenuItem.Click += (sender, args) => ActivateForm<SettingsPresenter, ISettingsView>();
+            menuIndex++;
+
             exitContextMenuItem.Index = menuIndex;
             exitContextMenuItem.Text = "&Exit";
             exitContextMenuItem.Click += (sender, e) => ExitThread();
                 };
         }
 
+        protected override void ExitThreadCore()
+        {
+            buildIndicatorService.ShutDownIndicators();
+            base.ExitThreadCore();
+        }
+
         private void DisposeView(IDisposable view)
         {
             if (view != null)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.