deploy webservice static-file routing for docsite folder URLs, per-instance step-cache db, and constant-height panel title bars.

This commit is contained in:
2026-08-27 14:51:37 +08:00
parent 1fc510af5e
commit 2a98ae7556
1468 changed files with 182837 additions and 18888 deletions
@@ -87,70 +87,73 @@
<article data-uid="">
<h1 id="session-message-panel">Session Message Panel</h1>
<p>The model is <a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_SessionProgress">SessionProgress</a>.</p>
<p><a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html">LocalProjectService</a> is obtained via dependency injection.</p>
<p>Session messages are partitioned by kind into three sinks on <a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html">LocalProjectService</a> (obtained via dependency injection):</p>
<ul>
<li><a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_ShellProgress">ShellProgress</a> — session-level routine / lifecycle messages (<a class="xref" href="../../api/Hi.MachiningProcs.ShellProgress.html">ShellProgress</a>). Session-scoped: the property is null outside <code>BeginSession</code>/<code>EndSession</code>.</li>
<li><a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_NcDiagnosticProgress">NcDiagnosticProgress</a> — NC-pipeline diagnostics (<a class="xref" href="../../api/Hi.NcParsers.NcDiagnosticProgress.html">NcDiagnosticProgress</a>), each anchored to its NC source sentence.</li>
<li><a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_StepDiagnosticProgress">StepDiagnosticProgress</a> — diagnostics anchored to a motion step (<a class="xref" href="../../api/Hi.MachiningProcs.StepDiagnosticProgress.html">StepDiagnosticProgress</a>).</li>
</ul>
<p>The panel surfaces each sink in its own tab, so one tab holds one message kind.</p>
<h2 id="layout">Layout</h2>
<ul>
<li>Top Message Filter ToolBar
<li>Tab Bar
<ul>
<li>Message Type Filter SubMenu
<ul>
<li>NC CheckBox</li>
<li>Progress CheckBox</li>
<li>Error CheckBox</li>
<li>Shell Tab</li>
<li>NC Diagnostics Tab</li>
<li>Step Diagnostics Tab</li>
</ul>
</li>
<li>Message Text Filter Input
<li>Per-Tab Content
<ul>
<li>Message Text Filter Input Text Area</li>
<li>Message Text Filter Reset Button</li>
<li>Message Text Filter Input</li>
<li>Message Table</li>
</ul>
</li>
<li>Export Button</li>
</ul>
</li>
<li>Central Message Table</li>
<h2 id="message-table-per-tab">Message Table (per tab)</h2>
<p>Each tab renders its sink's message list — <a class="xref" href="../../api/Hi.MachiningProcs.ShellProgress.html#Hi_MachiningProcs_ShellProgress_Messages">Messages</a>, <a class="xref" href="../../api/Hi.NcParsers.NcDiagnosticProgress.html#Hi_NcParsers_NcDiagnosticProgress_Diagnostics">Diagnostics</a>, or <a class="xref" href="../../api/Hi.MachiningProcs.StepDiagnosticProgress.html#Hi_MachiningProcs_StepDiagnosticProgress_Messages">Messages</a> — as rows of:</p>
<ul>
<li><code>Severity</code> (colour-coded via <a class="xref" href="../../api/Hi.Common.Messages.IMessage.html#Hi_Common_Messages_IMessage_GetSeverity">GetSeverity()</a>)</li>
<li><code>Anchor</code> — the kind-specific position: the NC sentence index (<code>Sn</code>) for NC diagnostics, the step index for step diagnostics, none for shell messages</li>
<li><code>Message</code> — <a class="xref" href="../../api/Hi.Common.Messages.IMessage.html#Hi_Common_Messages_IMessage_GetId">GetId()</a> and <a class="xref" href="../../api/Hi.Common.Messages.IMessage.html#Hi_Common_Messages_IMessage_GetNotification">GetNotification()</a></li>
</ul>
<h2 id="central-message-table">Central Message Table</h2>
<p>The model of Central Message Table is <a class="xref" href="../../api/Hi.MachiningProcs.SessionProgress.html">SessionProgress</a>.<a class="xref" href="../../api/Hi.MachiningProcs.SessionProgress.html#Hi_MachiningProcs_SessionProgress_MessageCollection">MessageCollection</a>.</p>
<p>Only take last 1000 filtered elements in the <a class="xref" href="../../api/Hi.MachiningProcs.SessionProgress.html#Hi_MachiningProcs_SessionProgress_MessageCollection">MessageCollection</a> by <a class="xref" href="../../api/Hi.MachiningProcs.SessionProgress.html#Hi_MachiningProcs_SessionProgress_GetFliteredList_Hi_MachiningProcs_SessionProgress_FilterFlag_System_String_">GetFliteredList(FilterFlag, string)</a> to show for user experience. Find the usage example in the code:</p>
<p>Only take the last filtered elements (e.g. 500–1000) for user experience. Find the usage example in the code:</p>
<pre><code class="lang-csharp" name="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&lt;IMessage&gt; shellMessages = shellProgress == null
? new List&lt;IMessage&gt;() : shellProgress.Messages.ToList();
foreach (IMessage message in shellMessages)
Console.WriteLine(
$&quot;Shell [{message.GetSeverity()}] {message.GetId()}: {message.GetNotification()}&quot;);
foreach (var sessionMessage in filteredSessionMessageList)
foreach (NcDiagnostic diagnostic in
localProjectService.NcDiagnosticProgress.Diagnostics.ToList())
{
//M.I.: Message Index.
Console.Write($&quot;M.I.: {sessionMessage.Index}; Role: {sessionMessage.MessageRoleText}&quot;);
// For SessionMessageHost.FilterFlag.NC
var nc = sessionMessage.DirectInstantSourceCommand;
if (nc != null)
Console.Write($&quot;Message/NC: {nc.Line}; File: {nc.FilePath}; LineNo: {nc.GetLineNo()}; &quot;);
// For SessionMessageHost.FilterFlag.Progress or Error.
var multiTagMessage = sessionMessage.MultiTagMessage;
if (multiTagMessage != null)
Console.WriteLine($&quot;Message/NC: {multiTagMessage.Message}&quot;);
var exception = sessionMessage.Exception;
if (exception != null)
Console.WriteLine($&quot;Message/NC: {exception.Message}&quot;);
var ncLine = diagnostic.SentenceCarrier?.GetSentence()?.FirstIndexedFileLine;
Console.WriteLine(
$&quot;NC [{diagnostic.GetSeverity()}] {diagnostic.GetId()}: {diagnostic.GetNotification()}; &quot; +
$&quot;File: {ncLine?.FilePath}; LineNo: {ncLine?.GetLineNo()}; NC: {ncLine?.Line}&quot;);
}
foreach (StepDiagnostic diagnostic in
localProjectService.StepDiagnosticProgress.Messages.ToList())
Console.WriteLine(
$&quot;Step {diagnostic.StepIndex} [{diagnostic.GetSeverity()}] &quot; +
$&quot;{diagnostic.GetId()}: {diagnostic.GetNotification()}&quot;);
File.WriteAllLines(&quot;output-session-messages.txt&quot;,
filteredSessionMessageList.Select(m =&gt;
$&quot;Msg[{m.Index}][{m.MessageRoleText}]: {m}&quot;));
shellMessages.Select(m =&gt;
$&quot;[{m.GetSeverity()}] {m.GetId()}: {m.GetNotification()}&quot;));
}
</code></pre>
<p>In the table, show the columns: <code>Role</code>, <code>NC/Message</code>.</p>
<p>Add update table event to <a class="xref" href="../../api/Hi.MachiningProcs.SessionProgress.html#Hi_MachiningProcs_SessionProgress_CollectionItemChanged">CollectionItemChanged</a>. The updating process has to be called by <a href="../general-rules.html">Loose Manner</a> for user experience.</p>
<p>Add the update-table event per sink: <a class="xref" href="../../api/Hi.MachiningProcs.StepDiagnosticProgress.html#Hi_MachiningProcs_StepDiagnosticProgress_MessageAdded">MessageAdded</a> / <a class="xref" href="../../api/Hi.NcParsers.NcDiagnosticProgress.html#Hi_NcParsers_NcDiagnosticProgress_MessageAdded">MessageAdded</a>, and for the session-scoped shell sink the app-lifetime bridge <a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_OnShellMessageAdded">OnShellMessageAdded</a> (with the matching <code>Cleared</code> events). The updating process has to be called by <a href="../general-rules.html">Loose Manner</a> for user experience.</p>
<div class="TIP">
<h5>Tip</h5>
<p>On window desktop application (WPF), consider use textarea instead of datagrid to MessageTable for better performance. Use padding to show the different columns. And use the font in the textarea that with consistent width.</p>
@@ -160,9 +163,9 @@
<p>The message display should be real-time.</p>
</div>
<h2 id="behavior-of-export-button">Behavior of Export Button</h2>
<p>Export ALL filtered elements in the <a class="xref" href="../../api/Hi.MachiningProcs.SessionProgress.html#Hi_MachiningProcs_SessionProgress_MessageCollection">MessageCollection</a> by <a class="xref" href="../../api/Hi.MachiningProcs.SessionProgress.html#Hi_MachiningProcs_SessionProgress_GetFliteredList_Hi_MachiningProcs_SessionProgress_FilterFlag_System_String_">GetFliteredList(FilterFlag, string)</a>.</p>
<p>Export ALL filtered elements of the active tab's sink.</p>
<h2 id="signalr-implementation-webapi-only">SignalR Implementation (Webapi Only)</h2>
<p><code>SessionMessageHub</code> provides real-time message updates with method <code>GetSessionMessages(string filterFlags, string filterText, int limit)</code> and event <code>SessionMessagesUpdated</code>. <code>SessionMessageService</code> monitors <a class="xref" href="../../api/Hi.MachiningProcs.SessionProgress.html#Hi_MachiningProcs_SessionProgress_CollectionItemChanged">CollectionItemChanged</a> and broadcasts updates. The service uses <a class="xref" href="../../api/Hi.Common.LooseRunner.html">LooseRunner</a> for non-blocking async operations. The JavaScript component connects to <code>/sessionMessageHub</code> to receive real-time message updates.</p>
<p>One hub per sink — <code>/shellMessageHub</code>, <code>/ncDiagnosticHub</code>, <code>/stepDiagnosticHub</code> — each with a <code>GetMessages(int limit)</code> pull returning <code>MessagesUpdate { messages, totalCount }</code>. A per-sink broadcast service subscribes its sink's <code>MessageAdded</code>/<code>Cleared</code> and raises a coalesced <code>MessagesChanged</code> notification via <a class="xref" href="../../api/Hi.Common.LooseRunner.html">LooseRunner</a>; the client re-pulls the recent window on each notification (loss-free regardless of how many appends coalesced). The JavaScript components connect to the three hubs to receive real-time updates.</p>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a href="../index.html">this page</a> for git repository.</p>
<h3 id="wpf-application-source-code-path">WPF Application Source Code Path</h3>
@@ -171,10 +174,12 @@
</ul>
<h3 id="web-page-application-source-code-path">Web Page Application Source Code Path</h3>
<ul>
<li>wwwroot/player/session-message-panel.js (Vue component)</li>
<li>Players/PlayerController.cs (REST API - GetSessionMessages endpoint)</li>
<li>Players/SessionMessageService.cs (Business logic)</li>
<li>Players/SessionMessageHub.cs (SignalR Hub for real-time updates)</li>
<li>wwwroot-src/src/components/player/SessionMessagePanel.vue (tabbed panel)</li>
<li>wwwroot-src/src/components/player/SessionMessageTab.vue + MessageRow.vue (per-tab list)</li>
<li>wwwroot-src/src/composables/useSessionSinkHub.ts (the three hub composables)</li>
<li>Players/SessionSinkHub.cs (SignalR hubs for the three sinks)</li>
<li>Players/SessionSinkBroadcastService.cs + {Shell,Nc,Step}*BroadcastService.cs (subscribe-and-rebroadcast)</li>
<li>Players/SessionSinkDtos.cs (typed DTOs)</li>
</ul>
</article>