This commit is contained in:
2026-09-09 18:06:21 +08:00
parent 161af68fe3
commit 45b9f9ed5d
79 changed files with 1167 additions and 1532 deletions
@@ -94,11 +94,10 @@
<li>The <code>Brief Message Text Field</code> content is updated</li>
<li>The message is appended to the daily log file at <code>logs/log-{DateTime.Now:yyyy-MM-dd}.txt</code></li>
</ol>
<p>The second step belongs to the logging path rather than to the bar, and only one client shares it.
The WPF brief field is fed by a logger provider, so a message that reaches the field has already
gone to the file sink. The web footer is fed by the routine-progress store instead: what lands there
is client-side, and it reaches the server's daily log only when the server logged it too — no part
of the shell posts a message to the log-append endpoint.</p>
<p>The second step belongs to the logging path rather than to the bar, and the footer does not share
it. The footer is fed by the routine-progress store: what lands there is client-side, and it reaches
the server's daily log only when the server logged it too — no part of the shell posts a message to
the log-append endpoint.</p>
<h3 id="message-types">Message Types</h3>
<p>The <a class="xref" href="../../api/Hi.Common.Messages.MessageFlag.html">MessageFlag</a> determines the display behavior:</p>
<table>
@@ -128,16 +127,6 @@ of the shell posts a message to the log-append endpoint.</p>
<p>When the message is an <a class="xref" href="https://learn.microsoft.com/dotnet/api/system.exception">Exception</a>, the brief message shows <a class="xref" href="https://learn.microsoft.com/dotnet/api/system.exception.message">Message</a> while the full exception details are logged to file.</p>
</div>
<h2 id="platform-specific-layouts">Platform-Specific Layouts</h2>
<h3 id="wpf-application">WPF Application</h3>
<p>The WPF version uses a fixed bottom bar:</p>
<ul>
<li><strong>Message Section Bottom Bar</strong>
<ul>
<li>Brief Message Text Field (selectable for copy)</li>
<li>Show Log Button</li>
</ul>
</li>
</ul>
<h3 id="web-application">Web Application</h3>
<p>The web version docks a single dense bar along the bottom of the layout — <code>AppFooter.vue</code>, not a
stack of toasts:</p>
@@ -159,16 +148,11 @@ Quasar notifications, anchored bottom-right and auto-hiding on a timeout that do
severity. A boot patch mirrors every one of them into the footer's foreground history, which is what
keeps a toast reviewable after it fades.</p>
<h2 id="show-log-button">Show Log Button</h2>
<p>The <code>Show Log</code> button is the only entrance to the log screen, and the two clients answer it
differently.</p>
<p>In the web application it is a router link in the menu bar's right-hand group — not on this bar —
and it opens the Log Viewer as a full page inside the shell. See
<a class="xref" href="../platform/log-viewer.html">Log Viewer Page</a> for that screen's toolbar, its auto-refresh interval, its
copy and download actions and the states of its text area.</p>
<p>In the WPF client the button sits at the right end of this bar. It looks for the current day's file
under the client's own <code>logs</code> folder and, when it is there, opens it with whatever application the
operating system associates with the file type; when it is not, it reports that in a message box
instead of opening anything.</p>
<p>The <code>Show Log</code> button is the only entrance to the log screen.</p>
<p>It is a router link in the menu bar's right-hand group — not on this bar — and it opens the Log
Viewer as a full page inside the shell. See <a class="xref" href="../platform/log-viewer.html">Log Viewer Page</a> for that
screen's toolbar, its auto-refresh interval, its copy and download actions and the states of its
text area.</p>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="main-panel.html">Main Panel</a> — the window frame this bar is docked in</li>
@@ -88,28 +88,18 @@
<h1 id="language-selection-submenu">Language Selection SubMenu</h1>
<p>The submenu locates on the <a class="xref" href="preference-menu.html">Preference Menu Dropdown</a>. It is the only
place either client offers for changing the interface language. What that choice sets in motion in
the web client — the message bundles, the locale applied before the first frame, the single switch
place the application offers for changing the interface language. What that choice sets in motion —
the message bundles, the locale applied before the first frame, the single switch
function and everything that re-renders behind it — is
<a class="xref" href="../platform/i18n.html">Internationalization</a>; this page covers the gesture.</p>
<p>The web submenu's model is the application-state store rather than a service handed down by the
<p>The submenu's model is the application-state store rather than a service handed down by the
parent component: it reads the current code and the available-code list from that store and calls
the store's language action. Both values are hydrated by the store's server-preferences load, which
the shell layout runs on mount — a separate path from the one that decides which locale the first
painted frame uses. The WPF client resolves its language manager during start-up, and the manager
reads the persisted code out of the user configuration as it is constructed.</p>
painted frame uses.</p>
<h2 id="layout">Layout</h2>
<p>The two clients draw the submenu differently.</p>
<ul>
<li><p>Language Selection SubMenu — WPF application</p>
<ul>
<li>English RadioButton</li>
<li>Simplified Chinese RadioButton</li>
<li>Traditional Chinese RadioButton</li>
</ul>
<p>Three fixed rows in one radio group, ticked at start-up to match the current language.</p>
</li>
<li><p>Language Selection SubMenu — web application</p>
<li><p>Language Selection SubMenu</p>
<ul>
<li>Language Row — one plain row per code in the store's available-code list, so the rows are
whatever the server reports rather than a fixed set of three. Each carries the language's own
@@ -120,13 +110,11 @@ the active state. No row carries a radio button or a checkbox.</li>
<p>The parent entry captions the current language, so it reads without opening the submenu.</p>
</li>
</ul>
<p>Choosing a row in the web client closes the popup and makes one call, the store's language action.
<p>Choosing a row closes the popup and makes one call, the store's language action.
The store writes the new code optimistically, POSTs it, adopts the current and available lists the
server echoes back, and only then switches the live catalogue — so the interface text flips after
the server has accepted the value, and the confirmation toast already reads in the language just
picked. A rejected write rolls the store back and changes no interface text at all. In the WPF
client picking a radio swaps the merged string dictionary in place and saves the code to the user
configuration, and the labels follow without a restart.</p>
picked. A rejected write rolls the store back and changes no interface text at all.</p>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-application">Web Application</h3>
@@ -90,19 +90,14 @@
<p>The Main Panel is the primary window of the HiNC application, providing navigation and access to all major features.</p>
<h2 id="key-models">Key Models</h2>
<ul>
<li>Project Service
<ul>
<li><strong>WPF Single-User Desktop Application</strong>: Uses self-hosted <a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html">LocalProjectService</a></li>
<li><strong>Web Service Application</strong>: injects <a class="xref" href="../../api/Hi.MachiningProcs.ProxyProjectService.html">ProxyProjectService</a>, the <a class="xref" href="../../api/Hi.MachiningProcs.IProjectService.html">IProjectService</a> implementation that reaches the session's project across the connection</li>
</ul>
</li>
<li><strong>Project Service</strong>: injects <a class="xref" href="../../api/Hi.MachiningProcs.ProxyProjectService.html">ProxyProjectService</a>, the <a class="xref" href="../../api/Hi.MachiningProcs.IProjectService.html">IProjectService</a> implementation that reaches the session's project across the connection</li>
<li><strong>User Service</strong>: <code>UserService</code></li>
</ul>
<h2 id="layout-structure">Layout Structure</h2>
<ul>
<li>Top <code>Navigation Menu</code>
<ul>
<li><p>Brand logo, and in the web application the HiAPI version as an outlined badge beside it. The
<li><p>Brand logo, and the HiAPI version as an outlined badge beside it. The
badge carries the HiNc assembly version reported by the anonymous authentication-status probe
the router runs before the first route resolves, so it needs neither a loaded project nor a
signed-in session; it is omitted only while that probe has reported no version.</p>
@@ -112,33 +107,14 @@ signed-in session; it is omitted only while that probe has reported no version.<
<li><code>Project Path Text Field</code> — readonly; reads &ldquo;No Project Loaded&rdquo; until a project is loaded</li>
<li><code>New MenuItem</code></li>
<li><code>Load MenuItem</code></li>
<li><code>ReLoad MenuItem</code> (web application only)</li>
<li><code>ReLoad MenuItem</code></li>
<li><code>Save MenuItem</code></li>
<li><code>Save As MenuItem</code></li>
<li><code>Close Project MenuItem</code> (web application only, below a separator)</li>
<li><code>Close Project MenuItem</code> (below a separator)</li>
</ul>
<p>In the web application ReLoad, Save, Save As and Close Project are disabled until a project is loaded, and New, Load and Save As open the shared file-picker dialog with a <code>.hincproj</code> filter. The picker is allowed the Admin and Project roots and is denied the Resource root, which holds read-only templates the project endpoints cannot save through; it asks for the Admin root by name, so that is where it opens, and it seeds its path box with the directory holding the loaded project, or with the admin root itself when there is none. The Project root is one of the two only while a project is loaded: the roots endpoint lists a root only when its directory resolves, and the project directory resolves to nothing until a project is open, so with no project the picker offers Admin alone. The WPF client's dropdown stops at Save As.</p>
<p>ReLoad, Save, Save As and Close Project are disabled until a project is loaded, and New, Load and Save As open the shared file-picker dialog with a <code>.hincproj</code> filter. The picker is allowed the Admin and Project roots and is denied the Resource root, which holds read-only templates the project endpoints cannot save through; it asks for the Admin root by name, so that is where it opens, and it seeds its path box with the directory holding the loaded project, or with the admin root itself when there is none. The Project root is one of the two only while a project is loaded: the roots endpoint lists a root only when its directory resolves, and the project directory resolves to nothing until a project is open, so with no project the picker offers Admin alone.</p>
</li>
<li><p><code>Environment Menu Dropdown</code> (WPF application only — the web application has no such dropdown; see below)</p>
<ul>
<li>Machine Tool MenuItem
Open <a class="xref" href="../general-setup/machine-tool.html">Machine Tool Page</a>
Sole window in WPF app.
The page manages <a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_MachiningEquipment">MachiningEquipment</a>.<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.MachiningEquipment.html#Hi_Machining_MachiningEquipmentUtils_MachiningEquipment_MachiningChain">MachiningChain</a>.</li>
<li>Controller MenuItem
Open <a class="xref" href="../legacy-controller.html">Legacy Controller Page</a></li>
<li>Tool House MenuItem</li>
<li>Fixture MenuItem
Open <a class="xref" href="../general-setup/fixture.html">Fixture Page</a></li>
<li>Workpiece MenuItem
Open <a class="xref" href="../general-setup/workpiece.html">Workpiece Page</a></li>
</ul>
</li>
<li><p>Mission MenuItem
Open <a class="xref" href="../execution/mission/mission-root-panel.html">Mission Page</a>
In the web application the Mission editor is a branch of the Execution page's Control Tree; <code>/mission</code> redirects there.</p>
</li>
<li><p><code>Page Menu Dropdown</code> (web application only) — the three workflow pages in setup order (fill the tool house, set up the equipment, run), then the utilities.</p>
<li><p><code>Page Menu Dropdown</code> — the three workflow pages in setup order (fill the tool house, set up the equipment, run), then the utilities.</p>
<ul>
<li>Tool House MenuItem
Open <a class="xref" href="../tool-house/index.html">Tool House Page</a></li>
@@ -156,18 +132,9 @@ Open <a class="xref" href="../legacy-controller.html">Legacy Controller Page</a>
</li>
<li><p><a class="xref" href="preference-menu.html">Preference Menu Dropdown</a></p>
</li>
<li><p><code>Debug Menu Dropdown</code> (WPF application only) — Transformers and Geometry Management, visible only while the client runs in debug mode</p>
</li>
<li><p>Help MenuItem</p>
<ul>
<li>HiAPI Version label
A label to show the HiNc library version.
The web application shows this as a version badge next to the brand logo, with no Help dropdown.</li>
</ul>
</li>
</ul>
</li>
<li><code>Show Log</code> Button — a button on both clients, not a menu item. In the web application it sits on the menu bar's right side and routes to the Log Viewer page; in the WPF client it sits at the right end of the bottom message bar.
<li><code>Show Log</code> Button — a button, not a menu item. It sits on the menu bar's right side and routes to the Log Viewer page.
The Log Viewer presents the server's application log for the current day, with a manual refresh, a selectable auto-refresh interval, copy to the clipboard, and download of the day's log file for offline analysis.</li>
<li>Central <code>Page Panel</code> — one router outlet wrapped in a keep-alive cache keyed on a project epoch: an
integer the layout owns, raised by two watchers and read by nothing else. The first watches the project
@@ -183,7 +150,7 @@ A bump discards every cached page and rebuilds it, so each page initializes on i
watching for project changes itself.</li>
<li>Bottom footer — the routine-progress bar: the latest foreground message with a recent-messages history menu on the left, the live session status in the middle, and in-flight background job progress on the right. See <a class="xref" href="bottom-message-bar.html">Message Section on Main Panel</a>.</li>
</ul>
<p>In the WPF application the run tool bars belong to the menu shell and appear only while the central panel is the player. The web application does not carry them on the menu bar at all: the transport controls ride the <a class="xref" href="../execution/index.html">Execution Page</a>&lsquo;s primary panel header and the view controls ride its canvas panel header. What the menu bar keeps instead is the page's set of column quick-toggles, the connection badge, the active page's title, the <code>Show Log</code> button and — when authentication is on — the logout button. The toggles follow whichever tree page is current: four on the Execution page (control dock, canvas, strip charts, step info) and three on General Setup (control dock, content, canvas); the Tool House page lays itself out with plain splitters and gets none. The connection badge is Execution-only — the other pages&rsquo; canvases carry their own in-panel badges.</p>
<p>The menu bar carries no run tool bars at all: the transport controls ride the <a class="xref" href="../execution/index.html">Execution Page</a>&lsquo;s primary panel header and the view controls ride its canvas panel header. What the menu bar keeps instead is the page's set of column quick-toggles, the connection badge, the active page's title, the <code>Show Log</code> button and — when authentication is on — the logout button. The toggles follow whichever tree page is current: four on the Execution page (control dock, canvas, strip charts, step info) and three on General Setup (control dock, content, canvas); the Tool House page lays itself out with plain splitters and gets none. The connection badge is Execution-only — the other pages&rsquo; canvases carry their own in-panel badges.</p>
<h2 id="project-menu-behavior">Project Menu Behavior</h2>
<p>The <code>Project Path Text Field</code> displays the current project path when a project is loaded. It is implemented as a pure text field (not a button) that allows users to select and copy the path.</p>
<p>The <code>Project</code> Menu manages <a class="xref" href="../../api/Hi.MachiningProcs.MachiningProject.html">MachiningProject</a> with the following operations:</p>
@@ -228,17 +195,12 @@ watching for project changes itself.</li>
</tr>
</tbody>
</table>
<p>In the web application every Project action reports through two channels: a background progress line on the footer while it runs, then a toast when it lands — positive on success, a &ldquo;busy&rdquo; warning when a concurrent action has already claimed the service (HTTP 409), negative otherwise. In the WPF client the operations log through <code>ILogger</code> and, after a load, set the player panel's DispEngine to the isometric view. What a project change drives in the web shell is the layout's keep-alive epoch, not a canvas view: every cached page is discarded and rebuilt.</p>
<p>Every Project action reports through two channels: a background progress line on the footer while it runs, then a toast when it lands — positive on success, a &ldquo;busy&rdquo; warning when a concurrent action has already claimed the service (HTTP 409), negative otherwise. What a project change drives in the shell is the layout's keep-alive epoch, not a canvas view: every cached page is discarded and rebuilt.</p>
<div class="NOTE">
<h5>Note</h5>
<p>The web implementation reports through the toast helper and the routine-progress footer store; the WPF client reports through <code>ILogger</code>. Neither routes project messages through MessageUtil. Project I/O is asynchronous on both clients, so the shell stays responsive during file I/O.</p>
<p>Project actions report through the toast helper and the routine-progress footer store, and none of them routes a message through MessageUtil. Project I/O is asynchronous, so the shell stays responsive during file I/O.</p>
</div>
<h2 id="platform-specific-differences">Platform-Specific Differences</h2>
<h3 id="wpf-application">WPF Application</h3>
<ul>
<li>Only a single instance of each sub-window (Mission, Workpiece, Fixture) can exist at a time</li>
<li>There is no menu entry for the run cockpit: the Main Panel itself is the player</li>
</ul>
<h3 id="web-application">Web Application</h3>
<ul>
<li>The <a class="xref" href="../execution/index.html">Execution Page</a> is the landing route — <code>/</code> redirects to <code>/execution</code></li>
@@ -88,20 +88,18 @@
<h1 id="preference-menu-dropdown">Preference Menu Dropdown</h1>
<p>The Preference dropdown holds the application's display settings, and it is on the <a class="xref" href="main-panel.html">Main Panel</a>. The server-backed ones are service-wide rather than per-account: one <code>UserService</code> singleton holds one <code>UserConfig</code>, saved to a single <code>UserConfig.xml</code> resolved against the process working directory, so every browser signed in to that service reads and writes the same values. The sign-in cookie carries a user-name claim that nothing outside the authentication endpoints reads, and no preference endpoint is keyed on an account.</p>
<p>In the WPF application the menu binds <code>UserService</code>, whose <code>UserConfig</code> holds the persisted values. In the web application the dropdown has two models: Language and Show Physics Options bind the <code>appState</code> store, hydrated from and written back through <code>/api/preference/*</code>, where the server keeps them on <code>UserConfig</code>; the CSV / CL Controller checkboxes bind the device-local <code>useViewPrefs</code> singleton, persisted in the browser's <code>localStorage</code> and never sent to the server.</p>
<p>The dropdown has two models: Language and Show Physics Options bind the <code>appState</code> store, hydrated from and written back through <code>/api/preference/*</code>, where the server keeps them on <code>UserConfig</code>; the CSV / CL Controller checkboxes bind the device-local <code>useViewPrefs</code> singleton, persisted in the browser's <code>localStorage</code> and never sent to the server.</p>
<h2 id="layout">Layout</h2>
<ul>
<li>Preference Menu Dropdown
<ul>
<li><a class="xref" href="../execution/step-present-dialog.html">Step Present Preference</a> Button
<ul>
<li>WPF application: the first entry of the menu, opening the Step Present window.</li>
<li>Web application: the editor belongs to the Execution page's Step Info column, where a small icon button in the Step Properties panel header opens it as a modal next to the list it configures.</li>
</ul>
</li>
<li><a class="xref" href="../execution/graphic-cache-menu.html">Graphic-Cache Dropdown</a>
<ul>
<li>WPF application: a nested menu of cache fields inside the dropdown.</li>
<li>Web application: an entry of the Execution page's <a class="xref" href="../execution/execution-extended-renderingcanvas-tool-bar.html">extended tool bar</a>, under <code>Meshed Geom ▾</code>, which gathers the workpiece rendering-cache settings.</li>
<li>Its write is the one preference write that does not persist. <code>POST /api/preference/graphic-cache</code> clamps the requested size between the stored limits, assigns the three fields on the live <code>UserConfig</code> and returns, without calling <code>UserService.SaveUserConfig()</code>. The new value is service-wide and takes effect at once, but it reaches the file only when some later preference save writes the config out.</li>
</ul>
@@ -133,7 +131,7 @@
<li>Show Log Button
<ul>
<li>See <a class="xref" href="bottom-message-bar.html">Message Section</a>.</li>
<li>It is not a Preference-dropdown entry on either platform: the WPF application puts it on the bottom message bar, and the web application puts it on the menu bar's right side as an always-visible button that opens the Log Viewer page.</li>
<li>It is not a Preference-dropdown entry: it sits on the menu bar's right side as an always-visible button that opens the Log Viewer page.</li>
</ul>
</li>
</ul>
@@ -165,10 +165,6 @@
}
</code></pre>
<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 class="xref" href="../conventions/index.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>
</div>
<div class="NOTE">
<h5>Note</h5>
<p>The message display should be real-time.</p>