旧版接口(已弃用)

⚠️

已弃用:本文档涵盖使用 InterfaceBaseClassInterfaceThreadedBaseClass 的旧版接口系统。这些方法已弃用,不应在新项目中使用。所有新接口开发请使用 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 解决方案
手动线程管理自动线程生命周期管理
复杂的错误处理内置重连和错误恢复
线程错误和竞态条件线程安全的设计模式
手动信号发现自动信号检测和管理
无性能优化内置变化检测和批处理
实现不一致所有接口的标准化架构
有限的调试工具全面的日志记录和状态监控
属性同步问题自动线程安全属性复制

另请参见