using System.Globalization; using System.Runtime.InteropServices.JavaScript; using System.Text; using System.Text.Json; using Quadarax.Foundation.Core.Data; using Quadarax.Foundation.Core.QConsole; using Quadarax.Foundation.Core.QConsole.Argument; using Quadarax.Foundation.Core.QConsole.Attributes; using Quadarax.Foundation.Core.QConsole.Value; using Quadarax.Foundation.Core.QMonitor.Interfaces; using Quadarax.Foundation.Core.Value.Extensions; using Result = Quadarax.Foundation.Core.Value.Result; namespace Quadarax.Foundation.Core.QMonitor.Console.Commands { [CommandDefinition] internal class MonitorCommand : BaseCommand { #region *** Constants *** private const string CS_CMD_MONITOR_NAME = "monitor"; private const string CS_CMD_MONITOR_DESC = "Monitor specific single channel"; protected const string CS_ARG_NAME_IID = "iid"; private const string CS_ARG_HINT_IID = ""; private const string CS_ARG_DESC_IID = "Specify instance identifier to listen from."; protected const string CS_ARG_NAME_CHANNEL = "channel"; private const string CS_ARG_HINT_CHANNEL = ""; private const string CS_ARG_DESC_CHANNEL = "Specify full channel name to monitor."; protected const string CS_ARG_NAME_REFRESH = "refresh"; private const string CS_ARG_HINT_REFRESH = ""; private const string CS_ARG_DESC_REFRESH = "Specify receiver refresh interval to detect changes. Default is 00:00:05."; protected const string CS_ARG_NAME_GENFILE = "genfile"; private const string CS_ARG_HINT_GENFILE = ""; private const string CS_ARG_DESC_GENFILE = "Use general metadata from json file during initialization (no needs to wait to receive)."; protected const string CS_ARG_NAME_REWIND = "rewind"; private const string CS_ARG_HINT_REWIND = ""; private const string CS_ARG_DESC_REWIND = "Flag defines if rewind to first available cached data (may live when no cached data), otherwise wait for new data."; #endregion #region *** Properties *** public override string Name => CS_CMD_MONITOR_NAME; public override string Description => CS_CMD_MONITOR_DESC; private string InstanceIdentifier { get; set; } private string ChannelName { get; set; } private TimeSpan RefreshInterval { get; set; } private string GeneralFile { get; set; } private bool IsRewind { get; set; } #endregion #region *** Private fields *** private long _dumpCnt; private MonData? _lastData; private IDictionary _lastDataCollection = new Dictionary(); #endregion #region *** Constructor *** public MonitorCommand(Engine engine) : base(engine) { } #endregion #region *** Execute *** protected override Result OnExecute() { WriteInfo($"Refresh interval is set to {RefreshInterval}"); WriteInfo($"Waiting for channel definition '{InstanceIdentifier}' and channel '{ChannelName}'..."); using (var hnd = Receiver.CreateHandler(InstanceIdentifier, ChannelName, RefreshInterval,GeneralFile, true)) { hnd.ReceiverHandlerStateChanged += OnReceiverHandlerStateChanged; hnd.ReceiverHandlerDataChanged += OnReceiverHandlerDataChanged; if (IsRewind) { WriteInfo("Rewind to first available cached data."); hnd.Rewind(isBackward:true); hnd.Play(false); } else { hnd.Play(true); } WriteCaption("To close receiver press any key ..."); System.Console.ReadKey(); } return new Result(); } #endregion #region *** Private Operations *** protected override void OnInitialize() { base.OnInitialize(); InstanceIdentifier = GetArgumentValueOrDefault(CS_ARG_NAME_IID); ChannelName = GetArgumentValueOrDefault(CS_ARG_NAME_CHANNEL); RefreshInterval = GetArgumentValueOrDefault(CS_ARG_NAME_REFRESH); GeneralFile = GetArgumentValueOrDefault(CS_ARG_NAME_GENFILE); IsRewind = GetArgumentValueOrDefault(CS_ARG_NAME_REWIND); } protected override IEnumerable OnSetupArguments() { var list = base.OnSetupArguments().ToList(); list.Add(new NamedArgument(CS_ARG_NAME_IID, CS_ARG_HINT_IID, CS_ARG_DESC_IID, TypeValuesEnum.String,string.Empty,true)); list.Add(new NamedArgument(CS_ARG_NAME_CHANNEL, CS_ARG_HINT_CHANNEL, CS_ARG_DESC_CHANNEL, TypeValuesEnum.String,string.Empty,true)); list.Add(new NamedArgument(CS_ARG_NAME_REFRESH, CS_ARG_HINT_REFRESH, CS_ARG_DESC_REFRESH, TypeValuesEnum.TimeSpan,TimeSpan.FromSeconds(5).ToString(),false)); list.Add(new NamedArgument(CS_ARG_NAME_GENFILE, CS_ARG_HINT_GENFILE, CS_ARG_DESC_GENFILE, TypeValuesEnum.String, string.Empty, false)); list.Add(new FlagArgument(CS_ARG_NAME_REWIND, CS_ARG_HINT_REWIND, CS_ARG_DESC_REWIND, false)); return list; } private void OnReceiverHandlerDataChanged(IReceiverHandler sender, MonData newData) { RenderData(sender.Descriptor, newData); } private void OnReceiverHandlerStateChanged(IReceiverHandler sender, ReceiverHandlerStateEnum state) { WriteInfo($"! Receiver state change to {sender.State.ToString().ToUpper()}"); } private void RenderData(MonItemGeneral descriptor, MonData data) { var firstLine = $"*** Dump [{_dumpCnt}] {descriptor.ViewType} * {descriptor.Caption} ***"; WriteInfo(firstLine); WriteCaption($"Timestamp: {DateTime.FromFileTimeUtc(data.TimestampFtUtc)} ({data.TimestampFtUtc})"); WriteCaption($"Name: {data.Name}"); WriteInfo(new('-', firstLine.Length)); var diff = _lastData?.Diff(data); if (descriptor.ViewType == MonItemGeneral.MonViewType.Item) { //item view foreach (var item in descriptor.Items.OrderBy(x=>x.Order)) { var sb = new StringBuilder(); if (diff != null) { if (diff.ContainsKey(item.Name)) sb.Append("[*] "); } sb.Append(string.IsNullOrEmpty(item.Label) ? item.Name : item.Label).Append(" : "); var properties = ParseProperties(JsonDocument.Parse(data.Data).RootElement); var arg = properties.FirstOrDefault(x => string.Equals(x.Name, item.Name, StringComparison.InvariantCultureIgnoreCase)); sb.Append(arg != null ? arg.Value : "N/A"); WriteInfo(sb.ToString()); } } WriteInfo(new('-', firstLine.Length)); _lastData = data; _dumpCnt++; } private TypedArgument[] ParseProperties(JsonElement element) { var result = new List(); foreach (var property in element.EnumerateObject()) { if (property.Value.ValueKind.In(JsonValueKind.False, JsonValueKind.True, JsonValueKind.Number, JsonValueKind.String)) { TypedArgument? arg = null; if (property.Value.ValueKind.In(JsonValueKind.False, JsonValueKind.True)) { arg = new TypedArgument(property.Name, property.Value.GetBoolean()); } else if (property.Value.ValueKind == JsonValueKind.Null) { result.Add(new TypedArgument(property.Name,null, typeof(string))); } else if (property.Value.ValueKind == JsonValueKind.Number) { result.Add(new TypedArgument(property.Name, property.Value.GetDecimal())); } else if (property.Value.ValueKind == JsonValueKind.String) { result.Add(new TypedArgument(property.Name, property.Value.GetString())); } if (arg != null) result.Add(arg); } } return result.ToArray(); } #endregion } }