/// <summary>
        /// Manage a race from real sensor data
        /// </summary>
        public void Start(RaceConfig config, List <Rider> riders)
        {
            Stop();

            CommunicationManager CommunicationManager = new CommunicationManager(source.Token);

            SerialTimingUnit timer = new SerialTimingUnit(CommunicationManager.GetCommunicationDevice(config.TimingUnitId), "timerUnit", source.Token, config.StartTimingGateId, config.EndTimingGateId);

            timing = timer;
            displays.Add(timer);
            BLERiderIdUnit realStartId = new BLERiderIdUnit(CommunicationManager.GetCommunicationDevice(config.StartIdUnitId), "startUnit", config.StartIdRange, source.Token);

            endGate = new BLERiderIdUnit(CommunicationManager.GetCommunicationDevice(config.EndIdUnitId), "finishUnit", config.EndIdRange, source.Token);

            startGate  = realStartId;
            startLight = realStartId;

            startGate?.ClearKnownRiders();
            endGate?.ClearKnownRiders();

            startLight.SetStartLightColor(StartLightColor.YELLOW);

            startGate.AddKnownRiders(riders);

            tracker = new RaceTracker(timing, startGate, endGate, config.ExtractTrackerConfig(), riders);
            HookEvents(tracker);

            CombinedTasks = tracker.Run(source.Token);
        }
        public void Start(IRaceTracker tracker, List <IDisplayUnit> displays)
        {
            Stop();
            this.displays = displays;

            this.tracker = tracker;
            HookEvents(tracker);

            CombinedTasks = tracker.Run(source.Token);
        }
        private void HookEvents(IRaceTracker tracker)
        {
            tracker.OnRiderFinished += HandleFinish;

            tracker.OnRiderFinished += (o, e) => Log.Info($"Rider {e.Lap.Rider.Name} finished with a lap time of {e.Lap.GetLapTime()} microseconds");
            tracker.OnRiderDNF      += (o, e) => Log.Info($"Rider {e.Lap.Rider.Name} did not finish since {(e.Lap.End as UnitDNFEvent).OtherRider} finshed before them");
            tracker.OnRiderWaiting  += (o, e) => Log.Info($"Rider {e.Rider.Rider.Name} can start");
            tracker.OnStartEmpty    += (o, e) => Log.Info("Start box is empty");

            tracker.OnRiderWaiting += (o, e) => startLight?.SetStartLightColor(StartLightColor.GREEN);
            tracker.OnStartEmpty   += (o, e) => startLight?.SetStartLightColor(StartLightColor.YELLOW);
        }
        /// <summary>
        /// Simulates a race from a json that contains all the race events
        /// </summary>
        /// <param name="simulationData"></param>
        public void Start(RaceSummary simulationData)
        {
            Stop();

            XmlConfigurator.Configure(new FileInfo("logConfig.xml"));

            //we need the simulation specific methods in the constructor
            SimulationRiderIdUnit startId    = new SimulationRiderIdUnit(simulationData.StartId, simulationData);
            SimulationRiderIdUnit endId      = new SimulationRiderIdUnit(simulationData.EndId, simulationData);
            SimulationTimingUnit  timingUnit = new SimulationTimingUnit(simulationData);

            displays.Add(timingUnit);
            startGate = startId;
            endGate   = endId;
            timing    = timingUnit;

            startId.Initialize();
            endId.Initialize();
            timingUnit.Initialize();

            tracker = new RaceTracker(timing, startId, endId, simulationData.Config, simulationData.Riders);
            HookEvents(tracker);

            var trackTask = tracker.Run(source.Token);

            var startTask = startId.Run(source.Token);
            var endTask   = endId.Run(source.Token);
            var timeTask  = timingUnit.Run(source.Token);

            //will complete when all units run out of events to simulate
            var unitsTask = Task.WhenAll(startTask, endTask, timeTask);

            CombinedTasks = Task.Run(() =>
            {
                unitsTask.Wait();
                source.Cancel();
                trackTask.Wait();
            });
        }