본문 바로가기

카테고리 없음

유니티에서 OPC-UA 서버 접근하기

 

 

1. NuGetForUnity 설치

 

https://github.com/GlitchEnzo/NuGetForUnity/releases

 

Releases · GlitchEnzo/NuGetForUnity

A NuGet Package Manager for Unity. Contribute to GlitchEnzo/NuGetForUnity development by creating an account on GitHub.

github.com

 

위 링크로 가서, 

 

유니티 패키지를 다운받음

 

패키지 임포트 실시

 

 

2. OPCFoundation.NetStandard.Opc.Ua 패키지 설치

 

 

로 가서

검색 실시

 

 

인스톨 실시

 

3. 플레이어 설정 수정

 닷넷 프레임워크로

 

 

4. 샘플 코드 작성

using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System;
using System.Collections;
using System.Threading.Tasks;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;

public class OPCUAClient : MonoBehaviour
{
    public TextMeshProUGUI temperatureText;
    public TextMeshProUGUI humidityText;
    public TextMeshProUGUI heightText;
    public TextMeshProUGUI powerText;
    public Button powerButton;

    private Session session;
    private const string serverUrl = "opc.tcp://127.0.0.1:4840/freeopcua/server/";
    private string ns;
    private NodeId temperatureNode;
    private NodeId humidityNode;
    private NodeId heightNode;
    private NodeId powerNode;

    void Start()
    {
        StartCoroutine(ConnectToOPCUAServer());
        powerButton.onClick.AddListener(TogglePower);

        // ✅ Subscription + Update 방식 병행
        StartCoroutine(UpdateSensorDataRoutine());
    }

    private IEnumerator ConnectToOPCUAServer()
    {
        yield return null;
        Task.Run(async () =>
        {
            try
            {
                Debug.Log("✅ OPC UA 클라이언트 시작...");

                ApplicationInstance application = new ApplicationInstance
                {
                    ApplicationName = "Unity_OPCUA_Client",
                    ApplicationType = ApplicationType.Client
                };

                application.ApplicationConfiguration = CreateApplicationConfiguration();
                application.CheckApplicationInstanceCertificate(false, 0);

                var config = application.ApplicationConfiguration;
                var endpointDescription = CoreClientUtils.SelectEndpoint(serverUrl, false);
                var endpointConfiguration = EndpointConfiguration.Create(config);
                var endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);

                session = await Session.Create(config, endpoint, false, "UnityClient", 60000, null, null);
                Debug.Log("✅ OPC UA 서버 연결 성공!");

                int nsIndex = session.NamespaceUris.GetIndex("http://example.org");
                ns = $"ns={nsIndex}";

                temperatureNode = new NodeId($"{ns};s=Temperature");
                humidityNode = new NodeId($"{ns};s=Humidity");
                heightNode = new NodeId($"{ns};s=Height");
                powerNode = new NodeId($"{ns};s=Power");

                // ✅ Subscription으로 값 변경 감지
            //    SubscribeToNode(temperatureNode, value => temperatureText.text = $"온도: {value.Value}°C");
             //   SubscribeToNode(humidityNode, value => humidityText.text = $"습도: {value.Value}%");
            //    SubscribeToNode(heightNode, value => heightText.text = $"높이: {value.Value}m");
            //    SubscribeToNode(powerNode, value => powerText.text = $"전력: {value.Value}W");
            }
            catch (System.Exception ex)
            {
                Debug.LogError($"❌ OPC UA 연결 오류: {ex.Message}");
            }
        });
    }

    private ApplicationConfiguration CreateApplicationConfiguration()
    {
        string certStorePath = System.IO.Path.Combine(Application.streamingAssetsPath, "OPCCerts/MachineDefault");
        certStorePath = certStorePath.Replace("\\", "/");

        if (!System.IO.Directory.Exists(certStorePath))
        {
            System.IO.Directory.CreateDirectory(certStorePath);
            Debug.Log($"✅ OPC UA 인증서 저장소 폴더 생성: {certStorePath}");
        }

        ApplicationConfiguration config = new ApplicationConfiguration()
        {
            ApplicationName = "Unity_OPCUA_Client",
            ApplicationType = ApplicationType.Client,
            SecurityConfiguration = new SecurityConfiguration()
            {
                ApplicationCertificate = new CertificateIdentifier()
                {
                    StoreType = "Directory",
                    StorePath = certStorePath
                },
                TrustedPeerCertificates = new CertificateTrustList(),
                TrustedIssuerCertificates = new CertificateTrustList(),
                RejectedCertificateStore = new CertificateTrustList(),
                AutoAcceptUntrustedCertificates = true
            },
            TransportConfigurations = new TransportConfigurationCollection(),
            TransportQuotas = new TransportQuotas() { OperationTimeout = 60000 },
            ClientConfiguration = new ClientConfiguration() { DefaultSessionTimeout = 60000 }
        };

        config.Validate(ApplicationType.Client);
        Debug.Log("✅ OPC UA 강제 설정 생성 완료!");

        return config;
    }

    private IEnumerator UpdateSensorDataRoutine()
    {
        while (true)
        {
            UpdateSensorData();
            yield return new WaitForSeconds(0.1f);
        }
    }

    private void UpdateSensorData()
    {
        if (session == null)
        {
            Debug.LogError("❌ OPC UA 세션이 null 입니다.");
            return;
        }

        if (!session.Connected)
        {
            Debug.LogError("❌ OPC UA 세션이 연결되지 않았습니다.");
            return;
        }

        double temp = ReadNodeValue(temperatureNode);
        double humid = ReadNodeValue(humidityNode);
        double height = ReadNodeValue(heightNode);
        double power = ReadNodeValue(powerNode);

        temperatureText.text = $"온도: {temp}°C";
        humidityText.text = $"습도: {humid}%";
        heightText.text = $"높이: {height}m";
        powerText.text = $"전력: {power}W";

        Debug.Log($"🌡️ 온도: {temp}°C, 💧 습도: {humid}%, 📏 높이: {height}m, ⚡ 전력: {power}W");
    }


    private void SubscribeToNode(NodeId nodeId, Action<DataValue> callback)
    {
        if (session != null && session.Connected)
        {
            Subscription subscription = new Subscription(session.DefaultSubscription) { PublishingInterval = 1000 };
            MonitoredItem monitoredItem = new MonitoredItem(subscription.DefaultItem) { StartNodeId = nodeId, AttributeId = Attributes.Value };

            monitoredItem.Notification += (monitoredItem, eventArgs) =>
            {
                MonitoredItemNotification notification = eventArgs.NotificationValue as MonitoredItemNotification;
                if (notification != null)
                {
                    callback?.Invoke(notification.Value);
                }
            };

            subscription.AddItem(monitoredItem);
            session.AddSubscription(subscription);
            subscription.Create();
        }
    }

    private void TogglePower()
    {
        if (session != null && session.Connected)
        {
            try
            {
                double currentPower = ReadNodeValue(powerNode);
                double newPower = (currentPower == 0) ? 1 : 0;
                WriteNodeValue(powerNode, newPower);
                powerText.text = $"전력: {newPower}W";
                Debug.Log($"✅ 전력 상태 변경: {newPower}");
            }
            catch (System.Exception ex)
            {
                Debug.LogError($"❌ 전력 변경 오류: {ex.Message}");
            }
        }
    }
    private double ReadNodeValue(NodeId nodeId)
    {
        try
        {
            if (session == null || !session.Connected)
            {
                Debug.LogError("❌ OPC UA 세션이 연결되지 않았습니다.");
                return -1;
            }

            DataValue dataValue = session.ReadValue(nodeId);

            if (dataValue == null || dataValue.Value == null)
            {
                Debug.LogError($"❌ 노드 값이 null 입니다: {nodeId}");
                return -1;
            }

            Debug.Log($"✅ [Read] {nodeId}: {dataValue.Value}");
            return Convert.ToDouble(dataValue.Value);
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"❌ 노드 값 읽기 오류: {ex.Message}");
            return -1;
        }
    }

    private void WriteNodeValue(NodeId nodeId, double newValue)
    {
        try
        {
            WriteValue writeValue = new WriteValue
            {
                NodeId = nodeId,
                AttributeId = Attributes.Value,
                Value = new DataValue(new Variant(newValue))
            };

            StatusCodeCollection results;
            DiagnosticInfoCollection diagnosticInfos;
            session.Write(null, new WriteValueCollection { writeValue }, out results, out diagnosticInfos);

            if (StatusCode.IsGood(results[0]))
            {
                Debug.Log($"✅ 값 쓰기 성공: {newValue}");
            }
            else
            {
                Debug.LogError($"❌ 값 쓰기 실패: {results[0]}");
            }
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"❌ 값을 쓰는 중 오류 발생: {ex.Message}");
        }
    }
}

 

 

5. 작동 확인