TKK_E32230371/UPSNet/UPSApp.cs

366 lines
13 KiB
C#

/*
* Created by SharpDevelop.
* User: EVXIO
* Date: 4/3/2025
* Time: 10:36 PM
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace UPSNet {
/// <summary>
/// Static application controller for UPS management
/// </summary>
///
enum ShutdownMode{
Disabled = 0,
OnBattery = 1,
OnBattery75 = 2,
OnBattery50 = 3,
OnBattery25 = 4,
OnBatteryCritical = 5
};
public static class UPSApp {
[DllImport("user32.dll")]
private static extern bool FlashWindow(IntPtr hWnd, bool bInvert);
public static bool dialogOk = false;
public static UPSForm upsForm = null;
public static string deviceAddress = "192.168.0.99";
public static int shutdownMode = 0;
public static bool initialized = false;
public static UPSApiFetcher fetcher = new UPSApiFetcher("http://your-ups-api/status");
public static System.Threading.SynchronizationContext uiContext;
public static bool isShutdownRequested = false;
public static long lastTickShutdownRequested = 0;
public static bool isShutdownCancelledByUser = false;
public static int lastKnownLineFrom = 1;
public static bool lastKnownBatteryCritical = false;
public static int lastKnownChargeLevel = 4;
static void OnStatusChanged(UPSApiFetcher fetcher){
//Debug.WriteLine("Charge: " + fetcher.ChargeLevel + ", Online: " + fetcher.IsReachable);
ManageShutdownRoutine();
}
static UPSApp() {
Init();
}
public static void Init(){
if (initialized) return;
initialized = true;
if (AppConfig.exists){
// Initialize default values
deviceAddress = AppConfig.Get("address", deviceAddress);
shutdownMode = AppConfig.GetInt("mode", 0);
AppConfig.Set("initialized", "true");
SaveConfig();
}else{
// Load saved values
deviceAddress = AppConfig.Get("address", deviceAddress);
if (deviceAddress.Length>200) deviceAddress = deviceAddress.Substring(0,200);
shutdownMode = AppConfig.GetInt("mode", shutdownMode);
if (shutdownMode>6) shutdownMode = 6;
if (shutdownMode<0) shutdownMode = 0;
}
fetcher.SetApiUrl("http://" + deviceAddress + "/state");
fetcher.StatusUpdated += OnStatusChanged;
fetcher.Start();
Debug.WriteLine("Initialized");
}
public static void SaveConfig(){
AppConfig.Set("address", deviceAddress);
AppConfig.Set("mode", shutdownMode);
AppConfig.SaveSettings();
}
public static void ShowForm(){
if (upsForm==null){
upsForm = new UPSForm();
upsForm.Show();
return;
}else{
if (upsForm.WindowState == FormWindowState.Minimized){
upsForm.WindowState = FormWindowState.Normal;
}else if (upsForm.WindowState == FormWindowState.Normal){
if (!upsForm.Visible){
upsForm.Visible = true;
upsForm.Show();
upsForm.Activate();
return;
}
}
upsForm.Activate(); // First bring to foreground
FlashWindow(upsForm.Handle, true);
}
}
public static void ShowOrHideForm(){
if (upsForm==null){
upsForm = new UPSForm();
upsForm.Show();
return;
}else{
if (upsForm.WindowState == FormWindowState.Minimized){
upsForm.WindowState = FormWindowState.Normal;
upsForm.Activate(); // First bring to foreground
FlashWindow(upsForm.Handle, true);
}else if (upsForm.WindowState == FormWindowState.Normal){
if (!upsForm.Visible){
upsForm.Visible = true;
upsForm.Show();
upsForm.Activate();
//upsForm.Close();
//upsForm = null;
return;
}else{
upsForm.Close();
upsForm = null;
return;
}
}
}
}
public static void PurgeUpsForm(){
upsForm = null;
}
public static void RestartFetcher(){
fetcher.Stop();
fetcher.SetApiUrl("http://" + deviceAddress + "/state");
fetcher.Start();
}
public static bool IsGoingToShutdown(){
if (isShutdownRequested){
//timeout after 35s
if ((Environment.TickCount-lastTickShutdownRequested) >= 35000){
isShutdownRequested = false;
}else{
return true;
}
}
return false;
}
public static bool IsRunningInDebuggerOrIDE(){
// Check for attached debugger
if (System.Diagnostics.Debugger.IsAttached)
return true;
// Check for common IDE environment variables
string[] ideVariables = {
"VisualStudioVersion",
"VSAPPIDNAME",
"SharpDevelop" // For SharpDevelop
};
foreach (var variable in ideVariables){
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(variable)))
return true;
}
return false;
}
public static bool RequestForShutdown(int sec){
if (IsGoingToShutdown()) return true;
Debug.WriteLine("Shutdown requested.");
// Mark as requested immediately to prevent re-entry
isShutdownRequested = true;
lastTickShutdownRequested = Environment.TickCount;
// Show countdown form on UI thread first, then execute shutdown only if not cancelled
if (uiContext != null) {
uiContext.Post(new System.Threading.SendOrPostCallback(delegate(object state) {
ShowCountdownThenShutdown(sec);
}), null);
}else{
// Fallback: no UI context, shutdown directly
ExecuteWindowsShutdown(sec);
}
return isShutdownRequested;
}
private static void ShowCountdownThenShutdown(int sec){
using (var countdownForm = new ShutdownCountdownForm(sec)){
// Show form and also show main form behind it
ShowForm();
countdownForm.ShowDialog();
if (countdownForm.WasCancelled){
// User cancelled - abort everything
Debug.WriteLine("Shutdown cancelled by user via countdown dialog.");
WindowsShutdown.AbortShutdown();
CancelTimedShutdown();
isShutdownRequested = false;
MessageBox.Show("Shutdown command has been aborted.", "Cancel Shutdown", MessageBoxButtons.OK, MessageBoxIcon.Information);
}else if (countdownForm.CountdownFinished){
// Countdown reached 0 - execute actual Windows shutdown
Debug.WriteLine("Countdown finished. Executing Windows shutdown.");
ExecuteWindowsShutdown(1); // 1 second remaining, shutdown immediately
}else{
// Form was closed without explicit action - treat as cancelled
Debug.WriteLine("Countdown form closed. Treating as cancelled.");
WindowsShutdown.AbortShutdown();
CancelTimedShutdown();
isShutdownRequested = false;
}
}
}
private static void ExecuteWindowsShutdown(int sec){
if (IsRunningInDebuggerOrIDE()){
Debug.WriteLine("Running from Debug/IDE. Bypassing actual OS shutdown!");
isShutdownRequested = true;
lastTickShutdownRequested = Environment.TickCount;
MessageBox.Show("[Debug Mode] Windows shutdown would execute now.", "Debug Shutdown", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
bool b = WindowsShutdown.TimedShutdown(false, sec, "EVXIO UPS Net shutdown activated.");
//non invasive shutdown
if (!b) b = WindowsShutdown.Shutdown(WindowsShutdown.ShutdownMode.Shutdown);
if (!b) b = WindowsShutdown.CmdShutdown(WindowsShutdown.ShutdownMode.Shutdown, sec);
if (b){
isShutdownRequested = true;
lastTickShutdownRequested = Environment.TickCount;
}else{
isShutdownRequested = false;
}
}
public static void SendTimedShutdown(){
string url = "http://" + deviceAddress + "/state?shutdown=30";
fetcher.SetApiUrlOnce(url);
}
public static void CancelTimedShutdown(){
isShutdownCancelledByUser = true;
string url = "http://" + deviceAddress + "/state?shutdown=0";
fetcher.SetApiUrlOnce(url);
}
public static void ManageShutdownRoutine(){
if (UPSApp.fetcher.IsReachable){
// Update last known state
lastKnownLineFrom = UPSApp.fetcher.UPSLineFrom;
lastKnownBatteryCritical = UPSApp.fetcher.BatteryCritical;
lastKnownChargeLevel = UPSApp.fetcher.ChargeLevel;
if (UPSApp.fetcher.OnTimedShutdown){
Debug.WriteLine("UPS going to shutdown: " + UPSApp.fetcher.TimedShutdownRemaining);
//checking if pc already flagged to shutdown
if (!IsGoingToShutdown() && !isShutdownCancelledByUser){
int tMax = UPSApp.fetcher.TimedShutdownRemaining - 10; // Cadangan waktu 10 detik agar PC sempat mati sebelum listrik UPS putus
if (tMax<1) tMax = 1;
int t = UPSApp.fetcher.BatteryCritical ? 1 : tMax;
RequestForShutdown(t);//request shutdown
}
}else{
//checking requirement
int realMode = 0;
bool isForced = UPSApp.shutdownMode>0;
if (!isForced){
realMode = UPSApp.fetcher.ShutdownSuggestMode;
}else{
realMode = UPSApp.shutdownMode-1;
}
bool shutFlag = false;
if (isForced){
//check setting requirmenet flag before decide to shutdown
bool onBattery = (UPSApp.fetcher.UPSLineFrom == 2);
int battLv = UPSApp.fetcher.ChargeLevel;
bool isCritical = UPSApp.fetcher.BatteryCritical;
ShutdownMode mode = (ShutdownMode) realMode;
// Calculate normalized percentage (0 to 100)
double percentage = 100.0;
if (UPSApp.fetcher.BattMaxLevel > 0) {
percentage = (double)battLv * 100.0 / UPSApp.fetcher.BattMaxLevel;
} else {
percentage = battLv > 4 ? battLv : (battLv * 25.0);
}
if (onBattery){
switch (mode){
case ShutdownMode.Disabled:{
//do nothing
}break;
case ShutdownMode.OnBattery:{
shutFlag = true;
}break;
case ShutdownMode.OnBattery75:{
shutFlag = percentage <= 75.0;
}break;
case ShutdownMode.OnBattery50:{
shutFlag = percentage <= 50.0;
}break;
case ShutdownMode.OnBattery25:{
shutFlag = percentage <= 25.0;
}break;
case ShutdownMode.OnBatteryCritical:{
shutFlag = (percentage <= 10.0) || isCritical;
}break;
}
}
}else{
//follow UPS flag
if (UPSApp.fetcher.ShutdownSuggested){
shutFlag = true;
}
}
if (UPSApp.fetcher.UPSLineFrom == 1 || UPSApp.fetcher.IsChargingOrBlink) {
isShutdownCancelledByUser = false;
}
if (shutFlag && !isShutdownCancelledByUser){
Debug.WriteLine("UPS Suggest to shutdown!");
//request timed shutdown in 120 seconds, next cycle just wait for flagged shutdown
SendTimedShutdown();
}
}
}else{
// Handle unreachable (offline) state:
// If the connection is lost while the UPS was on battery, or the battery was critical, or if the user's setting was triggered
double lastPercentage = 100.0;
lastPercentage = lastKnownChargeLevel > 4 ? lastKnownChargeLevel : (lastKnownChargeLevel * 25.0);
bool wasOnBattery = (lastKnownLineFrom == 2);
bool wasCritical = lastKnownBatteryCritical || (lastPercentage <= 25.0);
if (wasOnBattery && wasCritical){
if (!IsGoingToShutdown() && !isShutdownCancelledByUser){
Debug.WriteLine("UPS connection lost while on battery and battery is critical. Requesting shutdown!");
RequestForShutdown(30); // 30 seconds warning countdown
}
}
}
}
}
}