| 功能 | 說明 |
|---|---|
| 多協定 | TCP Server、TCP Client、UDP、Modbus TCP Master — 每個 port 獨立設定 |
| Modbus 輪詢 | 內建 FluentModbus master — 以可設定的間隔輪詢 ICP DAS / 一般 Modbus slave,結果以原生 EdgeLink 訊息進入路由管線 |
| 訊息路由 | 透過 SourceProtocolId 自動橋接各 port |
| Mask 系統 | 針對 IoT 韌體輸出的自訂協定解析與轉換規則 |
| 即時監控 | 每個 port 的 SSE 串流日誌,支援關鍵字搜尋 |
| Web 管理介面 | 瀏覽器端管理 — 卡片式 port 檢視,免前端建置 |
| 裝置識別 | TCP Server 與 UDP 上逐連線的裝置 ID 追蹤,於 WebUI 與 SDK 中呈現 |
| HTTPS | 自動產生含所有本機 IP SAN 的自簽憑證 |
| 安全性 | PBKDF2 密碼雜湊、工作階段持久化、HttpOnly cookie |
| 檔案日誌 | 每日輪替日誌檔,保留 7 天 |
| 用戶端 SDK | Unity(UPM,POCO + MonoBehaviour)、Arduino、C#(.NET 6)、Python(asyncio)、JavaScript(Node.js) |
EdgeLink Server 位於控制系統的核心,扮演扇出樞紐(fan-out hub):數值處理程式透過 Modbus TCP 輪詢現場硬體、計算出應用數值,再經由 SDK 用戶端推送進 EdgeLink。EdgeLink 對每筆訊息套用 Mask,並依 SourceProtocolId 路由,透過 UDP / TCP 一次送達所有下游消費端。
此參考部署是一套雲梯消防車水炮訓練平台。AX-5 運動控制器(霍爾搖桿按鈕 / 踏板 / 緊急停止、伺服驅動器)與三軸平台訊號擷取模組(16× DI 絕對編碼器、4× AI 雙軸搖桿)由數值處理程式讀取 —— 進行 Gray code→角度轉換、低通濾波、搖桿 0.25–4.75 V / 雙軸 5 V 檢查、姿態 + 奇異點偵測 + 逆向運動學 —— 再餵給 EdgeLink。EdgeLink 將處理後的數值扇出到 Unity 3D 與另外兩台電腦。
- Windows 10 / 11 x64
- 免安裝 .NET Runtime(self-contained 自帶執行環境)
- 從 Releases 下載
EdgeLink-Server-v2.1.1-win-x64.zip - 解壓縮 zip
- 執行
EdgeLinkServer.exe - 開啟瀏覽器前往
https://localhost:8443- 接受自簽憑證警告(點 進階 → 繼續前往)
- 預設密碼:
admin— 登入後請立即修改
| 分頁 | 說明 |
|---|---|
| Ports | 新增 / 移除 / 切換 port,即時監控連線狀態與流量 |
| Mask | 建立 / 編輯 / 刪除 Mask 定義 — 欄位解析規則與輸出範本 |
| 系統日誌 | 檢視伺服器事件日誌,支援關鍵字過濾 |
啟動伺服器後,完整文件位於 /manual。
EdgeLinkServer.exe [options]
--port <n> HTTP 埠(預設:8081)
--no-https 停用 HTTPS(預設啟用 HTTPS)
--https-port <n> HTTPS 埠(預設:8443)
環境變數:
EDGELINK_PORT, EDGELINK_HTTPS, EDGELINK_HTTPS_PORT
優先順序:命令列參數 > 環境變數 > 預設值
內建的 Modbus TCP master 可輪詢任何 Modbus slave(ICP DAS ET-7xxx、一般 PLC、ModRSsim2 等模擬器),並將結果以標準訊息注入 EdgeLink 路由管線 —— 下游消費端(Unity / 儀表板 / TCP Client)無從得知、也不在乎來源是 Modbus 還是原始 TCP。
於 WebUI 新增 port → + Add Port → 選擇 Modbus TCP Master:
| 欄位 | 值 |
|---|---|
| Slave IP | 192.168.1.10(本機模擬器用 127.0.0.1) |
| Modbus Port | 502(預設) |
| Slave ID | 1 |
| Polling Interval (ms) | 100(10 Hz) |
| Device ID | WaterCannon01 — 成為合成訊息中的 id: 欄位 |
| Registers | JSON 陣列(見下方) |
暫存器對應 JSON:
[
{ "name": "yaw", "functionCode": 2, "startAddress": 0, "quantity": 8, "dataType": "bits" },
{ "name": "pitch", "functionCode": 2, "startAddress": 8, "quantity": 8, "dataType": "bits" },
{ "name": "joyx", "functionCode": 4, "startAddress": 0, "quantity": 1, "dataType": "uint16", "scale": 0.001 },
{ "name": "joyy", "functionCode": 4, "startAddress": 1, "quantity": 1, "dataType": "uint16", "scale": 0.001 }
]每個輪詢間隔,EdgeLink 會發出如下的合成訊息:
id:WaterCannon01;yaw:128;pitch:64;joyx:2.5;joyy:1.802
| FunctionCode | Modbus 操作 | 用途 |
|---|---|---|
1 |
Read Coils | 輸出位元(繼電器狀態、LED) |
2 |
Read Discrete Inputs | 開關、按鈕、GrayCode 編碼器位元 |
3 |
Read Holding Registers | 一般 16-bit,可讀寫 |
4 |
Read Input Registers | 類比輸入(搖桿、感測器電壓) |
| dataType | 位元組 | 說明 |
|---|---|---|
bit |
1 bit | 單一 Coil / DI(quantity 必須為 1) |
bits |
N bits | 將 quantity 個位元打包成無號整數(LSB first) |
uint16 / int16 |
2 | 一個暫存器 |
uint32 / int32 |
4 | 兩個暫存器,big-endian |
float32 |
4 | 兩個暫存器,IEEE 754 big-endian |
選用的 scale 與 offset 會套用到暫存器讀值:output = raw * scale + offset。
若在無實體硬體的情況下開發,modbus_slave_sim.py 提供一個以 pymodbus 為基礎的 Modbus TCP slave,可產生合成的 GrayCode + 正弦波資料:
pip install pymodbus==3.6.6
python modbus_slave_sim.py
# 監聽 127.0.0.1:5020Base URL:https://<host>:8443
除認證外,所有 /api/* 端點都需要工作階段 cookie。互動式文件位於 /docs。
| 分類 | 方法 | 路徑 | 說明 |
|---|---|---|---|
| Auth | POST |
/api/auth/login |
登入 — body:{"password":"..."} |
| Auth | POST |
/api/auth/logout |
登出 |
| Auth | GET |
/api/auth/status |
查詢工作階段狀態 |
| Auth | POST |
/api/auth/change-password |
修改密碼 |
| Ports | GET |
/api/ports |
列出所有 port |
| Ports | POST |
/api/ports |
新增 port |
| Ports | PUT |
/api/ports/{id} |
更新 port |
| Ports | DELETE |
/api/ports |
刪除 port |
| Ports | POST |
/api/ports/{id}/enabled |
切換 port 啟用狀態 |
| Ports | GET |
/api/ports/{id}/clients |
列出已連線的 TCP client |
| Masks | GET |
/api/masks |
列出所有 mask ID |
| Masks | POST |
/api/masks |
新增 mask |
| Masks | GET |
/api/masks/{maskId} |
取得 mask 定義 |
| Masks | PUT |
/api/masks/{maskId} |
更新 mask |
| Masks | DELETE |
/api/masks/{maskId} |
刪除 mask |
| Monitor | GET |
/api/monitor-stream |
SSE 即時訊息串流 |
| Monitor | POST |
/api/monitor/port |
設定監控 port |
| Logs | GET |
/api/logs |
系統日誌(cursor 分頁) |
| Settings | GET |
/api/settings/export |
以 JSON 匯出所有設定 |
| Settings | POST |
/api/settings/import |
匯入設定 JSON |
EdgeLink Unity SDK 讓 Unity 應用程式接收 EdgeLink Server 轉發的資料。支援 TCP、TCP Listener 與 UDP 三種連線模式,並在執行期自動取得 Mask。
在 Unity 中 → Window → Package Manager → + → Add package from git URL:
https://github.com/extrakyo-io/EdgeLink-Server.git?path=SDK/Unity/Package
接著透過 Package Manager → EdgeLink SDK → Samples 匯入 Basic Example 範例。
| 風格 | 類別 | 設定方式 |
|---|---|---|
| MonoBehaviour(拖到 GameObject,以 Inspector 設定) | EdgeLinkManager |
掛到任一 GameObject,填入 Inspector 欄位 |
| POCO(以建構子設定,執行期決定 URL/Host/Port) | EdgeLinkBridge |
new EdgeLinkBridge(...) — 自行驅動生命週期 |
一般情況(靜態設定)用 EdgeLinkManager。當 URL / Host / Port 必須在執行期才決定(大廳分配的 IP、設定檔、多實例情境)時,用 EdgeLinkBridge。
將 EdgeLinkManager 掛到任一 GameObject,並在 Inspector 中設定:
| 欄位 | 說明 |
|---|---|
| Server URL | 供執行期取得 Mask 的 EdgeLink Server 位址 |
| Password | 登入密碼 |
| Mask ID | 用於欄位解析的 Mask 名稱 |
| Protocol | TCP / TCPListener / UDP |
| TCP Host / Port | (TCP 模式)EdgeLink Server IP 與埠 |
| Listen Port | (TCPListener 模式)Unity 監聽的本機埠 |
| UDP Local Port | (UDP 模式)本機 UDP 埠 |
| Device Id Key | 訊息中用來識別裝置的欄位名(例如 id)。留空則停用逾時追蹤。 |
| Device Timeout (s) | 多少秒未收到訊息即視為裝置離線(0 = 停用) |
純 C# 類別 — 以參數建構,由你自己的 MonoBehaviour 驅動生命週期:
using System.Collections;
using UnityEngine;
using EdgeLink;
public class RuntimeConfigured : MonoBehaviour
{
EdgeLinkBridge _bridge;
IEnumerator Start()
{
// URL / Host / Port 可來自任何地方 — 大廳、config.json、PlayerPrefs …
_bridge = new EdgeLinkBridge(
serverUrl: PlayerPrefs.GetString("edgelink.url"),
tcpHost: PlayerPrefs.GetString("edgelink.host"),
tcpPort: PlayerPrefs.GetInt ("edgelink.port", 9001));
// 或使用完整 Config(TCPListener / UDP / 裝置逾時 / 自訂 mask …):
// _bridge = new EdgeLinkBridge(new EdgeLinkBridge.Config {
// ServerUrl = "...", Protocol = EdgeLinkBridge.Protocol.TCPListener,
// TcpListenPort = 9001, DeviceTimeoutSeconds = 15,
// });
_bridge.OnMessage += msg => Debug.Log(msg);
_bridge.OnDeviceStatus += (online, ep, id) => Debug.Log($"{id}@{ep} {online}");
yield return _bridge.InitializeCoroutine();
}
void Update() => _bridge?.Tick();
void OnDestroy() => _bridge?.Dispose();
}EdgeLinkManager 與 EdgeLinkBridge 提供相同的事件介面與 Raw / Get(key) 存取子。
using UnityEngine;
public class Example : MonoBehaviour
{
EdgeLinkManager edgeLink;
string lastRaw;
void Start()
{
edgeLink = GetComponent<EdgeLinkManager>();
}
void Update()
{
if (edgeLink.Raw == lastRaw) return;
lastRaw = edgeLink.Raw;
string temp = edgeLink.Get("temp");
string humid = edgeLink.Get("humid");
Debug.Log($"Temp:{temp} Humid:{humid}");
}
}| 成員 | 型別 | 說明 |
|---|---|---|
Raw |
string |
最新的原始訊息字串(未解析) |
Get(key) |
string |
依欄位名取得最新解析值,找不到則為 null |
EdgeLinkManager 提供兩種互補的斷線偵測機制:
void Start()
{
edgeLink = GetComponent<EdgeLinkManager>();
// 當 EdgeLink Server 偵測到 TCP 連線開啟/關閉時觸發(斷電約需 15 秒)
edgeLink.OnDeviceStatus += (connected, endpoint) =>
Debug.Log(connected ? $"Online: {endpoint}" : $"Offline: {endpoint}");
// 當某個裝置 ID 超過 Device Timeout 秒未送資料時觸發
edgeLink.OnDeviceTimeout += id => Debug.LogWarning($"{id} timed out");
edgeLink.OnDeviceReconnected += id => Debug.Log($"{id} reconnected");
}| 事件 | 觸發時機 | 識別依據 |
|---|---|---|
OnDeviceStatus |
EdgeLink Server 偵測到 TCP 開啟/關閉 | IP 位址 |
OnDeviceTimeout |
超過 Device Timeout (s) 未收到訊息 |
裝置 ID 欄位 |
OnDeviceReconnected |
逾時後再次收到訊息 | 裝置 ID 欄位 |
斷電情境: EdgeLink 偵測到連續 3 次 PING 未回應(約 15 秒)後觸發
OnDeviceStatus(false, ...)。在你設定的逾時時間內仍無新訊息時,OnDeviceTimeout也會觸發。
| 情境 | OnDeviceStatus | OnDeviceTimeout |
|---|---|---|
| 裝置斷電(TCP) | ✓ 約 15 秒 | ✓ 依逾時設定 |
| 裝置在 UDP 上消失 | ✗ 無連線可偵測 | ✓ |
| TCP 存活但韌體當掉(無資料) | ✗ 連線正常 | ✓ |
| 多裝置共用一條 TCP 連線 | ✗ 只能偵測連線中斷 | ✓ 可識別每個裝置 |
建議兩者並用以完整涵蓋:OnDeviceStatus 捕捉 TCP 層的中斷;OnDeviceTimeout 捕捉無聲失效,且適用於所有協定。
EdgeLink Arduino Library 讓 ESP32 / ESP8266 / Arduino 裝置連上 EdgeLink Server。PING/PONG keepalive 會自動處理 — 無需額外程式碼。
方式 A — ZIP 匯入:
- 下載
SDK/Arduino/EdgeLink.zip - Arduino IDE → Sketch → Include Library → Add .ZIP Library…
方式 B — 手動:
將 SDK/Arduino/EdgeLink/ 資料夾複製到你的 Arduino libraries/ 目錄。
#include <WiFi.h>
#include <EdgeLink.h>
WiFiClient wifiClient;
EdgeLinkTCP edgelink(wifiClient);
void onMessage(const String& msg) {
Serial.println(msg); // 來自後端的回應
}
void setup() {
WiFi.begin("your-ssid", "your-password");
while (WiFi.status() != WL_CONNECTED) delay(500);
edgelink.onMessage(onMessage);
edgelink.setAutoReconnect(true, 5000);
edgelink.begin("192.168.1.100", 9001); // EdgeLink TCP Server 埠
}
void loop() {
edgelink.loop(); // 必須呼叫 — 處理 PING/PONG 與接收
static uint32_t t = 0;
if (millis() - t >= 3000 && edgelink.isConnected()) {
edgelink.send("id:ESP32_01;temp:25.3;humidity:60.0");
t = millis();
}
}#include <WiFi.h>
#include <WiFiUdp.h>
#include <EdgeLink.h>
WiFiUDP wifiUdp;
EdgeLinkUDP edgelink(wifiUdp);
void onMessage(const String& msg, IPAddress ip, uint16_t port) {
Serial.println(msg);
}
void setup() {
WiFi.begin("your-ssid", "your-password");
while (WiFi.status() != WL_CONNECTED) delay(500);
edgelink.begin(4210); // 本機接收埠
edgelink.onMessage(onMessage);
}
void loop() {
edgelink.loop();
static uint32_t t = 0;
if (millis() - t >= 3000) {
edgelink.send("192.168.1.100", 9002, "id:ESP32_01;temp:25.3;humidity:60.0");
t = millis();
}
}| 類別 | 方法 | 說明 |
|---|---|---|
EdgeLinkTCP |
begin(host, port) |
連上 EdgeLink TCP Server 埠 |
loop() |
必須在 loop() 中呼叫 — 處理 PING/PONG 與接收 |
|
send(msg) |
送出訊息(自動附加換行) | |
onMessage(cb) |
收到訊息的回呼(已濾除 EDGELINK_*) | |
isConnected() |
回傳連線狀態 | |
setAutoReconnect(enable, ms) |
斷線時自動重連(預設:啟用,5000 ms) | |
EdgeLinkUDP |
begin(localPort = 0) |
開始監聽本機 UDP 埠(0 = 僅送出) |
loop() |
必須在 loop() 中呼叫 — 接收封包 |
|
send(host, port, msg) |
送出 UDP 封包到 EdgeLink | |
onMessage(cb) |
回呼帶 (msg, remoteIP, remotePort) |
EdgeLink C# SDK 以 .NET 6+ 為目標,可用於任何非 Unity 的 .NET 應用程式(console、WPF、ASP.NET Core 等)。無第三方相依。
將 SDK/CSharp/ 資料夾複製進你的方案並加入專案參考,或編譯為類別庫後參考該 DLL。
using EdgeLink;
using var client = new EdgeLinkClient("192.168.1.100", 9001);
client.OnConnected += () => Console.WriteLine("Connected");
client.OnDisconnected += () => Console.WriteLine("Disconnected");
client.OnMessage += msg => Console.WriteLine($"Received: {msg}");
client.SetAutoReconnect(true, delayMs: 5000);
await client.ConnectAsync();
await client.SendAsync("id:DOTNET_01;temp:25.3;humidity:60.0");| 類別 | 成員 | 說明 |
|---|---|---|
EdgeLinkClient |
ConnectAsync() |
連線並在背景啟動讀取迴圈 |
SendAsync(msg) |
送出訊息 | |
IsConnected |
連線狀態 | |
SetAutoReconnect(enable, delayMs) |
斷線時自動重連(預設:啟用,5000 ms) | |
OnMessage / OnConnected / OnDisconnected / OnError |
事件 | |
TryDequeue(out msg) |
以輪詢取代事件的替代方式 | |
EdgeLinkTcpListener |
Start() |
接受進來的 TCP 連線 |
Stop() |
停止監聽器 | |
OnMessage / OnConnected / OnDisconnected / OnError |
事件 | |
EdgeLinkUdpClient |
Start() |
綁定本機埠並接收封包 |
OnMessage / OnError |
事件 | |
EdgeLinkUdpSender |
SendAsync(host, port, msg) |
送出 UDP 封包 |
EdgeLink Python SDK 需要 Python 3.10+,僅使用標準函式庫(asyncio、socket)。
pip install SDK/Python # 本機安裝
# 或直接把 edgelink/ 套件複製進你的專案import asyncio
from edgelink import EdgeLinkClient
async def main():
client = EdgeLinkClient("192.168.1.100", 9001)
client.on_connected(lambda: print("Connected"))
client.on_message(lambda msg: print(f"Received: {msg}"))
client.set_auto_reconnect(True, delay=5.0)
await client.connect()
while True:
await asyncio.sleep(3)
if client.is_connected:
await client.send("id:PYTHON_01;temp:25.3;humidity:60.0")
asyncio.run(main())| 類別 | 成員 | 說明 |
|---|---|---|
EdgeLinkClient |
connect() |
連線並啟動讀取迴圈(coroutine) |
send(msg) |
送出訊息(coroutine) | |
is_connected |
連線狀態 | |
set_auto_reconnect(enable, delay) |
斷線時自動重連(預設:啟用,5 秒) | |
on_message / on_connected / on_disconnected / on_error |
註冊回呼 | |
try_dequeue() |
以輪詢取代回呼的替代方式 | |
disconnect() |
關閉連線(coroutine) | |
EdgeLinkTcpListener |
start() |
開始監聽(coroutine) |
stop() |
停止監聽器(coroutine) | |
on_message / on_connected / on_disconnected / on_error |
註冊回呼 | |
EdgeLinkUdpClient |
start() |
綁定並開始接收(coroutine) |
on_message / on_error |
註冊回呼 | |
EdgeLinkUdpSender |
send(host, port, msg) |
送出 UDP 封包 |
send_async(host, port, msg) |
送出 UDP 封包(coroutine) |
EdgeLink JavaScript SDK 以 Node.js 18+ 為目標,僅使用內建模組(net、dgram、events)。
# 將 SDK/JavaScript/ 複製進你的專案,然後:
const { EdgeLinkClient } = require("./edgelink/src");const { EdgeLinkClient } = require("./edgelink/src");
const client = new EdgeLinkClient("192.168.1.100", 9001);
client.on("connected", () => console.log("Connected"));
client.on("message", (msg) => console.log("Received:", msg));
client.on("error", (err) => console.error("Error:", err.message));
client.setAutoReconnect(true, 5000);
client.connect();
setInterval(() => {
if (client.isConnected)
client.send("id:NODE_01;temp:25.3;humidity:60.0");
}, 3000);| 類別 | 成員 | 說明 |
|---|---|---|
EdgeLinkClient |
connect() |
連線並開始讀取 |
send(msg) |
送出訊息 | |
isConnected |
連線狀態 | |
setAutoReconnect(enable, delayMs) |
斷線時自動重連(預設:啟用,5000 ms) | |
disconnect() |
銷毀 socket | |
事件:"connected" / "disconnected" / "message" / "error" |
EventEmitter 事件 |
|
EdgeLinkTcpListener |
start() |
啟動 TCP 伺服器 |
stop() |
停止 TCP 伺服器 | |
事件:"connected" / "disconnected" / "message" / "error" |
EventEmitter 事件 |
|
EdgeLinkUdpClient |
start() |
綁定並接收 UDP 封包 |
stop() |
停止接收 | |
事件:"message" / "error" |
EventEmitter 事件 |
|
EdgeLinkUdpSender |
send(host, port, msg) |
送出 UDP 封包(回傳 Promise) |
close() |
關閉 socket |
EdgeLink-Server/
├── Server/
│ ├── Infrastructure/ # AppConfig、AppLogger、AppPaths、CertificateHelper
│ ├── NetworkServer/
│ │ ├── Base/ # Connector 基底、models(PortData…)
│ │ ├── TCP/ # TCPServerConnector、TCPClientConnector
│ │ ├── Udp/ # UdpConnector
│ │ ├── Router/ # NetworkMessageRouter
│ │ └── Services/ # PortManager、PortDataStorageService
│ ├── WebApi/ # HttpApiServer、Auth/Port/Mask/Monitor handlers
│ ├── WebUI/ # 前端 HTML/CSS/JS(index、manual、docs)
│ └── Program.cs # 進入點
└── SDK/
├── Unity/
│ └── Package/ # UPM 套件(Runtime + Editor + Samples~)
├── Arduino/
│ └── EdgeLink/ # Arduino 函式庫(TCP + UDP,自動處理 PING/PONG)
├── CSharp/ # .NET 6 類別庫(TCP client/listener + UDP)
├── Python/ # Python 3.10+ 套件,使用 asyncio(TCP + UDP)
└── JavaScript/ # Node.js 18+ 套件,使用 net/dgram(TCP + UDP)
| 版本 | 變更內容 |
|---|---|
| v2.2.0 | 通用二進位解析 Mask(BinarySpec:offset / 型別 u8–f64 / bit / bitrange / const、little/big-endian、discriminator 分派、長度驗證)— 將固定版面的二進位 UDP 解碼為 KV;WebUI 二進位版面編輯器 + hex 解碼預覽;POST /api/masks/preview-binary。Monitor SSE 前端節流(高頻率 port 不再卡住瀏覽器) |
| v2.1.3 | Unity SDK:連線資源清理 — 避免 socket / CTS 洩漏 |
| v2.1.2 | Unity SDK:新增 EdgeLinkBridge.cs.meta;套件版本更新 |
| v2.1.1 | Unity SDK:抽出 EdgeLinkBridge POCO,以建構子注入 URL/Host/Port(MonoBehaviour EdgeLinkManager 仍作為輕薄包裝);README 補上 Modbus + POCO 文件 |
| v2.1.0 | Modbus TCP Master port 類型(FluentModbus,FC 01/02/03/04,scale/offset);WebUI 卡片式版面翻新;4× fire-and-forget 任務抑制;UDP port 清單輪詢保留使用者選取 |
| v2.0.1 | Unity SDK:OnDeviceStatus 新增裝置 ID 參數;Arduino AsyncUDP 範例;Mono 上 HttpClientHandler 憑證回退至 ServicePointManager |
| v2.0.0 | TCP Server / UDP 上逐連線的裝置識別;PING/PONG TCP keepalive;EDGELINK_STATUS:CONNECTED/DISCONNECTED 事件隨裝置 ID 轉發;WebUI 顯示每個 port 已識別的裝置 |
| v1.1.0 | Unity SDK — 裝置連線/斷線偵測(OnDeviceStatus、OnDeviceTimeout、OnDeviceReconnected);修正 STATUS 端點改用穩定 IP;新增 C#、Python、JavaScript SDK |
| v1.0.0 | 首次發行 — .NET 8、預設 HTTPS、PBKDF2、工作階段持久化、輪替日誌、CORS、Unity SDK |
EdgeLink Server 採用 MIT 授權 發行 — 可自由用於個人、商業與閉源用途,包含專有整合與 OEM 內嵌,無 copyleft 義務。只需保留版權聲明即可。
單一作者宣告與第三方致謝請見 NOTICE。
Extrakyo · MIT 授權