<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tainuo Hengchuang Technology on TiJet</title><link>https://www.tainuohc.com/en/</link><description>Recent content in Tainuo Hengchuang Technology on TiJet</description><generator>Hugo</generator><language>en</language><lastBuildDate>Tue, 04 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.tainuohc.com/en/index.xml" rel="self" type="application/rss+xml"/><item><title>Current Print Workflow</title><link>https://www.tainuohc.com/en/docs/sdk/quickstart/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.tainuohc.com/en/docs/sdk/quickstart/</guid><description>&lt;h1 id="current-print-workflow">Current Print Workflow&lt;/h1>
&lt;p>This document describes the actual print workflow across the C client, C SDK, and &lt;code>tijet_1/tincore&lt;/code> gRPC server.&lt;/p>
&lt;p>This document only describes the current codebase and client behavior, and does not include future refactoring plans.&lt;/p>
&lt;h2 id="source-baseline">Source Baseline&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Module&lt;/th>
 &lt;th>Repository&lt;/th>
 &lt;th>Branch&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Proto contracts &amp;amp; C SDK&lt;/td>
 &lt;td>&lt;code>tijet_1/sw-grpc-libraries&lt;/code>&lt;/td>
 &lt;td>&lt;code>master&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>tincore gRPC server&lt;/td>
 &lt;td>&lt;code>tijet_1/tincore&lt;/code>&lt;/td>
 &lt;td>&lt;code>feature-grpc-cpp&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="participants">Participants&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Participant&lt;/th>
 &lt;th>Responsibility&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Qt Client Business Layer&lt;/td>
 &lt;td>Register event callbacks, upload print data, retry &lt;code>START_PRINT&lt;/code>, wait for final result&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>C SDK Call Thread&lt;/td>
 &lt;td>Encapsulate &lt;code>SetPrintData&lt;/code> and &lt;code>StartPrint&lt;/code> RPC calls&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>C SDK Event Thread&lt;/td>
 &lt;td>Continuously read &lt;code>SubscribeEvents&lt;/code> server stream and invoke registered callbacks&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>TinCoreServiceImpl&lt;/code>&lt;/td>
 &lt;td>gRPC protocol layer, dispatches RPC requests to server Handlers&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>DefaultTinCoreHandlers&lt;/code>&lt;/td>
 &lt;td>Handles print data reception, data readiness validation, and async print start logic&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>gAFBridge&lt;/code> / Printhead Hardware&lt;/td>
 &lt;td>Processes print data in background, controls driver board power, communicates with printhead and enables high voltage&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="current-call-sequence">Current Call Sequence&lt;/h2>
&lt;pre class="mermaid">sequenceDiagram
 autonumber
 participant Caller as Caller
 participant Callee as Callee

 Caller-&amp;gt;&amp;gt;Callee: tijet_client_set_event_callback(callback, user_data)
 Callee--&amp;gt;&amp;gt;Caller: Callback registered

 Caller-&amp;gt;&amp;gt;Callee: tijet_client_start_event_subscription()
 Callee--&amp;gt;&amp;gt;Caller: TIJET_OK
 Note over Callee: Background event thread created
 Note over Callee: SubscribeEvents() polling

 Caller-&amp;gt;&amp;gt;Callee: tijet_client_set_print_data(data, index, is_static)
 Callee--&amp;gt;&amp;gt;Caller: SetPrintDataResponse{success=true}
 Note over Callee: Print data enters background processing
 Note over Callee: DATA_READY may still be 0 on return

 loop START_PRINT retry, total timeout 10s
 Caller-&amp;gt;&amp;gt;Callee: tijet_client_print_control(START_PRINT)
 alt DATA_READY == 0
 Callee--&amp;gt;&amp;gt;Caller: TIJET_ERR_SERVER
 Note over Caller: Wait 1s before retry
 else DATA_READY != 0
 Callee--&amp;gt;&amp;gt;Caller: TIJET_OK
 Note over Callee: High-voltage thread created
 end
 end

 Note over Caller: After START_PRINT success, wait for PrintStarted

 alt High voltage successful
 Callee--&amp;gt;&amp;gt;Caller: PrintStarted{success=true}
 Note over Caller: Ready for printing
 else High voltage failed
 Callee--&amp;gt;&amp;gt;Caller: PrintStarted{success=false}
 Note over Caller: Failure, message contains failed head count
 else PrintStarted not received within 10s
 Note over Caller: Timeout
 end&lt;/pre>
&lt;h2 id="retry--timeout-flow">Retry &amp;amp; Timeout Flow&lt;/h2>
&lt;pre class="mermaid">flowchart TD
 A[&amp;#34;Caller: Connect to server&amp;#34;] --&amp;gt; B[&amp;#34;Caller: Register callback&amp;lt;br/&amp;gt;Start event subscription&amp;#34;]
 B --&amp;gt; C[&amp;#34;Caller: set_print_data() upload print data&amp;#34;]
 C --&amp;gt; D{&amp;#34;Callee: Upload RPC successful?&amp;#34;}

 D --&amp;gt;|&amp;#34;No&amp;#34;| D1[&amp;#34;Failed, flow ends&amp;#34;]
 D --&amp;gt;|&amp;#34;Yes&amp;#34;| E[&amp;#34;Callee: Data enters background processing&amp;lt;br/&amp;gt;DATA_READY may still be 0&amp;#34;]

 E --&amp;gt; F[&amp;#34;Caller: Start START_PRINT retry timer&amp;#34;]
 F --&amp;gt; G[&amp;#34;Caller: print_control(START_PRINT)&amp;#34;]
 G --&amp;gt; H{&amp;#34;Callee: Is DATA_READY == 0?&amp;#34;}

 H --&amp;gt;|&amp;#34;Yes: data not ready&amp;#34;| I{&amp;#34;Total retry time reached 10s?&amp;#34;}
 I --&amp;gt;|&amp;#34;No&amp;#34;| J[&amp;#34;Caller: Wait 1s&amp;#34;]
 J --&amp;gt; G
 I --&amp;gt;|&amp;#34;Yes&amp;#34;| K[&amp;#34;Failed: Print data preparation timeout&amp;#34;]

 H --&amp;gt;|&amp;#34;No: data ready&amp;#34;| L[&amp;#34;Callee: Create high-voltage thread&amp;lt;br/&amp;gt;Return TIJET_OK&amp;#34;]
 L --&amp;gt; M[&amp;#34;Caller: Wait for PrintStarted event&amp;lt;br/&amp;gt;timeout 10s&amp;#34;]
 M --&amp;gt; N{&amp;#34;Result&amp;#34;}

 N --&amp;gt;|&amp;#34;success=true&amp;#34;| O[&amp;#34;Success: High voltage enabled&amp;lt;br/&amp;gt;Ready for printing&amp;#34;]
 N --&amp;gt;|&amp;#34;success=false&amp;#34;| P[&amp;#34;Failed: High voltage activation failed&amp;#34;]
 N --&amp;gt;|&amp;#34;Timeout&amp;#34;| Q[&amp;#34;Failed: High voltage activation timed out&amp;#34;]&lt;/pre>
&lt;h2 id="current-return-value-semantics">Current Return Value Semantics&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Location&lt;/th>
 &lt;th>Return Value&lt;/th>
 &lt;th>Current Actual Meaning&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>tijet_client_set_print_data()&lt;/code>&lt;/td>
 &lt;td>&lt;code>TIJET_OK&lt;/code>&lt;/td>
 &lt;td>Print data upload RPC completed; does NOT mean backend processing is done or &lt;code>DATA_READY=1&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>tijet_client_print_control(..., &amp;quot;START_PRINT&amp;quot;)&lt;/code>&lt;/td>
 &lt;td>&lt;code>TIJET_ERR_SERVER&lt;/code>&lt;/td>
 &lt;td>gRPC call failed, or server returned &lt;code>success=false&lt;/code>; client does not distinguish cause, retries after 1s&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>tijet_client_print_control(..., &amp;quot;START_PRINT&amp;quot;)&lt;/code>&lt;/td>
 &lt;td>&lt;code>TIJET_OK&lt;/code>&lt;/td>
 &lt;td>Server sync validation passed and created high-voltage start thread; does NOT mean high voltage is enabled&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>PrintStarted.success&lt;/code>&lt;/td>
 &lt;td>&lt;code>true&lt;/code>&lt;/td>
 &lt;td>High voltage enabled successfully, &lt;code>setDataParamReady(true)&lt;/code> called&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>PrintStarted.success&lt;/code>&lt;/td>
 &lt;td>&lt;code>false&lt;/code>&lt;/td>
 &lt;td>High voltage activation failed, &lt;code>message&lt;/code> contains count of failed heads&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="source-index">Source Index&lt;/h2>
&lt;h3 id="c-sdk">C SDK&lt;/h3>
&lt;ul>
&lt;li>&lt;code>sdk/c/src/tijet_core.cpp:342&lt;/code>: &lt;code>tijet_client_set_print_data()&lt;/code>.&lt;/li>
&lt;li>&lt;code>sdk/c/src/tijet_core.cpp:396&lt;/code>: Register event callback.&lt;/li>
&lt;li>&lt;code>sdk/c/src/tijet_core.cpp:408&lt;/code>: Start event subscription and create event thread.&lt;/li>
&lt;li>&lt;code>sdk/c/src/tijet_core.cpp:641&lt;/code>: &lt;code>tijet_client_print_control()&lt;/code>.&lt;/li>
&lt;li>&lt;code>sdk/c/src/tijet_core.cpp:655&lt;/code>: Match &lt;code>START_PRINT&lt;/code> and call &lt;code>StartPrint&lt;/code> RPC.&lt;/li>
&lt;li>&lt;code>sdk/c/include/tijet_core.h:441&lt;/code>: Print data upload C API declaration.&lt;/li>
&lt;li>&lt;code>sdk/c/include/tijet_core.h:480&lt;/code>: Event callback registration C API declaration.&lt;/li>
&lt;li>&lt;code>sdk/c/include/tijet_core.h:500&lt;/code>: Event subscription C API declaration.&lt;/li>
&lt;li>&lt;code>sdk/c/include/tijet_core.h:650&lt;/code>: Print control C API declaration.&lt;/li>
&lt;/ul>
&lt;h3 id="proto-contracts">Proto Contracts&lt;/h3>
&lt;ul>
&lt;li>&lt;code>proto/print.proto:7&lt;/code>: &lt;code>SetPrintDataRequest&lt;/code>.&lt;/li>
&lt;li>&lt;code>proto/print.proto:19&lt;/code>: &lt;code>StartPrintRequest&lt;/code>.&lt;/li>
&lt;li>&lt;code>proto/print.proto:22&lt;/code>: &lt;code>StartPrintResponse&lt;/code>.&lt;/li>
&lt;li>&lt;code>proto/event.proto:48&lt;/code>: &lt;code>PrintDataStatus&lt;/code>.&lt;/li>
&lt;li>&lt;code>proto/event.proto:53&lt;/code>: &lt;code>PrintStarted&lt;/code>.&lt;/li>
&lt;li>&lt;code>proto/tincore.proto:27&lt;/code>: &lt;code>SetPrintData&lt;/code> RPC.&lt;/li>
&lt;li>&lt;code>proto/tincore.proto:28&lt;/code>: &lt;code>StartPrint&lt;/code> RPC.&lt;/li>
&lt;li>&lt;code>proto/tincore.proto:33&lt;/code>: &lt;code>SubscribeEvents&lt;/code> RPC.&lt;/li>
&lt;/ul>
&lt;h3 id="tijet_1tincore-grpc-server">&lt;code>tijet_1/tincore&lt;/code> gRPC Server&lt;/h3>
&lt;p>The following paths are relative to the &lt;code>feature-grpc-cpp&lt;/code> branch of &lt;code>tijet_1/tincore&lt;/code>:&lt;/p></description></item><item><title>Hugo Image Optimization: Cutting an 8.6MB Background to 310KB</title><link>https://www.tainuohc.com/en/blog/2026-08-04-hugo-image-optimization/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.tainuohc.com/en/blog/2026-08-04-hugo-image-optimization/</guid><description>&lt;h2 id="1-why-optimize-images">1. Why Optimize Images&lt;/h2>
&lt;p>Images are usually the biggest culprit for slow static sites:&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Reason&lt;/th>
 &lt;th>Explanation&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Large file size&lt;/td>
 &lt;td>A 2560×1440 PNG can be 8-10MB&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Inefficient format&lt;/td>
 &lt;td>PNG is lossless — great for screenshots, bad for photos&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>No lazy loading&lt;/td>
 &lt;td>Page loads all images at once&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>No caching&lt;/td>
 &lt;td>Re-downloads on every visit&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Core idea:&lt;/strong> make the browser download smaller files.&lt;/p>
&lt;h2 id="2-hugo-image-processing-pipeline">2. Hugo Image Processing Pipeline&lt;/h2>
&lt;p>Hugo has built-in image processing that runs at build time:&lt;/p></description></item><item><title>Jenkins CI/CD Hands-On: Building an Automated Pipeline from Scratch</title><link>https://www.tainuohc.com/en/blog/2026-08-04-jenkins-cicd/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.tainuohc.com/en/blog/2026-08-04-jenkins-cicd/</guid><description>&lt;h2 id="1-why-jenkins">1. Why Jenkins&lt;/h2>
&lt;p>&lt;strong>Jenkins&lt;/strong> is an open-source CI/CD tool. Its core purpose: after code is pushed to a Git repository, automatically run &amp;ldquo;fetch code → build → test → package → deploy&amp;rdquo;.&lt;/p>
&lt;h2 id="2-installation">2. Installation&lt;/h2>
&lt;h3 id="prerequisite-java">Prerequisite: Java&lt;/h3>
&lt;p>Jenkins requires &lt;strong>Java 17 or 21&lt;/strong>. Newer versions (e.g., Java 26) fail to start:&lt;/p>
&lt;pre tabindex="0">&lt;code>Running with Java 26 ... not yet fully supported. Supported Java versions are: [17, 21]
&lt;/code>&lt;/pre>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>wget https://get.jenkins.io/war-stable/latest/jenkins.war -O /tmp/jenkins.war
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/opt/jdk-21/bin/java -jar /tmp/jenkins.war --httpPort&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">9090&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="recommended-docker">Recommended: Docker&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>docker run -d &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> --name jenkins &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -p 9090:8080 &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -v jenkins_home:/var/jenkins_home &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> jenkins/jenkins:lts-jdk21
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="3-initial-setup">3. Initial Setup&lt;/h2>
&lt;p>Visit &lt;code>http://localhost:9090&lt;/code>:&lt;/p></description></item><item><title>Secure Boot and Firmware Signing: A Practical Guide for Industrial IoT Devices</title><link>https://www.tainuohc.com/en/blog/2026-07-22-industrial-security/</link><pubDate>Wed, 22 Jul 2026 00:00:00 +0000</pubDate><guid>https://www.tainuohc.com/en/blog/2026-07-22-industrial-security/</guid><description>&lt;h2 id="1-why-bother">1. Why Bother?&lt;/h2>
&lt;p>In industrial control, a compromised firmware image isn&amp;rsquo;t just a data breach — it&amp;rsquo;s a physical safety hazard. A tampered firmware could disable watchdog timers, override thermal shutdown thresholds, or silently exfiltrate proprietary motion-control parameters.&lt;/p>
&lt;p>Yet most industrial IoT devices ship with JTAG left open and firmware images verified by nothing more than a CRC32 checksum. CRC32 catches bit flips; it does nothing against a motivated attacker.&lt;/p></description></item><item><title>Taming Linux Jitter: Achieving Sub-50µs Cycle Times with PREEMPT_RT and Xenomai</title><link>https://www.tainuohc.com/en/blog/2026-07-21-linux-rt/</link><pubDate>Tue, 21 Jul 2026 00:00:00 +0000</pubDate><guid>https://www.tainuohc.com/en/blog/2026-07-21-linux-rt/</guid><description>&lt;h2 id="1-the-challenge-soft-plc-on-a-30-soc">1. The Challenge: Soft PLC on a $30 SoC&lt;/h2>
&lt;p>Tainuo&amp;rsquo;s next-generation motion controller needed to run a soft-PLC runtime doing 20 kHz closed-loop PID control on six servo axes simultaneously. The target hardware: a $30 Allwinner A40i quad-core Cortex-A7. No FPGA, no dedicated DSP — just plain ARM cores running Linux.&lt;/p>
&lt;p>The problem was obvious: standard Linux, even with &lt;code>SCHED_FIFO&lt;/code>, routinely exhibits 200–500µs scheduling jitter on this class of hardware. For a 20 kHz control loop (50µs period), that&amp;rsquo;s a non-starter. Missing the deadline means a phase error that accumulates and destabilizes the mechanical system.&lt;/p></description></item><item><title>FPGA-Accelerated Visual Inspection: How We Cut Latency from 200ms to 8µs</title><link>https://www.tainuohc.com/en/blog/2026-07-20-fpga-acceleration/</link><pubDate>Mon, 20 Jul 2026 00:00:00 +0000</pubDate><guid>https://www.tainuohc.com/en/blog/2026-07-20-fpga-acceleration/</guid><description>&lt;h2 id="1-the-problem-software-based-visual-inspection-hits-a-wall">1. The Problem: Software-Based Visual Inspection Hits a Wall&lt;/h2>
&lt;p>In Tainuo&amp;rsquo;s PCB printing pipeline, one of the most critical steps is real-time solder-paste inspection. Each board passing through the production line has less than 50ms total for defect detection — any slower and the line stalls.&lt;/p>
&lt;p>For years we ran a GPU-accelerated OpenCV pipeline on an x86 edge node. It worked — mostly. But under peak load with high-resolution 8K images, the pipeline occasionally hit 180–220ms per frame. That 180ms of jitter meant missed defects, false passes, and production-line backpressure. The root cause was architectural: a general-purpose GPU pipeline, no matter how optimized, introduces unpredictable scheduling latency and PCIe transfer overhead.&lt;/p></description></item><item><title>Rust on Bare Metal: Rewriting Our Bootloader Without a Single Page Fault</title><link>https://www.tainuohc.com/en/blog/2026-07-19-rust-embedded/</link><pubDate>Sun, 19 Jul 2026 00:00:00 +0000</pubDate><guid>https://www.tainuohc.com/en/blog/2026-07-19-rust-embedded/</guid><description>&lt;h2 id="1-the-bootloader-problem">1. The Bootloader Problem&lt;/h2>
&lt;p>Tainuo&amp;rsquo;s industrial control boards ship with an MCU that manages power sequencing, watchdog timers, and over-the-air (OTA) firmware updates. The bootloader — roughly 8,000 lines of C — had been stable for years, but &amp;ldquo;stable&amp;rdquo; in the embedded sense means &amp;ldquo;the bugs are known and we have workarounds.&amp;rdquo;&lt;/p>
&lt;p>Every 6–9 months, a new OTA edge case would surface: a buffer overflow during firmware image verification, an off-by-one in the flash erase routine, a use-after-free in the CAN bus command dispatcher. Each of these bugs required a full recall-cycle OTA fix, which on a deployed fleet of industrial controllers is a multi-week ordeal.&lt;/p></description></item><item><title>From Proprietary Protocols to gRPC: A Major Leap in Tainuo's Heterogeneous Industrial Control Communication Architecture</title><link>https://www.tainuohc.com/en/blog/2026-07-18-grpc-migration/</link><pubDate>Sat, 18 Jul 2026 00:00:00 +0000</pubDate><guid>https://www.tainuohc.com/en/blog/2026-07-18-grpc-migration/</guid><description>&lt;h2 id="1-why-we-had-to-abandon-our-legacy-proprietary-protocols">1. Why We Had to Abandon Our Legacy Proprietary Protocols&lt;/h2>
&lt;p>For a long time, Tainuo&amp;rsquo;s industrial control boards communicated with the host computer and print control terminals using self-built proprietary protocols. These protocols were indeed lightweight, flexible, and easy to modify during single-category development. However, as Tainuo gradually moves toward Industry 5.0 standards, our system architecture is evolving into a &amp;ldquo;universal industrial-grade heterogeneous computing and intelligent control platform.&amp;rdquo;&lt;/p>
&lt;p>Against this backdrop, the legacy proprietary protocols exposed three irreconcilable pain points: prohibitively high multi-language SDK maintenance costs (C#, C++/Rust heterogeneous endpoints required manual parser rewriting), lack of strong type safety boundaries, and difficulty in flexibly extending RPC services across heterogeneous systems.&lt;/p></description></item></channel></rss>