Example #1
0
        public void CreateBoatTest()
        {
            var apiBoat    = DefaultBoat();
            var boatEntity = BoatData.CreateBoat(apiBoat);

            Assert.IsTrue(boatEntity.Identifier != 0, $"Failed to create boat {apiBoat.BuilderName} {apiBoat.ModelYear} {apiBoat.Model} ");
        }
Example #2
0
        /// <summary>
        /// Tries to load everything from the saved data directory
        /// </summary>
        /// <param name="objects">Reference list to store the loaded objects in</param>
        public void TryLoadAll(ref List <DataObject> objects)
        {
            if (!Directory.Exists(directory))
            {
                return;
            }

            objects.AddRange(LoadFiles(directory + "/Members",
                                       (readData) =>
            {
                var res = new MemberData(
                    dataController,
                    readData[1],
                    long.Parse(readData[2]),
                    readData[3],
                    readData[4]
                    );

                return(res);
            }));
            objects.AddRange(LoadFiles(directory + "/Boats",
                                       (readData) =>
            {
                var res = new BoatData(
                    dataController,
                    readData[1],
                    long.Parse(readData[2]),
                    (MemberData)dataController.RetrieveByID(readData[3]),
                    int.Parse(readData[4]),
                    (BoatType)Enum.Parse(typeof(BoatType), readData[5])
                    );

                return(res);
            }));
        }
Example #3
0
 public void Setup(BoatData boat)
 {
     _boatData     = boat;
     _boat         = boat.Boat;
     nameText.text = boat.boatName;
     _rect         = transform as RectTransform;
 }
Example #4
0
        /// <summary>
        /// Tries to load everything from the saved data directory
        /// </summary>
        /// <param name="objects">Reference list to store the loaded objects in</param>
        public void TryLoadAll(ref List <DataObject> objects)
        {
            if (!Directory.Exists(directory))
            {
                return;
            }

            objects.AddRange(LoadFiles(directory + "/Members",
                                       (readData) =>
            {
                var res            = new MemberData(dataController, readData[1], long.Parse(readData[2]));
                res.Name           = readData[3];
                res.PersonalNumber = readData[4];

                return(res);
            }));
            objects.AddRange(LoadFiles(directory + "/Boats",
                                       (readData) =>
            {
                var res = new BoatData(dataController, readData[1], long.Parse(readData[2]));

                var owner = (MemberData)dataController.RetrieveByID(readData[3]);
                owner.RegisterBoat(res);

                res.Length   = int.Parse(readData[4]);
                res.BoatType = (BoatType)Enum.Parse(typeof(BoatType), readData[5]);

                return(res);
            }));
        }
Example #5
0
        private Boat UpdateBoat(Boat apiBoat)
        {
            var boatEntity = BoatData.UpdateBoat(apiBoat);

            Assert.IsNotNull(boatEntity.Identifier, "UpdateBoat Failed. Identifier is null");
            return(boatEntity);
        }
Example #6
0
        public void GetBoatTest()
        {
            var apiBoat    = DefaultBoat();
            var boatEntity = BoatData.CreateBoat(apiBoat);
            var boat       = BoatData.GetBoatbyId(boatEntity.Identifier);

            Assert.IsNotNull(boat.BuilderName, $"No boat is retrieved");
        }
Example #7
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Boat" /> class.
        /// </summary>
        /// <param name="boatData">The boat data.</param>
        public Boat(BoatData boatData)
        {
            StartNumber = boatData.BibNumber;
            Club        = boatData.TitleShort;

            Comment    = "";
            Status     = BoatStatus.None;
            Visibility = Visibility.Visible;
        }
Example #8
0
        public void CreateAndUpdateBoatTest()
        {
            var apiBoat    = DefaultBoat();
            var boatEntity = BoatData.CreateBoat(apiBoat);

            boatEntity.Model = "ChangedModel";
            var updatedBoat = UpdateBoat(boatEntity);

            Assert.IsTrue(boatEntity.Identifier == updatedBoat.Identifier, $"UpdateBoat failed. Id's {boatEntity.Identifier}!={updatedBoat.Identifier }");
            Assert.IsTrue("ChangedModel" == updatedBoat.Model, $"UpdateBoat failed. OriginalModel!={updatedBoat.Model}");
        }
Example #9
0
        public void SetBoatData(string targetID, string ownerID, int length, BoatType type)
        {
            BoatData bd = (BoatData)RetrieveByID(targetID);

            if (ownerID != null)
            {
                ((MemberData)RetrieveByID(ownerID)).RegisterBoat(bd);
            }
            bd.Length   = length;
            bd.BoatType = type;
            fileController.SaveToFile(bd);
        }
Example #10
0
        public void CreateAndDeleteBoatTest()
        {
            Boat apiBoat = null;

            apiBoat = DefaultBoat();
            var boatEntity = BoatData.CreateBoat(apiBoat);

            BoatData.DeleteBoatbyId(boatEntity.Identifier);
            var boat = BoatData.GetBoatbyId(boatEntity.Identifier);

            Assert.IsNull(boat, $"The boat is not deleted");
        }
Example #11
0
        public async Task <IHttpActionResult> GetBoat(int boatId)
        {
            var result = await Task <Boat> .Run(() => BoatData.GetBoatbyId(boatId));

            if (result != null)
            {
                return(Ok(result));
            }
            else
            {
                return(Content(HttpStatusCode.NotFound, $"No boat is available for the boatId provided"));
            }
        }
Example #12
0
        public async Task <IHttpActionResult> GetBoat()
        {
            var result = await Task <Boats> .Run(() => BoatData.GetBoats());

            if (result != null)
            {
                return(Ok(result));
            }
            else
            {
                return(Content(HttpStatusCode.NotFound, $"No Boats available"));
            }
        }
Example #13
0
        public void GetAllBoatTest()
        {
            var boat1      = DefaultBoat();
            var boatEntity = BoatData.CreateBoat(boat1);

            Assert.IsNotNull(boatEntity, $"CreateBoat failed.");
            var boat2 = DefaultAdditionalBoat();
            var boatEntityAdditional = BoatData.CreateBoat(boat2);

            Assert.IsNotNull(boatEntityAdditional, $"CreateBoat failed.");
            var boats = BoatData.GetBoats();

            Assert.IsTrue(boats.BoatCollection.Count > 0, $"All boats are not retrieved");
        }
Example #14
0
        public async Task <IHttpActionResult> PutBoat(Boat boat)
        {
            if (boat.Identifier == 0)
            {
                return(Content(HttpStatusCode.BadRequest, $"Identifier should be provided"));
            }
            var result = await Task <Boat> .Run(() => BoatData.UpdateBoat(boat));

            if (result != null)
            {
                return(Ok(result));
            }
            else
            {
                return(Content(HttpStatusCode.NotFound, $"No boat is available for the Identifier provided"));
            }
        }
Example #15
0
        public async Task <IHttpActionResult> PostBoat(Boat boat)
        {
            if (boat.Identifier != 0)
            {
                return(Content(HttpStatusCode.BadRequest, $"Identifier should be null"));
            }

            var result = await Task <Boat> .Run(() => BoatData.CreateBoat(boat));

            if (result != null)
            {
                return(Ok(result));
            }
            else
            {
                return(Content(HttpStatusCode.BadRequest, $"Not able to add the boat"));
            }
        }
Example #16
0
        public void Setup(BoatData boat, float scale = 0.0028f) // TODO magic number for mini map size
        {
            _boatData      = boat;
            _boat          = boat.Boat;
            _boatTransform = boat.Boat.transform;
            _rect          = transform as RectTransform;
            _scale         = scale;

            var p = _boatData.livery.primaryColor;

            p.a           = 1f;
            primary.color = p;
            var t = _boatData.livery.trimColor;

            t.a             = 1f;
            secondary.color = t;

            _playerCount = RaceManager.RaceData.boatCount;
        }
Example #17
0
        private void AddButton_OnClick(object sender, RoutedEventArgs e)
        {
            string id = IdInput.Text.ToUpperInvariant();

            if (!int.TryParse(WeightInput.Text, out int weight))
            {
                DoError();
            }
            if (!int.TryParse(SpeedInput.Text, out int speed))
            {
                DoError();
            }
            if (!int.TryParse(UniqueInput.Text, out int unique))
            {
                DoError();
            }
            var data = new BoatData
            {
                Type           = HarborHelper.RegisteredBoatTypes[(string)TypeCombo.SelectedValue].FullName,
                Prefix         = template.IdentityCode.First(),
                Code           = id,
                TopSpeed       = speed,
                Weight         = weight,
                Characteristic = unique
            };

            try
            {
                var newBoat = Boat.FromData(data);
                control.AddBoat(newBoat);
                Close();
            }
            catch
            {
                DoError();
            }
        }
Example #18
0
        /// <summary>
        /// Helper function for reading BoatData to a human-readable string
        /// </summary>
        /// <param name="data"></param>
        /// <param name="displayMode"></param>
        /// <returns></returns>
        string BoatToString(BoatData data, ListDisplayMode displayMode)
        {
            var owner = data.Owner;

            return($"ID: {data.ID}, {data.Length}m {data.BoatType}, " + (owner == null ? "ownerless" :$" owned by {owner.Name} (ID: {owner.ID})"));
        }
Example #19
0
        /// <summary>
        /// Shows a context-sensitive menu for listing, viewing, creating, editing, or deleting boats
        /// </summary>
        /// <param name="dataMode"></param>
        /// <param name="selectedID"></param>
        void ShowBoatMenu(DataMode dataMode, string selectedID = null)
        {
            Console.Clear();

            string   selected     = selectedID;
            BoatData selectedData = null;

            if (dataMode != DataMode.Create)
            {
                if (selected == null)
                {
                    selected = SelectBoat($"Please select the boat whose details you wish to {(dataMode == DataMode.View?"view":"edit")}:\n");
                }

                selectedData = (BoatData)DataController.RetrieveByID(selected);
            }

            switch (dataMode)
            {
            case DataMode.Create:
            case DataMode.Edit:
            {
                Dictionary <string, object> arguments = new Dictionary <string, object>();

                var owner = SelectMember("Please select the owner to register the new boat on\n");
                arguments.Add("owner", owner);

                Console.Write("Specify length of vessel in metre (m):");
                arguments.Add("length", int.Parse(Console.ReadLine()));

                var boatType = Enum.Parse(typeof(BoatType), ListSelection <string>("Please specify type of vessel", Enum.GetNames(typeof(BoatType)).ToList <object>()));
                arguments.Add("boat-type", boatType);

                if (dataMode == DataMode.Create)
                {
                    DataController.CreateData(typeof(BoatData), arguments);
                }
                else
                {
                    DataController.ChangeData(selected, arguments);
                }
                break;
            }

            case DataMode.View:
            {
                Console.Clear();
                Console.Write(
                    $"Boat ID: {selectedData.ID}\n" +
                    $"Time of creation: {selectedData.Timestamp}\n\n" +
                    $"Length: {selectedData.Length} metre\n" +
                    $"Type: {selectedData.BoatType}\n" +
                    (selectedData.Owner == null ? $"Owner: Owner not found\n" :
                     $"Owner: {selectedData.Owner.Name} (M-ID: {selectedData.Owner.ID})\n"));

                Console.WriteLine("\nPress E to edit this - Press D to delete this - Any else to return to main menu...");

                var key = Console.ReadKey().Key;

                switch (key)
                {
                case ConsoleKey.E: ShowBoatMenu(DataMode.Edit, selected); break;

                case ConsoleKey.D: ShowDeleteConfirmation(selected); break;

                default: return;
                }

                break;
            }
            }
        }
        /// <summary>
        /// Loads the data from database.
        /// </summary>
        private void LoadDataFromDb()
        {
            // Event-Id aus den Einstellungen holen
            var eventId = Properties.Settings.Default.EventId;

            using (var db = new AquariusDataContext(Properties.Settings.Default.ConnectionString))
            {
                // Test-Zugriff auf DB
                try
                {
                    var test = db.Events.ToList();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Fehler beim Datenbank-Zugriff! Verbindung zur Datenbank und Connection String in den Einstellungen prüfen.\r\n\r\nDetails:\r\n\r\n" + ex.ToString());
                    return;
                }

                var dbData = GlobalData.Instance;

                // Veranstaltungen laden
                var dbEvents = db.Events;
                dbData.EventsData = dbEvents.Select(e => new EventData()
                {
                    Id = e.Event_ID, Title = e.Event_Title
                }).ToList();
                if (dbData.EventsData.SingleOrDefault(ed => ed.Id == eventId) == null)
                {
                    MessageBox.Show("Veranstaltung nicht gefunden! Veranstaltung in den Einstellungen auswählen und Programm neu starten!");
                    return;
                }

                // Altersklassen einlesen
                var dbAgeClasses = db.AgeClasses.ToDictionary(ac => ac.AgeClass_ID, ac => ac);

                // Bootsklassen einlesen
                var dbBoatClasses = db.BoatClasses.ToDictionary(bc => bc.BoatClass_ID, bc => bc);

                // Ausschreibung einlesen
                var dbOffers = db.Offers.Where(o => o.Offer_Event_ID_FK == eventId).ToDictionary(o => o.Offer_ID, o => o);

                // Wettkämpfe einlesen
                var dbCompetions = db.Comps.Where(c => (c.Comp_Event_ID_FK == eventId) && c.Comp_DateTime != null).OrderBy(c => c.Comp_DateTime);

                // Boote einlesen (Entries sind Boote, in jedem Boot sitzt eine Mannschaft)
                // (gruppiert nach Meldung)
                var dbEntries = db.Entries.Where(e => e.Entry_Event_ID_FK == eventId).GroupBy(ee => ee.Entry_Race_ID_FK).ToDictionary(ee => ee.Key, ee => ee.Select(x => x).ToList());

                // Mannschaften einlesen
                // (gruppiert nach dem Boot, in dem sie sitzt)
                var dbCrews = db.Crews.GroupBy(c => c.Crew_Entry_ID_FK).ToDictionary(c => c.Key, c => c.Select(x => x).ToList());

                // Ruderer einlesen
                var dbAthlets = db.Athlets.ToDictionary(a => a.Athlet_ID, a => a);

                // Vereine einlesen
                var dbClubs = db.Clubs.ToDictionary(c => c.Club_ID, c => c);

                // Mappings Boot (!) <-> Verein
                var dbEntryLabels = db.EntryLabels.ToDictionary(el => el.EL_ID, el => el);

                // Labels für Boote (!)
                var dbLabels = db.Labels.ToDictionary(l => l.Label_ID, l => l);

                // Rennen tauchen mehrfach auf, falls es z. B. mehrere Leistungsgruppen gibt.
                // Da die Läufe aber ggf. noch nicht eingeteilt sind, lassen sich die Personen nicht
                // den einzelnen Rennen/Läufen zuweisen.
                // Derartig mehrfach vorkommende Rennen werden daher zu einem Rennen zusammengefasst:
                var competitionGroups = dbCompetions.GroupBy(c => c.Comp_Race_ID_FK).ToList();

                #region Wettkämpfe durchgehen

                dbData.RacesData = new List <RaceData>();
                foreach (var competionGroup in competitionGroups)
                {
                    _ = dbOffers.TryGetValue((int)competionGroup.Key, out var offer);
                    _ = dbAgeClasses.TryGetValue(offer.Offer_AgeClass_ID_FK, out var ageClass);
                    _ = dbBoatClasses.TryGetValue(offer.Offer_BoatClass_ID_FK, out var boatClass);

                    var  isChildrenRace    = (ageClass.AgeClass_MaxAge <= 14);
                    bool isLightweightRace = (offer.Offer_IsLightweight);
                    var  isCoxedRace       = (boatClass.BoatClass_Coxed == 1);

                    // Entscheiden, ob man das Rennen übernehmen muss
                    var raceOk = false;
                    if (isLightweightRace)
                    {
                        raceOk = true;                    // alle Leichtgewichtsrennen müssen zur Waage
                    }
                    if (isCoxedRace && !isChildrenRace)
                    {
                        raceOk = true;                                 // gesteuerte Rennen, die keine Kinderrennen sind, müssen zur Waage
                    }
                    if (raceOk)
                    {
                        // Rennzeit ist die Zeit des ersten Rennens dieser Gruppe
                        var raceTime = (DateTime)competionGroup.Select(x => x).OrderBy(c => c.Comp_DateTime).First().Comp_DateTime;

                        #region Gewichte bestimmen

                        var maxSingleWeight        = new Weight(ageClass.AgeClass_LW_UpperLimit, true);
                        var maxAverageWeight       = new Weight(ageClass.AgeClass_LW_AvgLimit, true);
                        var minCoxWeight           = new Weight(ageClass.AgeClass_LW_CoxLowerLimit, true);
                        var maxAdditionalCoxWeight = new Weight(ageClass.AgeClass_LW_CoxTolerance, true);

                        // wenn es kein gesteuertes Rennen ist, dann interessiert das Gewicht des Steuermanns nicht
                        if (!isCoxedRace)
                        {
                            minCoxWeight.Value = null;
                        }

                        // bei Kinderrennen interessiert das Gewicht des Steuermanns ebenfalls nicht
                        if (isChildrenRace)
                        {
                            minCoxWeight.Value = null;
                        }

                        // bei Einer-Rennen kein Mannschaftsdurchschnittsgewicht
                        if (boatClass.BoatClass_NumRowers == 1)
                        {
                            maxAverageWeight.Value = null;
                        }

                        // Wenn es kein Leichgewichtsrennen ist, dann interessieren auch die Gewichte der Ruderer nicht
                        if (!isLightweightRace)
                        {
                            maxSingleWeight.Value  = null;
                            maxAverageWeight.Value = null;
                        }

                        #endregion

                        // Rennen definieren und hinzufügen
                        var newRace = new RaceData()
                        {
                            Id                     = offer.Offer_ID,
                            RaceNumber             = offer.Offer_RaceNumber,
                            ShortTitle             = offer.Offer_ShortLabel,
                            LongTitle              = offer.Offer_LongLabel,
                            DateTime               = raceTime,
                            IsChildrenRace         = isChildrenRace,
                            IsCoxedRace            = isCoxedRace,
                            NumberOfRowers         = boatClass.BoatClass_NumRowers,
                            IsLightweightRace      = isLightweightRace,
                            MaxSingleWeight        = maxSingleWeight,
                            MaxAverageWeight       = maxAverageWeight,
                            MinCoxWeight           = minCoxWeight,
                            MaxAdditionalCoxWeight = maxAdditionalCoxWeight
                        };

                        dbData.RacesData.Add(newRace);

                        // zum aktuellen Rennen die Boote hinzufügen
                        newRace.BoatsData = new List <BoatData>();
                        _ = dbEntries.TryGetValue(offer.Offer_ID, out var entries);

                        if (entries != null)
                        {
                            foreach (var entry in entries)
                            {
                                // das Label für das Boot (!) (z. B. "Renngemeinschaft ...") bestimmen

                                var titleShort = "?";
                                var titleLong  = "?";

                                var entryLabel = dbEntryLabels.Values.FirstOrDefault(el => el.EL_Entry_ID_FK == entry.Entry_ID);
                                if (entryLabel != null)
                                {
                                    dbLabels.TryGetValue(entryLabel.EL_Label_ID_FK, out var label);
                                    titleShort = label?.Label_Short;
                                    titleLong  = label?.Label_Long;
                                }

                                var newBoat = new BoatData()
                                {
                                    TitleShort = titleShort,
                                    TitleLong  = titleLong,
                                    BibNumber  = (byte)entry.Entry_Bib,
                                    Canceled   = (entry.Entry_CancelValue > 0)
                                };

                                newRace.BoatsData.Add(newBoat);

                                // Zum aktuellen Boot die Mannschaft hinzufügen
                                newBoat.Rowers = new List <RowerData>();
                                _ = dbCrews.TryGetValue(entry.Entry_ID, out var crew);

                                #region Sonderbehandlung der Crew für den Fall, dass es Ummeldungen gibt

                                // TODO: Es ist nicht ganz klar, woran man Ummeldungen erkennt, bzw. woran
                                //       man erkennt, welches das alte, und welches das neue Crewmitglied
                                //       ist. Auf jeden Falls stehen beiden in der DB drin.

                                if (crew != null)
                                {
                                    // Gruppieren nach dem Sitzplatz
                                    var crewByPos = crew.GroupBy(c => c.Crew_Pos).ToList();

                                    // Crew-Liste löschen und neu füllen
                                    crew = new List <Crew>();
                                    foreach (var item in crewByPos)
                                    {
                                        // die Ruderer für die aktuelle Sitzposition sortiert nach DB-ID                       // TODO: Ist DB-ID wirklich das richtige ???
                                        var rowersOnCurrentPos = item.Select(x => x).OrderBy(x => x.Crew_ID);
                                        crew.Add(rowersOnCurrentPos.Last());
                                    }
                                }

                                #endregion

                                if (crew != null)
                                {
                                    // Crew durchgehen und ins Boot platzieren
                                    foreach (var crewMember in crew)
                                    {
                                        dbAthlets.TryGetValue(crewMember.Crew_Athlete_ID_FK, out var athlet);
                                        dbClubs.TryGetValue((int)athlet.Athlet_Club_ID_FK, out var club);

                                        if ((athlet != null) && (club != null))
                                        {
                                            var newRower = new RowerData()
                                            {
                                                Id             = athlet.Athlet_ID,
                                                ClubTitleLong  = club.Club_Name,
                                                ClubTitleShort = club.Club_Abbr,
                                                LastName       = athlet.Athlet_LastName,
                                                FirstName      = athlet.Athlet_FirstName,
                                                DateOfBirth    = athlet.Athlet_DOB,
                                                Gender         = (athlet.Athlet_Gender == 'M') ? Gender.Male : Gender.Female
                                            };

                                            if (crewMember.Crew_IsCox)
                                            {
                                                // es handelt sich um den Steuermann
                                                newBoat.Cox = newRower;
                                            }
                                            else
                                            {
                                                // normaler Ruderer
                                                newBoat.Rowers.Add(newRower);
                                            }
                                        }
                                        else
                                        {
                                            // Fehler: Ruderer oder Verein nicht gefunden
                                            Tools.LogErrorNoMessageBox("Warnung: Ruderer oder kein Verein zu Crew nicht gefunden. Rennen ", offer.Offer_RaceNumber, offer.Offer_ShortLabel);
                                        }
                                    }
                                }
                                else
                                {
                                    // Fehler: keine Mannschaft für das Boot gefunden
                                    Tools.LogErrorNoMessageBox("Warnung: Mannschaft zu Rennen nicht gefunden. Rennen ", offer.Offer_RaceNumber, offer.Offer_ShortLabel);
                                }
                            }
                        }

                        // Boote sortieren nach Startnummer
                        newRace.BoatsData = newRace.BoatsData.OrderBy(b => b.BibNumber).ToList();
                    }
                }

                #endregion

                #region Ruderer anhand der ermittelten Rennen bestimmen

                var newRowers = new List <RowerData>();
                foreach (var raceData in dbData.RacesData)
                {
                    foreach (var boatData in raceData.BoatsData)
                    {
                        // Ruderer hinzufügen
                        foreach (var rowerData in boatData.Rowers)
                        {
                            if (!newRowers.Any(r => r.Id == rowerData.Id))
                            {
                                newRowers.Add(rowerData);
                            }
                        }

                        // Steuermann hinzufügen
                        if (boatData.Cox != null)
                        {
                            if (!newRowers.Any(r => r.Id == boatData.Cox.Id))
                            {
                                newRowers.Add(boatData.Cox);
                            }
                        }
                    }
                }

                dbData.RowersData = newRowers.OrderBy(r => r.LastName).ThenBy(r => r.FirstName).ToList();

                #endregion

                // Rennen sortieren nach Startzeit
                dbData.RacesData = dbData.RacesData.OrderBy(r => r.DateTime).ToList();
            }
        }
Example #21
0
        /// <summary>
        /// Sets the boat status.
        /// </summary>
        /// <param name="boat">The boat.</param>
        public void SetBoatStatus(RaceData raceData, BoatData boatData, UserControls.Race race, UserControls.Boat boat)
        {
            if (boatData.Canceled)
            {
                // Boot ist abgemeldet
                boat.Comment  = "Boot abgemeldet!";
                boat.Status   = UserControls.BoatStatus.BoatOk;
                boat.Canceled = true;

                // Wenn das Boot abgemeldet ist, dann ist es egal, ob die einzelnen Ruderer darin zum Wiegen da waren oder nicht.
                // Diese Ruderer sollen dann nicht als zu schwer oder fehlend angezeigt werden, sondern als in Ordnung.
                var rowsers = boat.Rowers.Where(r => new[] { UserControls.RowerStatus.TooLate, UserControls.RowerStatus.WeightNotOk }.Contains(r.Status));
                foreach (var rower in rowsers)
                {
                    rower.Status = UserControls.RowerStatus.WaitingForTimeWindow;
                }

                return;
            }

            // Im UserControl ("boat") sind Ruderer und Steuermann in einer gemeinsamen Liste enthalten
            // und lassen sich nur durch das Type-Attribut des jeweiligen Eintrags unterscheiden
            var rowersWithoutCox = boat.Rowers.Where(r => r.Type == UserControls.RowerType.Rower);

            // Prüfen ob das Boot vollständig besetzt ist
            if (raceData.NumberOfRowers != rowersWithoutCox.Count())
            {
                boat.Comment = "Anzahl der Ruderer passt nicht zur Bootsklasse!";
                boat.Status  = UserControls.BoatStatus.BoatNok;
                return;
            }

            // Prüfen ob der Steuermann da ist
            if (raceData.IsCoxedRace && (boat.Rowers.Count(r => r.Type == UserControls.RowerType.Cox) != 1))
            {
                boat.Comment = "Steuermann fehlt!";
                boat.Status  = UserControls.BoatStatus.BoatNok;
                return;
            }

            // Durchschnittsgewicht wird berechnet, wenn es mehr als einen Ruderer gibt und
            // wenn mindestens schon von einem Ruderer das Gewicht vorliegt.
            var calculateAverageWeight = (rowersWithoutCox.Count() > 1) && (rowersWithoutCox?.Any(r => r.WeightInfo != null) == true);

            // Durchschnittsgewicht wird auf Richtigkeit geprüft, wenn ein Sollwert vorhanden ist
            var checkAverageWeightOk = calculateAverageWeight && (raceData.MaxAverageWeight?.Value != null) && (raceData.MaxAverageWeight.Value > 0);

            float averageWeight = 0;

            boat.AverageWeight = "";
            if (checkAverageWeightOk)
            {
                // Durschnittsgewicht berechnen
                var count = 0;
                foreach (var rower in rowersWithoutCox)
                {
                    if (rower.WeightInfo != null)
                    {
                        averageWeight += (float)rower.WeightInfo;
                        count++;
                    }
                }

                if (count == 0)
                {
                    averageWeight = 0;
                }
                else
                {
                    averageWeight      = averageWeight / count;
                    boat.AverageWeight = $"(ø {averageWeight} kg)";
                }

                if (checkAverageWeightOk)
                {
                }
            }

            // Reihenfolge beachten!
            if (boat?.Rowers?.Any(r => r.Status == UserControls.RowerStatus.WeightNotOk) == true)
            {
                // es gibt Ruderer, bei denen das Gewicht nicht passt
                boat.Status = UserControls.BoatStatus.BoatNok;
            }
            else if (boat?.Rowers?.Any(r => r.Status == UserControls.RowerStatus.TooLate) == true)
            {
                // Es gibt Ruderer, die das Wiegen verpasst haben
                boat.Status = UserControls.BoatStatus.BoatNok;
            }
            else if (boat?.Rowers?.Any(r => r.Status == UserControls.RowerStatus.WaitingForTimeWindow) == true)
            {
                // Es gibt Ruderer ohne Gewicht, für die es noch zu früh zum Wiegen ist
                boat.Status = UserControls.BoatStatus.WaitingForTimeWindow;
            }
            else if (boat?.Rowers?.Any(r => r.Status == UserControls.RowerStatus.WaitingInsideTimeWindow) == true)
            {
                // Es gibt Ruderer ohne Gewicht, die jetzt zum wiegen dran wären
                boat.Status = UserControls.BoatStatus.WaitingInsideTimeWindow;
            }
            else if (boat.Rowers.All(r => r.Status == UserControls.RowerStatus.WeightOk))
            {
                // alle Ruderer sind einzeln OK, jetzt muss noch das Durschnittsgewicht stimmen
                if (checkAverageWeightOk)
                {
                    boat.Status = (averageWeight <= raceData.MaxAverageWeight.Value) ? UserControls.BoatStatus.BoatOk : UserControls.BoatStatus.BoatNok;
                }
                else
                {
                    // Durschnittsgewicht interessiert nicht
                    boat.Status = UserControls.BoatStatus.BoatOk;
                }
            }

            // Zusatzgewicht bestimmen

            var cox = boat.Rowers.SingleOrDefault(r => r.Type == UserControls.RowerType.Cox);

            if (cox != null)
            {
                // es gibt einen Steuermann

                if (cox.WeightInfo != null)
                {
                    // es gibt ein Gewicht für den Steuermann

                    if (raceData.MinCoxWeight.IsSpecified())
                    {
                        // Sollgewicht für Steuermann vorhanden

                        var weight = (float)cox.WeightInfo;

                        var additionalWeight = raceData.MinCoxWeight.Value - weight;

                        if (additionalWeight > 0)
                        {
                            // Steuermann ist zu leicht
                            boat.Comment = $"Zusatzgewicht für Steuermann! => {additionalWeight:0.0} kg";
                        }
                    }
                }
            }
        }