fix SoftNcRunner GM code normalization.

This commit is contained in:
2026-05-06 13:21:19 +08:00
parent 0fe9497552
commit 2f57932000
1238 changed files with 69405 additions and 22511 deletions
@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Message Management | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Message Management | HiAPI-C# 2025 ">
<link rel="icon" href="../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../public/docfx.min.css">
<link rel="stylesheet" href="../../public/main.css">
<meta name="docfx:navrel" content="../../toc.html">
<meta name="docfx:tocrel" content="../toc.html">
<meta name="docfx:rel" content="../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../index.html">
<img id="logo" class="svg" src="../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="message-management">Message Management</h1>
<p>HiNc applications use three independent message categories. Each category serves a distinct purpose and should not be mixed.</p>
<h2 id="categories">Categories</h2>
<h3 id="1-diagnostic--iprogressobject">1. Diagnostic — <code>IProgress&lt;object&gt;</code></h3>
<p>Operation-scoped progress and diagnostic messages. The caller provides an <code>IProgress&lt;object&gt;</code> to the callee, which reports progress, warnings, and errors through it.</p>
<ul>
<li><strong>Session-scoped</strong>: <a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_SessionProgress">SessionProgress</a> feeds the <a href="../../app-anatomy/session-message-panel/index.html">Session Message Panel</a>.</li>
<li><strong>XML IO chain</strong>: <a class="xref" href="../../api/Hi.Common.XmlUtils.XFactory.html">XFactory</a> threads <code>IProgress&lt;object&gt;</code> through all deserialization calls so that parsing errors are reported to the caller rather than a global handler.</li>
<li><strong>Script-level</strong>: <a class="xref" href="../../api/Hi.MachiningProcs.RuntimeApi.html#Hi_MachiningProcs_RuntimeApi_SessionProgress">SessionProgress</a> exposes the session progress to user scripts.</li>
</ul>
<p>Use <a class="xref" href="../../api/Hi.Common.Messages.MultiTagMessageUtil.html">MultiTagMessageUtil</a> extension methods (<code>ReportProgress</code>, <code>ReportMessage</code>, <code>ReportWarning</code>, <code>ReportError</code>, <code>ReportInfo</code>) to report typed messages.</p>
<h3 id="2-ui-error-notification--messageboardutil">2. UI Error Notification — <code>MessageBoardUtil</code></h3>
<p>Toast-style popups for immediate user attention (e.g., &ldquo;File saved&rdquo;, &ldquo;Load failed&rdquo;). <a class="xref" href="../../api/Hi.Common.Messages.MessageBoardUtil.html">MessageBoardUtil</a> triggers the <code>ShowMessageBoard</code> event consumed by the GUI layer.</p>
<div class="NOTE">
<h5>Note</h5>
<p><code>MessageBoardUtil</code> is not yet mature for all scenarios. In practice, <code>ILogger</code> with level-filtered treatment is often applied instead.</p>
</div>
<h3 id="3-app-log--ilogger">3. App Log — <code>ILogger</code></h3>
<p>Standard .NET <code>ILogger</code> for application-level logging. Use <a class="xref" href="../../api/Hi.Common.Messages.ActionProgress-1.html">ActionProgress&lt;T&gt;</a>.<a class="xref" href="../../api/Hi.Common.Messages.ActionProgress-1.html#Hi_Common_Messages_ActionProgress_1_FromLogger_">FromLogger</a> to bridge <code>IProgress&lt;object&gt;</code> APIs to an <code>ILogger</code> instance:</p>
<pre><code class="lang-csharp">IProgress&lt;object&gt; progress = ActionProgress&lt;object&gt;.FromLogger(logger);
</code></pre>
<p>This routes <a class="xref" href="../../api/Hi.Common.Messages.MultiTagMessage.html">MultiTagMessage</a> to the appropriate log level (<code>LogError</code>, <code>LogWarning</code>, <code>LogInformation</code>) based on message tags.</p>
<h2 id="basic-component--utility-level">Basic-Component / Utility Level</h2>
<p>Low-level utilities (e.g., in <code>Hi.Common</code>, <code>Hi.Geom</code>) cannot assume which category the caller intends. These APIs accept <code>Action&lt;Exception&gt;</code> or <code>IProgress&lt;object&gt;</code> as parameters so the caller decides how to handle messages:</p>
<pre><code class="lang-csharp">await task.CatchExceptions(ex =&gt; progress?.Report(ex));
</code></pre>
<h2 id="design-rationale">Design Rationale</h2>
<p>Static/global message sinks mix the three categories, making it unclear whether a message is diagnostic, UI notification, or app log. The current pattern threads the handler explicitly through the call chain so each caller decides the appropriate category.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,319 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>About XML IO | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="About XML IO | HiAPI-C# 2025 ">
<link rel="icon" href="../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../public/docfx.min.css">
<link rel="stylesheet" href="../../public/main.css">
<meta name="docfx:navrel" content="../../toc.html">
<meta name="docfx:tocrel" content="../toc.html">
<meta name="docfx:rel" content="../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../index.html">
<img id="logo" class="svg" src="../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="about-xml-io">About XML IO</h1>
<p>The XML IO design pattern in HiNc Framework is based on <a class="xref" href="../../api/Hi.Common.XmlUtils.IMakeXmlSource.html">IMakeXmlSource</a> interface and <a class="xref" href="../../api/Hi.Common.XmlUtils.XFactory.html">XFactory</a> class. This pattern provides a standardized way to serialize and deserialize objects to and from XML format.</p>
<p>Don't serialize the runtime member object like <a class="xref" href="https://learn.microsoft.com/dotnet/api/system.func-1">Func&lt;TResult&gt;</a> or <a class="xref" href="https://learn.microsoft.com/dotnet/api/system.action">Action</a> either cache object. The runtime objects can be optionally sent by the <code>res</code> part on the XFactory Registration or set by the other host or dependent object. If it is set by the other object, then there is nothing can do to it in the XML IO procedure.</p>
<h2 id="core-components">Core Components</h2>
<h3 id="imakexmlsource-interface">IMakeXmlSource Interface</h3>
<p>The <a class="xref" href="../../api/Hi.Common.XmlUtils.IMakeXmlSource.html">IMakeXmlSource</a> interface defines the contract for objects that can be serialized to XML format. It contains a single method <code>MakeXmlSource</code>.</p>
<h3 id="xfactory-registration">XFactory Registration</h3>
<p>Every class implementing IMakeXmlSource must:</p>
<ol>
<li>Define a static <code>XName</code> property matching the class name.</li>
<li>Register itself in the static constructor using <code>XFactory.Regs.Add</code></li>
<li>Implement XML serialization and deserialization logic</li>
</ol>
<p>The registration delegate signature includes an <code>IProgress&lt;object&gt;</code> parameter for <a href="message-management.html">diagnostic message routing</a>:</p>
<pre><code class="lang-csharp">XFactory.Regs.Add(XName, (xml, baseDirectory, relFile, progress, res)
=&gt; new MyClass(xml, baseDirectory, relFile, progress));
</code></pre>
<p>For example, see <a class="xref" href="../../api/Hi.Milling.Apts.BallApt.html">BallApt</a>:</p>
<pre><code class="lang-csharp" name="XmlRegistration">static BallApt()
{
// Register to the &lt;see cref=&quot;XFactory.Default&quot;/&gt;.
XFactory.Regs.Add(XName, (xml,baseDirectory, relFile, progress, res) =&gt; new BallApt(xml));
}
</code></pre><h3 id="iprogress-threading">IProgress Threading</h3>
<p>The <code>IProgress&lt;object&gt;</code> parameter is threaded through the entire deserialization chain. When a class constructor calls <a class="xref" href="../../api/Hi.Common.XmlUtils.XFactory.html">XFactory</a> to deserialize child objects, it passes the same <code>progress</code> instance:</p>
<pre><code class="lang-csharp">public MyClass(XElement src, string baseDirectory, string relFile,
IProgress&lt;object&gt; progress)
{
Child = XFactory.GenByChild&lt;IChild&gt;(
src.Element(nameof(Child)), subBaseDirectory, progress);
}
</code></pre>
<p>Parsing errors are reported to the caller-provided <code>IProgress&lt;object&gt;</code> handler.</p>
<h2 id="implementation-patterns">Implementation Patterns</h2>
<h3 id="simple-value-objects">Simple Value Objects</h3>
<p>See <a class="xref" href="../../api/Hi.Milling.Apts.BallApt.html">BallApt</a> implementation:</p>
<pre><code class="lang-csharp" name="XmlImplementation">/// &lt;summary&gt;
/// Name for XML IO.
/// &lt;/summary&gt;
public static string XName =&gt; nameof(BallApt);
/// &lt;summary&gt;
/// Ctor.
/// &lt;/summary&gt;
/// &lt;param name=&quot;src&quot;&gt;XML&lt;/param&gt;
public BallApt(XElement src)
{
Diameter_mm = double.Parse(src.Element(&quot;D&quot;).Value);
FluteHeight_mm = double.Parse(src.Element(&quot;FluteH&quot;).Value);
}
/// &lt;inheritdoc/&gt;
public XElement MakeXmlSource(string baseDirectory, string relFile, bool exhibitionOnly) =&gt; ToXElement();
/// &lt;inheritdoc/&gt;
public XElement ToXElement()
{
return new XElement(XName,
new XElement(&quot;D&quot;, Diameter_mm),
new XElement(&quot;FluteH&quot;, FluteHeight_mm)
);
}
</code></pre><h3 id="complex-data-structures">Complex Data Structures</h3>
<p>See <a class="xref" href="../../api/Hi.Milling.SpindleCapability.html">SpindleCapability</a> implementation:</p>
<pre><code class="lang-csharp" name="XmlImplementation">/// &lt;summary&gt;
/// Name for XML IO.
/// &lt;/summary&gt;
public static string XName =&gt; nameof(SpindleCapability);
/// &lt;summary&gt;
/// Initializes a new instance of the &lt;see cref=&quot;SpindleCapability&quot;/&gt; class.
/// &lt;/summary&gt;
/// &lt;param name=&quot;src&quot;&gt;The XML element containing spindle data.&lt;/param&gt;
/// &lt;param name=&quot;baseDirectory&quot;&gt;The base directory for resolving relative paths.&lt;/param&gt;
/// &lt;param name=&quot;res&quot;&gt;Additional resolution parameters.&lt;/param&gt;
public SpindleCapability(XElement src, string baseDirectory, params object[] res)
{
this.SetNameNote(src);
if (src.Element(nameof(EnergyEfficiency)) != null)
EnergyEfficiency = XmlConvert.ToDouble(
src.Element(nameof(EnergyEfficiency)).Value);
src.Element(nameof(WorkingTemperatureUpperBoundary_C))?.SelfInvoke(
e =&gt; WorkingTemperatureUpperBoundary_C = XmlConvert.ToDouble(e.Value));
src.Element(nameof(GearShiftSpindleSpeed_rpm))?.Value?.SelfInvoke(
s =&gt; GearShiftSpindleSpeed_rpm = string.IsNullOrEmpty(s)
? null : XmlConvert.ToDouble(s));
if (src.Element(nameof(DryRunFrictionPowerCoefficient_mWdrpm)) != null)
DryRunFrictionPowerCoefficient_mWdrpm = XmlConvert.ToDouble(
src.Element(nameof(DryRunFrictionPowerCoefficient_mWdrpm)).Value);
if (src.Element(nameof(DryRunWindagePowerCoefficient_pWdrpm3)) != null)
DryRunWindagePowerCoefficient_pWdrpm3 = XmlConvert.ToDouble(
src.Element(nameof(DryRunWindagePowerCoefficient_pWdrpm3)).Value);
if (src.Element(&quot;SpindleSpeedToPowerContours&quot;) != null) //for legacy
WorkableDurationToSpindleSpeedPowerContoursDictionary_min_cycleDs_kW =
src.Element(&quot;SpindleSpeedToPowerContours&quot;).Elements(&quot;Contour&quot;)
.ToDictionary(
contourElem =&gt;
{
double r = XmlConvert.ToDouble(contourElem.Attribute(&quot;InsistentRatio&quot;)?.Value);
//600s=10mins
return r ==1?double.PositiveInfinity:(r * 600);
},
contourElem =&gt; contourElem.Elements(&quot;SpindleSpeedToPower&quot;).Select(
elem =&gt; new Vec2d(
XmlConvert.ToDouble(elem.Element(&quot;SpindleSpeed-RPM&quot;).Value) / 60,
XmlConvert.ToDouble(elem.Element(&quot;Power-kW&quot;).Value)))
.ToList());
src.Element(&quot;WorkableDurationToSpindleSpeedPowerContoursDictionary&quot;)
?.SelfInvoke(dicElem =&gt;
{
WorkableDurationToSpindleSpeedPowerContoursDictionary_min_cycleDs_kW
= dicElem.Elements(&quot;Contour&quot;)
.ToDictionary(
contourElem =&gt; XmlConvert.ToDouble(
contourElem.Attribute(&quot;WorkableDuration-min&quot;)?.Value),
contourElem =&gt; contourElem.Elements(&quot;SpindleSpeedToPower&quot;).Select(
elem =&gt; new Vec2d(
XmlConvert.ToDouble(elem.Element(&quot;SpindleSpeed-RPM&quot;).Value) / 60,
XmlConvert.ToDouble(elem.Element(&quot;Power-kW&quot;).Value)))
.ToList());
});
if (src.Element(&quot;SpindleSpeedToTorqueContours&quot;) != null) //for legacy
WorkableDurationToSpindleSpeedTorqueContoursDictionary_min_cycleDs_Nm =
src.Element(&quot;SpindleSpeedToTorqueContours&quot;).Elements(&quot;Contour&quot;)
.ToDictionary(
contourElem =&gt;
{
double r = XmlConvert.ToDouble(contourElem.Attribute(&quot;InsistentRatio&quot;)?.Value);
//600s=10mins
return r == 1 ? double.PositiveInfinity : (r * 600);
},
contourElem =&gt; contourElem.Elements(&quot;SpindleSpeedToTorque&quot;).Select(
elem =&gt; new Vec2d(
XmlConvert.ToDouble(elem.Element(&quot;SpindleSpeed-RPM&quot;).Value) / 60,
XmlConvert.ToDouble(elem.Element(&quot;Torque-Nm&quot;).Value)))
.ToList());
src.Element(&quot;WorkableDurationToSpindleSpeedTorqueContoursDictionary&quot;)
?.SelfInvoke(dicElem =&gt;
{
//MessageUtil.WriteLine($&quot;dicElem: {dicElem}&quot;);
WorkableDurationToSpindleSpeedTorqueContoursDictionary_min_cycleDs_Nm =
dicElem.Elements(&quot;Contour&quot;).ToDictionary(
contourElem =&gt; XmlConvert.ToDouble(
contourElem.Attribute(&quot;WorkableDuration-min&quot;)?.Value),
contourElem =&gt; contourElem.Elements(&quot;SpindleSpeedToTorque&quot;).Select(
elem =&gt; new Vec2d(
XmlConvert.ToDouble(elem.Element(&quot;SpindleSpeed-RPM&quot;).Value) / 60,
XmlConvert.ToDouble(elem.Element(&quot;Torque-Nm&quot;).Value)))
.ToList());
//MessageUtil.WriteLine($&quot;keys: {string.Join(',',WorkableDurationToSpindleSpeedTorqueContoursDictionary_min_cycleDs_Nm.Select(e=&gt;e.Key))}&quot;);
});
//for legacy compatible.
if (src.Element(&quot;SpindleSpeedToPower--RPM-to-kW&quot;) != null)
InfInsistentSpindleSpeedToPower_cycleDs_kW =
src.Element(&quot;SpindleSpeedToPower--RPM-to-kW&quot;).Elements()
.Select(elem =&gt; new Vec2d(XmlConvert.ToDouble(elem.Attribute(
&quot;SpindleSpeed-RPM&quot;).Value) / 60,
XmlConvert.ToDouble(elem.Value))).ToList();
//for legacy compatible.
if (src.Element(&quot;SpindleSpeedToTorque--RPM-to-Nm&quot;) != null)
InfInsistentSpindleSpeedToTorque_cycleDs_Nm =
src.Element(&quot;SpindleSpeedToTorque--RPM-to-Nm&quot;).Elements()
.Select(elem =&gt; new Vec2d(XmlConvert.ToDouble(elem.Attribute(
&quot;SpindleSpeed-RPM&quot;).Value) / 60,
XmlConvert.ToDouble(elem.Value))).ToList();
}
/// &lt;inheritdoc/&gt;
public XElement MakeXmlSource(string baseDirectory, string relFile, bool exhibitionOnly)
{
return new XElement(XName,
this.GetNameNoteXElementList(),
new XElement(nameof(EnergyEfficiency), EnergyEfficiency),
new XElement(nameof(GearShiftSpindleSpeed_rpm), GearShiftSpindleSpeed_rpm),
new XElement(nameof(DryRunFrictionPowerCoefficient_mWdrpm),
DryRunFrictionPowerCoefficient_mWdrpm),
new XElement(nameof(DryRunWindagePowerCoefficient_pWdrpm3),
DryRunWindagePowerCoefficient_pWdrpm3),
new XElement(&quot;WorkableDurationToSpindleSpeedPowerContoursDictionary&quot;,
WorkableDurationToSpindleSpeedPowerContoursDictionary_min_cycleDs_kW.OrderBy(entry =&gt; entry.Key)
.Select(entry =&gt; new XElement(&quot;Contour&quot;,
new XAttribute(&quot;WorkableDuration-min&quot;, entry.Key),
entry.Value.Select(entry
=&gt; new XElement(&quot;SpindleSpeedToPower&quot;,
new XElement(&quot;SpindleSpeed-RPM&quot;, entry.X * 60),
new XElement(&quot;Power-kW&quot;, entry.Y)))))
),
new XElement(&quot;WorkableDurationToSpindleSpeedTorqueContoursDictionary&quot;,
WorkableDurationToSpindleSpeedTorqueContoursDictionary_min_cycleDs_Nm.OrderBy(entry =&gt; entry.Key)
.Select(entry =&gt; new XElement(&quot;Contour&quot;,
new XAttribute(&quot;WorkableDuration-min&quot;, entry.Key),
entry.Value.Select(entry
=&gt; new XElement(&quot;SpindleSpeedToTorque&quot;,
new XElement(&quot;SpindleSpeed-RPM&quot;, entry.X * 60),
new XElement(&quot;Torque-Nm&quot;, entry.Y)))))
)
);
}
</code></pre><h2 id="best-practices">Best Practices</h2>
<ol>
<li><strong>XName</strong>: Always define static <code>XName</code> property matching the class name.</li>
<li><strong>Registration</strong>: Register in static constructor using <a class="xref" href="../../api/Hi.Common.XmlUtils.XFactory.html">XFactory</a>.Regs</li>
<li>Call the <code>XName</code> such like <code>_ = CalleeClass.XName;</code> in the caller class static initialization field so that the registration takes effect before calling the Callee construction by <a class="xref" href="../../api/Hi.Common.XmlUtils.XFactory.html">XFactory</a>.</li>
<li><strong>Progress Threading</strong>: Pass the <code>IProgress&lt;object&gt;</code> parameter through all nested <code>XFactory</code> calls. See <a href="message-management.html">Message Management</a> for the rationale.</li>
<li><strong>Legacy Support</strong>: Maintain backward compatibility when needed</li>
</ol>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>