본문 바로가기

Windows

OPC.DA 관련

반응형

% OPC는 기존의 OLE를 개선하여 데이터 통신을 하기위한 Framework 입니다.

% OPC는 UA, DA, HDA등이 있습니다. 데이터 통신은 DA를 사용하기 때문에 여기서는 DA를 다루겠습니다.

% OPC DA는 현재 version 3.0까지 있습니다.

제가 사용하는 DLL은

OpcNetApi.dll, OpcNetApi.Com.dll, OpcRcw.Da.dll, OpcRcw.Comm.dll, OpcRcw.Hda.dll

입니다.

% 솔루션에서 첨부하는 화일은 OpcNetApi.dll, OpcNetApi.Com.dll만 합니다. 나머지는 같은 폴더에 있으면 복사합니다.

% 소스는 C#, WPF 기준입니다.

 

1. IDiscovery를 만듭니다. 제일 기본이 되는 것으로 서버를 가져올때 사용됩니다.

        private IDiscovery m_discovery = new OpcCom.ServerEnumerator();

 

2. 서버를 가져오는 코드입니다.

            Opc.Server[] servers = m_discovery.GetAvailableServers(Specification.COM_DA_20, ServerText.Text.ToString(), null);
            if (servers != null)
            {
                foreach (Opc.Da.Server server in servers)
                {
                    if (String.Compare(server.Name, ServerNameText.Text, true) == 0)
                    {
                        //establish connection.
                        m_server = server;
                        break;
                    }
                }
            }
            

 

첫줄에 사용되는 "Specification.COM_DA_20"은 DA 2.0서버를 가져오는 명령입니다.

서버의 종류는 다음과 같습니다.

        public static readonly Specification COM_AE_10;
        public static readonly Specification COM_HDA_10;
        public static readonly Specification COM_DX_10;
        public static readonly Specification COM_DA_30;
        public static readonly Specification XML_DA_10;
        public static readonly Specification COM_DA_10;
        public static readonly Specification COM_BATCH_20;
        public static readonly Specification COM_BATCH_10;
        public static readonly Specification COM_DA_20;

ServerNameText.Text에 사용하려는 서버의 이름이 들어있습니다.

예를 들어 "SWToolbox.TOPServer.V5" 같은 것이 될 수 있습니다.

참고로 이 서버는 http://opchub.com/product/topserver.asp 여기서 제공하는 Trial 서버입니다.

다운로드 링크는 아래와 같습니다.

www.opchub.com/download/TOPserver_v5.exe

 

3. 서버에서 아이템들을 가져오는 코드입니다.

            BrowsePosition position;
            ItemIdentifier tItem = new ItemIdentifier();
            BrowseFilters tFilter = new BrowseFilters();
            tFilter.BrowseFilter = Opc.Da.browseFilter.all;
            BrowseElement[] children = m_server.Browse(tItem, tFilter, out position);

chidren에 서버 루트에 있는 아이템이나 브랜치들이 들어 있습니다.

BrowserFilters의 값은 다음과 같이 될 수 있습니다.

Opc.Da.browseFilter.all;
Opc.Da.browseFilter.branch;
Opc.Da.browseFilter.item;

 

다음의 함수는 mItemNameList에 모든 아이템을 넣은 예제입니다.

        List<string> mItempNameList;

        private void GetItemsInChildren(BrowseElement[] tParent)
        {
            for (int i = 0; i < tParent.Length; i++)
            {
                if (tParent[i].IsItem == true)
                {
                    mItempNameList.Add(tParent[i].ItemName);
                }
                else {
                    BrowsePosition position;
                    ItemIdentifier tItemChild = new ItemIdentifier();
                    BrowseFilters tFilterChild = new BrowseFilters();
                    tFilterChild.BrowseFilter = Opc.Da.browseFilter.all;
                    tItemChild.ItemName = tParent[i].ItemName;
                    BrowseElement[] tChildren = m_server.Browse(tItemChild, tFilterChild, out position);
                    GetItemsInChildren(tChildren);
                }
            }
        }

        private void GetAllItemInServer()
        {
            mItempNameList = new List<string>();

            BrowsePosition position;
            ItemIdentifier tItem = new ItemIdentifier();
            BrowseFilters tFilter = new BrowseFilters();
            tFilter.BrowseFilter = Opc.Da.browseFilter.all;
            BrowseElement[] children = m_server.Browse(tItem, tFilter, out position);
            GetItemsInChildren(children);
        }

 

4. Subscription을 만들어서 값을 모니터링하는 코드입니다.

// Set group status
// Group (subscriber) status, equivalent to the parameters of the group in the OPC specification
mReadGroup = new SubscriptionState();
mReadGroup.Name = "Monitoring";                       // Group Name
mReadGroup.ServerHandle = null;                          // The handle assigned by the server to the group.
mReadGroup.ClientHandle = Guid.NewGuid().ToString();     // The handle assigned by the client to the group.
mReadGroup.Active = true;                                // Activate the group.
mReadGroup.UpdateRate = 10;                             // The refresh rate is 1 second. -> 1000
mReadGroup.Deadband = 0;                                 // When the dead zone value is set to 0, the server will notify the group of any data changes in the group.
mReadGroup.Locale = null;                                //No regional values are set.

// Add Group
mMonitoringSubscription = (Subscription)m_server.CreateSubscription(mReadGroup); // Create Group

10ms로 값을 가져오게 하였습니다.

 

5. Group에 모니터링할 item을 추가하는 부분입니다.

// Define Item List
Item[] items = new Item[MonitoringItemNames.Length];                             // Define the data item, ie item
for (int i = 0; i < items.Length; i++)                      // Item initial assignment
{
	items[i] = new Item();                              // Create an Item object.
	items[i].ClientHandle = Guid.NewGuid().ToString();  // The handle assigned by the client to the data item.
	items[i].ItemPath = null;                           // The path of the data item in the server.
	items[i].ItemName = MonitoringItemNames[i];                    // The name of the data item in the server.
    SensorData tSensorItem;
    tSensorItem = new SensorData();                              // Create an Item object.
    tSensorItem.Name = MonitoringItemNames[i];
    cSensorViewModel.ListSensorItem.Add(tSensorItem);
}

// Add Item
ItemResult[] tItemResult = mMonitoringSubscription.AddItems(items);
ItemValueResult[] itemValues = mMonitoringSubscription.Read(mMonitoringSubscription.Items);
foreach (ItemValueResult titem in itemValues)
{
	// setup type
	cSensorViewModel.AddType(titem.ItemName, titem.Value.GetType().ToString());
}

// Register callback event
mMonitoringSubscription.DataChanged += new DataChangedEventHandler(OnDataChange);

 

6. Data값이 변할때 호출되는 함수입니다.

// DataChange callback
public void OnDataChange(object subscriptionHandle, ItemValueResult[] values)
{
	foreach (ItemValueResult item in values)
	{
		cSensorViewModel.AddValue(item.ItemName, item.Value);
	}
}

 

7. 사용되는 SensorData Class와 SensorViewModel Class 입니다.

public class SensorData : INotifyPropertyChanged
{
	private string _name;
	public string Name
	{
		get { return _name; }
		set { _name = value; }
	}

	private string _dataType;
	public string DataType
	{
		get { return _dataType; }
		set { _dataType = value; }
	}

	private string _value;
	public string Value
	{
		get { return _value; }
		set
		{
			_value = value;
			OnPropertyChanged();
		}
	}

	public event PropertyChangedEventHandler PropertyChanged;

	protected virtual void OnPropertyChanged(string propertyName = null)
	{
		PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
	}
}

public class SensorViewModel
{
	public ObservableCollection<SensorData> ListSensorItem;

	public SensorViewModel()
	{
		ListSensorItem = new ObservableCollection<SensorData>();
	}

	public void AddValue(string tName, object tValue)
	{
		for (int i = 0; i < ListSensorItem.Count; i++)
		{
			if (ListSensorItem[i].Name == tName)
			{
				ListSensorItem[i].Value = tValue.ToString();
			}
		}
	}

	public void AddType(string tName, string strType)
	{
		for (int i = 0; i < ListSensorItem.Count; i++)
		{
			if (ListSensorItem[i].Name == tName)
			{
				ListSensorItem[i].DataType = strType;
			}
		}
	}

	public event PropertyChangedEventHandler PropertyChanged;

	protected virtual void OnPropertyChanged(string propertyName = null)
	{
		PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
	}
}

 

전체 프로젝트는 아래에 있습니다.

https://github.com/bagng/OPCClient

 

bagng/OPCClient

Sample OPC Client. Contribute to bagng/OPCClient development by creating an account on GitHub.

github.com

% 2020년 9월 10일 추가

OPC DA를 통해 값을 Write하는 함수입니다.

 

            Item OPC_WriteItem = Array.Find(mMonitoringSubscription.Items, x => x.ItemName.Equals(tItem.Name));
            ItemValue[] writeValues = new ItemValue[1];
            writeValues[0] = new ItemValue(OPC_WriteItem);
            writeValues[0].Value = tItem.Value;
            IdentifiedResult[] retValues = mMonitoringSubscription.Write(writeValues);
            

 

반응형