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. 작동 확인