예제 #1
0
        /// <summary>
        /// Generates a 3-letter pillow profile using an intelligent algorithm for the specified customer.
        /// </summary>
        /// <param name="position"></param>
        /// <param name="pressureMeasurementSupine">Expects 12 measurement values between 0 and 100 millibar as measured with the test person laying on the back.</param>
        /// <param name="pressureMeasurementLateral">Expects 12 measurement values between 0 and 100 millibar as measured with the test person laying on the side.</param>
        /// <param name="gender"></param>
        /// <param name="result">Holds the resulting pillow setup that was generated through this algorithm.</param>
        /// <returns>NULL if everything went fine or an exception.</returns>
        public static Exception GenerateProfileBasedOnPressureMapping(TestpersonSleepPositions sleepPosition, int[] pressureMeasurementSupine, int[] pressureMeasurementLateral, Genders gender, out PillowProfileGenerationResult result)
        {
            try
            {
                PillowBaseModuleVariants baseModule = PillowBaseModuleVariants.NoRole;
                PillowInsertVariants     inserts    = PillowInsertVariants.None;
                PillowWedgeVariants      wedge      = PillowWedgeVariants.None;

                if (sleepPosition == TestpersonSleepPositions.Lateral) //calculation for lateral position is more complex than the other 2
                {
                    #region Lateral sleeping position
                    int shoulderIndex;

                    /* Determine shoudler index (highest pressure from roles 1-4) */
                    int[] shoulderAreaArray = pressureMeasurementLateral;
                    shoulderIndex = GenerationUtils.GetIndexOfMaximum(shoulderAreaArray, 0, 3);

                    if (!GenerationUtils.IsLeftSideEqual(shoulderIndex, shoulderAreaArray) && shoulderIndex < 3 && GenerationUtils.IsRightSideEqual(shoulderIndex, shoulderAreaArray)) //if the value to the right is the same, move the shoulder index one to the right (e.g. 17 19 19 15)
                    {
                        shoulderIndex++;
                    }
                    else if (shoulderIndex == 0 && GenerationUtils.IsRightSideEqual(shoulderIndex, shoulderAreaArray) && shoulderAreaArray[2] == shoulderAreaArray[0]) //e.g. 17 17 17 19
                    {
                        shoulderIndex = 1;
                    }

                    int shoulderIndexPressureValue = pressureMeasurementLateral[shoulderIndex]; //the absolute pressure value in millibar
                    baseModule = PillowBaseModuleVariants.WithRole;                             //we always have the base module

                    if (gender == Genders.Female)
                    {
                        if (shoulderIndexPressureValue < 8)
                        {
                            inserts = PillowInsertVariants.Thin;
                            wedge   = PillowWedgeVariants.ThickTowardsHeadEnd;
                        }
                        else if (shoulderIndexPressureValue < 14)
                        {
                            inserts = PillowInsertVariants.Thick;
                            wedge   = PillowWedgeVariants.ThickTowardsHeadEnd;
                        }
                        else
                        {
                            inserts = PillowInsertVariants.Both;
                            wedge   = PillowWedgeVariants.ThickTowardsHeadEnd;
                        }
                    }
                    else
                    {
                        if (shoulderIndexPressureValue < 7)
                        {
                            inserts = PillowInsertVariants.Thin;
                            wedge   = PillowWedgeVariants.ThickTowardsHeadEnd;
                        }
                        else if (shoulderIndexPressureValue < 13)
                        {
                            inserts = PillowInsertVariants.Thick;
                            wedge   = PillowWedgeVariants.ThickTowardsHeadEnd;
                        }
                        else
                        {
                            inserts = PillowInsertVariants.Both;
                            wedge   = PillowWedgeVariants.ThickTowardsHeadEnd;
                        }
                    }
                    #endregion
                }
                else if (sleepPosition == TestpersonSleepPositions.Supine)
                {
                    //supine position only adds a wedge for males
                    if (gender == Genders.Male)
                    {
                        baseModule = PillowBaseModuleVariants.WithRole;
                        wedge      = PillowWedgeVariants.ThickTowardsHeadEnd;
                    }
                    else
                    {
                        baseModule = PillowBaseModuleVariants.WithRole;
                    }
                }
                else if (sleepPosition == TestpersonSleepPositions.Prone)
                {
                    //prone position is always only with the base module; nothing else
                    baseModule = PillowBaseModuleVariants.WithRole;
                }

                //concat the resulting pillow code
                string code = ((int)baseModule).ToString() + ((int)inserts).ToString() + ((int)wedge).ToString();
                result = new PillowProfileGenerationResult()
                {
                    PillowCode = code, BaseModule = baseModule, Inserts = inserts, Wedge = wedge
                };

                return(null);
            }
            catch (Exception ex)
            {
                result = default(PillowProfileGenerationResult);
                return(ex);
            }
        }
        /// <summary>
        /// Generates a support profile using an intelligent algorithm for the specified customer.
        /// </summary>
        /// <param name="position"></param>
        /// <param name="pressureMeasurementSupine">Expects 12 measurement values between 0 and 100 millibar as measured with the test person laying on the back.</param>
        /// <param name="pressureMeasurementLateral">Expects 12 measurement values between 0 and 100 millibar as measured with the test person laying on the side.</param>
        /// <param name="gender"></param>
        /// <param name="personHeightCm">The height of the test person in centimeters.</param>
        /// <param name="personWeightKg">The weight of the test person in kilogram.</param>
        /// <param name="neutralProfileLetter">The letter representing the neutral (middle) stamp firmness on which the profile is based. For example, Vitario uses 'S' and Ergomometer NL uses 'D' as neutral stamp.</param>
        /// <param name="result">Holds the resulting profile that was generated through this algorithm. NULL if something went wrong.</param>
        /// <returns>Null if everything went fine or an exception.</returns>
        public static Exception GenerateProfileBasedOnPressureMapping(MeasurementPositions position, int[] pressureMeasurementSupine, int[] pressureMeasurementLateral, Genders gender, int personHeightCm, int personWeightKg, char neutralProfileLetter, out StampProfileGenerationResult result)
        {
            try
            {
                result = default(StampProfileGenerationResult);

                if (position == MeasurementPositions.Lateral)
                {
                    int shoulderIndex, lordosisIndex, pelvisIndex = pressureMeasurementSupine.Length - 1;

                    //generate the standard profile
                    string profile = string.Join("", Enumerable.Repeat(neutralProfileLetter, 12));
                    bool   personLyingPositionWrong = false;

                    #region Shoulder

                    /*Shoulder area is always stamps 1-4. If the measurement in supine position is bad (equal values), the measurement in lateral position is used. */
                    int[] shoulderAreaArray = position == MeasurementPositions.Supine ? pressureMeasurementSupine : pressureMeasurementLateral;
                    shoulderIndex = GenerationUtils.GetIndexOfMaximum(shoulderAreaArray, 0, 3);

                    if (GenerationUtils.IsLeftSideEqual(shoulderIndex, shoulderAreaArray) || shoulderIndex < 3 && GenerationUtils.IsRightSideEqual(shoulderIndex, shoulderAreaArray)) //if one of the values around the shoulder index is the same
                    {
                        shoulderAreaArray = position == MeasurementPositions.Supine ? pressureMeasurementLateral : pressureMeasurementSupine;
                        shoulderIndex     = GenerationUtils.GetIndexOfMaximum(shoulderAreaArray, 0, 3);
                    }

                    if (!GenerationUtils.IsLeftSideEqual(shoulderIndex, shoulderAreaArray) && shoulderIndex < 3 && GenerationUtils.IsRightSideEqual(shoulderIndex, shoulderAreaArray)) //if the value to the right is the same, move the shoulder index one to the right (e.g. 17 19 19 15)
                    {
                        shoulderIndex++;
                    }
                    else if (shoulderIndex == 0 && GenerationUtils.IsRightSideEqual(shoulderIndex, shoulderAreaArray) && shoulderAreaArray[2] == shoulderAreaArray[0]) //e.g. 17 17 17 19
                    {
                        shoulderIndex = 1;
                    }

                    //now generate the profile for this area
                    if (shoulderIndex == 0)
                    {
                        profile = profile.ReplaceAtIndex(0, 'T');
                        profile = profile.ReplaceAtIndex(1, 'K');
                        profile = profile.ReplaceAtIndex(2, 'K');

                        if (GenerationUtils.IsInRange(shoulderAreaArray[0], shoulderAreaArray[1], 2)) //if the first 2 values are similar, the profile changes a bit
                        {
                            profile = profile.ReplaceAtIndex(1, 'T');
                            profile = profile.ReplaceAtIndex(3, 'K');
                        }
                    }
                    else if (shoulderIndex == 1)
                    {
                        profile = profile.ReplaceAtIndex(0, 'K');
                        profile = profile.ReplaceAtIndex(1, 'T');
                        profile = profile.ReplaceAtIndex(2, 'K');

                        //if the values around the shoulder index are similar, the profile changes a bit
                        if (GenerationUtils.IsInRange(shoulderAreaArray[0], shoulderAreaArray[1], 2))
                        {
                            profile = profile.ReplaceAtIndex(0, 'T');
                            profile = profile.ReplaceAtIndex(3, 'K');
                        }

                        if (GenerationUtils.IsInRange(shoulderAreaArray[1], shoulderAreaArray[2], 2))
                        {
                            profile = profile.ReplaceAtIndex(2, 'T');
                            profile = profile.ReplaceAtIndex(3, 'K');
                        }
                    }
                    else if (shoulderIndex == 2 || shoulderIndex == 3) //indices at position 2 and 3 are treated the same, but an index at 3 hints that the person is not properly positioned on the mattress
                    {
                        profile = profile.ReplaceAtIndex(0, 'K');
                        profile = profile.ReplaceAtIndex(1, 'K');
                        profile = profile.ReplaceAtIndex(2, 'T');
                        profile = profile.ReplaceAtIndex(3, 'K');

                        if (shoulderAreaArray[1] + 2 >= shoulderAreaArray[2]) //if the value before the shoulder index is no more less than 2, the profile changes
                        {
                            if (gender == Genders.Female)
                            {
                                profile = profile.ReplaceAtIndex(0, 'T');
                                profile = profile.ReplaceAtIndex(1, 'T');
                            }
                            else
                            {
                                profile = profile.ReplaceAtIndex(0, 'W');
                                profile = profile.ReplaceAtIndex(1, 'T');
                            }
                        }

                        if (shoulderIndex == 3)
                        {
                            personLyingPositionWrong = true;
                        }
                    }

                    #endregion

                    #region Pelvis area
                    /*Pelvis area is always the peak value from right to left in a specific area. */
                    int[] pelvisAreaArray = position == MeasurementPositions.Supine ? pressureMeasurementSupine : pressureMeasurementLateral;

                    if (personHeightCm >= 170)
                    {
                        //get the pelvis index (peak value from bottom to top, taking 6 values into account)
                        for (int i = pelvisAreaArray.Length - 1; i >= 6; i--)
                        {
                            if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                            {
                                pelvisIndex = i;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    if (personHeightCm >= 160)
                    {
                        //get the pelvis index (peak value from bottom to top, taking 4 values into account)
                        for (int i = pelvisAreaArray.Length - 3; i >= 6; i--)
                        {
                            if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                            {
                                pelvisIndex = i;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    else if (personHeightCm >= 150)
                    {
                        //get the pelvis index (peak value from bottom-3 to top, taking 3 values into account)
                        for (int i = pelvisAreaArray.Length - 4; i >= 6; i--)
                        {
                            if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                            {
                                pelvisIndex = i;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    else //small persons < 150cm
                    {
                        //get the pelvis index (peak value from bottom-3 to top, taking 4 values into account)
                        for (int i = pelvisAreaArray.Length - 4; i >= 5; i--)
                        {
                            if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                            {
                                pelvisIndex = i;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                    if (pelvisIndex == 11 && personHeightCm < 190) //pelvis area cannot be at the last stamp for person smaller than 190cm
                    {
                        pelvisIndex = 10;
                    }

                    //modify profile
                    profile = profile.ReplaceAtIndex(pelvisIndex, 'K');

                    if (gender == Genders.Male)                                                                           //males have a smaller pelvis area --> smaller range of values (+/- 3) and allow only 2 "K"-stamps
                    {
                        if (GenerationUtils.IsInRange(pelvisAreaArray[pelvisIndex], pelvisAreaArray[pelvisIndex - 1], 3)) //if the value before the pelvis index is within 3mB, change its corresponding profile stamp
                        {
                            profile = profile.ReplaceAtIndex(pelvisIndex - 1, 'K');
                        }
                    }
                    else //females have a bigger pelvis area --> higher range of values (+/- 5)
                    {
                        if (pelvisIndex < 11 && GenerationUtils.IsInRange(pelvisAreaArray[pelvisIndex], pelvisAreaArray[pelvisIndex + 1], 5)) //if the value after the pelvis index is within 4mB, change its corresponding profile stamp
                        {
                            profile = profile.ReplaceAtIndex(pelvisIndex + 1, 'K');
                        }

                        if (GenerationUtils.IsInRange(pelvisAreaArray[pelvisIndex], pelvisAreaArray[pelvisIndex - 1], 5)) //if the value before the pelvis index is within 5mB, change its corresponding profile stamp
                        {
                            profile = profile.ReplaceAtIndex(pelvisIndex - 1, 'K');
                        }
                    }
                    #endregion

                    #region Lordosis area
                    /* Lordosis area is between shoulder and pelvis area and can never be before stamp 5. The lordosis index is always the index of the lowest value between shoulder and pelvis. */

                    //calculate the BMI of the test person (determines which stamp is used in the lordosis area)
                    double heightM = personHeightCm / 100d;
                    double bmi     = personWeightKg / (heightM * heightM); //body mass index

                    int[] lordosisAreaArray = position == MeasurementPositions.Supine ? pressureMeasurementSupine : pressureMeasurementLateral;

                    lordosisIndex = GenerationUtils.GetIndexOfMinimum(lordosisAreaArray, 4, pelvisIndex - 1);

                    if (bmi > 30) //special rule: if the person is overweight, the lordosis index should be calculated based on the supine measurement
                    {
                        int lordosisIdxSupine  = GenerationUtils.GetIndexOfMinimum(pressureMeasurementSupine, 4, pelvisIndex - 1);
                        int lordosisIdxLateral = GenerationUtils.GetIndexOfMinimum(pressureMeasurementLateral, 4, pelvisIndex - 1);

                        if (lordosisIdxSupine > lordosisIdxLateral)
                        {
                            lordosisIndex = lordosisIdxSupine;
                        }
                    }

                    //this step is useless because we always use the lateral values here
                    if (lordosisAreaArray[lordosisIndex - 1] == lordosisAreaArray[lordosisIndex] && lordosisAreaArray[lordosisIndex + 1] == lordosisAreaArray[lordosisIndex]) //if the values around the lordosis index are equal, try to get a better result by using the lateral measurement
                    {
                        lordosisAreaArray = pressureMeasurementLateral;
                        lordosisIndex     = GenerationUtils.GetIndexOfMinimum(lordosisAreaArray, 4, pelvisIndex - 1);
                    }

                    if (personHeightCm > 155 && lordosisIndex <= 4 && gender == Genders.Male) //special rule: lordosis area cannot be at stamp 5 or below for small persons
                    {
                        lordosisIndex = 5;
                    }
                    else if (personHeightCm > 160 && lordosisIndex <= 4 && gender == Genders.Female) //special rule: lordosis area cannot be at stamp 5 or below for small persons
                    {
                        lordosisIndex = 5;
                    }

                    if (pelvisIndex - lordosisIndex > 3) //special rule: if the distance between pelvis and lordosis is more than 2 stamps, something is wrong --> "move" the lordosis index closer to the pelvis index
                    {
                        lordosisIndex = pelvisIndex - 3;
                    }

                    if (pelvisIndex - lordosisIndex == 1) //special rule: if lordosis and pelvis indices were determined to be next to each other, they have to be separated by 1 stamp
                    {
                        lordosisIndex = lordosisIndex - 1;
                    }

                    //modify the profile
                    if ((gender == Genders.Male && bmi < 20) || (gender == Genders.Female && bmi < 19)) //person appears to be underweight --> neutral ("D" or "S") stamp (so the same stamp as in surrounding area)
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, neutralProfileLetter);
                    }
                    else if (bmi > 30) //person appears to be clearly overweight --> "B" stamp
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, 'B');
                    }
                    else
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, 'L');              //normal weight --> "L" stamp
                    }
                    if (lordosisIndex == pelvisIndex - 1 && profile[lordosisIndex] == 'K') //if the lordosis index is directly before the pelvis index, no 'K'-stamp is allowed in the lordosis area (which can't happen with the current algorithm anyways)
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, 'L');
                    }

                    //special rule: "K" must not come directly after "L"
                    if (profile.Contains("LK")) //"L" must not immediately followed by "K" (distance between lordosis and pelvis area is at least 1 neutral stamp)
                    {
                        profile = profile.Replace("LK", "L" + neutralProfileLetter.ToString());

                        if (profile.Contains("L" + neutralProfileLetter + neutralProfileLetter))                                            //e.g. "LDD"
                        {
                            profile = profile.Replace("L" + neutralProfileLetter + neutralProfileLetter, "L" + neutralProfileLetter + "K"); //add the replaced "K" at the end of the pelvis area
                        }
                    }

                    //special rule: if the lordosis index is on stamp 5, stamp 4 must be neutral and stamp 3 must not be "T"
                    if (lordosisIndex == 4)
                    {
                        profile = profile.ReplaceAtIndex(3, neutralProfileLetter);

                        if (profile[2] == 'T')
                        {
                            profile = profile.ReplaceAtIndex(2, 'K');
                        }
                    }
                    #endregion

                    result = new StampProfileGenerationResult()
                    {
                        SupportProfile = profile.Select(s => s.ToString()).ToArray(), IsPersonLyingPositionWrong = personLyingPositionWrong
                    };
                    return(null);
                }
                else if (position == MeasurementPositions.Supine)
                {
                    int shoulderIndex, lordosisIndex, pelvisIndex = pressureMeasurementSupine.Length - 1;

                    //generate the standard profile
                    string profile = string.Join("", Enumerable.Repeat(neutralProfileLetter, 12));
                    bool   personLyingPositionWrong = false;

                    #region Shoulder

                    /*Shoulder area is always stamps 1-3.  */
                    int[] shoulderAreaArray = pressureMeasurementSupine;
                    shoulderIndex = GenerationUtils.GetIndexOfMaximum(shoulderAreaArray, 0, 2);

                    //now generate the profile for this area
                    if (shoulderIndex == 0)
                    {
                        profile = profile.ReplaceAtIndex(0, 'T');
                        profile = profile.ReplaceAtIndex(1, 'K');
                        profile = profile.ReplaceAtIndex(2, 'K');
                    }
                    else if (shoulderIndex == 1 || shoulderIndex == 2)
                    {
                        profile = profile.ReplaceAtIndex(0, 'K');
                        profile = profile.ReplaceAtIndex(1, 'T');
                        profile = profile.ReplaceAtIndex(2, 'K');
                    }

                    #endregion

                    #region Pelvis area
                    /*Pelvis area is always the peak value from right to left in a specific area. */
                    int[] pelvisAreaArray = position == MeasurementPositions.Supine ? pressureMeasurementSupine : pressureMeasurementLateral;

                    if (personHeightCm >= 170)
                    {
                        //get the pelvis index (peak value from bottom to top, taking 6 values into account)
                        for (int i = pelvisAreaArray.Length - 1; i >= 6; i--)
                        {
                            if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                            {
                                pelvisIndex = i;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    if (personHeightCm >= 160)
                    {
                        //get the pelvis index (peak value from bottom to top, taking 4 values into account)
                        for (int i = pelvisAreaArray.Length - 3; i >= 6; i--)
                        {
                            if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                            {
                                pelvisIndex = i;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    else if (personHeightCm >= 150)
                    {
                        //get the pelvis index (peak value from bottom-3 to top, taking 3 values into account)
                        for (int i = pelvisAreaArray.Length - 4; i >= 6; i--)
                        {
                            if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                            {
                                pelvisIndex = i;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    else //small persons < 150cm
                    {
                        //get the pelvis index (peak value from bottom-3 to top, taking 4 values into account)
                        for (int i = pelvisAreaArray.Length - 4; i >= 5; i--)
                        {
                            if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                            {
                                pelvisIndex = i;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                    if (pelvisIndex == 11 && personHeightCm < 190) //pelvis area cannot be at the last stamp for person smaller than 190cm
                    {
                        pelvisIndex = 10;
                    }

                    //modify profile
                    profile = profile.ReplaceAtIndex(pelvisIndex, 'K');

                    #endregion

                    #region Lordosis area
                    /* Lordosis area is between shoulder and pelvis area and can never be before stamp 5. The lordosis index is always the index of the lowest value between shoulder and pelvis. */

                    //calculate the BMI of the test person (determines which stamp is used in the lordosis area)
                    double heightM = personHeightCm / 100d;
                    double bmi     = personWeightKg / (heightM * heightM); //body mass index

                    int[] lordosisAreaArray = pressureMeasurementSupine;

                    lordosisIndex = GenerationUtils.GetIndexOfMinimum(lordosisAreaArray, 4, pelvisIndex - 1);

                    if (personHeightCm > 155 && lordosisIndex <= 4 && gender == Genders.Male) //special rule: lordosis area cannot be at stamp 5 or below for small persons
                    {
                        lordosisIndex = 5;
                    }
                    else if (personHeightCm > 160 && lordosisIndex <= 4 && gender == Genders.Female) //special rule: lordosis area cannot be at stamp 5 or below for small persons
                    {
                        lordosisIndex = 5;
                    }

                    if (pelvisIndex - lordosisIndex == 1) //special rule: if lordosis and pelvis indices were determined to be next to each other, they have to be separated by 1 stamp
                    {
                        lordosisIndex = lordosisIndex - 1;
                    }

                    //modify the profile
                    if ((gender == Genders.Male && bmi < 20) || (gender == Genders.Female && bmi < 19)) //person appears to be underweight --> neutral ("D" or "S") stamp (so the same stamp as in surrounding area)
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, neutralProfileLetter);
                    }
                    else if (bmi > 30) //person appears to be clearly overweight --> "B" stamp
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, 'B');
                    }
                    else
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, 'L'); //normal weight --> "L" stamp
                    }
                    #endregion

                    result = new StampProfileGenerationResult()
                    {
                        SupportProfile = profile.Select(s => s.ToString()).ToArray(), IsPersonLyingPositionWrong = personLyingPositionWrong
                    };
                    return(null);
                }
                else
                {
                    return(null);
                }
            }
            catch (Exception ex)
            {
                result = default(StampProfileGenerationResult);
                return(ex);
            }
        }
예제 #3
0
        /// <summary>
        /// Generates a support profile using an intelligent algorithm for the specified customer.
        /// </summary>
        /// <param name="position"></param>
        /// <param name="pressureMeasurementSupine">Expects 12 measurement values between 0 and 100 millibar as measured with the test person laying on the back.</param>
        /// <param name="pressureMeasurementLateral">Expects 12 measurement values between 0 and 100 millibar as measured with the test person laying on the side.</param>
        /// <param name="gender"></param>
        /// <param name="personHeightCm">The height of the test person in centimeters.</param>
        /// <param name="personWeightKg">The weight of the test person in kilogram.</param>
        /// <param name="neutralProfileLetter">The letter representing the neutral (middle) role firmness on which the profile is based. For example, Vitario uses 'S' and Ergomometer NL uses 'D' as neutral element.</param>
        /// <param name="result">Holds the resulting profile that was generated through this algorithm. NULL if something went wrong.</param>
        /// <returns>Null if everything went fine or an exception.</returns>
        public static Exception GenerateProfileBasedOnPressureMapping(MeasurementPositions position, int[] pressureMeasurementSupine, int[] pressureMeasurementLateral, Genders gender, int personHeightCm, int personWeightKg, char neutralProfileLetter, out RoleProfileGenerationResult result)
        {
            try
            {
                result = default(RoleProfileGenerationResult);

                if (position == MeasurementPositions.Lateral)
                {
                    int shoulderIndex, lordosisIndex, pelvisIndex = pressureMeasurementSupine.Length - 1;

                    //generate the standard profile
                    string profile = string.Join("", Enumerable.Repeat(neutralProfileLetter, 12));
                    bool   personLyingPositionWrong = false;

                    #region Shoulder

                    /*Shoulder area is always roles 1-4. If the measurement in supine position is bad (equal values), the measurement in lateral position is used. */
                    int[] shoulderAreaArray = position == MeasurementPositions.Supine ? pressureMeasurementSupine : pressureMeasurementLateral;
                    shoulderIndex = GenerationUtils.GetIndexOfMaximum(shoulderAreaArray, 0, 3);

                    if (GenerationUtils.IsLeftSideEqual(shoulderIndex, shoulderAreaArray) || shoulderIndex < 3 && GenerationUtils.IsRightSideEqual(shoulderIndex, shoulderAreaArray)) //if one of the values around the shoulder index is the same
                    {
                        shoulderAreaArray = position == MeasurementPositions.Supine ? pressureMeasurementLateral : pressureMeasurementSupine;
                        shoulderIndex     = GenerationUtils.GetIndexOfMaximum(shoulderAreaArray, 0, 3);
                    }

                    if (!GenerationUtils.IsLeftSideEqual(shoulderIndex, shoulderAreaArray) && shoulderIndex < 3 && GenerationUtils.IsRightSideEqual(shoulderIndex, shoulderAreaArray)) //if the value to the right is the same, move the shoulder index one to the right (e.g. 17 19 19 15)
                    {
                        shoulderIndex++;
                    }
                    else if (shoulderIndex == 0 && GenerationUtils.IsRightSideEqual(shoulderIndex, shoulderAreaArray) && shoulderAreaArray[2] == shoulderAreaArray[0]) //e.g. 17 17 17 19
                    {
                        shoulderIndex = 1;
                    }

                    //now generate the profile for this area
                    if (shoulderIndex == 0)
                    {
                        profile = profile.ReplaceAtIndex(0, 'T');
                        profile = profile.ReplaceAtIndex(1, 'K');
                        profile = profile.ReplaceAtIndex(2, 'K');

                        if (GenerationUtils.IsInRange(shoulderAreaArray[0], shoulderAreaArray[1], 2)) //if the first 2 values are similar, the profile changes a bit
                        {
                            profile = profile.ReplaceAtIndex(1, 'T');
                            profile = profile.ReplaceAtIndex(3, 'K');
                        }
                    }
                    else if (shoulderIndex == 1)
                    {
                        profile = profile.ReplaceAtIndex(0, 'K');
                        profile = profile.ReplaceAtIndex(1, 'T');
                        profile = profile.ReplaceAtIndex(2, 'K');

                        //if the values around the shoulder index are similar, the profile changes a bit
                        if (GenerationUtils.IsInRange(shoulderAreaArray[0], shoulderAreaArray[1], 2))
                        {
                            profile = profile.ReplaceAtIndex(0, 'T');
                            profile = profile.ReplaceAtIndex(3, 'K');
                        }

                        if (GenerationUtils.IsInRange(shoulderAreaArray[1], shoulderAreaArray[2], 2))
                        {
                            profile = profile.ReplaceAtIndex(2, 'T');
                            profile = profile.ReplaceAtIndex(3, 'K');
                        }
                    }
                    else if (shoulderIndex == 2 || shoulderIndex == 3) //indices at position 2 and 3 are treated the same, but an index at 3 hints that the person is not properly positioned on the mattress
                    {
                        profile = profile.ReplaceAtIndex(0, 'K');
                        profile = profile.ReplaceAtIndex(1, 'K');
                        profile = profile.ReplaceAtIndex(2, 'T');
                        profile = profile.ReplaceAtIndex(3, 'K');

                        if (shoulderAreaArray[1] + 2 >= shoulderAreaArray[2]) //if the value before the shoulder index is no more less than 2, the profile changes
                        {
                            if (gender == Genders.Female)
                            {
                                profile = profile.ReplaceAtIndex(0, 'T');
                                profile = profile.ReplaceAtIndex(1, 'T');
                            }
                            else
                            {
                                profile = profile.ReplaceAtIndex(0, 'W');
                                profile = profile.ReplaceAtIndex(1, 'T');
                            }
                        }

                        if (shoulderIndex == 3)
                        {
                            personLyingPositionWrong = true;
                        }
                    }

                    #endregion

                    #region Pelvis area
                    /*Pelvis area is always the peak value from right to left. */
                    int[] pelvisAreaArray = position == MeasurementPositions.Supine ? pressureMeasurementSupine : pressureMeasurementLateral;

                    //get the pelvis index (peak value from bottom to top, taking 6 values into account)
                    for (int i = pelvisAreaArray.Length - 1; i >= 6; i--)
                    {
                        if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                        {
                            pelvisIndex = i;
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (pelvisIndex == 11 && personHeightCm < 190) //pelvis area cannot be at the last role for person smaller than 190cm
                    {
                        pelvisIndex = 10;
                    }

                    //modify profile
                    profile = profile.ReplaceAtIndex(pelvisIndex, 'K');

                    if (gender == Genders.Male)                                                                           //males have a smaller pelvis area --> smaller range of values (+/- 3) and allow only 2 "K"-roles
                    {
                        if (GenerationUtils.IsInRange(pelvisAreaArray[pelvisIndex], pelvisAreaArray[pelvisIndex - 1], 3)) //if the value before the pelvis index is within 4mB, change its corresponding profile role
                        {
                            profile = profile.ReplaceAtIndex(pelvisIndex - 1, 'K');
                        }
                    }
                    else //females have a bigger pelvis area --> higher range of values (+/- 5)
                    {
                        if (pelvisIndex < 11 && GenerationUtils.IsInRange(pelvisAreaArray[pelvisIndex], pelvisAreaArray[pelvisIndex + 1], 5)) //if the value after the pelvis index is within 4mB, change its corresponding profile role
                        {
                            profile = profile.ReplaceAtIndex(pelvisIndex + 1, 'K');
                        }

                        if (GenerationUtils.IsInRange(pelvisAreaArray[pelvisIndex], pelvisAreaArray[pelvisIndex - 1], 5)) //if the value before the pelvis index is within 4mB, change its corresponding profile role
                        {
                            profile = profile.ReplaceAtIndex(pelvisIndex - 1, 'K');
                        }
                    }
                    #endregion

                    #region Lordosis area
                    /* Lordosis area is between shoulder and pelvis area and can never be before role 5. The lordosis index is always the index of the lowest value between shoulder and pelvis. */

                    //calculate the BMI of the test person (determines which role is used in the lordosis area)
                    double heightM = personHeightCm / 100d;
                    double bmi     = personWeightKg / (heightM * heightM); //body mass index

                    int[] lordosisAreaArray = pressureMeasurementSupine;

                    lordosisIndex = GenerationUtils.GetIndexOfMinimum(lordosisAreaArray, 4, pelvisIndex - 1);

                    if (personHeightCm > 155 && lordosisIndex <= 4 && gender == Genders.Male) //special rule: lordosis area cannot be at role 5 or below for small persons
                    {
                        lordosisIndex = 5;
                    }
                    else if (personHeightCm > 160 && lordosisIndex <= 4 && gender == Genders.Female) //special rule: lordosis area cannot be at role 5 or below for small persons
                    {
                        lordosisIndex = 5;
                    }

                    if (pelvisIndex - lordosisIndex == 1) //special rule: if lordosis and pelvis indices were determined to be next to each other, they have to be separated by 1 role
                    {
                        lordosisIndex = lordosisIndex - 1;
                    }

                    //modify the profile
                    if (bmi > 30) //person appears to be clearly overweight --> "B" element
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, 'B');
                    }
                    else
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, neutralProfileLetter); //normal weight --> neutral element
                    }
                    //special rule: "K" must not come directly after "B"
                    if (profile.Contains("BK"))                                                                                                                                   //"B" must not be immediately followed by "K" (distance between lordosis and pelvis area is at least 1 neutral role)
                    {
                        profile = profile.Replace("BK", "B" + neutralProfileLetter).Replace("B" + neutralProfileLetter + neutralProfileLetter, "B" + neutralProfileLetter + "K"); //also add the replaced "K" at the end of the pelvis area
                    }
                    #endregion

                    result = new RoleProfileGenerationResult()
                    {
                        SupportProfile = profile.Select(s => s.ToString()).ToArray(), IsPersonLyingPositionWrong = personLyingPositionWrong
                    };
                    return(null);
                }
                else if (position == MeasurementPositions.Supine)
                {
                    int shoulderIndex, lordosisIndex, pelvisIndex = pressureMeasurementSupine.Length - 1;

                    //generate the standard profile
                    string profile = string.Join("", Enumerable.Repeat(neutralProfileLetter, 12));
                    bool   personLyingPositionWrong = false;

                    #region Shoulder
                    /*Shoulder area is always roles 1-3. Ony stam 1 and 2 can be changed to 'T'. */
                    int[] shoulderAreaArray = pressureMeasurementSupine;
                    shoulderIndex = GenerationUtils.GetIndexOfMaximum(shoulderAreaArray, 0, 2);

                    //now generate the profile for this area
                    if (shoulderIndex == 0)
                    {
                        profile = profile.ReplaceAtIndex(0, 'T');
                        profile = profile.ReplaceAtIndex(1, 'K');
                        profile = profile.ReplaceAtIndex(2, 'K');
                    }
                    else if (shoulderIndex == 1 || shoulderIndex == 2)
                    {
                        profile = profile.ReplaceAtIndex(0, 'K');
                        profile = profile.ReplaceAtIndex(1, 'T');
                        profile = profile.ReplaceAtIndex(2, 'K');
                    }

                    #endregion

                    #region Pelvis area
                    /*Pelvis area is always the peak value from right to left. */
                    int[] pelvisAreaArray = pressureMeasurementSupine;

                    //get the pelvis index (peak value from bottom to top, taking 6 values into account)
                    for (int i = pelvisAreaArray.Length - 1; i >= 6; i--)
                    {
                        if (pelvisAreaArray[i] >= pelvisAreaArray[pelvisIndex])
                        {
                            pelvisIndex = i;
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (pelvisIndex == 11 && personHeightCm < 190) //pelvis area cannot be at the last role for person smaller than 190cm
                    {
                        pelvisIndex = 10;
                    }

                    //modify profile
                    profile = profile.ReplaceAtIndex(pelvisIndex, 'K');
                    #endregion

                    #region Lordosis area
                    /* Lordosis area is between shoulder and pelvis area and can never be before role 5. The lordosis index is always the index of the lowest value between shoulder and pelvis. */

                    //calculate the BMI of the test person (determines which role is used in the lordosis area)
                    double heightM = personHeightCm / 100d;
                    double bmi     = personWeightKg / (heightM * heightM); //body mass index

                    int[] lordosisAreaArray = pressureMeasurementSupine;

                    lordosisIndex = GenerationUtils.GetIndexOfMinimum(lordosisAreaArray, 4, pelvisIndex - 1);

                    if (personHeightCm > 155 && lordosisIndex <= 4 && gender == Genders.Male) //special rule: lordosis area cannot be at role 5 or below for small persons
                    {
                        lordosisIndex = 5;
                    }
                    else if (personHeightCm > 160 && lordosisIndex <= 4 && gender == Genders.Female) //special rule: lordosis area cannot be at role 5 or below for small persons
                    {
                        lordosisIndex = 5;
                    }

                    if (pelvisIndex - lordosisIndex == 1) //special rule: if lordosis and pelvis indices were determined to be next to each other, they have to be separated by 1 role
                    {
                        lordosisIndex = lordosisIndex - 1;
                    }

                    //modify the profile
                    if (bmi > 30) //person appears to be clearly overweight --> "B" role
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, 'B');
                    }
                    else
                    {
                        profile = profile.ReplaceAtIndex(lordosisIndex, neutralProfileLetter); //normal weight --> neutral role
                    }
                    //special rule: "K" must not come directly after "B"
                    if (profile.Contains("BK"))                                                                                                                                   //"B" must not immediately followed by "K" (distance between lordosis and pelvis area is at least 1 neutral role)
                    {
                        profile = profile.Replace("BK", "B" + neutralProfileLetter).Replace("B" + neutralProfileLetter + neutralProfileLetter, "B" + neutralProfileLetter + "K"); //also add the replaced "K" at the end of the pelvis area
                    }
                    #endregion

                    result = new RoleProfileGenerationResult()
                    {
                        SupportProfile = profile.Select(s => s.ToString()).ToArray(), IsPersonLyingPositionWrong = personLyingPositionWrong
                    };
                    return(null);
                }
                else
                {
                    return(null);
                }
            }
            catch (Exception ex)
            {
                result = default(RoleProfileGenerationResult);
                return(ex);
            }
        }
        /// <summary>
        /// Generates a profile based in the input person data.
        /// An  Liegesimulator_2._0 profile consists of 4 letters. 3 of these letters are the same, the other one is individual. The individual one can have 1 out of 3 available firmnesses.
        /// The position of the individual profile role is based on the position of the person's lordosis area.
        /// This algorithm attempts to find the position and hardness of the individual role and returns a support profile.
        /// Currently, the lordosis is considered to be between then 6th and the 9th role and thus one of these roles has to be the individual one. All other roles are set to the neutral letter 'N'.
        /// </summary>
        /// <param name="gender"></param>
        /// <param name="height">Height in cm.</param>
        /// <param name="weight">Weight in kg.</param>
        /// <param name="pressureMeasurementValues">4 measurement values from the simulator's pressure measurement.</param>
        /// <returns></returns>
        public static Exception GetProfileForPerson(MeasurementPositions position, Genders gender, int height, int weight, int[] pressureMeasurementSupine, int[] pressureMeasurementLateral, out SfContactProfile result)
        {
            result = default(SfContactProfile);

            if (pressureMeasurementSupine == null || pressureMeasurementSupine.Length != 12)
            {
                return(new ArgumentException("Measurement values invalid!", "pressureMeasurementSupine"));
            }

            if (pressureMeasurementLateral == null || pressureMeasurementLateral.Length != 12)
            {
                return(new ArgumentException("Measurement values invalid!", "pressureMeasurementLateral"));
            }

            if (height < 1 || weight < 1)
            {
                return(new ArgumentException("Body data invalid!", "height/weight"));
            }

            result = new  SfContactProfile()
            {
                Gender = gender, Height = height, Weight = weight
            };
            result.SupportProfile = "NNNN".ToStringArray();

            /*Pelvis area is always the peak value from right to left. */
            int[] measurementArray = position == MeasurementPositions.Supine ? pressureMeasurementSupine : pressureMeasurementLateral;
            int   pelvisIndex      = measurementArray.Length - 1;

            //get the pelvis index (peak value from bottom to top, taking 6 values into account)
            for (int i = measurementArray.Length - 1; i >= 6; i--)
            {
                if (measurementArray[i] >= measurementArray[pelvisIndex])
                {
                    pelvisIndex = i;
                }
                else if (measurementArray[i] < measurementArray[pelvisIndex])
                {
                    break;
                }
            }

            if (pelvisIndex == 11 && height < 190) //pelvis area cannot be at the last stamp for person smaller than 190cm
            {
                pelvisIndex = 10;
            }

            if (measurementArray[pelvisIndex] == measurementArray[pelvisIndex - 1])
            {
                pelvisIndex--;
            }

            /* now try to find the position of the lordosis role in order to be able to modify the hardness of the role in that position */
            int lordosisIndex = GenerationUtils.GetIndexOfMinimum(measurementArray, 5, pelvisIndex - 1);

            /*special rules to make sure the profile makes sense */
            if (lordosisIndex + 1 == pelvisIndex) //lordosis and pelvis index must not be after one another
            {
                lordosisIndex--;
            }

            if (lordosisIndex < 5)
            {
                lordosisIndex = 5; //lordosis must always be between role 6 and role 9
            }
            else if (lordosisIndex > 8)
            {
                lordosisIndex = 8; //lordosis must always be between role 6 and role 9
            }
            //calculate BMI
            double bmi = weight / (Math.Pow((double)height / 100d, 2));

            //determine the position and hardness of the individual lordosis role
            string replacementLetter = bmi < 20 ? "G" : bmi < 27 ? "R" : "B";                                     //< 20 means underweight person --> soft role. 20-27 = normal. >27 = overweight person --> firm role.
            int    replacementIndex  = lordosisIndex - 5;                                                         //-5 because the 4 roles used in  Liegesimulator_2._0 are roles 6 through 9 (and the indices represent roles 1 through 12)

            result.SupportProfile = result.SupportProfile.ReplaceAtIndex(replacementIndex, replacementLetter[0]); //replace the normal role in the standard profile with the individual role that was generated by the algorithm
            result.FirmRoleIndex  = lordosisIndex;
            return(null);
        }
        /// <summary>
        /// Generates a profile based in the input person data.
        /// An Ergo 4 profile consists of 4 letters. 3 of these letters are the same, the other one is individual.
        /// The position of the individual profile role is based on the position of the person's lordosis area and the firmness of the role is based on the body mass index of the person.
        /// This algorithm attempts to find the position and firmness of the individual role as well as the firmness of the 3 remaining roles and returns a support profile.
        /// </summary>
        /// <param name="gender"></param>
        /// <param name="height"></param>
        /// <param name="weight"></param>
        /// <param name="pressureMeasurementValues"></param>
        /// <returns></returns>
        public static Exception GenerateProfileBasedOnPressureMapping(Genders gender, int height, int weight, int[] pressureMeasurementValuesComplete, out Ergo4ProfileGenerationResult result)
        {
            try
            {
                result = null;

                if (pressureMeasurementValuesComplete == null || pressureMeasurementValuesComplete.Length != 12)
                {
                    return(new ArgumentException("pressureMeasurementValuesComplete is invalid: required length is 12."));
                }


                int[] pressureMeasurementValues = new int[4]; //Ergo 4 only has individual roles in the lordosis area
                Array.Copy(pressureMeasurementValuesComplete, 4, pressureMeasurementValues, 0, 4);

                result = new Ergo4ProfileGenerationResult();

                //calculate BMI
                double bmi = weight / (Math.Pow(((double)height) / 100, 2));
                char   replacementLetter = ' '; //this is used to hold the letter of the individual role
                int    replacementIndex  = -1;  //this holds the position of the individual role in the profile

                if (gender == Genders.Female)
                {
                    //determine mattress hardness and standard profile based on BMI
                    if (bmi < 19)
                    {
                        result.Firmness       = FirmnessLevels.H1;
                        result.SupportProfile = "0000";
                        replacementLetter     = '1';
                    }
                    else if (bmi < 25)
                    {
                        result.Firmness       = FirmnessLevels.H2;
                        result.SupportProfile = "1111";
                        replacementLetter     = '2';
                    }
                    else if (bmi < 29)
                    {
                        result.Firmness       = FirmnessLevels.H3;
                        result.SupportProfile = "2111";
                        replacementLetter     = '3';
                    }
                    else
                    {
                        result.Firmness       = FirmnessLevels.H4;
                        result.SupportProfile = "2111";
                        replacementLetter     = '3';
                    }
                }
                else
                {
                    //determine mattress hardness and standard profile based on BMI
                    if (bmi < 20)
                    {
                        result.Firmness       = FirmnessLevels.H1;
                        result.SupportProfile = "0000";
                        replacementLetter     = '1';
                    }
                    else if (bmi < 26)
                    {
                        result.Firmness       = FirmnessLevels.H2;
                        result.SupportProfile = "1111";
                        replacementLetter     = '2';
                    }
                    else if (bmi < 30)
                    {
                        result.Firmness       = FirmnessLevels.H3;
                        result.SupportProfile = "2111";
                        replacementLetter     = '3';
                    }
                    else
                    {
                        result.Firmness       = FirmnessLevels.H4;
                        result.SupportProfile = "2111";
                        replacementLetter     = '3';
                    }
                }

                /*now try to find the position of the lordosis role in order to be able to modify the hardness of the role in that position */
                int  maxIndex           = GenerationUtils.GetIndexOfMaximum(pressureMeasurementValues, 0, pressureMeasurementValues.Length - 1);
                int  maxValue           = pressureMeasurementValues[maxIndex];
                bool maxValueIsDistinct = GenerationUtils.IsValueDistinct(maxValue, pressureMeasurementValues); //make sure that the max value only occurs once

                //method 1: if the highest value is on position 2, 3 or 4, take the role before that
                if (maxValueIsDistinct && maxIndex > 0)
                {
                    replacementIndex        = maxIndex - 1; //role before the one with the highest pressure is individual
                    result.GenerationMethod = "1";
                }
                else if (pressureMeasurementValues[1] > pressureMeasurementValues[0] && pressureMeasurementValues[1] > pressureMeasurementValues[3] && pressureMeasurementValues[1] == pressureMeasurementValues[2])
                {
                    //if the 2 middle values are equal and both are higher than the first and last value (e.g. 7 11 11 6)
                    replacementIndex        = 1; //the second role is individual
                    result.GenerationMethod = "2";
                }
                else if (pressureMeasurementValues[3] > pressureMeasurementValues[0] && pressureMeasurementValues[3] > pressureMeasurementValues[1] && pressureMeasurementValues[3] == pressureMeasurementValues[2])
                {
                    //if the 2 last values are equal and the highest ones (e.g. 6 7 11 11)
                    replacementIndex        = 2; //the third role is individual
                    result.GenerationMethod = "3";
                }
                else //standard method (no pattern recognized): set individual role based on height and BMI
                {
                    if (gender == Genders.Female)
                    {
                        if (height < 166)
                        {
                            replacementIndex = 0;
                        }
                        else if (height < 180)
                        {
                            replacementIndex = 1;
                        }
                        else
                        {
                            replacementIndex = 2;
                        }
                    }
                    else
                    {
                        if (height < 166)
                        {
                            replacementIndex = 0;
                        }
                        else if (height < 180)
                        {
                            replacementIndex = 1;
                        }
                        else if (height < 190)
                        {
                            replacementIndex = 2;
                        }
                        else
                        {
                            replacementIndex = 3;
                        }
                    }

                    result.GenerationMethod = "Std";
                }

                if (replacementIndex < 0 || (replacementLetter == ' ')) //could not generate a profile, should not happen
                {
                    return(null);
                }

                //replace one neutral role in the standard profile with the individual role that was defined by the algorithm
                string numberProfile = GenerationUtils.ReplaceAtIndex(result.SupportProfile, replacementIndex, replacementLetter);

                //convert the number indexes in roles
                result.SupportProfile = ReplaceNumbersProfileWithLetters(numberProfile);

                return(null);
            }
            catch (Exception ex)
            {
                result = null;
                return(ex);
            }
        }