diff --git a/Common/PlayerDivConfig.cs b/Common/PlayerDivConfig.cs new file mode 100644 index 0000000..8609785 --- /dev/null +++ b/Common/PlayerDivConfig.cs @@ -0,0 +1,141 @@ +using Hi.Common; +using Hi.Common.XmlUtils; +using System.Xml; +using System.Xml.Linq; + +namespace Sample.Common; + +/// +/// Per-user visibility flags for the Player page's divisions (charts and +/// info panels), flattened into a single all-boolean config persisted through +/// . +/// +/// +/// Defaults are tuned for a first-time user: the two info panels +/// ( and ) are ON +/// so the Player page looks populated without having to open the +/// Division Visibility dropdown; every chart toggle starts OFF. +/// +public class PlayerDivConfig : IMakeXmlSource +{ + #region XML IO + /// + /// Registers this type's deserializer with the given + /// (or when is + /// null). Idempotent. + /// + public static void Reg(XFactory factory = null) + { + factory ??= XFactory.Default; + factory.Generators.TryAdd(XName, (xml, baseDirectory, relFile, progress, res) + => new PlayerDivConfig(xml)); + } + + /// + /// Name for XML IO. + /// + public static string XName => nameof(PlayerDivConfig); + + /// + /// Default constructor. + /// + public PlayerDivConfig() { } + + /// + /// Initializes a new instance of the class from XML data. + /// + /// XML element containing player-div data. + public PlayerDivConfig(XElement src) + { + if (src == null) return; + + src.Element(nameof(EnableStripAvailabilityChart))?.Value?.SelfInvoke( + v => EnableStripAvailabilityChart = XmlConvert.ToBoolean(v)); + src.Element(nameof(EnableStripRoughnessChart))?.Value?.SelfInvoke( + v => EnableStripRoughnessChart = XmlConvert.ToBoolean(v)); + src.Element(nameof(ColorIndexTimeChart))?.Value?.SelfInvoke( + v => ColorIndexTimeChart = XmlConvert.ToBoolean(v)); + src.Element(nameof(ForceCycleLineDiv))?.Value?.SelfInvoke( + v => ForceCycleLineDiv = XmlConvert.ToBoolean(v)); + src.Element(nameof(SimSpindleMomentCycleLineDiv))?.Value?.SelfInvoke( + v => SimSpindleMomentCycleLineDiv = XmlConvert.ToBoolean(v)); + src.Element(nameof(SensorSpindleMomentCycleLineDiv))?.Value?.SelfInvoke( + v => SensorSpindleMomentCycleLineDiv = XmlConvert.ToBoolean(v)); + src.Element(nameof(DynamometerForceCycleLineDiv))?.Value?.SelfInvoke( + v => DynamometerForceCycleLineDiv = XmlConvert.ToBoolean(v)); + src.Element(nameof(EnableStepDiv))?.Value?.SelfInvoke( + v => EnableStepDiv = XmlConvert.ToBoolean(v)); + src.Element(nameof(MessageTable))?.Value?.SelfInvoke( + v => MessageTable = XmlConvert.ToBoolean(v)); + src.Element(nameof(EnableDetailDiv0))?.Value?.SelfInvoke( + v => EnableDetailDiv0 = XmlConvert.ToBoolean(v)); + } + + /// + public XElement MakeXmlSource(string baseDirectory, string relFile, bool exhibitionOnly) + { + return new XElement(XName, + new XElement(nameof(EnableStripAvailabilityChart), EnableStripAvailabilityChart), + new XElement(nameof(EnableStripRoughnessChart), EnableStripRoughnessChart), + new XElement(nameof(ColorIndexTimeChart), ColorIndexTimeChart), + new XElement(nameof(ForceCycleLineDiv), ForceCycleLineDiv), + new XElement(nameof(SimSpindleMomentCycleLineDiv), SimSpindleMomentCycleLineDiv), + new XElement(nameof(SensorSpindleMomentCycleLineDiv), SensorSpindleMomentCycleLineDiv), + new XElement(nameof(DynamometerForceCycleLineDiv), DynamometerForceCycleLineDiv), + new XElement(nameof(EnableStepDiv), EnableStepDiv), + new XElement(nameof(MessageTable), MessageTable), + new XElement(nameof(EnableDetailDiv0), EnableDetailDiv0) + ); + } + #endregion + + /// + /// Availability strip chart (per-step timeline coloured by availability aspect). + /// + public bool EnableStripAvailabilityChart { get; set; } = false; + + /// + /// Surface-roughness strip chart (per-step timeline coloured by surface-roughness aspect). + /// + public bool EnableStripRoughnessChart { get; set; } = false; + + /// + /// Color-index strip chart (single-strip timeline coloured by arbitrary numeric/bool aspect). + /// + public bool ColorIndexTimeChart { get; set; } = false; + + /// + /// Cycle-line chart of cutting force along the step's rotation cycle. + /// + public bool ForceCycleLineDiv { get; set; } = false; + + /// + /// Cycle-line chart of simulation-derived spindle moment on spindle-rotation coordinate. + /// + public bool SimSpindleMomentCycleLineDiv { get; set; } = false; + + /// + /// Cycle-line chart of sensor-measured spindle moment (overlay on sim moment). + /// + public bool SensorSpindleMomentCycleLineDiv { get; set; } = false; + + /// + /// Cycle-line chart of sensor-measured force from an external dynamometer. + /// + public bool DynamometerForceCycleLineDiv { get; set; } = false; + + /// + /// Full / condensed step information panel (Selected Step Info). + /// + public bool EnableStepDiv { get; set; } = true; + + /// + /// Session messages panel. + /// + public bool MessageTable { get; set; } = true; + + /// + /// Developer-only raw step-property dump panel. + /// + public bool EnableDetailDiv0 { get; set; } = false; +} diff --git a/Common/UserConfig.cs b/Common/UserConfig.cs new file mode 100644 index 0000000..9539216 --- /dev/null +++ b/Common/UserConfig.cs @@ -0,0 +1,173 @@ +using Hi.Cbtr; +using Hi.Common; +using Hi.Common.XmlUtils; +using Hi.MachiningSteps; +using Hi.NcMech.Fixtures; +using Hi.NcMech.Workpieces; +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using System.Xml.Linq; + +namespace Sample.Common; + +/// +/// User Configuration. +/// +/// +/// Sample-owned copy of the per-GUI user configuration, kept so the +/// step-present demo still compiles. Each +/// GUI/APP keeps its own version; the core engine package (HiNc) no longer +/// carries any GUI configuration type. +/// +public class UserConfig : IMakeXmlSource +{ + /// + /// Registers this type's deserializer with the given + /// (or when is + /// null). Idempotent. + /// + public static void Reg(XFactory factory = null) + { + factory ??= XFactory.Default; + factory.Generators.TryAdd(XName, (xml, baseDirectory, relFile, progress, res) + => new UserConfig(xml, baseDirectory)); + } + #region XML IO + /// + /// Name for XML IO. + /// + public static string XName => nameof(UserConfig); + /// + /// Initializes a new instance of the class from XML data. + /// + /// XML element containing configuration data + /// Base directory for resolving relative paths + public UserConfig(XElement src, string baseDirectory) + { + if (src == null) return; + + src.Element(nameof(ShowPhysicsOptions))?.Value?.SelfInvoke( + v => ShowPhysicsOptions = XmlConvert.ToBoolean(v)); + src.Element(nameof(LanguageCode))?.Value?.SelfInvoke(v => LanguageCode = v); + src.Element(nameof(EnableFullControl))?.Value?.SelfInvoke( + v => EnableFullControl = XmlConvert.ToBoolean(v)); + + src.Element(nameof(GraphicCacheLowerLimitMb))?.Value?.SelfInvoke( + v => GraphicCacheLowerLimitMb = XmlConvert.ToDouble(v)); + src.Element(nameof(GraphicCacheUpperLimitMb))?.Value?.SelfInvoke( + v => GraphicCacheUpperLimitMb = XmlConvert.ToDouble(v)); + src.Element(nameof(GraphicCacheMb))?.Value?.SelfInvoke( + v => GraphicCacheMb = XmlConvert.ToInt64(v)); + + src.Element(nameof(DisplayedStepPresentKeyList))?.SelfInvoke(ee => + { + DisplayedStepPresentKeyList = ee.Elements("Item").Select(e => e.Value).ToList(); + }); + + // Load FixtureSetupDisplayeeConfig + src.Element(nameof(FixtureSetupDisplayeeConfig))?.SelfInvoke(e => + { + FixtureSetupDisplayeeConfig = new FixtureEditorDisplayeeConfig(e); + }); + + // Load EquipmentWorkpieceSetupDisplayeeConfig + src.Element(nameof(EquipmentWorkpieceSetupDisplayeeConfig))?.SelfInvoke(e => + { + EquipmentWorkpieceSetupDisplayeeConfig = new WorkpieceEditorDisplayeeConfig(e); + }); + + // Load PlayerDivConfig + src.Element(nameof(PlayerDivConfig))?.SelfInvoke(e => + { + PlayerDivConfig = new PlayerDivConfig(e); + }); + } + /// + public XElement MakeXmlSource(string baseDirectory, string relFile, bool exhibitionOnly) + { + XElement dst = new XElement(XName, + new XElement(nameof(ShowPhysicsOptions), ShowPhysicsOptions), + new XElement(nameof(LanguageCode), LanguageCode), + new XElement(nameof(EnableFullControl), EnableFullControl), + new XElement(nameof(GraphicCacheLowerLimitMb), GraphicCacheLowerLimitMb), + new XElement(nameof(GraphicCacheUpperLimitMb), GraphicCacheUpperLimitMb), + new XElement(nameof(GraphicCacheMb), GraphicCacheMb), + new XElement(nameof(DisplayedStepPresentKeyList), + DisplayedStepPresentKeyList.Select(v => new XElement("Item", v))), + new XElement(nameof(FixtureSetupDisplayeeConfig), + FixtureSetupDisplayeeConfig?.MakeXmlSource(baseDirectory, relFile, exhibitionOnly)), + new XElement(nameof(EquipmentWorkpieceSetupDisplayeeConfig), + EquipmentWorkpieceSetupDisplayeeConfig?.MakeXmlSource(baseDirectory, relFile, exhibitionOnly)), + PlayerDivConfig?.MakeXmlSource(baseDirectory, relFile, exhibitionOnly) + ); + return dst; + } + #endregion + + /// + /// Gets or sets whether to show physics options in the UI. + /// + public bool ShowPhysicsOptions { get; set; } = false; + /// + /// Gets or sets the language code for the application UI. + /// + public string LanguageCode { get; set; } = "en"; + /// + /// Enable Full control of the application. + /// Eanble System.Diagnostics.Process in GUI Script Command. + /// Not used yet. + /// + public bool EnableFullControl { get; set; } + /// + /// Gets or sets the lower limit of graphic cache in megabytes. + /// + public double GraphicCacheLowerLimitMb { get; set; } = 10; + /// + /// Gets or sets the upper limit of graphic cache in megabytes. + /// + public double GraphicCacheUpperLimitMb { get; set; } = 1200; + /// + /// Gets or sets the graphic cache size in megabytes. + /// + public long GraphicCacheMb + { + get => CubeTree.DispCacheMb; + set => CubeTree.DispCacheMb = value; + } + + /// + /// Step infomation key list to show. + /// + public List DisplayedStepPresentKeyList { get; set; } = new List([ + nameof(MachiningStep.StepIndex), + nameof(MachiningStep.FileNo), nameof(MachiningStep.LineNo), + nameof(MachiningStep.FilePath),nameof(MachiningStep.AccumulatedTime), + nameof(MachiningStep.LineText),nameof(MachiningStep.FlagsText), + nameof(MachiningStep.ToolId), + nameof(MachiningStep.SpindleSpeed_rpm),nameof(MachiningStep.Feedrate_mmdmin), + nameof(MachiningStep.Mrr_mm3ds), + nameof(MachiningStep.AvgAbsTorque_Nm), + ]); + + /// + /// Configuration for FixtureSetupDisplayee + /// + public FixtureEditorDisplayeeConfig FixtureSetupDisplayeeConfig { get; set; } = new FixtureEditorDisplayeeConfig(); + + /// + /// Configuration for EquipmentWorkpieceSetupDisplayee + /// + public WorkpieceEditorDisplayeeConfig EquipmentWorkpieceSetupDisplayeeConfig { get; set; } = new WorkpieceEditorDisplayeeConfig(); + + /// + /// Per-user visibility flags for the Player page's divisions (charts and info panels). + /// Persisted alongside the rest of this . + /// + public PlayerDivConfig PlayerDivConfig { get; set; } = new PlayerDivConfig(); + + /// + /// Default constructor + /// + public UserConfig() { } +} diff --git a/Common/UserService.cs b/Common/UserService.cs new file mode 100644 index 0000000..8e11fdd --- /dev/null +++ b/Common/UserService.cs @@ -0,0 +1,160 @@ +using Hi.Common; +using Hi.Common.XmlUtils; +using Hi.Licenses; +using Hi.MachiningSteps; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Sample.Common; + +/// +/// User Service. +/// +/// +/// Sample-owned copy, kept so the step-present +/// demo still compiles. Each GUI/APP keeps its own version; the core engine +/// package (HiNc) no longer carries this GUI service type. +/// +public class UserService : IDisposable +{ + /// + /// Gets or sets the application configuration. + /// + public UserConfig UserConfig { get; set; } = new UserConfig(); + /// + /// Gets or sets the path to the application configuration file. + /// + public string UserConfigPath { get; set; } + /// + /// Gets whether physics features are enabled based on configuration and license. + /// + public bool EnablePhysics => + UserConfig.ShowPhysicsOptions && IsPhysicsLicensed; + /// + /// Gets whether advanced physics features are licensed. + /// + public bool IsPhysicsLicensed => License.IsLoggedIn(AuthFeature.AdvancedPhysics); + + //runtime properties are not in the category of canonical IO. + #region Runtime Property + private readonly ILogger _logger; + LooseRunner SaveConfigLooseRunner; + /// + /// Gets or sets the currently selected item in the application. + /// + public object SelectedItem { get; set; } + private bool disposedValue; + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The logger instance. + public UserService(ILogger logger) { _logger = logger; SaveConfigLooseRunner = new LooseRunner(logger); } + /// + /// Initializes a new instance of the class with the specified configuration. + /// + /// The application configuration. + /// The logger instance. + public UserService(UserConfig appConfig, ILogger logger) : this(logger) { UserConfig = appConfig; } + + /// + /// Saves the user configuration to the file specified by . + /// + public void SaveUserConfig() + { + try + { + if (UserConfigPath != null) + UserConfig.MakeXmlSourceToFile(UserConfigPath); + } + catch (Exception ex) + { + _logger?.LogError(ex, "{Message}", ex.Message); + } + } + /// + /// Schedules a loose save of the user configuration using a LooseRunner. + /// + public void LooseSaveUserConfig() + { + SaveConfigLooseRunner.TryRun(token => SaveUserConfig()); + } + + #region step present + /// + /// Gets or sets additional step presentation access configurations. + /// + public Dictionary AdditionalStepPresentAccess { get; set; } = new Dictionary(); + /// + /// StepPresentAccessDictionary. + /// Read only. + /// + public Dictionary StepPresentAccessDictionary + { + get + { + var originalPresentAccessDictionary = typeof(MachiningStep).GetProperties() + .Where(p => p.GetCustomAttribute() != null) + .ToDictionary(prop => prop.Name, prop => + new PresentAccess(prop.GetCustomAttribute(), + step => prop.GetValue(step))); + var presentAccessDictionary = new Dictionary( + AdditionalStepPresentAccess.Concat(originalPresentAccessDictionary)); + return presentAccessDictionary; + } + } + /// + /// Candidate Step Present Key List for display. + /// Read only. + /// + public List CandidateStepPresentKeyList => StepPresentAccessDictionary.Keys.ToList(); + /// + /// StepPresentAccessList for display. + /// Read only. + /// + public List> DisplayedStepPresentAccessList + { + get + { + var displayedStepPresentKeyList = UserConfig.DisplayedStepPresentKeyList; + List> dst + = new List>( + displayedStepPresentKeyList.Count); + var stepPresentAccessDictionary = StepPresentAccessDictionary; + foreach (var key in displayedStepPresentKeyList) + { + if (stepPresentAccessDictionary.TryGetValue( + key, out var access)) + { + dst.Add(new KeyValuePair(key, access)); + } + } + return dst; + } + } + #endregion + + /// + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + SaveConfigLooseRunner.Dispose(); + } + disposedValue = true; + } + } + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +}