[TestMethod]
public async Task InputAvg()
{
    // dp, sp, temp, base density and flowing density use the same class, so this covers all of those use cases
    TestScript script = Flow.GetAerTestCase01_UpstreamTap();
    script.RunTime = new TimeSpan(56, 0, 0);
    script.StartTime = DateTime.UtcNow;

    int secondsOffset = 60 - script.StartTime.Second;
    int minuteOffset = 59 - script.StartTime.Minute;  // time to the end of the current hour. minute offset may be 0!
    int offsetHr = secondsOffset + minuteOffset * 60;
    // this is the contract time tomorrow
    DateTime contractTs = new DateTime(script.StartTime.Year, script.StartTime.Month, script.StartTime.Day, 8, 0, 0).AddDays(1.0d);
    // totalOffset is the time to the next contract day in seconds          
    int offsetDy = Convert.ToInt32(Math.Truncate(contractTs.Subtract(script.StartTime).TotalSeconds)) + 1;
    if(offsetDy > 86400)
    {
        offsetDy -= 86400;
    }

    Console.WriteLine($"Time is {script.StartTime}, seconds offset is {secondsOffset}, minute offset is {minuteOffset}, hour offset is {offsetHr}, day offset {offsetDy}");

    // user defined values. These have to happen after the app is initialized
    script.Changes.Add(1, new List());
    script.Changes[1].Add(new TestChange((int)GasFlow_001_005.ParametersEnum.spTag, 2818.09d)); // kPa
    script.Changes[1].Add(new TestChange((int)GasFlow_001_005.ParametersEnum.dpTag, 102.0d)); // millibar     
    script.Changes[1].Add(new TestChange((int)GasFlow_001_005.ParametersEnum.tempTag, 57.0d)); // C
    // change logging level
    script.Changes[1].Add(new TestChange((int)AppBase_v1.AppBaseParameterIds.logLevel, (object)3));

    Random r = new Random();

    double dp = 102.0d;

    int dpMnCount = 0;
    double dpMnSum = 0.0d;
    double dpMnAvg = 0.0d;

    int dpHrCount = 0;
    double dpHrSum = 0.0d;
    double dpHrAvg = 0.0;

    int dpDyCount = 0;
    double dpDySum = 0.0d;
    double dpDyAvg = 0.0d;

   
    double dpHrAvg_ = 0.0d;
    double dpDyAvg_ = 0.0d;

    for(int i = 2; i < script.RunTime.TotalSeconds; i++)
    {
        if(r.NextDouble() >= 0.90)
        {
            // change input values when a randomly generated number is above some threshold
            // this prevents the dp from changing very second.
            // the dp actually changes in the next second, so do averaging first when a change is coming
            dpMnSum += dp;
            dpMnCount++;
            dpMnAvg = dpMnSum / dpMnCount;
            // change the value
            dp = (r.NextDouble() * 100.0d) + 10.0d; // between 10 and 110;
            if(script.Changes.ContainsKey(i) == false)
            {
                script.Changes.Add(i, new List());
            }
            script.Changes[i].Add(new TestChange((int)GasFlow_001_005.ParametersEnum.dpTag, dp)); // millibar              
        }
        else
        {
            // the value didn't change, just track the sum and count
            dpMnSum += dp;
            dpMnCount++;
            dpMnAvg = dpMnSum / dpMnCount;
        }

        if(i >= secondsOffset)
        {
            if((i - secondsOffset) % 60 == 0)
            {
                // on a minute boundary
                // verify that the minute average from the app matches our calculated value. 
                // If the assert is not true then the test will stop
                if (script.Asserts.ContainsKey(i) == false)
                {
                    script.Asserts.Add(i, new List());
                }
                script.Asserts[i].Add(new TestAssert(AssertTypes.areEqual, (int)GasFlow_001_005.ParametersEnum.mnDpAvg, dpMnAvg, 0.0001));

                // increment hour values when a new minute occurs
                // get a weighted average
                dpHrSum += dpMnAvg * dpMnCount;
                dpHrCount += dpMnCount;
                dpHrAvg = dpHrSum / dpHrCount;
                
                // reset minute values
                dpMnSum = 0;
                dpMnCount = 0;
            }
        }

        if(i >= offsetHr)
        {
            if ((i - offsetHr) % 3600 == 0)
            {
                // on an hour boundary
                // verify that the hour average matches our our calculated value
                // If the assert is not true then the test will stop
                if (script.Asserts.ContainsKey(i) == false)
                {
                    script.Asserts.Add(i, new List());
                }
                script.Asserts[i].Add(new TestAssert(AssertTypes.areEqual, (int)GasFlow_001_005.ParametersEnum.hrDpAvg, dpHrAvg, 0.0001));

                // increment day values when a new hour occurs
                dpDySum += dpHrAvg * dpHrCount;
                dpDyCount += dpHrCount;
                dpDyAvg = dpDySum / dpDyCount;

                // reset hour values
                dpHrCount = 0;
                dpHrSum = 0;
            }
        }

        if(i >= offsetDy)
        {
            if((i - offsetDy) % 86400 == 0)
            {
                // on a day boundary
                // verify that the day average matches our calculated vaulue.
                // If the assert is not true then the test will stop
                if (script.Asserts.ContainsKey(i) == false)
                {
                    script.Asserts.Add(i, new List());
                }
                script.Asserts[i].Add(new TestAssert(AssertTypes.areEqual, (int)GasFlow_001_005.ParametersEnum.dyDpAvg, dpDyAvg, 0.0001));

                // reset day value
                dpDyCount = 0;
                dpDySum = 0;
            }
        }

        if(dpMnCount == 0 && dpHrCount == 0)
        {
            // cut loop short for the first time through
            continue;
        }

        if(r.NextDouble() < 0.02)
        {
            // If a randomly generated value is less that some amount, 
            // update day and hour as required, not just on boundaries
            dpHrAvg_ = ((dpMnAvg * dpMnCount) + (dpHrAvg * dpHrCount)) / (dpMnCount + dpHrCount);
            dpDyAvg_ = ((dpMnAvg * dpMnCount) + (dpHrAvg * dpHrCount) + (dpDyAvg * dpDyCount)) / (dpMnCount + dpHrCount + dpDyCount);

            // Assert that values are correct
            if (script.Asserts.ContainsKey(i) == false)
            {
                script.Asserts.Add(i, new List());
            }
            script.Asserts[i].Add(new TestAssert(AssertTypes.areEqual, (int)GasFlow_001_005.ParametersEnum.mnDpAvg, dpMnAvg, 0.0001));
            script.Asserts[i].Add(new TestAssert(AssertTypes.areEqual, (int)GasFlow_001_005.ParametersEnum.hrDpAvg, dpHrAvg_, 0.0001));
            script.Asserts[i].Add(new TestAssert(AssertTypes.areEqual, (int)GasFlow_001_005.ParametersEnum.dyDpAvg, dpDyAvg_, 0.0001));

        }
    }
    // execute the test script
    AppRunBase_ runtime = await AppSpecificTestHelpers.ExecuteScript(script);
}