2025-11-28 00:35:46 +09:00

181 lines
6.1 KiB
C++

#include <winrt/Windows.Devices.Power.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Globalization.DateTimeFormatting.h>
#include <winrt/Windows.Globalization.NumberFormatting.h>
#include <algorithm>
#include <chrono>
#include <iostream>
#include <string>
namespace winrt
{
using namespace winrt::Windows::Devices::Power;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::Globalization::DateTimeFormatting;
using namespace winrt::Windows::Globalization::NumberFormatting;
}
std::wstring FormatDateTime(winrt::DateTime dateTime)
{
return std::wstring{ winrt::DateTimeFormatter(L"shortdate shorttime").Format(dateTime) };
}
std::wstring FormatSeverity(double severity)
{
winrt::DecimalFormatter severityFormatter;
severityFormatter.FractionDigits(2);
severityFormatter.IntegerDigits(1);
severityFormatter.IsDecimalPointAlwaysDisplayed(true);
winrt::IncrementNumberRounder rounder;
rounder.Increment(0.01);
severityFormatter.NumberRounder(rounder);
return std::wstring{ severityFormatter.Format(severity) };
}
void ShowForecast()
{
winrt::PowerGridForecast gridForecast = winrt::PowerGridForecast::GetForecast();
// If the API cannot obtain a forecast, the forecast is empty
winrt::IVectorView<winrt::PowerGridData> forecast = gridForecast.Forecast();
if (forecast.Size() > 0)
{
// Print some forecast general information.
winrt::DateTime blockStartTime = gridForecast.StartTime();
winrt::TimeSpan blockDuration = gridForecast.BlockDuration();
std::wcout << L"Forecast start time:" << FormatDateTime(blockStartTime) << L"\n";
std::wcout << L"Forecast block duration (minutes):" << std::chrono::round<std::chrono::minutes>(blockDuration).count() << L"\n";
std::wcout << L"\n";
// Print each entry in the forecast.
for (winrt::PowerGridData const& data : forecast)
{
std::wcout << L"Date/Time: " << FormatDateTime(blockStartTime) <<
L", Severity: " << FormatSeverity(data.Severity()) <<
L", Is low user impact: " << (data.IsLowUserExperienceImpact() ? "Yes" : "No") << "\n";
blockStartTime += blockDuration;
}
}
else
{
std::wcout << L"No forecast available. Try again later.\n";
}
std::wcout << L"\n";
}
// Calculate the index of the forecast entry that contains the requested time.
// If the time is before the start of the forecast, then returns 0.
// If the time is past the end of the forecast, then returns the number of forecasts.
int GetForecastIndexContainingTime(winrt::PowerGridForecast const& gridForecast, winrt::DateTime time)
{
winrt::TimeSpan blockDuration = gridForecast.BlockDuration();
// Avoid division by zero.
if (blockDuration.count() == 0)
{
return 0;
}
auto startBlock = static_cast<int>((time - gridForecast.StartTime()) / blockDuration);
return std::clamp(startBlock, 0, static_cast<int>(gridForecast.Forecast().Size()));
}
void FindBest(winrt::TimeSpan lookAhead, bool restrictToLowUXImpact)
{
winrt::PowerGridForecast gridForecast = winrt::PowerGridForecast::GetForecast();
// Find the first and last blocks that include the time range we are
// interested in.
winrt::DateTime startTime = winrt::clock::now();
winrt::DateTime endTime = startTime + lookAhead;
int startBlock = GetForecastIndexContainingTime(gridForecast, startTime);
int endBlock = GetForecastIndexContainingTime(gridForecast, endTime + gridForecast.BlockDuration());
double lowestSeverity = (std::numeric_limits<double>::max)();
winrt::DateTime timeWithLowestSeverity = (winrt::DateTime::max)();
for (int index = startBlock; index < endBlock; ++index)
{
winrt::PowerGridData data = gridForecast.Forecast().GetAt(index);
// If we are restricting to low impact, then use only low impact time periods.
if (restrictToLowUXImpact && !data.IsLowUserExperienceImpact())
{
continue;
}
// If the severity is not an improvement, then don't use this one.
double severity = data.Severity();
if (severity >= lowestSeverity)
{
continue;
}
lowestSeverity = severity;
timeWithLowestSeverity = gridForecast.StartTime() + index * gridForecast.BlockDuration();
}
// Print the results.
if (lowestSeverity <= 1.0)
{
std::wcout <<
FormatDateTime(timeWithLowestSeverity) <<
L" to " <<
FormatDateTime(timeWithLowestSeverity + gridForecast.BlockDuration()) <<
L" (severity = " <<
FormatSeverity(lowestSeverity) <<
L")\n";
}
else
{
std::wcout << L"Unable to find a good time to do work\n";
}
std::wcout << L"\n";
}
void PerformForecastCalculations()
{
// Show the entire forecast.
ShowForecast();
// Arbitrarily look ahead 10 hours with low user impact.
std::wcout << L"Best time to do work in the next 10 hours with low user experience impact:\n";
FindBest(std::chrono::hours(10), true);
// Arbitrarily look ahead 10 hours with no regard for low user impact.
std::wcout << L"Best time to do work in the next 10 hours without regard for user experience impact:\n";
FindBest(std::chrono::hours(10), false);
}
int wmain(int /* argc */, wchar_t** /* argv */)
{
winrt::init_apartment();
// Do calculations immediately.
PerformForecastCalculations();
// If the forecast changes, then do the calculations again.
auto token = winrt::PowerGridForecast::ForecastUpdated([](auto&&, auto&&)
{
PerformForecastCalculations();
});
// Wait until the user presses a key to exit.
std::wcout << L"Waiting for the forecast to change...\n";
std::wcout << L"Press Enter to exit the program.\n";
std::cin.get();
// Clean up.
winrt::PowerGridForecast::ForecastUpdated(token); // unsubscribe from event
winrt::uninit_apartment();
return 0;
}