旧版接口(已弃用)
已弃用:本文档涵盖使用 InterfaceBaseClass 和 InterfaceThreadedBaseClass 的旧版接口系统。这些方法已弃用,不应在新项目中使用。所有新接口开发请使用 FastInterface。
本指南提供了 realvirtual 在引入 FastInterface 之前使用的旧版接口系统的参考文档。如果您有使用这些已弃用基类构建的现有接口,建议迁移到 FastInterface 以获得更好的性能、可靠性和可维护性。
旧版接口基类
旧版系统提供了两个用于自定义接口开发的基类:
InterfaceBaseClass(单线程)
最简单的方法,适用于在 Unity 主线程上运行的基本协议通信:
using UnityEngine;
namespace realvirtual
{
public class MyLegacyInterface : InterfaceBaseClass
{
[Header("Connection Settings")]
public string ServerIP = "192.168.1.100";
public int Port = 502;
private bool isConnected = false;
public override void OpenInterface()
{
try
{
// 连接外部系统
ConnectToServer(ServerIP, Port);
isConnected = true;
Debug.Log("Interface connected");
}
catch (System.Exception ex)
{
Debug.LogError($"Connection failed: {ex.Message}");
isConnected = false;
}
}
public override void CloseInterface()
{
try
{
DisconnectFromServer();
isConnected = false;
Debug.Log("Interface disconnected");
}
catch (System.Exception ex)
{
Debug.LogError($"Disconnect error: {ex.Message}");
}
}
public override void CommunicationUpdate()
{
if (!isConnected) return;
try
{
// 读取 Unity 信号并发送到外部系统
var inputValues = ReadInputSignals();
SendToExternalSystem(inputValues);
// 从外部系统读取并更新 Unity 信号
var receivedValues = ReceiveFromExternalSystem();
WriteOutputSignals(receivedValues);
}
catch (System.Exception ex)
{
Debug.LogError($"Communication error: {ex.Message}");
isConnected = false;
}
}
public override bool GetCommunicationState()
{
return isConnected;
}
}
}
主要特征:
- 在 Unity 主线程上运行
- 简单的同步操作
- 通信缓慢时可能导致 Unity 帧率下降
- 仅适用于非常快的协议或测试
InterfaceThreadedBaseClass(多线程)
更高级的方法,在后台线程上运行通信:
using UnityEngine;
using System.Threading;
namespace realvirtual
{
public class MyThreadedInterface : InterfaceThreadedBaseClass
{
[Header("Connection Settings")]
public string ServerIP = "192.168.1.100";
public int Port = 502;
private bool isConnected = false;
private object connectionLock = new object();
protected override void ThreadMethod()
{
while (!shouldStop)
{
try
{
if (!isConnected)
{
AttemptConnection();
}
else
{
PerformCommunication();
}
Thread.Sleep(UpdateCycleMs);
}
catch (System.Exception ex)
{
Debug.LogError($"Thread error: {ex.Message}");
lock (connectionLock)
{
isConnected = false;
}
Thread.Sleep(1000); // 重试前等待
}
}
}
private void AttemptConnection()
{
try
{
ConnectToServer(ServerIP, Port);
lock (connectionLock)
{
isConnected = true;
}
Debug.Log("Interface connected");
}
catch (System.Exception ex)
{
Debug.LogError($"Connection failed: {ex.Message}");
}
}
private void PerformCommunication()
{
// 读取 Unity 信号(线程安全)
var inputValues = GetInputValuesThreadSafe();
SendToExternalSystem(inputValues);
// 从外部系统读取并更新 Unity 信号
var receivedValues = ReceiveFromExternalSystem();
SetOutputValuesThreadSafe(receivedValues);
}
public override bool GetCommunicationState()
{
lock (connectionLock)
{
return isConnected;
}
}
protected override void OnDestroy()
{
CloseInterface();
base.OnDestroy();
}
}
}
主要特征:
- 在后台线程上运行
- 比单线程方法性能更好
- 需要手动线程管理
- 错误处理和同步复杂
- 容易出现线程问题和竞态条件
旧版信号管理
旧版系统使用不同的信号访问方法:
读取输入信号(旧版)
// InterfaceBaseClass 方法
private Dictionary<string, object> ReadInputSignals()
{
var values = new Dictionary<string, object>();
// 手动信号发现和读取
var signals = GetComponentsInChildren<Signal>();
foreach (var signal in signals)
{
if (signal.Direction == SIGNALDIRECTION.INPUT)
{
values[signal.name] = signal.GetValue();
}
}
return values;
}
// InterfaceThreadedBaseClass 方法
private Dictionary<string, object> GetInputValuesThreadSafe()
{
var values = new Dictionary<string, object>();
lock (signalLock)
{
foreach (var signal in inputSignals)
{
values[signal.Name] = signal.ThreadSafeValue;
}
}
return values;
}
写入输出信号(旧版)
// InterfaceBaseClass 方法
private void WriteOutputSignals(Dictionary<string, object> values)
{
var signals = GetComponentsInChildren<Signal>();
foreach (var signal in signals)
{
if (signal.Direction == SIGNALDIRECTION.OUTPUT && values.ContainsKey(signal.name))
{
signal.SetValue(values[signal.name]);
}
}
}
// InterfaceThreadedBaseClass 方法
private void SetOutputValuesThreadSafe(Dictionary<string, object> values)
{
lock (signalLock)
{
foreach (var kvp in values)
{
if (outputSignals.ContainsKey(kvp.Key))
{
outputSignals[kvp.Key].ThreadSafeValue = kvp.Value;
}
}
}
}
旧版系统的常见问题
线程问题
旧版线程方法容易出现各种线程问题:
// 有问题的:从后台线程直接访问 Unity API
protected override void ThreadMethod()
{
while (!shouldStop)
{
// 这会导致错误 - Unity API 不是线程安全的
Debug.Log("This will crash!");
transform.position = Vector3.zero; // 这会崩溃!
Thread.Sleep(UpdateCycleMs);
}
}
手动信号管理
旧版接口需要手动信号发现和管理:
private List<Signal> inputSignals = new List<Signal>();
private List<Signal> outputSignals = new List<Signal>();
private void DiscoverSignals()
{
// 手动信号发现 - 容易出错
var allSignals = GetComponentsInChildren<Signal>();
foreach (var signal in allSignals)
{
if (signal.Direction == SIGNALDIRECTION.INPUT)
inputSignals.Add(signal);
else if (signal.Direction == SIGNALDIRECTION.OUTPUT)
outputSignals.Add(signal);
}
}
无自动重连
旧版接口需要手动重连逻辑:
protected override void ThreadMethod()
{
while (!shouldStop)
{
try
{
if (!isConnected)
{
// 手动重连尝试
reconnectAttempts++;
if (reconnectAttempts > maxReconnectAttempts)
{
Thread.Sleep(5000); // 等待更长时间后重试
reconnectAttempts = 0;
}
AttemptConnection();
}
else
{
PerformCommunication();
reconnectAttempts = 0; // 通信成功后重置
}
}
catch (Exception ex)
{
Debug.LogError($"Communication error: {ex.Message}");
isConnected = false;
}
Thread.Sleep(UpdateCycleMs);
}
}
迁移到 FastInterface 的指南
要从旧版接口迁移到 FastInterface,请遵循以下关键步骤:
1. 更改基类
// 旧:旧版方法
public class MyInterface : InterfaceThreadedBaseClass
{
// 旧版实现
}
// 新:FastInterface 方法
public class MyInterface : FastInterfaceBase
{
// FastInterface 实现
}
2. 替换线程管理
// 旧:手动线程管理
protected override void ThreadMethod()
{
while (!shouldStop)
{
try
{
if (!isConnected)
AttemptConnection();
else
PerformCommunication();
Thread.Sleep(UpdateCycleMs);
}
catch (Exception ex)
{
// 手动错误处理
}
}
}
// 新:FastInterface 方法
protected override async Task EstablishConnection(CancellationToken cancellationToken)
{
// 连接逻辑
}
protected override async Task CommunicationLoop(CancellationToken cancellationToken)
{
// 通信逻辑
}
protected override void CloseConnection()
{
// 清理逻辑
}
3. 使用内置信号管理
// 旧:手动信号处理
private void ReadInputSignals()
{
var signals = GetComponentsInChildren<Signal>();
foreach (var signal in signals)
{
if (signal.Direction == SIGNALDIRECTION.INPUT)
{
var value = signal.GetValue();
// 发送到外部系统
}
}
}
// 新:FastInterface 信号管理
protected override async Task CommunicationLoop(CancellationToken cancellationToken)
{
// 自动信号发现和管理
var inputs = GetInputsForPLC();
await SendToExternalSystem(inputs);
var receivedData = await ReceiveFromExternalSystem();
SetOutputsFromPLC(receivedData);
}
4. 更新日志
// 旧:Unity Debug.Log(导致线程问题)
Debug.Log("Connection established");
Debug.LogError($"Error: {ex.Message}");
// 新:线程安全日志
ThreadSafeLogger.LogInfo("Connection established", GetType().Name);
ThreadSafeLogger.LogError($"Error: {ex.Message}", GetType().Name);
5. 线程安全地处理属性
// 旧:直接属性访问(线程问题)
protected override void ThreadMethod()
{
// 直接访问 Inspector 属性 - 非线程安全
ConnectToServer(ServerIP, Port);
}
// 新:线程安全属性复制
public string ServerIP = "192.168.1.100";
private string threadSafeServerIP;
protected override void CopyPropertiesToThreadSafe()
{
threadSafeServerIP = ServerIP;
}
protected override async Task EstablishConnection(CancellationToken cancellationToken)
{
// 使用线程安全副本
await ConnectToServer(threadSafeServerIP, threadSafePort);
}
旧版接口示例
以下是一个完整的旧版接口示例供参考:
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
using NaughtyAttributes;
namespace realvirtual
{
public class LegacyTCPInterface : InterfaceThreadedBaseClass
{
[Header("TCP Settings")]
public string ServerIP = "192.168.1.100";
public int Port = 8080;
public int TimeoutMs = 2000;
private TcpClient tcpClient;
private NetworkStream stream;
private bool isConnected = false;
private object connectionLock = new object();
private List<Signal> inputSignals = new List<Signal>();
private List<Signal> outputSignals = new List<Signal>();
protected override void Start()
{
base.Start();
DiscoverSignals();
}
private void DiscoverSignals()
{
var allSignals = GetComponentsInChildren<Signal>();
foreach (var signal in allSignals)
{
if (signal.Direction == SIGNALDIRECTION.INPUT)
inputSignals.Add(signal);
else if (signal.Direction == SIGNALDIRECTION.OUTPUT)
outputSignals.Add(signal);
}
Debug.Log($"Discovered {inputSignals.Count} inputs, {outputSignals.Count} outputs");
}
protected override void ThreadMethod()
{
int reconnectAttempts = 0;
const int maxReconnectAttempts = 5;
while (!shouldStop)
{
try
{
lock (connectionLock)
{
if (!isConnected)
{
AttemptConnection();
if (!isConnected)
{
reconnectAttempts++;
if (reconnectAttempts >= maxReconnectAttempts)
{
Thread.Sleep(5000);
reconnectAttempts = 0;
}
continue;
}
}
}
PerformCommunication();
reconnectAttempts = 0;
}
catch (Exception ex)
{
Debug.LogError($"Communication error: {ex.Message}");
lock (connectionLock)
{
isConnected = false;
CleanupConnection();
}
}
Thread.Sleep(UpdateCycleMs);
}
}
private void AttemptConnection()
{
try
{
tcpClient = new TcpClient();
tcpClient.ConnectTimeout = TimeoutMs;
tcpClient.Connect(ServerIP, Port);
stream = tcpClient.GetStream();
isConnected = true;
Debug.Log("TCP connection established");
}
catch (Exception ex)
{
Debug.LogError($"Connection failed: {ex.Message}");
CleanupConnection();
}
}
private void PerformCommunication()
{
// 读取 Unity 输入信号
var inputData = new Dictionary<string, object>();
foreach (var signal in inputSignals)
{
inputData[signal.name] = signal.GetValue();
}
// 发送到外部系统
if (inputData.Count > 0)
{
SendDataToServer(inputData);
}
// 从外部系统请求数据
var receivedData = RequestDataFromServer();
// 更新 Unity 输出信号
foreach (var kvp in receivedData)
{
var signal = outputSignals.Find(s => s.name == kvp.Key);
if (signal != null)
{
signal.SetValue(kvp.Value);
}
}
}
private void SendDataToServer(Dictionary<string, object> data)
{
// 实现取决于您的协议
}
private Dictionary<string, object> RequestDataFromServer()
{
// 实现取决于您的协议
return new Dictionary<string, object>();
}
private void CleanupConnection()
{
try
{
stream?.Close();
tcpClient?.Close();
}
catch (Exception ex)
{
Debug.LogError($"Cleanup error: {ex.Message}");
}
finally
{
stream = null;
tcpClient = null;
isConnected = false;
}
}
public override bool GetCommunicationState()
{
lock (connectionLock)
{
return isConnected;
}
}
protected override void OnDestroy()
{
shouldStop = true;
lock (connectionLock)
{
CleanupConnection();
}
base.OnDestroy();
}
}
}
FastInterface 的优势
FastInterface 解决了旧版系统的所有主要问题:
| 旧版系统问题 | FastInterface 解决方案 |
|---|---|
| 手动线程管理 | 自动线程生命周期管理 |
| 复杂的错误处理 | 内置重连和错误恢复 |
| 线程错误和竞态条件 | 线程安全的设计模式 |
| 手动信号发现 | 自动信号检测和管理 |
| 无性能优化 | 内置变化检测和批处理 |
| 实现不一致 | 所有接口的标准化架构 |
| 有限的调试工具 | 全面的日志记录和状态监控 |
| 属性同步问题 | 自动线程安全属性复制 |