Compare commits

..

5 Commits

Author SHA1 Message Date
1ba32813bb refactor(demos): retarget session-message samples off MixedProgress0 to the three partitioned sinks
DemoUseSessionMessageHost now reads ShellProgress / NcDiagnosticProgress /
StepDiagnosticProgress (severity + id + notification, with the per-kind NC-line
/ step anchors). Drop DemoUseSessionMessageHost2 — its premise (reading
MachiningStep objects out of the mixed message list) dissolves under the
partition; step-data demos live in ShowStepPresent and SessionStepBuilt.
DemoUseMachiningProject's abnormal-message log now subscribes the three sinks'
MessageAdded events (shell via the OnShellMessageAdded app-lifetime bridge).

Part of MixedProgress0 retirement (migration P6). Build x64 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 12:50:33 +08:00
7a4d0d7999 refactor(demos): use MachiningStep.EndTimecode instead of obsolete AccumulatedTime
DemoSessionMessage console output and the step-present default key list switch off the [Obsolete] AccumulatedTime alias.
2026-06-27 19:16:45 +08:00
0fdba701fa fix(demos): DemoUseMachiningProject subscribes CollectionItemAdded on MixedProgress
CollectionItemAdded is an event on MixedProgress0; the new SessionProgress sink does not expose it. Follows the SessionProgress->MixedProgress property rename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:25:14 +08:00
6f0cb1bc1c refactor(demos): DemoSessionMessage reads IMessage instead of MultiTagMessage
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 21:38:20 +08:00
5ed0c3311e refactor(demos): own UserConfig/UserService copy for Hi.Sample
UserService moved out of the HiNc core package; Hi.Sample keeps its own copy under Sample.Common so the DemoSessionMessage #ShowStepPresent region (a live docfx code snippet referenced by two app-anatomy pages) still compiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 14:24:34 +08:00
5 changed files with 520 additions and 118 deletions

View File

@ -2,16 +2,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using Hi.Common;
using Hi.Common.FileLines; using Hi.Common.FileLines;
using Hi.Geom; using Hi.Common.Messages;
using Hi.HiNcKits; using Hi.HiNcKits;
using Hi.MachiningProcs; using Hi.MachiningProcs;
using Hi.MachiningSteps; using Hi.MachiningSteps;
using Hi.Mech; using Hi.NcParsers;
using Hi.Mech.Topo;
using Hi.Numerical;
namespace Sample.Common; namespace Sample.Common;
@ -24,114 +20,40 @@ public static class DemoSessionMessage
#region Demo_UseSessionMessageHost #region Demo_UseSessionMessageHost
internal static void DemoUseSessionMessageHost(LocalProjectService localProjectService) internal static void DemoUseSessionMessageHost(LocalProjectService localProjectService)
{ {
SessionProgress sessionMessageHost = localProjectService.SessionProgress; // Session messages are partitioned by kind into three sinks on LocalProjectService:
// - ShellProgress: session-level routine / lifecycle messages
// (session-scoped: null outside BeginSession/EndSession).
// - NcDiagnosticProgress: NC-pipeline diagnostics, anchored to the NC source sentence.
// - StepDiagnosticProgress: diagnostics anchored to a motion step.
SessionProgress.FilterFlag filterFlags = ShellProgress shellProgress = localProjectService.ShellProgress;
SessionProgress.FilterFlag.NC | List<IMessage> shellMessages = shellProgress == null
SessionProgress.FilterFlag.Progress | ? new List<IMessage>() : shellProgress.Messages.ToList();
SessionProgress.FilterFlag.Error; foreach (IMessage message in shellMessages)
string filterText = null; Console.WriteLine(
var filteredSessionMessageList = sessionMessageHost $"Shell [{message.GetSeverity()}] {message.GetId()}: {message.GetNotification()}");
.GetFliteredList(filterFlags, filterText);
foreach (var sessionMessage in filteredSessionMessageList) foreach (NcDiagnostic diagnostic in
localProjectService.NcDiagnosticProgress.Diagnostics.ToList())
{ {
//M.I.: Message Index. var ncLine = diagnostic.SentenceCarrier?.GetSentence()?.FirstIndexedFileLine;
Console.Write($"M.I.: {sessionMessage.Index}; Role: {sessionMessage.MessageRoleText}"); Console.WriteLine(
$"NC [{diagnostic.GetSeverity()}] {diagnostic.GetId()}: {diagnostic.GetNotification()}; " +
// For SessionMessageHost.FilterFlag.NC $"File: {ncLine?.FilePath}; LineNo: {ncLine?.GetLineNo()}; NC: {ncLine?.Line}");
var nc = sessionMessage.DirectInstantSourceCommand;
if (nc != null)
Console.Write($"Message/NC: {nc.Line}; File: {nc.FilePath}; LineNo: {nc.GetLineNo()}; ");
// For SessionMessageHost.FilterFlag.Progress or Error.
var multiTagMessage = sessionMessage.MultiTagMessage;
if (multiTagMessage != null)
Console.WriteLine($"Message/NC: {multiTagMessage.Message}");
var exception = sessionMessage.Exception;
if (exception != null)
Console.WriteLine($"Message/NC: {exception.Message}");
} }
foreach (StepDiagnostic diagnostic in
localProjectService.StepDiagnosticProgress.Messages.ToList())
Console.WriteLine(
$"Step {diagnostic.StepIndex} [{diagnostic.GetSeverity()}] " +
$"{diagnostic.GetId()}: {diagnostic.GetNotification()}");
File.WriteAllLines("output-session-messages.txt", File.WriteAllLines("output-session-messages.txt",
filteredSessionMessageList.Select(m => shellMessages.Select(m =>
$"Msg[{m.Index}][{m.MessageRoleText}]: {m}")); $"[{m.GetSeverity()}] {m.GetId()}: {m.GetNotification()}"));
} }
#endregion #endregion
internal static void DemoUseSessionMessageHost2(LocalProjectService localProjectService)
{
SessionProgress sessionMessageHost = localProjectService.SessionProgress;
IMachiningChain machiningChain = localProjectService.MachiningChain;
PresentAttribute mrrPresent = typeof(MachiningStep).GetProperty(nameof(MachiningStep.Mrr_mm3ds)).GetCustomAttribute<PresentAttribute>();
string mrrUnit = mrrPresent?.TailUnitString;
string mrrFormat = mrrPresent?.DataFormatString;
PresentAttribute torquePresent = typeof(MachiningStep).GetProperty(nameof(MachiningStep.AvgAbsTorque_Nm)).GetCustomAttribute<PresentAttribute>();
string torqueUnit = torquePresent?.TailUnitString;
string torqueFormat = torquePresent?.DataFormatString;
SessionProgress.FilterFlag filterFlags =
SessionProgress.FilterFlag.Step |
SessionProgress.FilterFlag.NC |
SessionProgress.FilterFlag.Progress |
SessionProgress.FilterFlag.Error;
string filterText = null;
var filteredSessionMessageList = sessionMessageHost
.GetFliteredList(filterFlags, filterText);
foreach (var sessionMessage in filteredSessionMessageList)
{
//M.I.: Message Index.
Console.Write($"M.I.: {sessionMessage.Index}; Role: {sessionMessage.MessageRoleText}");
// For SessionMessageHost.FilterFlag.Step
var step = sessionMessage.MachiningStep;
if (step != null)
{
string[] machineCoordinateValueTexts = GetMachineCoordinateValueTexts(step, machiningChain);
var machineCoordinatesText = string.Join("; ", Enumerable.Range(0, machiningChain.McCodes.Length)
.Select(i => $"MC.{machiningChain.McCodes[i]}: {machineCoordinateValueTexts[i]}"));
Console.Write($"Time: {step.AccumulatedTime:G}; MRR = {step.Mrr_mm3ds.ToString(mrrFormat)} {mrrUnit}; Torque = {step.AvgAbsTorque_Nm?.ToString(torqueFormat)} {torqueUnit}; {machineCoordinatesText}; ");
var nc_ = sessionMessageHost.GetSourceCommand(sessionMessage);
Console.WriteLine($"Message/NC: {nc_.Line}; File: {nc_.FilePath}; LineNo: {nc_.GetLineNo()}");
}
// For SessionMessageHost.FilterFlag.NC
var nc = sessionMessage.DirectInstantSourceCommand;
if (nc != null)
{
Console.Write($"Message/NC: {nc.Line}; File: {nc.FilePath}; LineNo: {nc.GetLineNo()}; ");
if (nc is HardNcLine ncLine)
Console.WriteLine($"T: {ncLine.T}; S: {ncLine.S}; F: {ncLine.F}; NC-Flags: {ncLine.FlagsText}");
}
// For SessionMessageHost.FilterFlag.Progress or Error.
var multiTagMessage = sessionMessage.MultiTagMessage;
if (multiTagMessage != null)
Console.WriteLine($"Message/NC: {multiTagMessage.Message}");
var exception = sessionMessage.Exception;
if (exception != null)
Console.WriteLine($"Message/NC: {exception.Message}");
}
}
static string[] GetMachineCoordinateValueTexts(MachiningStep step, IMachiningChain machiningChain)
{
var mcTransformers = machiningChain.McTransformers;
string[] dst = new string[mcTransformers.Length];
if (mcTransformers != null)
{
for (int i = 0; i < mcTransformers.Length; i++)
{
if (mcTransformers[i] == null)
continue;
if (mcTransformers[i] is DynamicRotation)
dst[i] = MathUtil.ToDeg(step.GetMcValue(i).Value).ToString("F4");
else
dst[i] = step.GetMcValue(i)?.ToString("F5");
}
}
return dst;
}
#region ShowStepPresent #region ShowStepPresent
internal static void ShowStepPresent( internal static void ShowStepPresent(
UserService userEnv, MachiningStep machiningStep) UserService userEnv, MachiningStep machiningStep)

141
Common/PlayerDivConfig.cs Normal file
View File

@ -0,0 +1,141 @@
using Hi.Common;
using Hi.Common.XmlUtils;
using System.Xml;
using System.Xml.Linq;
namespace Sample.Common;
/// <summary>
/// Per-user visibility flags for the Player page's divisions (charts and
/// info panels), flattened into a single all-boolean config persisted through
/// <see cref="UserConfig"/>.
/// </summary>
/// <remarks>
/// Defaults are tuned for a first-time user: the two info panels
/// (<see cref="EnableStepDiv"/> and <see cref="MessageTable"/>) are ON
/// so the Player page looks populated without having to open the
/// Division Visibility dropdown; every chart toggle starts OFF.
/// </remarks>
public class PlayerDivConfig : IMakeXmlSource
{
#region XML IO
/// <summary>
/// Registers this type's deserializer with the given <see cref="XFactory"/>
/// (or <see cref="XFactory.Default"/> when <paramref name="factory"/> is
/// <c>null</c>). Idempotent.
/// </summary>
public static void Reg(XFactory factory = null)
{
factory ??= XFactory.Default;
factory.Generators.TryAdd(XName, (xml, baseDirectory, relFile, progress, res)
=> new PlayerDivConfig(xml));
}
/// <summary>
/// Name for XML IO.
/// </summary>
public static string XName => nameof(PlayerDivConfig);
/// <summary>
/// Default constructor.
/// </summary>
public PlayerDivConfig() { }
/// <summary>
/// Initializes a new instance of the <see cref="PlayerDivConfig"/> class from XML data.
/// </summary>
/// <param name="src">XML element containing player-div data.</param>
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));
}
/// <inheritdoc/>
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
/// <summary>
/// Availability strip chart (per-step timeline coloured by availability aspect).
/// </summary>
public bool EnableStripAvailabilityChart { get; set; } = false;
/// <summary>
/// Surface-roughness strip chart (per-step timeline coloured by surface-roughness aspect).
/// </summary>
public bool EnableStripRoughnessChart { get; set; } = false;
/// <summary>
/// Color-index strip chart (single-strip timeline coloured by arbitrary numeric/bool aspect).
/// </summary>
public bool ColorIndexTimeChart { get; set; } = false;
/// <summary>
/// Cycle-line chart of cutting force along the step's rotation cycle.
/// </summary>
public bool ForceCycleLineDiv { get; set; } = false;
/// <summary>
/// Cycle-line chart of simulation-derived spindle moment on spindle-rotation coordinate.
/// </summary>
public bool SimSpindleMomentCycleLineDiv { get; set; } = false;
/// <summary>
/// Cycle-line chart of sensor-measured spindle moment (overlay on sim moment).
/// </summary>
public bool SensorSpindleMomentCycleLineDiv { get; set; } = false;
/// <summary>
/// Cycle-line chart of sensor-measured force from an external dynamometer.
/// </summary>
public bool DynamometerForceCycleLineDiv { get; set; } = false;
/// <summary>
/// Full / condensed step information panel (Selected Step Info).
/// </summary>
public bool EnableStepDiv { get; set; } = true;
/// <summary>
/// Session messages panel.
/// </summary>
public bool MessageTable { get; set; } = true;
/// <summary>
/// Developer-only raw step-property dump panel.
/// </summary>
public bool EnableDetailDiv0 { get; set; } = false;
}

173
Common/UserConfig.cs Normal file
View File

@ -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;
/// <summary>
/// User Configuration.
/// </summary>
/// <remarks>
/// Sample-owned copy of the per-GUI user configuration, kept so the
/// <see cref="DemoSessionMessage"/> step-present demo still compiles. Each
/// GUI/APP keeps its own version; the core engine package (HiNc) no longer
/// carries any GUI configuration type.
/// </remarks>
public class UserConfig : IMakeXmlSource
{
/// <summary>
/// Registers this type's deserializer with the given <see cref="XFactory"/>
/// (or <see cref="XFactory.Default"/> when <paramref name="factory"/> is
/// <c>null</c>). Idempotent.
/// </summary>
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
/// <summary>
/// Name for XML IO.
/// </summary>
public static string XName => nameof(UserConfig);
/// <summary>
/// Initializes a new instance of the <see cref="UserConfig"/> class from XML data.
/// </summary>
/// <param name="src">XML element containing configuration data</param>
/// <param name="baseDirectory">Base directory for resolving relative paths</param>
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);
});
}
/// <inheritdoc/>
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
/// <summary>
/// Gets or sets whether to show physics options in the UI.
/// </summary>
public bool ShowPhysicsOptions { get; set; } = false;
/// <summary>
/// Gets or sets the language code for the application UI.
/// </summary>
public string LanguageCode { get; set; } = "en";
/// <summary>
/// Enable Full control of the application.
/// Eanble System.Diagnostics.Process in GUI Script Command.
/// Not used yet.
/// </summary>
public bool EnableFullControl { get; set; }
/// <summary>
/// Gets or sets the lower limit of graphic cache in megabytes.
/// </summary>
public double GraphicCacheLowerLimitMb { get; set; } = 10;
/// <summary>
/// Gets or sets the upper limit of graphic cache in megabytes.
/// </summary>
public double GraphicCacheUpperLimitMb { get; set; } = 1200;
/// <summary>
/// Gets or sets the graphic cache size in megabytes.
/// </summary>
public long GraphicCacheMb
{
get => CubeTree.DispCacheMb;
set => CubeTree.DispCacheMb = value;
}
/// <summary>
/// Step infomation key list to show.
/// </summary>
public List<string> DisplayedStepPresentKeyList { get; set; } = new List<string>([
nameof(MachiningStep.StepIndex),
nameof(MachiningStep.FileNo), nameof(MachiningStep.LineNo),
nameof(MachiningStep.FilePath),nameof(MachiningStep.EndTimecode),
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),
]);
/// <summary>
/// Configuration for FixtureSetupDisplayee
/// </summary>
public FixtureEditorDisplayeeConfig FixtureSetupDisplayeeConfig { get; set; } = new FixtureEditorDisplayeeConfig();
/// <summary>
/// Configuration for EquipmentWorkpieceSetupDisplayee
/// </summary>
public WorkpieceEditorDisplayeeConfig EquipmentWorkpieceSetupDisplayeeConfig { get; set; } = new WorkpieceEditorDisplayeeConfig();
/// <summary>
/// Per-user visibility flags for the Player page's divisions (charts and info panels).
/// Persisted alongside the rest of this <see cref="UserConfig"/>.
/// </summary>
public PlayerDivConfig PlayerDivConfig { get; set; } = new PlayerDivConfig();
/// <summary>
/// Default constructor
/// </summary>
public UserConfig() { }
}

160
Common/UserService.cs Normal file
View File

@ -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;
/// <summary>
/// User Service.
/// </summary>
/// <remarks>
/// Sample-owned copy, kept so the <see cref="DemoSessionMessage"/> step-present
/// demo still compiles. Each GUI/APP keeps its own version; the core engine
/// package (HiNc) no longer carries this GUI service type.
/// </remarks>
public class UserService : IDisposable
{
/// <summary>
/// Gets or sets the application configuration.
/// </summary>
public UserConfig UserConfig { get; set; } = new UserConfig();
/// <summary>
/// Gets or sets the path to the application configuration file.
/// </summary>
public string UserConfigPath { get; set; }
/// <summary>
/// Gets whether physics features are enabled based on configuration and license.
/// </summary>
public bool EnablePhysics =>
UserConfig.ShowPhysicsOptions && IsPhysicsLicensed;
/// <summary>
/// Gets whether advanced physics features are licensed.
/// </summary>
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;
/// <summary>
/// Gets or sets the currently selected item in the application.
/// </summary>
public object SelectedItem { get; set; }
private bool disposedValue;
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="UserService"/> class.
/// </summary>
/// <param name="logger">The logger instance.</param>
public UserService(ILogger logger) { _logger = logger; SaveConfigLooseRunner = new LooseRunner(logger); }
/// <summary>
/// Initializes a new instance of the <see cref="UserService"/> class with the specified configuration.
/// </summary>
/// <param name="appConfig">The application configuration.</param>
/// <param name="logger">The logger instance.</param>
public UserService(UserConfig appConfig, ILogger logger) : this(logger) { UserConfig = appConfig; }
/// <summary>
/// Saves the user configuration to the file specified by <see cref="UserConfigPath"/>.
/// </summary>
public void SaveUserConfig()
{
try
{
if (UserConfigPath != null)
UserConfig.MakeXmlSourceToFile(UserConfigPath);
}
catch (Exception ex)
{
_logger?.LogError(ex, "{Message}", ex.Message);
}
}
/// <summary>
/// Schedules a loose save of the user configuration using a LooseRunner.
/// </summary>
public void LooseSaveUserConfig()
{
SaveConfigLooseRunner.TryRun(token => SaveUserConfig());
}
#region step present
/// <summary>
/// Gets or sets additional step presentation access configurations.
/// </summary>
public Dictionary<string, PresentAccess> AdditionalStepPresentAccess { get; set; } = new Dictionary<string, PresentAccess>();
/// <summary>
/// StepPresentAccessDictionary.
/// Read only.
/// </summary>
public Dictionary<string, PresentAccess> StepPresentAccessDictionary
{
get
{
var originalPresentAccessDictionary = typeof(MachiningStep).GetProperties()
.Where(p => p.GetCustomAttribute<PresentAttribute>() != null)
.ToDictionary(prop => prop.Name, prop =>
new PresentAccess(prop.GetCustomAttribute<PresentAttribute>(),
step => prop.GetValue(step)));
var presentAccessDictionary = new Dictionary<string, PresentAccess>(
AdditionalStepPresentAccess.Concat(originalPresentAccessDictionary));
return presentAccessDictionary;
}
}
/// <summary>
/// Candidate Step Present Key List for display.
/// Read only.
/// </summary>
public List<string> CandidateStepPresentKeyList => StepPresentAccessDictionary.Keys.ToList();
/// <summary>
/// StepPresentAccessList for display.
/// Read only.
/// </summary>
public List<KeyValuePair<string, PresentAccess>> DisplayedStepPresentAccessList
{
get
{
var displayedStepPresentKeyList = UserConfig.DisplayedStepPresentKeyList;
List<KeyValuePair<string, PresentAccess>> dst
= new List<KeyValuePair<string, PresentAccess>>(
displayedStepPresentKeyList.Count);
var stepPresentAccessDictionary = StepPresentAccessDictionary;
foreach (var key in displayedStepPresentKeyList)
{
if (stepPresentAccessDictionary.TryGetValue(
key, out var access))
{
dst.Add(new KeyValuePair<string, PresentAccess>(key, access));
}
}
return dst;
}
}
#endregion
/// <inheritdoc/>
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
SaveConfigLooseRunner.Dispose();
}
disposedValue = true;
}
}
/// <inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

View File

@ -2,6 +2,7 @@
using Hi.Common.Messages; using Hi.Common.Messages;
using Hi.HiNcKits; using Hi.HiNcKits;
using Hi.MachiningProcs; using Hi.MachiningProcs;
using Hi.NcParsers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.IO; using System.IO;
@ -37,17 +38,22 @@ public static class DemoUseMachiningProject
Console.WriteLine($"Set message event."); Console.WriteLine($"Set message event.");
using StreamWriter writer = new StreamWriter("msg.txt"); using StreamWriter writer = new StreamWriter("msg.txt");
//show message if something abnormal. //show message if something abnormal, from each partitioned message sink:
localProjectService.SessionProgress.CollectionItemAdded += pack => //shell (session lifecycle), NC diagnostics, and step-anchored diagnostics.
void LogIfAbnormal(IMessage message, ISentenceCarrier sentenceCarrier)
{ {
if (pack.Tags.Contains(MessageFlag.Warning.ToString()) || var severity = message.GetSeverity();
pack.Tags.Contains(MessageFlag.Error.ToString()) || if (severity != Severity.Warning && severity != Severity.Error)
pack.Tags.Contains(MessageFlag.Exception.ToString())) return;
{ var ncLine = sentenceCarrier?.GetSentence()?.FirstIndexedFileLine;
var sourceCommand = pack.SourceCommand; writer.WriteLine($"{message.GetNotification()} At \"{ncLine?.FilePath}\" (Line {ncLine?.GetLineNo()}) \"{ncLine?.Line}\"");
writer.WriteLine($"{pack.Message} At \"{sourceCommand?.FilePath}\" (Line {sourceCommand?.GetLineNo()}) \"{sourceCommand?.Line}\"");
} }
}; localProjectService.OnShellMessageAdded += (index, message)
=> LogIfAbnormal(message, null);
localProjectService.NcDiagnosticProgress.MessageAdded += (index, diagnostic)
=> LogIfAbnormal(diagnostic, diagnostic.SentenceCarrier);
localProjectService.StepDiagnosticProgress.MessageAdded += (index, diagnostic)
=> LogIfAbnormal(diagnostic, diagnostic.SentenceCarrier);
Console.WriteLine($"Set machining step event."); Console.WriteLine($"Set machining step event.");
//show MRR. //show MRR.
localProjectService.SessionShell.SessionStepBuilt += (preStep, curStep) => localProjectService.SessionShell.SessionStepBuilt += (preStep, curStep) =>