Files
mapo-iob-man/IOB-MAN/Components/Compo/ApplicationCheck.razor.cs
T
Samuele Locatelli 8b4ee01fb7 Refresh componente
2026-01-08 11:59:33 +01:00

851 lines
27 KiB
C#

/// <summary>
/// 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.
/// </summary>
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
/// <summary>
/// Cleans up event subscriptions and resources when the component is disposed.
///
/// Specifically:
/// - Unsubscribes from ACService.StatusUpdated event.
/// </summary>
public void Dispose()
{
ACService.EA_StatusUpdated -= ACService_EA_StatusUpdated;
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Selected IOB type filter (e.g., "PLC", "SCADA", "HMI").
/// Used to filter displayed records in the UI.
/// </summary>
protected string iobTypeSel = "";
/// <summary>
/// List of page sizes available for record pagination (e.g., 5, 10, 20 records per page).
/// Used in UI for user-controlled pagination.
/// </summary>
protected List<int> PageSizeDispl = new List<int>() { 5, 10, 15, 20, 25, 30, 40, 50 };
#endregion Protected Fields
#region Protected Properties
/// <summary>
/// Injected service for managing external applications and service states.
/// Responsible for opening, closing, and monitoring IOB processes.
/// </summary>
[Inject]
protected AppControlService ACService { get; set; } = null!;
/// <summary>
/// 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.
/// </summary>
protected bool AutoRestart
{
get => ACService.AutoRestartEnabled;
set
{
if (ACService.AutoRestartEnabled != value)
{
ACService.AutoRestartEnabled = value;
if (value)
{
ACService.DoRestartPlcKo();
ACService.DoReopenClosed();
}
else
{
countAutoRestart = "0000";
ACService.DelayRestart(!value);
}
currPage = 1;
DoReload();
}
}
}
/// <summary>
/// CSS style for context menu (positioned absolutely at mouse coordinates).
/// Dynamically set based on user right-click position.
/// </summary>
protected string contextMenuStyle
{
get => $"position: absolute; top: {menuYpx}; left: {menuXpx}; z-index:999;";
}
/// <summary>
/// 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"
/// </summary>
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;
}
}
/// <summary>
/// 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"
/// </summary>
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;
}
}
/// <summary>
/// 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"
/// </summary>
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;
}
}
/// <summary>
/// Current list of IOB types (e.g., PLC, SCADA) available in the system.
/// Retrieved from ACService.
/// </summary>
protected List<string> CurrIobType
{
get => ACService.CurrIobType;
}
/// <summary>
/// Injected service for managing flux log data and opening log views.
/// Used to open specific log files or view logs for a selected IOB.
/// </summary>
[Inject]
protected FluxLogManService FLMService { get; set; } = null!;
/// <summary>
/// Selected IOB type (user-filtered value).
/// Updates UI when changed and triggers a reload.
/// </summary>
protected string IobTypeSel
{
get => iobTypeSel;
set
{
if (iobTypeSel != value)
{
iobTypeSel = value;
DoReload();
}
}
}
/// <summary>
/// Injected JS runtime for interacting with browser APIs (e.g., confirm dialogs).
/// Used for user confirmation before reboot or restart actions.
/// </summary>
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
/// <summary>
/// Current logging level (e.g., Debug, Info, Warning).
/// Controls verbosity of logs in the application.
/// </summary>
protected LogLevelIob LogLevel
{
get => ACService.LogLevel;
set => ACService.LogLevel = value;
}
/// <summary>
/// Navigation manager for routing to pages (e.g., About, Settings).
/// </summary>
[Inject]
protected NavigationManager NavMan { get; set; } = null!;
/// <summary>
/// Total number of configured IOB processes.
/// Used to calculate startup and running ratios.
/// </summary>
protected int NumProcConfig
{
get => ACService.NumProcConfig;
}
/// <summary>
/// Number of IOB processes currently running.
/// Used in status indicators and health checks.
/// </summary>
protected int NumProcRunning
{
get => ACService.NumProcRunning;
}
/// <summary>
/// Number of IOB processes that have been started (not necessarily running).
/// Used in status indicators and auto-restart logic.
/// </summary>
protected int NumProcStarted
{
get => ACService.NumProcStarted;
}
/// <summary>
/// Total number of configured IOB types.
/// Used for type-based filtering and UI display.
/// </summary>
protected int NumTypeConfig
{
get => ACService.NumTypeConfig;
}
/// <summary>
/// Round-trip time (in ms) for communication between components and services.
/// Used to measure responsiveness and performance.
/// </summary>
protected double RoundTrip
{
get => ACService.RoundTrip;
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Returns CSS class based on whether a specific IOB is running.
///
/// Style:
/// - If not running → "bg-danger bg-opacity-25"
/// - Otherwise → empty (default background)
/// </summary>
/// <param name="currRec">The IOB record to evaluate.</param>
/// <returns>Appropriate CSS class string.</returns>
protected string CssClassIob(IobAdapt currRec)
{
string css = !currRec.isRunning ? "bg-danger bg-opacity-25" : "";
return css;
}
/// <summary>
/// Delays a restart request by setting a countdown timer in the UI.
///
/// Used when auto-restart is disabled to prevent immediate restarts.
/// </summary>
protected void DelayRestart()
{
ACService.DelayRestart(false);
}
/// <summary>
/// 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.
/// </summary>
protected async Task DoCloseAll()
{
CloseAllChild(false);
await ACService.DoScan();
// DoReload(); // Optional: can be removed if UI refresh is handled elsewhere
}
/// <summary>
/// Closes a specific child IOB process.
///
/// Action:
/// - Sends a close command to the service.
/// - Hides the right-click menu.
/// </summary>
/// <param name="currRec">The IOB record to close.</param>
protected void DoCloseChild(IobAdapt currRec)
{
ACService.DoCloseChild(currRec);
dxMenuVisible = false;
}
/// <summary>
/// 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).
/// </summary>
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}");
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="reloadConf">If true, re-reads configuration after restart.</param>
protected async Task DoRestartAll(bool reloadConf)
{
CloseAllChild(true);
if (reloadConf)
{
await ACService.DoScan();
ACService.DoReloadConfig();
}
ACService.DoOpenAllChild();
await ACService.DoScan();
}
/// <summary>
/// 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).
/// </summary>
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}");
}
}
}
/// <summary>
/// Restores a specific IOB process window.
///
/// Action:
/// - Finds the process by ID and restores it to normal view.
/// </summary>
/// <param name="item">The IOB record to restore.</param>
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}");
}
}
/// <summary>
/// Starts a specific IOB process.
///
/// Action:
/// - Sends a command to ACService to open and start the selected IOB.
/// - Hides the right-click menu.
/// </summary>
/// <param name="currRec">The IOB record to start.</param>
protected void DoStartChild(IobAdapt currRec)
{
ACService.DoOpenChildSel(currRec);
dxMenuVisible = false;
}
/// <summary>
/// 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.
/// </summary>
protected async Task ForceCheck()
{
isLoading = true;
ListRecords = new List<IobAdapt>();
await ACService.DoScan();
isLoading = false;
}
/// <summary>
/// Initializes the component.
///
/// Action:
/// - Subscribes to ACService.StatusUpdated event to update UI on status changes.
/// </summary>
protected override void OnInitialized()
{
ACService.EA_StatusUpdated += ACService_EA_StatusUpdated;
}
/// <summary>
/// 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.
/// </summary>
protected override async Task OnParametersSetAsync()
{
await ACService.DoScan();
DoReload();
}
/// <summary>
/// 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.
/// </summary>
/// <param name="newNum">New number of records per page.</param>
protected void SetNumRec(int newNum)
{
numRecord = newNum;
currPage = 1;
ForceReload();
}
/// <summary>
/// Updates the current page (e.g., page 2, 3, etc.).
///
/// Behavior:
/// - Updates the page number and triggers a reload to refresh the UI.
/// </summary>
/// <param name="newNum">New page number.</param>
protected void SetPage(int newNum)
{
currPage = newNum;
ForceReload();
}
#endregion Protected Methods
#region Private Fields
/// <summary>
/// Logger instance for structured logging within this component.
/// </summary>
private static Logger Log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Timestamp of last UI render to prevent rapid re-renders.
/// Prevents excessive updates when data changes quickly.
/// </summary>
private DateTime _lastRender = DateTime.MinValue;
/// <summary>
/// Auto-restart countdown (in seconds) displayed in UI.
/// Shows time remaining before auto-restart is triggered.
/// </summary>
private string countAutoRestart = "";
/// <summary>
/// Flag indicating whether the right-click menu is visible.
/// Used for UI state management.
/// </summary>
private bool dxMenuVisible = false;
/// <summary>
/// Flag controlling whether task killing is allowed (e.g., for safety).
/// </summary>
private bool enableKillTask = true;
/// <summary>
/// X-coordinate for context menu positioning (in pixels).
/// Set dynamically on right-click.
/// </summary>
private string menuXpx = "0px";
/// <summary>
/// Y-coordinate for context menu positioning (in pixels).
/// Set dynamically on right-click.
/// </summary>
private string menuYpx = "0px";
/// <summary>
/// Currently selected IOB record (used for actions like opening folders).
/// </summary>
private IobAdapt? selIOB = null;
#endregion Private Fields
#region Private Properties
/// <summary>
/// Current computer name (e.g., "DESKTOP-ABC123").
/// </summary>
private string ComputerName
{
get => Environment.MachineName;
}
/// <summary>
/// CSS class for "Close All" button.
///
/// Behavior:
/// - Enabled only if at least one service is currently running.
/// - Disabled otherwise (grayed out).
/// </summary>
private string cssBtnCloseAll
{
get => NumProcStarted > 0 ? "" : "disabled opacity-50";
}
/// <summary>
/// Current page number (1-indexed).
/// </summary>
private int currPage { get; set; } = 1;
/// <summary>
/// Current list of IOB service records (e.g., PLC, HMI).
/// Retrieved from ACService and used for UI display.
/// </summary>
private List<IobAdapt> CurrRecords
{
get => ACService.ListIobAdapters;
}
/// <summary>
/// User domain (e.g., "DOMAIN\\username").
/// </summary>
private string DomainName
{
get => Environment.UserDomainName;
}
/// <summary>
/// Indicates whether the UI is loading (used for spinner or placeholder state).
/// </summary>
private bool isLoading { get; set; } = false;
/// <summary>
/// Paginated list of records currently displayed in the UI.
/// Filtered and paginated based on IOB type and page size.
/// </summary>
private List<IobAdapt>? ListRecords { get; set; } = null;
/// <summary>
/// Number of records per page (e.g., 10, 20).
/// </summary>
private int numRecord { get; set; } = 10;
/// <summary>
/// Target path for a selected IOB (e.g., C:\IOB\PLC\001).
/// Built dynamically from CodIOB and ACService.
/// </summary>
private string selIobTgtPath
{
get
{
string answ = selIOB != null ? ACService.IobTgtPath(selIOB.CodIOB) : "";
return answ;
}
}
/// <summary>
/// Total number of records (used for pagination and UI display).
/// </summary>
private int totalCount { get; set; } = 0;
/// <summary>
/// Current user name (e.g., "JohnDoe").
/// </summary>
private string UserName
{
get => Environment.UserName;
}
#endregion Private Properties
#region Private Methods
/// <summary>
/// Windows API: Shows a window with a specified style (e.g., minimized, normal).
/// Used to minimize or restore IOB process windows.
/// </summary>
/// <param name="hWnd">Handle to the window.</param>
/// <param name="nCmdShow">Style (e.g., SW_SHOWMINIMIZED, SW_SHOWNORMAL).</param>
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
/// <summary>
/// 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.
/// </summary>
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;
}
}
DoReload();
}
/// <summary>
/// Closes all child IOB processes.
///
/// Parameters:
/// - doReset: if true, resets the internal list of services.
/// </summary>
/// <param name="doReset">Whether to reset the service list after closing.</param>
private void CloseAllChild(bool doReset)
{
enableKillTask = false;
ACService.DoCloseAll(doReset);
enableKillTask = true;
}
/// <summary>
/// Check if reload is needed (no more than 2Hz) then triggers a ForceReload
/// </summary>
private void DoReload()
{
if ((DateTime.Now - _lastRender).TotalSeconds < 0.5)
return;
ForceReload();
}
/// <summary>
/// Triggers a reboot request with user confirmation.
///
/// Behavior:
/// - Uses JSRuntime to show a confirmation dialog.
/// - If confirmed, raises a reboot request to ACService.
/// </summary>
/// <param name="args">Mouse event arguments (used for context menu).</param>
private async Task ForceReboot(Microsoft.AspNetCore.Components.Web.MouseEventArgs args)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", "Sicuro di voler riavviare il programma?"))
return;
ACService.RaiseRebootReq();
}
/// <summary>
/// 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.
/// </summary>
private void ForceReload()
{
_lastRender = DateTime.Now;
if (CurrRecords != null && CurrRecords.Count > 0)
{
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);
}
}
/// <summary>
/// Opens the target folder of the selected IOB (e.g., for configuration).
/// </summary>
private void IobFolderOpenApp()
{
if (selIOB != null)
Process.Start("explorer.exe", selIobTgtPath);
selIOB = null;
dxMenuVisible = false;
}
/// <summary>
/// Opens the configuration folder of the selected IOB.
/// </summary>
private void IobFolderOpenConf()
{
if (selIOB != null)
Process.Start("explorer.exe", Path.Combine(selIobTgtPath, "DATA", "CONF"));
selIOB = null;
dxMenuVisible = false;
}
/// <summary>
/// Opens the logs folder of the selected IOB.
/// </summary>
private void IobFolderOpenLog()
{
if (selIOB != null)
Process.Start("explorer.exe", Path.Combine(selIobTgtPath, "logs", selIOB.CodIOB));
selIOB = null;
dxMenuVisible = false;
}
/// <summary>
/// Navigates to the "About" page.
/// </summary>
private void NavAbout()
{
NavMan.NavigateTo("About");
}
/// <summary>
/// Opens a FluxLog view for the selected IOB.
/// </summary>
private void OpenFLForm()
{
if (selIOB != null)
FLMService.RequestLoadFLogData(selIOB.CodIOB);
selIOB = null;
dxMenuVisible = false;
}
/// <summary>
/// Handles right-click event on an IOB record.
///
/// Behavior:
/// - Sets selected IOB.
/// - Shows context menu at mouse position.
/// </summary>
/// <param name="e">Mouse event arguments.</param>
/// <param name="currRec">The IOB record being right-clicked.</param>
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
}
}