Archived
311 lines
23 KiB
HTML
311 lines
23 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>Legacy Controller Model (HardNcEnv) | HiAPI-C# 2025 </title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="title" content="Legacy Controller Model (HardNcEnv) | 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 "{query}"">
|
||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||
<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-LegacyControllerSettings">
|
||
<h1 id="legacy-controller-model-hardncenv">Legacy Controller Model (HardNcEnv)</h1>
|
||
|
||
<p><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html">HardNcEnv</a> is the HardNc controller model: the one the project still carries as
|
||
<code>MachiningProject.NcEnv</code> and serializes as its <code>NcEnv</code> element, and the one the deprecating HardNc
|
||
pipeline plays. It is a model with no screen. The Legacy-Controller page at <code>/controller/:tab?</code> that
|
||
edited it was removed on 2026-09-11 — <code>/controller/…</code> now redirects to
|
||
<code>/general-setup?tree=equipment/controller</code>, following the route file's convention for retired
|
||
pages — and controller settings are edited on the General Setup page's
|
||
<a class="xref" href="general-setup/controller/index.html">Controller Branch</a>, the settings face of the SoftNc runner, which is the
|
||
live pipeline. What survives of the legacy face is its REST implementation,
|
||
<code>Controller/ControllerController.cs</code>, still mounted under <code>/api/Controller/*</code>, and its typed client
|
||
<code>wwwroot-src/src/api/controller.ts</code>, kept as those endpoints' only client; one wrapper in it,
|
||
<code>getCncBrand</code>, is still called from a live component. This page is what the model holds: how it
|
||
stands to the runner, what the live pipeline still reads from it, the settings that retire with it,
|
||
the ones the branch edits a runner-side copy of, and the one marker class both stores draw a work
|
||
coordinate with.</p>
|
||
<h2 id="the-model-and-the-runner-beside-it">The Model, and the Runner Beside It</h2>
|
||
<p>The project carries both models side by side and serializes both. <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html">HardNcEnv</a> is
|
||
loaded from the project's own <code>NcEnv</code> element unconditionally; <a class="xref" href="../api/Hi.NcParsers.NcRunnerSuit.html">NcRunnerSuit</a> — the
|
||
runner plus the project's per-case dependency list — is read from its own nested element. The legacy
|
||
element is a <em>fallback source</em> for the suit rather than a shared store: a project file that carries
|
||
no runner element derives one from the legacy element at load time, and from then on the two drift
|
||
apart, because every later edit lands on one of them alone.</p>
|
||
<p>The legacy model is still read at play time. The legacy NC runner is constructed over a delegate
|
||
onto the project's <code>NcEnv</code>, so a replacement installed through the <code>update</code> endpoint — the removed
|
||
page's object-management chain was its caller, and nothing in the SPA calls it now — propagates
|
||
without rewiring, and a project-level switch selects which of the two pipelines plays. That switch
|
||
defaults to the SoftNc pipeline and no screen in the web application changes it; it is a scripting
|
||
property, <a class="xref" href="../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_EnableSoftNcRunner_">EnableSoftNcRunner</a><small>(API)</small>. The legacy
|
||
NC optimisation route is the other live reader: it is taken whenever the switch is off or the
|
||
session holds no played SoftNc layers, and it is handed <code>MachiningProject.NcEnv</code> directly.</p>
|
||
<h2 id="what-the-live-pipeline-still-reads-from-it">What the Live Pipeline Still Reads From It</h2>
|
||
<p>Selecting the SoftNc pipeline does not silence the legacy model. Four reads still land on it with
|
||
the switch on, so a value that only the legacy endpoints or the project XML can change still shows
|
||
up on a runner play.</p>
|
||
<ul>
|
||
<li><strong>Stroke limits, as a fallback and as a seed.</strong> The per-step stroke check reads the runner's
|
||
<a class="xref" href="../api/Hi.NcParsers.Dependencys.IStrokeLimitConfig.html">IStrokeLimitConfig</a> when the switch is on and one is present, and
|
||
falls back to the legacy stroke boxes otherwise — a CSV or NX-CL suit carries no stroke config, so
|
||
a play on those falls through to the boxes. When a project loads, the boxes' finite ends are also
|
||
copied into the runner's controller parameter table for every axis that carries no end there
|
||
(<a class="xref" href="../api/Hi.NcParsers.SoftNcRunner.html#Hi_NcParsers_SoftNcRunner_PopulateLegacyStrokeLimitsWhereUnset_">PopulateLegacyStrokeLimitsWhereUnset</a>; an explicit row wins), so
|
||
a project that declared its travel only in the boxes is checked against it on SoftNc — and its
|
||
tilt solves prefer the rotary branch inside that travel. The pre-play audit that warns of an
|
||
unlimited linear axis reads both sources end by end, the runner's first and the legacy box behind
|
||
it, and stays silent for an axis either source guards.</li>
|
||
<li><strong>The home position, as a fallback.</strong> The chain is re-homed from the runner's home config; the
|
||
legacy <code>HomeMc</code> fills in only when no linear axis was ever configured there.</li>
|
||
<li><strong>The Execution canvas's coordinate markers.</strong> The <code>IsoCoordinate</code> and <code>HeidenhainCoordinate</code>
|
||
rendering flags on the Execution page draw from the legacy tables — see
|
||
<a href="#two-faces-of-a-work-coordinate">Two Faces of a Work Coordinate</a>.</li>
|
||
<li><strong>The Execution tool bar's brand.</strong> The Scene dropdown offers its Heidenhain-coordinate entry only
|
||
while the brand is Heidenhain, and the brand it asks for is the legacy model's, through
|
||
<code>getCncBrand</code> — the one wrapper in <code>wwwroot-src/src/api/controller.ts</code> a live component still
|
||
calls.</li>
|
||
</ul>
|
||
<p>The kinematics solver the service builds is mirrored onto <code>NcEnv.XyzabcSolver</code> as well, but that
|
||
slot is wiring the service maintains rather than a setting, and it is never serialized.</p>
|
||
<h2 id="settings-that-retire-with-the-model">Settings That Retire With the Model</h2>
|
||
<p>Four controls had no editor anywhere in the web application but the removed screen. None of them
|
||
gained one on the branch: three are legacy-only settings and retire with the model, and the fourth is an
|
||
action the runner path does not offer. Every one keeps its endpoint under <code>/api/Controller/*</code>, so
|
||
the value can still be read and written over REST or in the project XML — there is just no UI for
|
||
it.</p>
|
||
<h3 id="align-p0--not-offered-on-the-runner-path">Align P0 — not offered on the runner path</h3>
|
||
<p>The removed Coordinate Table tab carried a third row action beside <strong>P0</strong> and <strong>M0</strong>: <strong>Align P0</strong>,
|
||
which moves the <em>part</em> so that program zero lands on the offset the row holds — the reproducing
|
||
direction, <em>program zero onto the work offset</em> in the manual's terms. It did not write the
|
||
coordinate; it wrote the <strong>fixture's geometry-to-table transformer</strong>, so the workpiece and fixture
|
||
moved together and the runtime face followed at the next rebuild. The branch's Work Coordinates
|
||
leaf carries P0 and M0 only, which write the row <em>from</em> the machine position — the opposite
|
||
direction — and no alignment. The operation is not offered on the runner path.</p>
|
||
<p>The tab's undo history — <strong>Undo Align</strong> and <strong>Redo Align</strong> over two component-local stacks, the
|
||
undo stack capped at 32 entries — was the component's own and went with it; the endpoint never kept
|
||
an undo slot. It still answers with the assigned translation plus the transformer as it stood
|
||
<strong>before</strong> and <strong>after</strong> the write, both serialized as XML, and the stateless revert endpoint still
|
||
parses such a snapshot and assigns it as the fixture's transformer; nothing in the SPA posts to
|
||
either now. The engine exposes the same operation as a script call,
|
||
<a class="xref" href="../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_AlignWorkpieceProgramZeroToIso_">AlignWorkpieceProgramZeroToIso</a><small>(API)</small>, which
|
||
looks the row up in the legacy model's ISO coordinate table — an API for a script, not a
|
||
replacement for the removed control.</p>
|
||
<h3 id="enable-shortest-rotary-path">Enable Shortest Rotary Path</h3>
|
||
<p><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_EnableShortestRotary">EnableShortestRotary</a> constructs <code>true</code>, and its only editor was the
|
||
removed Config tab's single toggle. On the legacy pipeline the flag gates one step: each rotary
|
||
axis of a block is cycled into the ±180° window around the previous block's value. The flag is
|
||
narrower than its name, because that same cycle is applied unconditionally on
|
||
<a class="xref" href="../api/Hi.Numerical.CncBrand.html#Hi_Numerical_CncBrand_Heidenhain">Heidenhain</a> — the brand branch runs it before the flag is consulted, so
|
||
clearing it changes nothing there.</p>
|
||
<p>The runner's dependency layer declares no counterpart: no brand parameter table, no generic config
|
||
and no branch leaf carries a shortest-rotary switch. What the runner pipeline has instead is the
|
||
Heidenhain <code>M126</code> / <code>M127</code> pair, modal codes read from the program text rather than from any
|
||
setting.</p>
|
||
<h3 id="max-rotary-speed-per-axis">Max rotary speed per axis</h3>
|
||
<p><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_MaxRotarySpeedABC_radds">MaxRotarySpeedABC_radds</a> is the per-axis rotary speed ceiling the
|
||
HardNc pipeline clamps feed and cycle time against; its editor was the removed Machine tab's
|
||
<strong>Max Speed (rpm)</strong> column. On the runner pipeline the ceiling is not a setting of its own: the
|
||
polar feed clamp reads it from the per-axis rapid-feedrate bucket,
|
||
<a class="xref" href="../api/Hi.NcParsers.Dependencys.IRapidFeedrateConfig.html">IRapidFeedrateConfig</a>, and the legacy import funnels this field into
|
||
that bucket at rpm × 360 deg/min, so the branch's Rapid Feedrates leaf edits the runner-side
|
||
number. The legacy field itself retires with the model.</p>
|
||
<h3 id="heidenhain-master-axis-character">Heidenhain master-axis character</h3>
|
||
<p><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_HeidenhainMasterAxisChar">HeidenhainMasterAxisChar</a> is a character face over the integer axis
|
||
direction the <code>PLANE … SEQ</code> solution family is resolved against; its editor was a second card the
|
||
removed Brand tab grew while the brand was Heidenhain, one select over <code>A</code>, <code>B</code>, <code>C</code>. The setter
|
||
takes only <code>A</code>, <code>B</code> or <code>C</code> and throws on anything else; the <code>PUT</code> endpoint upper-cases the first
|
||
character it is sent and answers 400 to anything but those three. On the runner pipeline the master
|
||
rotary is <strong>derived rather than configured</strong> — it is the first declared rotary axis — so the branch
|
||
has nothing to expose and no leaf for it.</p>
|
||
<h2 id="what-the-branch-edits-a-runner-side-copy-of">What the Branch Edits a Runner-Side Copy Of</h2>
|
||
<p>Naming what is <em>not</em> legacy-only matters as much. The CNC brand, the stroke limits, the rapid feed,
|
||
the tool-change time, the tool offset table with its tool-house dependence, the work coordinates
|
||
with their P0 and M0 actions, and the Heidenhain datum preset and datum shift tables all have
|
||
editors on the branch — see <a class="xref" href="general-setup/controller/brand-matrix.html">Brand Matrix</a> for which of those
|
||
leaves each brand grows. The branch edits the <em>runner's</em> copy of each; the legacy copy in
|
||
<a class="xref" href="../api/Hi.Numerical.HardNcEnv.html">HardNcEnv</a> is reachable only through its endpoint and the project XML, and the
|
||
two are not kept in step.</p>
|
||
<p>One flag sits outside both models. <strong>Set ideal offset dependent on tool house</strong> is a project-level
|
||
configuration flag, and the branch's Tool Offsets leaf and the legacy <code>ideal-offset-dependent</code>
|
||
endpoint read and write that one flag. The two <em>tables</em> stay separate: the legacy
|
||
<code>set-ideal-offset-from-toolhouse</code> endpoint — the removed Offset Table tab's <strong>Refresh from Tool
|
||
House</strong> — recomputes <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_MillingToolOffsetTable">MillingToolOffsetTable</a> from
|
||
<a class="xref" href="../api/Hi.Machining.MachiningToolHouse.html">MachiningToolHouse</a>, while the branch's refresh recomputes the runner's own
|
||
tool-offset table.</p>
|
||
<h2 id="two-faces-of-a-work-coordinate">Two Faces of a Work Coordinate</h2>
|
||
<p>The work-coordinate marker is one displayee class serving two providers. The Execution canvas draws
|
||
it from <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_IsoCoordinateTable">IsoCoordinateTable</a> — a
|
||
<a class="xref" href="../api/Hi.NcParsers.Dependencys.Generic.IsoCoordinateTable.html">IsoCoordinateTable</a> instance owned by the legacy model, which
|
||
constructs with <code>G54</code> … <code>G59</code> and <code>G59.1</code> … <code>G59.9</code> all at zero — and draws its Heidenhain datum
|
||
marker from the legacy datum tables the same way. The General Setup canvas builds the same
|
||
displayee over the active runner's effective <a class="xref" href="../api/Hi.NcParsers.Dependencys.IIsoCoordinateConfig.html">IIsoCoordinateConfig</a>
|
||
instead, which on most brands is the brand parameter table. Same marker, same code, two stores.</p>
|
||
<p>Which row the Execution marker draws is the displayee's own id, constructed as <code>G54</code>. Its only
|
||
writer is the legacy <code>iso-coordinate-selection</code> endpoint, which the removed tab's row selection
|
||
posted to and nothing in the SPA posts to now, so the Execution marker stays on <code>G54</code>. Whether it
|
||
is drawn is the <code>IsoCoordinate</code> rendering flag, toggled from the Execution page's Scene dropdown;
|
||
all three coordinate flags are off in the shipped rendering-flag set — as are the machine tool and
|
||
the cutter, leaving the workpiece, the fixture, the dimension bar and the cutter-location strip as
|
||
the four that arrive on. The General Setup marker's row is a user-config value of its own, with an
|
||
endpoint on the equipment-setup display controller.</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>
|
||
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
|
||
<ul>
|
||
<li><code>Controller/ControllerController.cs</code> — the model's REST face, still mounted: every legacy table
|
||
and setting, the align endpoint that snapshots the transformer either side of the write, the
|
||
stateless revert, the <code>update</code> rebind, and the marker-row selection.</li>
|
||
<li><code>wwwroot-src/src/api/controller.ts</code> — the typed wrappers over all of it, kept as those endpoints'
|
||
only client; <code>getCncBrand</code> and the brand constants are what a live component still imports.</li>
|
||
<li><code>wwwroot-src/src/components/execution/ExecutionExtendedToolBar.vue</code> — that component: the Scene
|
||
dropdown that toggles the coordinate flags, and asks the legacy model for its brand.</li>
|
||
<li><code>wwwroot-src/src/api/renderingFlags.ts</code> — the flag indices the Scene dropdown writes.</li>
|
||
<li><code>Disp/ExecutionDisplayee.cs</code> — the Execution canvas's displayee: the shipped flag set, and the
|
||
work-coordinate and datum markers it builds over the legacy tables.</li>
|
||
<li><code>Disp/IsoCoordinateEntryDisplayee.cs</code> — the one marker class both canvases draw with.</li>
|
||
<li><code>Disp/EquipmentSetupDisplayee.cs</code> — the General Setup canvas's marker, over the runner's
|
||
providers.</li>
|
||
<li><code>wwwroot-src/src/router/routes.ts</code> — the <code>/controller/…</code> redirect stub onto the Controller branch.</li>
|
||
</ul>
|
||
<h3 id="hiapi-engine">HiAPI Engine</h3>
|
||
<ul>
|
||
<li><code>HiUniNc/Numerical/HardNcEnv.cs</code> — the model: the coordinate, datum and offset tables, the stroke
|
||
boxes that construct infinite, the rapid rate and tooling time, the per-axis rotary speed
|
||
ceiling, the shortest-rotary flag with its Heidenhain exclusion, and the master-axis character
|
||
over its integer direction.</li>
|
||
<li><code>HiUniNc/Numerical/HardNcLine.cs</code> — the legacy consumers of two of those settings: the
|
||
shortest-rotary path application and the <code>SEQ</code> solve that reads the master-axis direction.</li>
|
||
<li><code>HiUniNc/Numerical/NcProc.cs</code> — the legacy consumer of the rotary speed ceiling: feed and cycle
|
||
time re-limited per rotary axis.</li>
|
||
<li><code>HiUniNc/Numerical/MillingToolOffsetTable.cs</code> — the offset table and the tool-house recompute the
|
||
legacy refresh endpoint calls.</li>
|
||
<li><code>HiMech/NcParsers/Dependencys/Generic/IsoCoordinateTable.cs</code> — the coordinate table type and the
|
||
fifteen G-code keys it constructs with, shared by name with the runner's brand-agnostic table.</li>
|
||
<li><code>HiMech/Machining/MachiningEquipmentUtils/MachiningEquipmentUtil.cs</code> — the alignment itself: the
|
||
translation written into the fixture's geometry-to-table transformer.</li>
|
||
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the legacy import: which legacy fields are funnelled into
|
||
which runner dependency, including the rotary speed conversion into the rapid-feedrate config.</li>
|
||
<li><code>HiMech/NcParsers/LogicSyntaxs/PolarInterpolationUtil.cs</code> — the rotary speed ceiling the runner
|
||
pipeline reads, and why it is the rapid-rate bucket rather than a setting of its own.</li>
|
||
<li><code>HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainPlaneTiltSyntax.cs</code> — the derived master rotary
|
||
on the runner pipeline: the first declared rotary axis, with no configured alternative.</li>
|
||
<li><code>HiNc/MachiningProcs/MachiningProject.cs</code> — where both models hang off the project, and the load
|
||
path that derives a runner from the legacy element only when no runner element is present.</li>
|
||
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — the legacy runner built over a delegate onto the
|
||
project's model, the switch that selects which pipeline is active, and the stroke-limit and
|
||
home-position fallbacks onto the legacy model.</li>
|
||
<li><code>HiNc/Numerical/FilePlayers/HardNcRunner.cs</code> — the legacy runner itself.</li>
|
||
<li><code>HiNc/MachiningProcs/SessionShell.cs</code> — the scripting face of the pipeline switch and of the
|
||
alignment, and the optimisation route that passes the legacy model directly.</li>
|
||
</ul>
|
||
<h2 id="see-also">See Also</h2>
|
||
<ul>
|
||
<li><a class="xref" href="general-setup/controller/index.html">Controller Branch</a> — the SoftNcRunner-native controller branch: the live settings face,
|
||
and the editor for everything named above as having a runner-side copy</li>
|
||
<li><a class="xref" href="general-setup/index.html">General Setup Page</a> — the page that hosts that branch, and the rest of the equipment tree
|
||
beside it</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>
|