This repository has been archived on 2026-10-11. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
HiNC-2025-win-desktop-Deploy/App/wwwroot/HiAPI-docsite/anatomy/conventions/rendering-canvas-web-service.html
T
2026-09-12 18:16:27 +08:00

245 lines
15 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Rendering Canvas on Web Service Application | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Rendering Canvas on Web Service Application | 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="Anatomy-RenderingCanvasWebService">
<h1 id="rendering-canvas-on-web-service-application">Rendering Canvas on Web Service Application</h1>
<h2 id="overview">Overview</h2>
<p>The web application's 3D canvas is rendered on the server and streamed to the browser over a
SignalR hub connection at <code>/renderingHub</code>. <code>Disp/RenderingHub.cs</code> serves that hub, <code>Program.cs</code>
maps it, and the singleton in <code>Disp/RenderingService.cs</code> owns one native display engine per hub
connection. The browser paints the frames that arrive and forwards the user's input back.</p>
<h2 id="core-component">Core Component</h2>
<ul>
<li><strong>Location</strong>: <code>wwwroot-src/src/components/RenderingCanvas.vue</code></li>
<li><strong>Purpose</strong>: owns one hub connection, forwards input to the server engine, and paints the frames
the server streams back</li>
</ul>
<p>Its <code>hubUrl</code> prop defaults to <code>/renderingHub</code>, and it exposes <code>connect</code>, <code>disconnect</code>, the eight
view commands and <code>clearCache</code> — the surface through which
<code>wwwroot-src/src/components/RenderingCanvasToolBar.vue</code> drives the canvas instance it is bound to.</p>
<h2 id="connection-management">Connection Management</h2>
<h3 id="signalr-hub-connection">SignalR Hub Connection</h3>
<p>Each <code>RenderingCanvas</code> instance opens its own hub connection and therefore owns one connection ID;
the server keeps one display engine per connection ID, so several canvases can be live at the same
time. On initialization the hub sends the connection ID back to the caller as <code>CanvasInitialized</code>,
and the component re-emits it as <code>serverInitialized</code>. It is re-emitted after every reconnect,
because the server disposes the engine on disconnect and a reconnect gets a new ID — the parent
page answers by registering its content against that new ID.</p>
<p>The connection ID is the index for every canvas operation. <code>Disp/RenderingService.cs</code> keys the
engine dictionary, the last-input time, the last lossless-frame hash and the negotiated frame
format by it, and <code>Disp/RenderingHub.cs</code> keys its own sketch-view cache the same way.</p>
<h3 id="engine-lifetime">Engine Lifetime</h3>
<p>The engine's life is the connection's: <code>GetOrCreateEngine</code> creates it when the canvas initializes,
and <code>RemoveEngine</code> — called from <code>OnDisconnectedAsync</code> — disposes it and raises <code>EngineRemoved</code>.
An HTTP endpoint therefore resolves an engine through the non-creating <code>GetEngine</code>; creating one
for an ID that has already disconnected would orphan an engine no disconnect can ever clean up.
<code>Disp/StlPreviewService.cs</code> subscribes to <code>EngineRemoved</code> to release the per-connection native
topology the engine itself does not own.</p>
<h3 id="frame-encoding">Frame Encoding</h3>
<p>The frame encoding is negotiated at initialization. <code>InitializeCanvasV2(width, height, formats)</code>
with <code>jpeg</code> in the accepted list selects SkiaSharp JPEG frames delivered on <code>ImageUpdateV2</code> at an
adaptive quality — interactive frames compress harder and a still frame refines once — and adding
<code>png</code> lets that refine be encoded lossless. A browser without <code>createImageBitmap</code>, or a server that
does not carry the V2 method, falls back to <code>InitializeCanvas</code> and the gzip-RGBA frames delivered
on <code>ImageUpdate</code>.</p>
<h3 id="connection-id-naming-convention">Connection ID Naming Convention</h3>
<p>One value carries two names on the frontend:</p>
<ul>
<li><code>renderingConnectionId</code> — the parameter name used throughout the API wrappers under
<code>wwwroot-src/src/api</code>, and the route-parameter name on most display controllers</li>
<li><code>renderingConnId</code> — how the Execution page holds it (<code>wwwroot-src/src/pages/ExecutionPage.vue</code>),
filled from the canvas's <code>@server-initialized</code> and passed to the typed wrappers in
<code>wwwroot-src/src/api/execution.ts</code>, whose parameter is <code>renderingConnectionId</code></li>
</ul>
<h2 id="data-flow-architecture">Data Flow Architecture</h2>
<h3 id="frontend-responsibilities">Frontend Responsibilities</h3>
<p>The component owns the connection and its reconnect schedule, forwards pointer, wheel, key, touch,
resize and visibility events to the hub, and paints each frame as it arrives. A canvas the layout
has hidden keeps its connection open and tells the engine to pause rendering instead, so a hidden
canvas costs nothing while a simulation runs.</p>
<h3 id="backend-integration">Backend Integration</h3>
<p>Rendering and frame encoding happen on the server; a controller decides only what the engine shows,
by resolving the engine from the connection ID and assigning its displayee. That is why any feature
can put its own content on a canvas without touching the transport.</p>
<h4 id="example-execution-controller">Example: Execution Controller</h4>
<ul>
<li><strong>File</strong>: <code>Execution/ExecutionController.cs</code></li>
<li><strong>Method</strong>: <code>InitializeExecution</code> (<code>POST /api/Execution/initialize/{connectionId}</code>)</li>
<li><strong>Purpose</strong>: resolves the engine with <code>RenderingService.GetEngine(connectionId)</code> and assigns the
Execution displayee to it</li>
</ul>
<p>Six further controllers bind content the same way — the STL preview, the Tool House, the General
Setup equipment, the machine tool, the Mech Builder general mechanism and the legacy HardNcEnv
controller — each with its own route, all reaching the engine through the same connection ID. The
first five bind a displayee of their own; the last binds the shared Execution displayee, and it has
had no caller since the Legacy Controller page was removed on 2026-09-11 — the route is still
mounted, but no canvas in the SPA initializes against it.</p>
<h2 id="key-points">Key Points</h2>
<ul>
<li>Every canvas data-stream operation is indexed by the connection ID.</li>
<li>Rendering and frame encoding happen on the server; the frontend owns the connection, the input
events and the painting of received frames.</li>
<li>The engine is created and destroyed with the hub connection, so anything else that holds
per-connection resources releases them from <code>EngineRemoved</code>.</li>
</ul>
<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-service-source-code-path">Web Service Source Code Path</h3>
<ul>
<li><code>Disp/RenderingHub.cs</code> — the hub: creates the per-connection engine, encodes and pumps frames,
handles mouse, wheel, key, touch, resize and visibility, and disposes the engine on disconnect.</li>
<li><code>Disp/RenderingService.cs</code> — the singleton holding the per-connection maps, the playback render
throttle and the <code>EngineRemoved</code> event.</li>
<li><code>Disp/StlPreviewService.cs</code> — the <code>EngineRemoved</code> subscriber that frees the File Explorer
preview's native topology.</li>
<li><code>Program.cs</code> — registers SignalR and maps the hub at <code>/renderingHub</code>.</li>
<li><code>Execution/ExecutionController.cs</code> — the worked example above: <code>InitializeExecution</code> assigns the
Execution displayee to the engine named by the connection ID.</li>
<li><code>Disp/StlPreviewController.cs</code> — binds an STL preview to a canvas (<code>api/stl-preview</code>).</li>
<li><code>Mech/ToolHouseDisplayController.cs</code> — the Tool House canvas binding
(<code>api/mech/tool-house-display</code>).</li>
<li><code>Mech/EquipmentSetupDisplayController.cs</code> — the General Setup canvas binding
(<code>api/mech/equipment-setup-display</code>).</li>
<li><code>Mech/MachineToolDisplayController.cs</code> — the machine-tool canvas binding
(<code>api/mech/machine-tool/display</code>).</li>
<li><code>Mech/MechBuilder/GeneralMechanismDisplayController.cs</code> — the Mech Builder canvas binding
(<code>api/general-mechanism/display</code>).</li>
<li><code>Controller/ControllerController.cs</code> — the legacy controller surface's canvas binding
(<code>initialize-display/{connectionId}</code>), which assigns the shared Execution displayee; still mounted,
with no SPA caller since the Legacy Controller page was removed.</li>
</ul>
<h3 id="web-page-application-source-code-path">Web Page Application Source Code Path</h3>
<ul>
<li><code>wwwroot-src/src/components/RenderingCanvas.vue</code> — the client half: one hub connection per
instance, encoding negotiation, frame painting, input forwarding and the reconnect loop.</li>
<li><code>wwwroot-src/src/components/RenderingCanvasToolBar.vue</code> — the view-control bar bound to one
canvas instance through its exposed methods.</li>
<li><code>wwwroot-src/src/pages/ExecutionPage.vue</code> — mounts the main canvas, stores the ID as
<code>renderingConnId</code> and initializes the Execution content from <code>@server-initialized</code>.</li>
<li><code>wwwroot-src/src/api/execution.ts</code> — the typed wrappers whose <code>renderingConnectionId</code> parameter
addresses that canvas.</li>
<li><code>wwwroot-src/src/components/execution/StepVolumePanel.vue</code> — a second canvas on the Execution
page, with its own connection and its own engine.</li>
<li><code>wwwroot-src/src/components/mech/EquipmentSetupPanel.vue</code> — the General Setup page's canvas
column.</li>
<li><code>wwwroot-src/src/components/toolhouse/ToolHouseSetupPanel.vue</code> — the Tool House page's canvas
column.</li>
<li><code>wwwroot-src/src/components/StlPreviewPane.vue</code> — the File Explorer STL preview canvas.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a> — the <code>View</code> menu that drives the hub, and the <code>Scene</code> menu beside it</li>
<li><a class="xref" href="../platform/program-and-hosting.html">Program and Hosting</a> — the host that maps this hub and registers the services behind it</li>
<li><a class="xref" href="../util/stl-preview-pane.html">STL Preview Pane</a> — the one caller that opens a rendering connection outside a project</li>
</ul>
</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>