/// /// Component responsible for monitoring and managing IOB (Industrial Operation Block) services. /// /// This Blazor component displays real-time status of running processes, provides UI controls /// for starting/stopping services, and manages auto-restart logic based on configuration and thresholds. /// /// Key Features: /// - Displays live status of IOB services (running, stopped, ratio, color-coded indicators). /// - Enables user to open process folders (config, logs, target paths). /// - Supports dynamic filtering by IOB type. /// - Implements auto-restart logic triggered when service health degrades. /// - Integrates with external services (AppControlService, FluxLogManService) via DI. /// - Handles UI state updates via parameterized events and background scanning. /// using IOB_MAN.Core; using IOB_MAN.Core.Services; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using NLog; using System.Diagnostics; using System.Runtime.InteropServices; using static IOB_MAN.Core.CoreEnum; namespace IOB_MAN.Components.Compo { public partial class ApplicationCheck : IDisposable { #region Public Methods /// /// Cleans up event subscriptions and resources when the component is disposed. /// /// Specifically: /// - Unsubscribes from ACService.StatusUpdated event. /// public void Dispose() { ACService.EA_StatusUpdated -= ACService_EA_StatusUpdated; } #endregion Public Methods #region Protected Fields /// /// Selected IOB type filter (e.g., "PLC", "SCADA", "HMI"). /// Used to filter displayed records in the UI. /// protected string iobTypeSel = ""; /// /// List of page sizes available for record pagination (e.g., 5, 10, 20 records per page). /// Used in UI for user-controlled pagination. /// protected List PageSizeDispl = new List() { 5, 10, 15, 20, 25, 50 }; #endregion Protected Fields #region Protected Properties /// /// Injected service for managing external applications and service states. /// Responsible for opening, closing, and monitoring IOB processes. /// [Inject] protected AppControlService ACService { get; set; } = null!; /// /// Gets or sets whether auto-restart is enabled. /// /// Behavior: /// - When enabled, triggers automatic restart of closed services. /// - When disabled, delays restart until a configurable threshold (VetoAutoCheck) is reached. /// - On change, updates service state and forces UI reload. /// protected bool AutoRestart { get => ACService.AutoRestartEnabled; set { if (ACService.AutoRestartEnabled != value) { ACService.AutoRestartEnabled = value; if (value) { ACService.DoReopenClosed(); } else { countAutoRestart = "0000"; ACService.DelayRestart(!value); } currPage = 1; ForceReload(); } } } /// /// CSS style for context menu (positioned absolutely at mouse coordinates). /// Dynamically set based on user right-click position. /// protected string contextMenuStyle { get => $"position: absolute; top: {menuYpx}; left: {menuXpx}; z-index:999;"; } /// /// CSS class for process startup status indicator. /// /// Color logic: /// - If fewer than configured processes are started → "text-info" /// - If <50% of processes are running → "text-danger" /// - If 50% to 100% running → "text-warning" /// protected string cssCheckProc { get { string answ = "text-light"; if (NumProcStarted < NumProcConfig) { answ = "text-info"; } else { double ratio = (double)NumProcRunning / NumProcStarted; if (ratio < 0.5) { answ = "text-danger"; } else if (ratio < 1) { answ = "text-warning"; } } return answ; } } /// /// CSS class for "active processes" status. /// /// Color logic: /// - If <50% of started processes are running → "bg-danger" /// - If 50% to 100% running → "bg-warning" /// - Otherwise → "bg-success" /// protected string cssProcAttivi { get { string answ = "bg-success bg-opacity-50 px-2"; double ratio = (double)NumProcRunning / NumProcStarted; if (ratio < 0.5 || NumProcStarted == 0) { answ = "bg-danger bg-opacity-50 px-2"; } else if (ratio < 1) { answ = "bg-warning bg-opacity-50 px-2"; } return answ; } } /// /// CSS class for "started processes" status. /// /// Color logic: /// - If <50% of configured processes are started → "bg-danger" /// - If 50% to 100% started → "bg-warning" /// - Otherwise → "bg-success" /// protected string cssProcAvviati { get { string answ = "bg-success bg-opacity-50 px-2"; double ratio = (double)NumProcStarted / NumProcConfig; if (ratio < 0.5) { answ = "bg-danger bg-opacity-50 px-2"; } else if (ratio < 1) { answ = "bg-warning bg-opacity-50 px-2"; } return answ; } } /// /// Current list of IOB types (e.g., PLC, SCADA) available in the system. /// Retrieved from ACService. /// protected List CurrIobType { get => ACService.CurrIobType; } /// /// Injected service for managing flux log data and opening log views. /// Used to open specific log files or view logs for a selected IOB. /// [Inject] protected FluxLogManService FLMService { get; set; } = null!; /// /// Selected IOB type (user-filtered value). /// Updates UI when changed and triggers a reload. /// protected string IobTypeSel { get => iobTypeSel; set { if (iobTypeSel != value) { iobTypeSel = value; ForceReload(); } } } /// /// Injected JS runtime for interacting with browser APIs (e.g., confirm dialogs). /// Used for user confirmation before reboot or restart actions. /// [Inject] protected IJSRuntime JSRuntime { get; set; } = null!; /// /// Current logging level (e.g., Debug, Info, Warning). /// Controls verbosity of logs in the application. /// protected LogLevelIob LogLevel { get => ACService.LogLevel; set => ACService.LogLevel = value; } /// /// Navigation manager for routing to pages (e.g., About, Settings). /// [Inject] protected NavigationManager NavMan { get; set; } = null!; /// /// Total number of configured IOB processes. /// Used to calculate startup and running ratios. /// protected int NumProcConfig { get => ACService.NumProcConfig; } /// /// Number of IOB processes currently running. /// Used in status indicators and health checks. /// protected int NumProcRunning { get => ACService.NumProcRunning; } /// /// Number of IOB processes that have been started (not necessarily running). /// Used in status indicators and auto-restart logic. /// protected int NumProcStarted { get => ACService.NumProcStarted; } /// /// Total number of configured IOB types. /// Used for type-based filtering and UI display. /// protected int NumTypeConfig { get => ACService.NumTypeConfig; } /// /// Round-trip time (in ms) for communication between components and services. /// Used to measure responsiveness and performance. /// protected double RoundTrip { get => ACService.RoundTrip; } #endregion Protected Properties #region Protected Methods /// /// Returns CSS class based on whether a specific IOB is running. /// /// Style: /// - If not running → "bg-danger bg-opacity-25" /// - Otherwise → empty (default background) /// /// The IOB record to evaluate. /// Appropriate CSS class string. protected string CssClassIob(IobAdapt currRec) { string css = !currRec.isRunning ? "bg-danger bg-opacity-25" : ""; return css; } /// /// Delays a restart request by setting a countdown timer in the UI. /// /// Used when auto-restart is disabled to prevent immediate restarts. /// protected void DelayRestart() { ACService.DelayRestart(false); } /// /// Closes all child IOB processes and refreshes the UI. /// /// Actions: /// - Stops all running services. /// - Triggers a full scan to update state. /// - Optionally resets the list of services. /// protected async Task DoCloseAll() { CloseAllChild(false); await ACService.DoScan(); // ForceReload(); // Optional: can be removed if UI refresh is handled elsewhere } /// /// Closes a specific child IOB process. /// /// Action: /// - Sends a close command to the service. /// - Hides the right-click menu. /// /// The IOB record to close. protected void DoCloseChild(IobAdapt currRec) { ACService.DoCloseChild(currRec); dxMenuVisible = false; } /// /// Minimizes all IOB process windows to the system tray. /// /// Behavior: /// - Iterates over all CurrRecords. /// - For each process, retrieves its MainWindowHandle and minimizes it. /// - Logs any errors (e.g., process already closed). /// protected void DoHideAll() { foreach (var item in CurrRecords) { try { Process p = Process.GetProcessById(item.pID); var windowsHandle = p.MainWindowHandle; ShowWindowAsync(windowsHandle, CoreEnum.SW_SHOWMINIMIZED); } catch (Exception exc) { Log.Error($"Errore in HIDE windows:{Environment.NewLine}{exc}"); } } } /// /// Reboots all IOB processes and optionally reloads configuration. /// /// Actions: /// - Closes all services. /// - If 'reloadConf' is true, re-scans configuration and reloads it. /// - Reopens all services. /// - Triggers a final scan to update UI. /// /// If true, re-reads configuration after restart. protected async Task DoRestartAll(bool reloadConf) { CloseAllChild(true); if (reloadConf) { await ACService.DoScan(); ACService.DoReloadConfig(); } ACService.DoOpenAllChild(); await ACService.DoScan(); } /// /// Restores all IOB process windows to full screen. /// /// Behavior: /// - Iterates over all CurrRecords. /// - For each process, retrieves its MainWindowHandle and restores it. /// - Logs any errors (e.g., process already closed). /// protected void DoShowAll() { foreach (var item in CurrRecords) { try { Process p = Process.GetProcessById(item.pID); var windowsHandle = p.MainWindowHandle; ShowWindowAsync(windowsHandle, CoreEnum.SW_SHOWNORMAL); } catch (Exception exc) { Log.Error($"Errore in SHOW windows:{Environment.NewLine}{exc}"); } } } /// /// Restores a specific IOB process window. /// /// Action: /// - Finds the process by ID and restores it to normal view. /// /// The IOB record to restore. protected void DoShowChild(IobAdapt item) { try { Process p = Process.GetProcessById(item.pID); var windowsHandle = p.MainWindowHandle; ShowWindowAsync(windowsHandle, CoreEnum.SW_SHOWNORMAL); } catch (Exception exc) { Log.Error($"Errore in SHOW windows:{Environment.NewLine}{exc}"); } } /// /// Starts a specific IOB process. /// /// Action: /// - Sends a command to ACService to open and start the selected IOB. /// - Hides the right-click menu. /// /// The IOB record to start. protected void DoStartChild(IobAdapt currRec) { ACService.DoOpenChildSel(currRec); dxMenuVisible = false; } /// /// Forces a full scan of all IOB services and updates UI state. /// /// Actions: /// - Sets isLoading to true. /// - Clears current records. /// - Calls DoScan() to refresh service list. /// - Updates filtered list based on current filters (e.g., IOB type). /// - Applies pagination. /// - Sets isLoading to false and triggers UI update. /// protected async Task ForceCheck() { isLoading = true; ListRecords = new List(); await ACService.DoScan(); isLoading = false; } /// /// Initializes the component. /// /// Action: /// - Subscribes to ACService.StatusUpdated event to update UI on status changes. /// protected override void OnInitialized() { ACService.EA_StatusUpdated += ACService_EA_StatusUpdated; } /// /// Called when parameters change (e.g., user selects a new IOB type). /// /// Actions: /// - Triggers a full scan of services. /// - Updates the UI with filtered records. /// protected override async Task OnParametersSetAsync() { await ACService.DoScan(); ForceReload(); } /// /// Updates the number of records per page (e.g., from 10 to 20). /// /// Behavior: /// - Updates the page count and triggers a reload to refresh the UI. /// /// New number of records per page. protected void SetNumRec(int newNum) { numRecord = newNum; currPage = 1; ForceReload(); } /// /// Updates the current page (e.g., page 2, 3, etc.). /// /// Behavior: /// - Updates the page number and triggers a reload to refresh the UI. /// /// New page number. protected void SetPage(int newNum) { currPage = newNum; ForceReload(); } #endregion Protected Methods #region Private Fields /// /// Logger instance for structured logging within this component. /// private static Logger Log = LogManager.GetCurrentClassLogger(); /// /// Timestamp of last UI render to prevent rapid re-renders. /// Prevents excessive updates when data changes quickly. /// private DateTime _lastRender = DateTime.MinValue; /// /// Auto-restart countdown (in seconds) displayed in UI. /// Shows time remaining before auto-restart is triggered. /// private string countAutoRestart = ""; /// /// Flag indicating whether the right-click menu is visible. /// Used for UI state management. /// private bool dxMenuVisible = false; /// /// Flag controlling whether task killing is allowed (e.g., for safety). /// private bool enableKillTask = true; /// /// X-coordinate for context menu positioning (in pixels). /// Set dynamically on right-click. /// private string menuXpx = "0px"; /// /// Y-coordinate for context menu positioning (in pixels). /// Set dynamically on right-click. /// private string menuYpx = "0px"; /// /// Currently selected IOB record (used for actions like opening folders). /// private IobAdapt? selIOB = null; #endregion Private Fields #region Private Properties /// /// Current computer name (e.g., "DESKTOP-ABC123"). /// private string ComputerName { get => Environment.MachineName; } /// /// CSS class for "Close All" button. /// /// Behavior: /// - Enabled only if at least one service is currently running. /// - Disabled otherwise (grayed out). /// private string cssBtnCloseAll { get => NumProcStarted > 0 ? "" : "disabled opacity-50"; } /// /// Current page number (1-indexed). /// private int currPage { get; set; } = 1; /// /// Current list of IOB service records (e.g., PLC, HMI). /// Retrieved from ACService and used for UI display. /// private List CurrRecords { get => ACService.ListIobAdapters; } /// /// User domain (e.g., "DOMAIN\\username"). /// private string DomainName { get => Environment.UserDomainName; } /// /// Indicates whether the UI is loading (used for spinner or placeholder state). /// private bool isLoading { get; set; } = false; /// /// Paginated list of records currently displayed in the UI. /// Filtered and paginated based on IOB type and page size. /// private List? ListRecords { get; set; } = null; /// /// Number of records per page (e.g., 10, 20). /// private int numRecord { get; set; } = 10; /// /// Target path for a selected IOB (e.g., C:\IOB\PLC\001). /// Built dynamically from CodIOB and ACService. /// private string selIobTgtPath { get { string answ = selIOB != null ? ACService.IobTgtPath(selIOB.CodIOB) : ""; return answ; } } /// /// Total number of records (used for pagination and UI display). /// private int totalCount { get; set; } = 0; /// /// Current user name (e.g., "JohnDoe"). /// private string UserName { get => Environment.UserName; } #endregion Private Properties #region Private Methods /// /// Windows API: Shows a window with a specified style (e.g., minimized, normal). /// Used to minimize or restore IOB process windows. /// /// Handle to the window. /// Style (e.g., SW_SHOWMINIMIZED, SW_SHOWNORMAL). [DllImport("user32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); /// /// Handles status updates from ACService. /// /// Logic: /// - If auto-restart is disabled, shows countdown until auto-restart is triggered. /// - When countdown reaches zero, enables auto-restart. /// - Forces UI reload to reflect new state. /// private void ACService_EA_StatusUpdated() { if (!AutoRestart) { var remSec = (int)ACService.VetoAutoCheck.Subtract(DateTime.Now).TotalSeconds; countAutoRestart = remSec > 0 ? $"{remSec:N0}" : "!!!"; if (remSec <= 0) { AutoRestart = true; } } ForceReload(); } /// /// Closes all child IOB processes. /// /// Parameters: /// - doReset: if true, resets the internal list of services. /// /// Whether to reset the service list after closing. private void CloseAllChild(bool doReset) { enableKillTask = false; ACService.DoCloseAll(doReset); enableKillTask = true; } /// /// Triggers a reboot request with user confirmation. /// /// Behavior: /// - Uses JSRuntime to show a confirmation dialog. /// - If confirmed, raises a reboot request to ACService. /// /// Mouse event arguments (used for context menu). private async Task ForceReboot(Microsoft.AspNetCore.Components.Web.MouseEventArgs args) { if (!await JSRuntime.InvokeAsync("confirm", "Sicuro di voler riavviare il programma?")) return; ACService.RaiseRebootReq(); } /// /// Forces a UI reload with current filters and pagination. /// /// Behavior: /// - Skips if too soon (less than 1 second since last render). /// - Updates filtered list based on IOB type. /// - Applies pagination. /// - Updates UI state and triggers re-render. /// private void ForceReload() { if ((DateTime.Now - _lastRender).TotalSeconds < 1) return; _lastRender = DateTime.Now; isLoading = true; var filtered = CurrRecords; if (!string.IsNullOrEmpty(IobTypeSel)) filtered = filtered.Where(x => x.TgtName == IobTypeSel).ToList(); totalCount = filtered.Count(); ListRecords = filtered .OrderBy(x => x.CodIOB) .Skip(numRecord * (currPage - 1)) .Take(numRecord) .ToList(); isLoading = false; _ = InvokeAsync(StateHasChanged); } /// /// Opens the target folder of the selected IOB (e.g., for configuration). /// private void IobFolderOpenApp() { if (selIOB != null) Process.Start("explorer.exe", selIobTgtPath); selIOB = null; dxMenuVisible = false; } /// /// Opens the configuration folder of the selected IOB. /// private void IobFolderOpenConf() { if (selIOB != null) Process.Start("explorer.exe", Path.Combine(selIobTgtPath, "DATA", "CONF")); selIOB = null; dxMenuVisible = false; } /// /// Opens the logs folder of the selected IOB. /// private void IobFolderOpenLog() { if (selIOB != null) Process.Start("explorer.exe", Path.Combine(selIobTgtPath, "logs", selIOB.CodIOB)); selIOB = null; dxMenuVisible = false; } /// /// Navigates to the "About" page. /// private void NavAbout() { NavMan.NavigateTo("About"); } /// /// Opens a FluxLog view for the selected IOB. /// private void OpenFLForm() { if (selIOB != null) FLMService.RequestLoadFLogData(selIOB.CodIOB); selIOB = null; dxMenuVisible = false; } /// /// Handles right-click event on an IOB record. /// /// Behavior: /// - Sets selected IOB. /// - Shows context menu at mouse position. /// /// Mouse event arguments. /// The IOB record being right-clicked. private void RightClick(Microsoft.AspNetCore.Components.Web.MouseEventArgs e, IobAdapt currRec) { selIOB = currRec; if (e.Button == 2) { dxMenuVisible = true; menuXpx = $"{e.ClientX}px"; menuYpx = $"{e.ClientY - 90}px"; } } #endregion Private Methods } }