Compare commits

...
13 Commits
Author SHA1 Message Date
iambossandClaude Opus 5 84f9828233 build(Hi.Sample): keep the version line on 3.1
Follows the libraries back off the retracted 3.2 line: VersionPrefix returns to
3.1 and the floating HiAPI references to 3.1.*, which is what the feed serves.
This build counter was never reset, so it just keeps running.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 14:27:48 +08:00
iambossandClaude Opus 5 038e7469d2 build(Hi.Sample): move the version line to 3.2 and retarget HiAPI references
VersionPrefix follows the workspace onto the 3.2 line and the floating HiAPI
package references move from 3.1.* to 3.2.*, which is what the libraries now
publish. The build counter keeps running -- only the packages on the Gitea feed
restart, the same split as the 1.4 -> 3.1 move.

The last 3.1 build of this repo is tagged and kept alive on support/3.1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 18:00:19 +08:00
iambossandClaude Fable 5 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
iamboss 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
iambossandClaude Opus 4.8 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
iambossandClaude Opus 4.8 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
iambossandClaude Opus 4.8 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
iambossandClaude Opus 4.7 05ada6189b refactor(Hi.Sample): convert DemoBuildMachineTool to Reg(XFactory factory=null)
Static ctor rewritten as public static void Reg(XFactory factory = null).

Part of the workspace-wide ~270-class XFactory refactor; see HiGeom commit
for the XFactory instance refactor and full pattern rationale.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 10:34:11 +08:00
iamboss 7e2a4b116a rename XFactory.Regs to XFactory.Generators. 2026-05-23 21:23:11 +08:00
iamboss af599f36e6 fix SoftNcRunner GM code normalization. 2026-05-16 18:08:34 +08:00
iamboss 6139c84ff9 refactor(Sample): adapt demos for SessionStepBuilt rename and ILogger injection
Made-with: Cursor
2026-04-14 17:18:05 +08:00
iamboss dd4c3ef28f refactor(Sample): adapt demos to XFactory IProgress<object> signature
Made-with: Cursor
2026-04-11 11:19:36 +08:00
iamboss e5ea1961a5 refactor(demos): update machining demos and remove legacy demo message handling
Made-with: Cursor
2026-04-08 16:33:17 +08:00
11 changed files with 625 additions and 264 deletions
-57
View File
@@ -1,57 +0,0 @@
using System;
using System.Threading.Tasks;
using Hi.Common;
using Hi.Common.Messages;
namespace Sample.Common;
/// <summary>
/// Demonstrates common message and exception handling patterns in HiAPI applications
/// </summary>
/// <remarks>
/// ### Source Code
/// [!code-csharp[SampleCode](~/../Hi.Sample/Common/DemoMessageAndExceptionHandling.cs)]
/// </remarks>
public static class DemoMessageAndExceptionHandling
{
/// <summary>
/// Demonstrates normal message handling
/// </summary>
internal static void DemoNormalMessages()
{
#region Normal_Messages
MessageUtil.ReportMessage("Operation completed successfully.");
MessageUtil.ReportWarning("Please check your input.");
#endregion
}
/// <summary>
/// Demonstrates exception handling in synchronous code
/// </summary>
internal static void DemoSynchronousExceptionHandling()
{
#region Sync_Exception
try
{
// Your code here
throw new NotImplementedException("Demo exception");
}
catch (Exception ex)
{
ExceptionUtil.ShowException(ex, null);
}
#endregion
}
/// <summary>
/// Demonstrates exception handling in asynchronous code
/// </summary>
internal static async Task DemoAsynchronousExceptionHandling()
{
#region Async_Exception
await Task.Run(() =>
{
// Your async operation here
throw new NotImplementedException("Demo async exception");
}).ShowIfCatched(null);
#endregion
}
}
+28 -106
View File
@@ -2,16 +2,12 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Hi.Common;
using Hi.Common.FileLines;
using Hi.Geom;
using Hi.Common.Messages;
using Hi.HiNcKits;
using Hi.MachiningProcs;
using Hi.MachiningSteps;
using Hi.Mech;
using Hi.Mech.Topo;
using Hi.Numerical;
using Hi.NcParsers;
namespace Sample.Common;
@@ -24,114 +20,40 @@ public static class DemoSessionMessage
#region Demo_UseSessionMessageHost
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 =
SessionProgress.FilterFlag.NC |
SessionProgress.FilterFlag.Progress |
SessionProgress.FilterFlag.Error;
string filterText = null;
var filteredSessionMessageList = sessionMessageHost
.GetFliteredList(filterFlags, filterText);
ShellProgress shellProgress = localProjectService.ShellProgress;
List<IMessage> shellMessages = shellProgress == null
? new List<IMessage>() : shellProgress.Messages.ToList();
foreach (IMessage message in shellMessages)
Console.WriteLine(
$"Shell [{message.GetSeverity()}] {message.GetId()}: {message.GetNotification()}");
foreach (var sessionMessage in filteredSessionMessageList)
foreach (NcDiagnostic diagnostic in
localProjectService.NcDiagnosticProgress.Diagnostics.ToList())
{
//M.I.: Message Index.
Console.Write($"M.I.: {sessionMessage.Index}; Role: {sessionMessage.MessageRoleText}");
// For SessionMessageHost.FilterFlag.NC
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}");
var ncLine = diagnostic.SentenceCarrier?.GetSentence()?.FirstIndexedFileLine;
Console.WriteLine(
$"NC [{diagnostic.GetSeverity()}] {diagnostic.GetId()}: {diagnostic.GetNotification()}; " +
$"File: {ncLine?.FilePath}; LineNo: {ncLine?.GetLineNo()}; NC: {ncLine?.Line}");
}
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",
filteredSessionMessageList.Select(m =>
$"Msg[{m.Index}][{m.MessageRoleText}]: {m}"));
shellMessages.Select(m =>
$"[{m.GetSeverity()}] {m.GetId()}: {m.GetNotification()}"));
}
#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
internal static void ShowStepPresent(
UserService userEnv, MachiningStep machiningStep)
+141
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
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
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);
}
}
+3 -1
View File
@@ -1,4 +1,5 @@
using Hi.HiNcKits;
using Microsoft.Extensions.Logging;
using System;
namespace Sample
@@ -18,7 +19,8 @@ namespace Sample
static int Main(string[] args)
{
Console.WriteLine("HiAPI starting.");
LocalApp.AppBegin();
using var loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(b => b.AddConsole());
LocalApp.AppBegin(loggerFactory.CreateLogger("Hi.Sample"));
Console.WriteLine("Hello World! HiAPI.");
+9 -3
View File
@@ -1,4 +1,4 @@
using Hi.Common.XmlUtils;
using Hi.Common.XmlUtils;
using Hi.Geom;
using Hi.Mech;
using Hi.Mech.Topo;
@@ -23,9 +23,15 @@ namespace Sample.MachineTool
/// </remarks>
public class DemoBuildMachineTool : IGetCodeXyzabcMachineTool
{
static DemoBuildMachineTool()
/// <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)
{
XFactory.Regs.Add(XName, (xml, baseDirectory, relFile, res) => new DemoBuildMachineTool());
factory ??= XFactory.Default;
factory.Generators.TryAdd(XName, (xml, baseDirectory, relFile, progress, res) => new DemoBuildMachineTool());
}
/// <summary>
/// Generates an XYZ-ABC machine tool instance from embedded resources.
@@ -1,4 +1,4 @@
using Hi.Common.XmlUtils;
using Hi.Common.XmlUtils;
using Hi.Geom;
using Hi.MachiningProcs;
using Hi.Mech.Topo;
@@ -14,6 +14,7 @@ using Hi.NcMech.Holders;
using Hi.Machining;
using Hi.HiNcKits;
using Hi.Milling.MillingTools;
using Microsoft.Extensions.Logging;
namespace Sample.Machining;
@@ -42,7 +43,8 @@ public static class DemoBuildGeomOnlyMachiningProject
static void Main()
{
LocalApp.AppBegin();
using var loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(b => b.AddConsole());
LocalApp.AppBegin(loggerFactory.CreateLogger("Hi.Sample"));
LocalProjectService localProjectService = new LocalProjectService();
var projectPath = "C:/HiNC-Projects/NewProject/Main.hincproj";
@@ -82,9 +84,10 @@ public static class DemoBuildGeomOnlyMachiningProject
WorkpieceGeomToFixtureBuckleTransformer = new StaticTranslation(new Vec3d(0, 0, 0)),
};
IProgress<object> progress = null;
localProjectService.MachiningChain
= XFactory.GenByFile<CodeXyzabcMachineTool>(
"Resource", "MachineTool/PMC-B1/PMC-B1.mt", GenMode.Default);
"Resource", "MachineTool/PMC-B1/PMC-B1.mt", progress);
localProjectService.MachiningChainFile = "PMC-B1/PMC-B1.mt";
localProjectService.SaveProject();
+9 -5
View File
@@ -1,4 +1,4 @@
using System;
using System;
using Hi.Milling.Apts;
using Hi.Common.XmlUtils;
using Hi.Geom;
@@ -17,6 +17,7 @@ using Hi.MachiningProcs;
using System.IO;
using Hi.HiNcKits;
using Hi.Milling.MillingTools;
using Microsoft.Extensions.Logging;
namespace Sample.Machining;
@@ -142,7 +143,8 @@ public static class DemoBuildMachiningProject
[STAThread]
static void Main()
{
LocalApp.AppBegin();
using var loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(b => b.AddConsole());
LocalApp.AppBegin(loggerFactory.CreateLogger("Hi.Sample"));
LocalProjectService localProjectService = new LocalProjectService();
var projectPath = "C:/HiNC-Projects/NewProject/Main.hincproj";
@@ -151,6 +153,8 @@ public static class DemoBuildMachiningProject
localProjectService.LoadProject(projectPath);
MachiningProject machiningProject = localProjectService.MachiningProject;
IProgress<object> progress = null;
#region ConfigureMachiningToolHouse
localProjectService.MachiningToolHouse = new MachiningToolHouse()
{
@@ -194,16 +198,16 @@ public static class DemoBuildMachiningProject
IdealGeom = null,
WorkpieceGeomToFixtureBuckleTransformer = new StaticTranslation(new Vec3d(0, 0, 0)),
CuttingPara = XFactory.GenByFile<ICuttingPara>(
"Resource/CuttingParameter", "Al6061T6.mp", GenMode.Default),
"Resource/CuttingParameter", "Al6061T6.mp", progress),
WorkpieceMaterial = XFactory.GenByFile<WorkpieceMaterial>(
"Resource/WorkpieceMaterial", "Al6061T6.WorkpieceMaterial", GenMode.Default),
"Resource/WorkpieceMaterial", "Al6061T6.WorkpieceMaterial", progress),
};
#endregion
#region ConfigureMachineChain
localProjectService.MachiningChain
= XFactory.GenByFile<CodeXyzabcMachineTool>(
"Resource", "MachineTool/PMC-B1/PMC-B1.mt", GenMode.Default);
"Resource", "MachineTool/PMC-B1/PMC-B1.mt", progress);
#endregion
machiningProject.MakeXmlSourceToFile(projectPath);
+28 -20
View File
@@ -2,6 +2,8 @@
using Hi.Common.Messages;
using Hi.HiNcKits;
using Hi.MachiningProcs;
using Hi.NcParsers;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
@@ -21,7 +23,8 @@ public static class DemoUseMachiningProject
{
static void Main()
{
LocalApp.AppBegin();
using var loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(b => b.AddConsole());
LocalApp.AppBegin(loggerFactory.CreateLogger("Hi.Sample"));
LocalProjectService localProjectService = new LocalProjectService();
#region ProjectLoading
@@ -35,23 +38,28 @@ public static class DemoUseMachiningProject
Console.WriteLine($"Set message event.");
using StreamWriter writer = new StreamWriter("msg.txt");
//show message if something abnormal.
localProjectService.SessionProgress.CollectionItemAdded += pack =>
{
if (pack.Tags.Contains(MessageFlag.Warning.ToString()) ||
pack.Tags.Contains(MessageFlag.Error.ToString()) ||
pack.Tags.Contains(MessageFlag.Exception.ToString()))
{
var sourceCommand = pack.SourceCommand;
writer.WriteLine($"{pack.Message} At \"{sourceCommand?.FilePath}\" (Line {sourceCommand?.GetLineNo()}) \"{sourceCommand?.Line}\"");
}
};
//show message if something abnormal, from each partitioned message sink:
//shell (session lifecycle), NC diagnostics, and step-anchored diagnostics.
void LogIfAbnormal(IMessage message, ISentenceCarrier sentenceCarrier)
{
var severity = message.GetSeverity();
if (severity != Severity.Warning && severity != Severity.Error)
return;
var ncLine = sentenceCarrier?.GetSentence()?.FirstIndexedFileLine;
writer.WriteLine($"{message.GetNotification()} At \"{ncLine?.FilePath}\" (Line {ncLine?.GetLineNo()}) \"{ncLine?.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.");
//show MRR.
localProjectService.RuntimeApi.MachiningStepBuilt += (preStep, curStep) =>
localProjectService.SessionShell.SessionStepBuilt += (preStep, curStep) =>
{
var sourceCommand = curStep.SourceCommand;
var indexedFileLine=sourceCommand?.GetSentence()?.IndexedFileLine;
var indexedFileLine=sourceCommand?.GetSentence()?.FirstIndexedFileLine;
if (curStep.Mrr_mm3ds > 500) //show only the step that contains large MRR.
Console.WriteLine($"MRR = {curStep.Mrr_mm3ds} At \"{indexedFileLine?.FilePath}\" (Line {indexedFileLine?.GetLineNo()}) \"{indexedFileLine?.Line}\"");
};
@@ -63,13 +71,13 @@ public static class DemoUseMachiningProject
Console.WriteLine($"Session begin.");
localProjectService.BeginSession();
localProjectService.RuntimeApi.MachiningResolution_mm = 1;
localProjectService.RuntimeApi.EnableCollisionDetection = true;
localProjectService.RuntimeApi.EnablePauseOnFailure = false;
localProjectService.RuntimeApi.EnablePhysics = false;
localProjectService.SessionShell.MachiningResolution_mm = 1;
localProjectService.SessionShell.EnableCollisionDetection = true;
localProjectService.SessionShell.EnablePauseOnFailure = false;
localProjectService.SessionShell.EnablePhysics = false;
//the path from Shell-API is relative by project directory.
localProjectService.RuntimeApi.PlayNcFile("NC/side.ptp");
localProjectService.RuntimeApi.PlayNcFile("NC/circle.ptp");
localProjectService.SessionShell.PlayNcFile("NC/side.ptp");
localProjectService.SessionShell.PlayNcFile("NC/circle.ptp");
localProjectService.EndSession();
Console.WriteLine($"Session end.");
#endregion
+66 -67
View File
@@ -4,79 +4,78 @@ using Hi.Disp;
using Hi.Geom;
using Hi.Mech.Topo;
namespace Sample.Mech
namespace Sample.Mech;
/// <summary>
/// Demonstrates the creation and visualization of mechanical assemblies with kinematic linkages.
/// Shows how to build coordinate systems, establish kinematic relationships, and capture visual output.
/// </summary>
/// <remarks>
/// ### Source Code
/// [!code-csharp[SampleCode](~/../Hi.Sample/Mech/DemoTopo1.cs)]
/// </remarks>
public static class DemoTopo1
{
/// <summary>
/// Demonstrates the creation and visualization of mechanical assemblies with kinematic linkages.
/// Shows how to build coordinate systems, establish kinematic relationships, and capture visual output.
/// </summary>
/// <remarks>
/// ### Source Code
/// [!code-csharp[SampleCode](~/../Hi.Sample/Mech/DemoTopo1.cs)]
/// </remarks>
public static class DemoTopo1
{
/// <summary>
/// Creates a demonstration assembly with kinematic linkages.
/// Builds a mechanical assembly with multiple anchors and branches, including both static and dynamic transformations.
/// </summary>
/// <returns>A tuple containing the assembly and root anchor</returns>
static (Asmb asmb,Anchor root) GetDemoAsmb()
{
#region DocSite.DemoTopo1
//build coordinate systems and the assembly.
Asmb asmb = new Asmb { Name = "Mech" };
Anchor O = new Anchor(asmb, "O");
Anchor O1 = new Anchor(asmb, "O1");
Anchor X = new Anchor(asmb, "X");
Anchor Z = new Anchor(asmb, "Z");
Anchor B = new Anchor(asmb, "B");
/// <summary>
/// Creates a demonstration assembly with kinematic linkages.
/// Builds a mechanical assembly with multiple anchors and branches, including both static and dynamic transformations.
/// </summary>
/// <returns>A tuple containing the assembly and root anchor</returns>
static (Asmb asmb, Anchor root) GetDemoAsmb()
{
#region DocSite.DemoTopo1
//build coordinate systems and the assembly.
Asmb asmb = new Asmb { Name = "Mech" };
Anchor O = new Anchor(asmb, "O");
Anchor O1 = new Anchor(asmb, "O1");
Anchor X = new Anchor(asmb, "X");
Anchor Z = new Anchor(asmb, "Z");
Anchor B = new Anchor(asmb, "B");
//build kinematic link
Branch.Attach(O, O1, new StaticTranslation(new Vec3d(0, 0, 80)));
Branch brnX = Branch.Attach(O1, X, new DynamicTranslation(new Vec3d(1, 0, 0)));
Branch brnZ = Branch.Attach(X, Z, new DynamicTranslation(new Vec3d(0, 0, 1)));
Branch brnB = Branch.Attach(Z, B, new DynamicRotation(new Vec3d(0, 1, 0), 0, new Vec3d(-100, 0, 0)));
//build kinematic link
Branch.Attach(O, O1, new StaticTranslation(new Vec3d(0, 0, 80)));
Branch brnX = Branch.Attach(O1, X, new DynamicTranslation(new Vec3d(1, 0, 0)));
Branch brnZ = Branch.Attach(X, Z, new DynamicTranslation(new Vec3d(0, 0, 1)));
Branch brnB = Branch.Attach(Z, B, new DynamicRotation(new Vec3d(0, 1, 0), 0, new Vec3d(-100, 0, 0)));
//drive the dynamic transformation by single value for each branch.
brnX.Step = 200;
brnZ.Step = 100;
brnB.Step = MathUtil.ToRad(-60);
//drive the dynamic transformation by single value for each branch.
brnX.Step = 200;
brnZ.Step = 100;
brnB.Step = MathUtil.ToRad(-60);
//Get and show the transform matrices relative to O.
Dictionary<Anchor, Mat4d> matMap = asmb.GetMat4dMap(O);
Console.WriteLine("Transform Matrix relative to O:");
foreach (KeyValuePair<Anchor, Mat4d> keyValue in matMap)
Console.WriteLine($"{keyValue.Key.Name} : {keyValue.Value}");
#endregion
//Get and show the transform matrices relative to O.
Dictionary<Anchor, Mat4d> matMap = asmb.GetMat4dMap(O);
Console.WriteLine("Transform Matrix relative to O:");
foreach (KeyValuePair<Anchor, Mat4d> keyValue in matMap)
Console.WriteLine($"{keyValue.Key.Name} : {keyValue.Value}");
#endregion
return (asmb,O);
}
return (asmb, O);
}
/// <summary>
/// Captures the assembly visualization and saves it to a file.
/// Initializes the display engine, sets up the assembly visualization with an isometric view, and saves a snapshot to a bitmap file.
/// </summary>
/// <param name="src">A tuple containing the assembly and root anchor for visualization</param>
static void SnapshotToFile((Asmb asmb, Anchor root) src)
{
//all the drawing function has to call DispEngine.Init() before using.
DispEngine.Init();
DispEngine.EnableSuppressDefaultLogo = true;
/// <summary>
/// Captures the assembly visualization and saves it to a file.
/// Initializes the display engine, sets up the assembly visualization with an isometric view, and saves a snapshot to a bitmap file.
/// </summary>
/// <param name="src">A tuple containing the assembly and root anchor for visualization</param>
static void SnapshotToFile((Asmb asmb, Anchor root) src)
{
//all the drawing function has to call DispEngine.Init() before using.
DispEngine.Init();
DispEngine.EnableSuppressDefaultLogo = true;
using (DispEngine dispEngine = new DispEngine(
src.asmb.GetAsmbDraw(src.root)))
{
dispEngine.SetViewToIsometricView();
dispEngine.Snapshot("DemoTopo1.bmp", 680, 480);
}
Console.WriteLine("Snapshot file output.");
using (DispEngine dispEngine = new DispEngine(
src.asmb.GetAsmbDraw(src.root)))
{
dispEngine.SetViewToIsometricView();
dispEngine.Snapshot("DemoTopo1.bmp", 680, 480);
}
Console.WriteLine("Snapshot file output.");
DispEngine.FinishDisp();
}
static void Main()
{
SnapshotToFile(GetDemoAsmb());
}
}
DispEngine.FinishDisp();
}
static void Main()
{
SnapshotToFile(GetDemoAsmb());
}
}