/* * 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 { /// /// Static application controller for UPS management /// /// 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 } } } } } }