#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
//-----------------------------------------------------------------------------
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
// Web: https://www.technosoftware.com
//
// The source code in this file is covered under a dual-license scenario:
// - Owner of a purchased license: SCLA 1.0
// - GPL V3: everybody else
//
// SCLA license terms accompanied with this source code.
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
//
// GNU General Public License as published by the Free Software Foundation;
// version 3 of the License are accompanied with this source code.
// See https://technosoftware.com/license/GPLv3License.txt
//
// This source code is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.
//-----------------------------------------------------------------------------
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
#region Using Directives
using System;
using System.Collections;
using Technosoftware.DaAeHdaClient.Hda;
using Technosoftware.OpcRcw.Hda;
#endregion
namespace Technosoftware.DaAeHdaClient.Com.Hda
{
///
/// A class that implements the HDA data callback interface.
///
internal class DataCallback : IOPCHDA_DataCallback
{
///
/// Initializes the object with the containing subscription object.
///
public DataCallback() { }
///
/// Fired when an exception occurs during callback processing.
///
public event TsCHdaCallbackExceptionEventHandler CallbackExceptionEvent
{
add { lock (this) { _callbackExceptionEvent += value; } }
remove { lock (this) { _callbackExceptionEvent -= value; } }
}
///
/// Creates a new request object.
///
public Request CreateRequest(object requestHandle, Delegate callback)
{
lock (this)
{
// create a new request.
var request = new Request(requestHandle, callback, ++m_nextID);
// no items yet - callback may return before async call returns.
m_requests[request.RequestID] = request;
// return requests.
return request;
}
}
///
/// Cancels an existing request.
///
public bool CancelRequest(Request request, TsCHdaCancelCompleteEventHandler callback)
{
lock (this)
{
// check if it is a valid request.
if (!m_requests.Contains(request.RequestID))
{
return false;
}
// request will be removed when the cancel complete callback arrives.
if (callback != null)
{
request.CancelCompleteEvent += callback;
}
// no confirmation required - remove request immediately.
else
{
m_requests.Remove(request.RequestID);
}
// request will be cancelled.
return true;
}
}
#region IOPCHDA_DataCallback Members
///
/// Called when new data arrives for a subscription.
///
public void OnDataChange(
int dwTransactionID,
int hrStatus,
int dwNumItems,
OPCHDA_ITEM[] pItemValues,
int[] phrErrors)
{
try
{
lock (this)
{
// lookup request transaction.
var request = (Request)m_requests[dwTransactionID];
if (request == null)
{
return;
}
// unmarshal results.
var results = new TsCHdaItemValueCollection[pItemValues.Length];
for (var ii = 0; ii < pItemValues.Length; ii++)
{
results[ii] = Interop.GetItemValueCollection(pItemValues[ii], false);
results[ii].ServerHandle = results[ii].ClientHandle;
results[ii].ClientHandle = null;
results[ii].Result = Utilities.Interop.GetResultId(phrErrors[ii]);
}
// invoke callback - remove request if unexpected error occured.
if (request.InvokeCallback(results))
{
m_requests.Remove(request.RequestID);
}
}
}
catch (Exception exception)
{
HandleException(dwTransactionID, exception);
}
}
///
/// Called when an asynchronous read request completes.
///
public void OnReadComplete(
int dwTransactionID,
int hrStatus,
int dwNumItems,
OPCHDA_ITEM[] pItemValues,
int[] phrErrors)
{
try
{
lock (this)
{
// lookup request transaction.
var request = (Request)m_requests[dwTransactionID];
if (request == null)
{
return;
}
// unmarshal results.
var results = new TsCHdaItemValueCollection[pItemValues.Length];
for (var ii = 0; ii < pItemValues.Length; ii++)
{
results[ii] = Interop.GetItemValueCollection(pItemValues[ii], false);
results[ii].ServerHandle = pItemValues[ii].hClient;
results[ii].Result = Utilities.Interop.GetResultId(phrErrors[ii]);
}
// invoke callback - remove request if all results arrived.
if (request.InvokeCallback(results))
{
m_requests.Remove(request.RequestID);
}
}
}
catch (Exception exception)
{
HandleException(dwTransactionID, exception);
}
}
///
/// Called when an asynchronous read modified request completes.
///
public void OnReadModifiedComplete(
int dwTransactionID,
int hrStatus,
int dwNumItems,
OPCHDA_MODIFIEDITEM[] pItemValues,
int[] phrErrors)
{
try
{
lock (this)
{
// lookup request transaction.
var request = (Request)m_requests[dwTransactionID];
if (request == null)
{
return;
}
// unmarshal results.
var results = new TsCHdaModifiedValueCollection[pItemValues.Length];
for (var ii = 0; ii < pItemValues.Length; ii++)
{
results[ii] = Interop.GetModifiedValueCollection(pItemValues[ii], false);
results[ii].ServerHandle = pItemValues[ii].hClient;
results[ii].Result = Utilities.Interop.GetResultId(phrErrors[ii]);
}
// invoke callback - remove request if all results arrived.
if (request.InvokeCallback(results))
{
m_requests.Remove(request.RequestID);
}
}
}
catch (Exception exception)
{
HandleException(dwTransactionID, exception);
}
}
///
/// Called when an asynchronous read attributes request completes.
///
public void OnReadAttributeComplete(
int dwTransactionID,
int hrStatus,
int hClient,
int dwNumItems,
OPCHDA_ATTRIBUTE[] pAttributeValues,
int[] phrErrors)
{
try
{
lock (this)
{
// lookup request transaction.
var request = (Request)m_requests[dwTransactionID];
if (request == null)
{
return;
}
// create item object to collect results.
var item = new TsCHdaItemAttributeCollection();
item.ServerHandle = hClient;
// unmarshal results.
var results = new TsCHdaAttributeValueCollection[pAttributeValues.Length];
for (var ii = 0; ii < pAttributeValues.Length; ii++)
{
results[ii] = Interop.GetAttributeValueCollection(pAttributeValues[ii], false);
results[ii].Result = Utilities.Interop.GetResultId(phrErrors[ii]);
item.Add(results[ii]);
}
// invoke callback - remove request if all results arrived.
if (request.InvokeCallback(item))
{
m_requests.Remove(request.RequestID);
}
}
}
catch (Exception exception)
{
HandleException(dwTransactionID, exception);
}
}
///
/// Called when an asynchronous read annotations request completes.
///
public void OnReadAnnotations(
int dwTransactionID,
int hrStatus,
int dwNumItems,
OPCHDA_ANNOTATION[] pAnnotationValues,
int[] phrErrors)
{
try
{
lock (this)
{
// lookup request transaction.
var request = (Request)m_requests[dwTransactionID];
if (request == null)
{
return;
}
// unmarshal results.
var results = new TsCHdaAnnotationValueCollection[pAnnotationValues.Length];
for (var ii = 0; ii < pAnnotationValues.Length; ii++)
{
results[ii] = Interop.GetAnnotationValueCollection(pAnnotationValues[ii], false);
results[ii].ServerHandle = pAnnotationValues[ii].hClient;
results[ii].Result = Utilities.Interop.GetResultId(phrErrors[ii]);
}
// invoke callback - remove request if all results arrived.
if (request.InvokeCallback(results))
{
m_requests.Remove(request.RequestID);
}
}
}
catch (Exception exception)
{
HandleException(dwTransactionID, exception);
}
}
///
/// Called when an asynchronous insert annotations request completes.
///
public void OnInsertAnnotations(
int dwTransactionID,
int hrStatus,
int dwCount,
int[] phClients,
int[] phrErrors)
{
try
{
lock (this)
{
// lookup request transaction.
var request = (Request)m_requests[dwTransactionID];
if (request == null)
{
return;
}
// unmarshal results.
var results = new ArrayList();
if (dwCount > 0)
{
// subscription results in collections for the same item id.
var currentHandle = phClients[0];
var itemResults = new TsCHdaResultCollection();
for (var ii = 0; ii < dwCount; ii++)
{
// create a new collection for the next item's results.
if (phClients[ii] != currentHandle)
{
itemResults.ServerHandle = currentHandle;
results.Add(itemResults);
currentHandle = phClients[ii];
itemResults = new TsCHdaResultCollection();
}
var result = new TsCHdaResult(Utilities.Interop.GetResultId(phrErrors[ii]));
itemResults.Add(result);
}
// add the last set of item results.
itemResults.ServerHandle = currentHandle;
results.Add(itemResults);
}
// invoke callback - remove request if all results arrived.
if (request.InvokeCallback((TsCHdaResultCollection[])results.ToArray(typeof(TsCHdaResultCollection))))
{
m_requests.Remove(request.RequestID);
}
}
}
catch (Exception exception)
{
HandleException(dwTransactionID, exception);
}
}
///
/// Called when a batch of data from playback request arrives.
///
public void OnPlayback(
int dwTransactionID,
int hrStatus,
int dwNumItems,
IntPtr ppItemValues,
int[] phrErrors)
{
try
{
lock (this)
{
// lookup request transaction.
var request = (Request)m_requests[dwTransactionID];
if (request == null)
{
return;
}
// unmarshal results.
var results = new TsCHdaItemValueCollection[dwNumItems];
// the data is transfered as a array of pointers to items instead of simply
// as an array of items. This is due to a mistake in the HDA IDL.
var pItems = Utilities.Interop.GetInt32s(ref ppItemValues, dwNumItems, false);
for (var ii = 0; ii < dwNumItems; ii++)
{
// get pointer to item.
var pItem = (IntPtr)pItems[ii];
// unmarshal item as an array of length 1.
var item = Interop.GetItemValueCollections(ref pItem, 1, false);
if (item != null && item.Length == 1)
{
results[ii] = item[0];
results[ii].ServerHandle = results[ii].ClientHandle;
results[ii].ClientHandle = null;
results[ii].Result = Utilities.Interop.GetResultId(phrErrors[ii]);
}
}
// invoke callback - remove request if unexpected error occured.
if (request.InvokeCallback(results))
{
m_requests.Remove(request.RequestID);
}
}
}
catch (Exception exception)
{
HandleException(dwTransactionID, exception);
}
}
///
/// Called when an asynchronous update request completes.
///
public void OnUpdateComplete(
int dwTransactionID,
int hrStatus,
int dwCount,
int[] phClients,
int[] phrErrors)
{
try
{
lock (this)
{
// lookup request transaction.
var request = (Request)m_requests[dwTransactionID];
if (request == null)
{
return;
}
// unmarshal results.
var results = new ArrayList();
if (dwCount > 0)
{
// subscription results in collections for the same item id.
var currentHandle = phClients[0];
var itemResults = new TsCHdaResultCollection();
for (var ii = 0; ii < dwCount; ii++)
{
// create a new collection for the next item's results.
if (phClients[ii] != currentHandle)
{
itemResults.ServerHandle = currentHandle;
results.Add(itemResults);
currentHandle = phClients[ii];
itemResults = new TsCHdaResultCollection();
}
var result = new TsCHdaResult(Utilities.Interop.GetResultId(phrErrors[ii]));
itemResults.Add(result);
}
// add the last set of item results.
itemResults.ServerHandle = currentHandle;
results.Add(itemResults);
}
// invoke callback - remove request if all results arrived.
if (request.InvokeCallback((TsCHdaResultCollection[])results.ToArray(typeof(TsCHdaResultCollection))))
{
m_requests.Remove(request.RequestID);
}
}
}
catch (Exception exception)
{
HandleException(dwTransactionID, exception);
}
}
///
/// Called when an asynchronous request was cancelled successfully.
///
public void OnCancelComplete(int dwCancelID)
{
try
{
lock (this)
{
// lookup request.
var request = (Request)m_requests[dwCancelID];
if (request == null)
{
return;
}
// send the cancel complete notification.
request.OnCancelComplete();
// remove the request.
m_requests.Remove(request.RequestID);
}
}
catch (Exception exception)
{
HandleException(dwCancelID, exception);
}
}
#endregion
#region Private Methods
///
/// Fires an event indicating an exception occurred during callback processing.
///
void HandleException(int requestID, Exception exception)
{
lock (this)
{
// lookup request.
var request = (Request)m_requests[requestID];
if (request != null)
{
// send notification.
if (_callbackExceptionEvent != null)
{
_callbackExceptionEvent(request, exception);
}
}
}
}
#endregion
#region Private Members
private int m_nextID;
private Hashtable m_requests = new Hashtable();
private TsCHdaCallbackExceptionEventHandler _callbackExceptionEvent;
#endregion
}
}