Fix
This commit is contained in:
60
Technosoftware/DaAeHdaClient.Com/Da/BrowsePosition.cs
Normal file
60
Technosoftware/DaAeHdaClient.Com/Da/BrowsePosition.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
#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 Technosoftware.DaAeHdaClient.Da;
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Da
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements an object that handles multi-step browse operations.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class BrowsePosition : TsCDaBrowsePosition
|
||||
{
|
||||
/// <summary>
|
||||
/// The continuation point for a browse operation.
|
||||
/// </summary>
|
||||
internal string ContinuationPoint = null;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that elements that meet the filter criteria have not been returned.
|
||||
/// </summary>
|
||||
internal bool MoreElements = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a browse position
|
||||
/// </summary>
|
||||
internal BrowsePosition(
|
||||
OpcItem itemID,
|
||||
TsCDaBrowseFilters filters,
|
||||
string continuationPoint)
|
||||
:
|
||||
base(itemID, filters)
|
||||
{
|
||||
ContinuationPoint = continuationPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
906
Technosoftware/DaAeHdaClient.Com/Da/Interop.cs
Normal file
906
Technosoftware/DaAeHdaClient.Com/Da/Interop.cs
Normal file
@@ -0,0 +1,906 @@
|
||||
#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 System.Runtime.InteropServices;
|
||||
using System.Reflection;
|
||||
|
||||
using Technosoftware.DaAeHdaClient.Da;
|
||||
#endregion
|
||||
|
||||
#pragma warning disable 0618
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Da
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains state information for a single asynchronous Technosoftware.DaAeHdaClient.Com.Da.Interop.
|
||||
/// </summary>
|
||||
internal class Interop
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a standard FILETIME to an OpcRcw.Da.FILETIME structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.FILETIME Convert(FILETIME input)
|
||||
{
|
||||
var output = new OpcRcw.Da.FILETIME();
|
||||
output.dwLowDateTime = input.dwLowDateTime;
|
||||
output.dwHighDateTime = input.dwHighDateTime;
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpcRcw.Da.FILETIME to a standard FILETIME structure.
|
||||
/// </summary>
|
||||
internal static FILETIME Convert(OpcRcw.Da.FILETIME input)
|
||||
{
|
||||
var output = new FILETIME();
|
||||
output.dwLowDateTime = input.dwLowDateTime;
|
||||
output.dwHighDateTime = input.dwHighDateTime;
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals a OPCSERVERSTATUS structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCSERVERSTATUS GetServerStatus(OpcServerStatus input, int groupCount)
|
||||
{
|
||||
var output = new OpcRcw.Da.OPCSERVERSTATUS();
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output.szVendorInfo = input.VendorInfo;
|
||||
output.wMajorVersion = 0;
|
||||
output.wMinorVersion = 0;
|
||||
output.wBuildNumber = 0;
|
||||
output.dwServerState = (OpcRcw.Da.OPCSERVERSTATE)input.ServerState;
|
||||
output.ftStartTime = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(input.StartTime));
|
||||
output.ftCurrentTime = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(input.CurrentTime));
|
||||
output.ftLastUpdateTime = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(input.LastUpdateTime));
|
||||
output.dwBandWidth = -1;
|
||||
output.dwGroupCount = groupCount;
|
||||
output.wReserved = 0;
|
||||
|
||||
if (input.ProductVersion != null)
|
||||
{
|
||||
var versions = input.ProductVersion.Split(new char[] { '.' });
|
||||
|
||||
if (versions.Length > 0)
|
||||
{
|
||||
try { output.wMajorVersion = System.Convert.ToInt16(versions[0]); }
|
||||
catch { output.wMajorVersion = 0; }
|
||||
}
|
||||
|
||||
if (versions.Length > 1)
|
||||
{
|
||||
try { output.wMinorVersion = System.Convert.ToInt16(versions[1]); }
|
||||
catch { output.wMinorVersion = 0; }
|
||||
}
|
||||
|
||||
output.wBuildNumber = 0;
|
||||
|
||||
for (var ii = 2; ii < versions.Length; ii++)
|
||||
{
|
||||
try
|
||||
{
|
||||
output.wBuildNumber = (short)(output.wBuildNumber * 100 + System.Convert.ToInt16(versions[ii]));
|
||||
}
|
||||
catch
|
||||
{
|
||||
output.wBuildNumber = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCSERVERSTATUS structure.
|
||||
/// </summary>
|
||||
internal static OpcServerStatus GetServerStatus(ref IntPtr pInput, bool deallocate)
|
||||
{
|
||||
OpcServerStatus output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero)
|
||||
{
|
||||
var status = (OpcRcw.Da.OPCSERVERSTATUS)Marshal.PtrToStructure(pInput, typeof(OpcRcw.Da.OPCSERVERSTATUS));
|
||||
|
||||
output = new OpcServerStatus();
|
||||
|
||||
output.VendorInfo = status.szVendorInfo;
|
||||
output.ProductVersion = string.Format("{0}.{1}.{2}", status.wMajorVersion, status.wMinorVersion, status.wBuildNumber);
|
||||
output.ServerState = (OpcServerState)status.dwServerState;
|
||||
output.StatusInfo = null;
|
||||
output.StartTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftStartTime));
|
||||
output.CurrentTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftCurrentTime));
|
||||
output.LastUpdateTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftLastUpdateTime));
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pInput, typeof(OpcRcw.Da.OPCSERVERSTATUS));
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a browseFilter values to the COM equivalent.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCBROWSEFILTER GetBrowseFilter(TsCDaBrowseFilter input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case TsCDaBrowseFilter.All: return OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL;
|
||||
case TsCDaBrowseFilter.Branch: return OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_BRANCHES;
|
||||
case TsCDaBrowseFilter.Item: return OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ITEMS;
|
||||
}
|
||||
|
||||
return OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a browseFilter values from the COM equivalent.
|
||||
/// </summary>
|
||||
internal static TsCDaBrowseFilter GetBrowseFilter(OpcRcw.Da.OPCBROWSEFILTER input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL: return TsCDaBrowseFilter.All;
|
||||
case OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_BRANCHES: return TsCDaBrowseFilter.Branch;
|
||||
case OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ITEMS: return TsCDaBrowseFilter.Item;
|
||||
}
|
||||
|
||||
return TsCDaBrowseFilter.All;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of HRESULT codes.
|
||||
/// </summary>
|
||||
internal static IntPtr GetHRESULTs(IOpcResult[] results)
|
||||
{
|
||||
// extract error codes from results.
|
||||
var errors = new int[results.Length];
|
||||
|
||||
for (var ii = 0; ii < results.Length; ii++)
|
||||
{
|
||||
if (results[ii] != null)
|
||||
{
|
||||
errors[ii] = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(results[ii].Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
errors[ii] = Result.E_INVALIDHANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
// marshal error codes.
|
||||
var pErrors = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * results.Length);
|
||||
Marshal.Copy(errors, 0, pErrors, results.Length);
|
||||
|
||||
// return results.
|
||||
return pErrors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates an array of OPCBROWSEELEMENT structures.
|
||||
/// </summary>
|
||||
internal static TsCDaBrowseElement[] GetBrowseElements(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
TsCDaBrowseElement[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new TsCDaBrowseElement[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
output[ii] = GetBrowseElement(pos, deallocate);
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCBROWSEELEMENT)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of OPCBROWSEELEMENT structures.
|
||||
/// </summary>
|
||||
internal static IntPtr GetBrowseElements(TsCDaBrowseElement[] input, bool propertiesRequested)
|
||||
{
|
||||
var output = IntPtr.Zero;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OpcRcw.Da.OPCBROWSEELEMENT)) * input.Length);
|
||||
|
||||
var pos = output;
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
var element = GetBrowseElement(input[ii], propertiesRequested);
|
||||
Marshal.StructureToPtr(element, pos, false);
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCBROWSEELEMENT)));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCBROWSEELEMENT structures.
|
||||
/// </summary>
|
||||
internal static TsCDaBrowseElement GetBrowseElement(IntPtr pInput, bool deallocate)
|
||||
{
|
||||
TsCDaBrowseElement output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero)
|
||||
{
|
||||
var element = (OpcRcw.Da.OPCBROWSEELEMENT)Marshal.PtrToStructure(pInput, typeof(OpcRcw.Da.OPCBROWSEELEMENT));
|
||||
|
||||
output = new TsCDaBrowseElement();
|
||||
|
||||
output.Name = element.szName;
|
||||
output.ItemPath = null;
|
||||
output.ItemName = element.szItemID;
|
||||
output.IsItem = ((element.dwFlagValue & OpcRcw.Da.Constants.OPC_BROWSE_ISITEM) != 0);
|
||||
output.HasChildren = ((element.dwFlagValue & OpcRcw.Da.Constants.OPC_BROWSE_HASCHILDREN) != 0);
|
||||
output.Properties = GetItemProperties(ref element.ItemProperties, deallocate);
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pInput, typeof(OpcRcw.Da.OPCBROWSEELEMENT));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an OPCBROWSEELEMENT structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCBROWSEELEMENT GetBrowseElement(TsCDaBrowseElement input, bool propertiesRequested)
|
||||
{
|
||||
var output = new OpcRcw.Da.OPCBROWSEELEMENT();
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output.szName = input.Name;
|
||||
output.szItemID = input.ItemName;
|
||||
output.dwFlagValue = 0;
|
||||
output.ItemProperties = GetItemProperties(input.Properties);
|
||||
|
||||
if (input.IsItem)
|
||||
{
|
||||
output.dwFlagValue |= OpcRcw.Da.Constants.OPC_BROWSE_ISITEM;
|
||||
}
|
||||
|
||||
if (input.HasChildren)
|
||||
{
|
||||
output.dwFlagValue |= OpcRcw.Da.Constants.OPC_BROWSE_HASCHILDREN;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an array of property codes.
|
||||
/// </summary>
|
||||
internal static int[] GetPropertyIDs(TsDaPropertyID[] propertyIDs)
|
||||
{
|
||||
var output = new ArrayList();
|
||||
|
||||
if (propertyIDs != null)
|
||||
{
|
||||
foreach (var propertyID in propertyIDs)
|
||||
{
|
||||
output.Add(propertyID.Code);
|
||||
}
|
||||
}
|
||||
|
||||
return (int[])output.ToArray(typeof(int));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an array of property codes.
|
||||
/// </summary>
|
||||
internal static TsDaPropertyID[] GetPropertyIDs(int[] propertyIDs)
|
||||
{
|
||||
var output = new ArrayList();
|
||||
|
||||
if (propertyIDs != null)
|
||||
{
|
||||
foreach (var propertyID in propertyIDs)
|
||||
{
|
||||
output.Add(GetPropertyID(propertyID));
|
||||
}
|
||||
}
|
||||
|
||||
return (TsDaPropertyID[])output.ToArray(typeof(TsDaPropertyID));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates an array of OPCITEMPROPERTIES structures.
|
||||
/// </summary>
|
||||
internal static TsCDaItemPropertyCollection[] GetItemPropertyCollections(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
TsCDaItemPropertyCollection[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new TsCDaItemPropertyCollection[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
var list = (OpcRcw.Da.OPCITEMPROPERTIES)Marshal.PtrToStructure(pos, typeof(OpcRcw.Da.OPCITEMPROPERTIES));
|
||||
|
||||
output[ii] = new TsCDaItemPropertyCollection();
|
||||
output[ii].ItemPath = null;
|
||||
output[ii].ItemName = null;
|
||||
output[ii].Result = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(list.hrErrorID);
|
||||
|
||||
var properties = GetItemProperties(ref list, deallocate);
|
||||
|
||||
if (properties != null)
|
||||
{
|
||||
output[ii].AddRange(properties);
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Da.OPCITEMPROPERTIES));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTIES)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of OPCITEMPROPERTIES structures.
|
||||
/// </summary>
|
||||
internal static IntPtr GetItemPropertyCollections(TsCDaItemPropertyCollection[] input)
|
||||
{
|
||||
var output = IntPtr.Zero;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTIES)) * input.Length);
|
||||
|
||||
var pos = output;
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
var properties = new OpcRcw.Da.OPCITEMPROPERTIES();
|
||||
|
||||
if (input[ii].Count > 0)
|
||||
{
|
||||
properties = GetItemProperties((TsCDaItemProperty[])input[ii].ToArray(typeof(TsCDaItemProperty)));
|
||||
}
|
||||
|
||||
properties.hrErrorID = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(input[ii].Result);
|
||||
|
||||
Marshal.StructureToPtr(properties, pos, false);
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTIES)));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCITEMPROPERTIES structures.
|
||||
/// </summary>
|
||||
internal static TsCDaItemProperty[] GetItemProperties(ref OpcRcw.Da.OPCITEMPROPERTIES input, bool deallocate)
|
||||
{
|
||||
TsCDaItemProperty[] output = null;
|
||||
|
||||
if (input.dwNumProperties > 0)
|
||||
{
|
||||
output = new TsCDaItemProperty[input.dwNumProperties];
|
||||
|
||||
var pos = input.pItemProperties;
|
||||
|
||||
for (var ii = 0; ii < output.Length; ii++)
|
||||
{
|
||||
try
|
||||
{
|
||||
output[ii] = GetItemProperty(pos, deallocate);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
output[ii] = new TsCDaItemProperty();
|
||||
output[ii].Description = e.Message;
|
||||
output[ii].Result = OpcResult.E_FAIL;
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTY)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(input.pItemProperties);
|
||||
input.pItemProperties = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of OPCITEMPROPERTIES structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCITEMPROPERTIES GetItemProperties(TsCDaItemProperty[] input)
|
||||
{
|
||||
var output = new OpcRcw.Da.OPCITEMPROPERTIES();
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output.hrErrorID = Result.S_OK;
|
||||
output.dwReserved = 0;
|
||||
output.dwNumProperties = input.Length;
|
||||
output.pItemProperties = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTY)) * input.Length);
|
||||
|
||||
var error = false;
|
||||
|
||||
var pos = output.pItemProperties;
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
var property = GetItemProperty(input[ii]);
|
||||
Marshal.StructureToPtr(property, pos, false);
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTY)));
|
||||
|
||||
if (input[ii].Result.Failed())
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// set flag indicating one or more properties contained errors.
|
||||
if (error)
|
||||
{
|
||||
output.hrErrorID = Result.S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCITEMPROPERTY structures.
|
||||
/// </summary>
|
||||
internal static TsCDaItemProperty GetItemProperty(IntPtr pInput, bool deallocate)
|
||||
{
|
||||
TsCDaItemProperty output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero)
|
||||
{
|
||||
try
|
||||
{
|
||||
var property = (OpcRcw.Da.OPCITEMPROPERTY)Marshal.PtrToStructure(pInput, typeof(OpcRcw.Da.OPCITEMPROPERTY));
|
||||
|
||||
output = new TsCDaItemProperty();
|
||||
|
||||
output.ID = GetPropertyID(property.dwPropertyID);
|
||||
output.Description = property.szDescription;
|
||||
output.DataType = Technosoftware.DaAeHdaClient.Com.Interop.GetType((VarEnum)property.vtDataType);
|
||||
output.ItemPath = null;
|
||||
output.ItemName = property.szItemID;
|
||||
output.Value = UnmarshalPropertyValue(output.ID, property.vValue);
|
||||
output.Result = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(property.hrErrorID);
|
||||
|
||||
// convert COM DA code to unified DA code.
|
||||
if (property.hrErrorID == Result.E_BADRIGHTS) output.Result = new OpcResult(OpcResult.Da.E_WRITEONLY, Result.E_BADRIGHTS);
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pInput, typeof(OpcRcw.Da.OPCITEMPROPERTY));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an arary of OPCITEMPROPERTY structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCITEMPROPERTY GetItemProperty(TsCDaItemProperty input)
|
||||
{
|
||||
var output = new OpcRcw.Da.OPCITEMPROPERTY();
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output.dwPropertyID = input.ID.Code;
|
||||
output.szDescription = input.Description;
|
||||
output.vtDataType = (short)Technosoftware.DaAeHdaClient.Com.Interop.GetType(input.DataType);
|
||||
output.vValue = MarshalPropertyValue(input.ID, input.Value);
|
||||
output.wReserved = 0;
|
||||
output.hrErrorID = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(input.Result);
|
||||
|
||||
// set the property data type.
|
||||
var description = TsDaPropertyDescription.Find(input.ID);
|
||||
|
||||
if (description != null)
|
||||
{
|
||||
output.vtDataType = (short)Technosoftware.DaAeHdaClient.Com.Interop.GetType(description.Type);
|
||||
}
|
||||
|
||||
// convert unified DA code to COM DA code.
|
||||
if (input.Result == OpcResult.Da.E_WRITEONLY) output.hrErrorID = Result.E_BADRIGHTS;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public static TsDaPropertyID GetPropertyID(int input)
|
||||
{
|
||||
var fields = typeof(TsDaProperty).GetFields(BindingFlags.Static | BindingFlags.Public);
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
var property = (TsDaPropertyID)field.GetValue(typeof(TsDaPropertyID));
|
||||
|
||||
if (input == property.Code)
|
||||
{
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
return new TsDaPropertyID(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the property value to a type supported by the unified interface.
|
||||
/// </summary>
|
||||
internal static object UnmarshalPropertyValue(TsDaPropertyID propertyID, object input)
|
||||
{
|
||||
if (input == null) return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (propertyID == TsDaProperty.DATATYPE)
|
||||
{
|
||||
return Technosoftware.DaAeHdaClient.Com.Interop.GetType((VarEnum)System.Convert.ToUInt16(input));
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.ACCESSRIGHTS)
|
||||
{
|
||||
switch (System.Convert.ToInt32(input))
|
||||
{
|
||||
case OpcRcw.Da.Constants.OPC_READABLE: return TsDaAccessRights.Readable;
|
||||
case OpcRcw.Da.Constants.OPC_WRITEABLE: return TsDaAccessRights.Writable;
|
||||
|
||||
case OpcRcw.Da.Constants.OPC_READABLE | OpcRcw.Da.Constants.OPC_WRITEABLE:
|
||||
{
|
||||
return TsDaAccessRights.ReadWritable;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.EUTYPE)
|
||||
{
|
||||
switch ((OpcRcw.Da.OPCEUTYPE)input)
|
||||
{
|
||||
case OpcRcw.Da.OPCEUTYPE.OPC_NOENUM: return TsDaEuType.NoEnum;
|
||||
case OpcRcw.Da.OPCEUTYPE.OPC_ANALOG: return TsDaEuType.Analog;
|
||||
case OpcRcw.Da.OPCEUTYPE.OPC_ENUMERATED: return TsDaEuType.Enumerated;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.QUALITY)
|
||||
{
|
||||
return new TsCDaQuality(System.Convert.ToInt16(input));
|
||||
}
|
||||
|
||||
// convert UTC time in property to local time for the unified DA interface.
|
||||
if (propertyID == TsDaProperty.TIMESTAMP)
|
||||
{
|
||||
if (input.GetType() == typeof(DateTime))
|
||||
{
|
||||
var dateTime = (DateTime)input;
|
||||
|
||||
if (dateTime != DateTime.MinValue)
|
||||
{
|
||||
return dateTime.ToLocalTime();
|
||||
}
|
||||
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the property value to a type supported by COM-DA interface.
|
||||
/// </summary>
|
||||
internal static object MarshalPropertyValue(TsDaPropertyID propertyID, object input)
|
||||
{
|
||||
if (input == null) return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (propertyID == TsDaProperty.DATATYPE)
|
||||
{
|
||||
return (short)Technosoftware.DaAeHdaClient.Com.Interop.GetType((Type)input);
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.ACCESSRIGHTS)
|
||||
{
|
||||
switch ((TsDaAccessRights)input)
|
||||
{
|
||||
case TsDaAccessRights.Readable: return OpcRcw.Da.Constants.OPC_READABLE;
|
||||
case TsDaAccessRights.Writable: return OpcRcw.Da.Constants.OPC_WRITEABLE;
|
||||
case TsDaAccessRights.ReadWritable: return OpcRcw.Da.Constants.OPC_READABLE | OpcRcw.Da.Constants.OPC_WRITEABLE;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.EUTYPE)
|
||||
{
|
||||
switch ((TsDaEuType)input)
|
||||
{
|
||||
case TsDaEuType.NoEnum: return OpcRcw.Da.OPCEUTYPE.OPC_NOENUM;
|
||||
case TsDaEuType.Analog: return OpcRcw.Da.OPCEUTYPE.OPC_ANALOG;
|
||||
case TsDaEuType.Enumerated: return OpcRcw.Da.OPCEUTYPE.OPC_ENUMERATED;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.QUALITY)
|
||||
{
|
||||
return ((TsCDaQuality)input).GetCode();
|
||||
}
|
||||
|
||||
// convert local time in property to UTC time for the COM DA interface.
|
||||
if (propertyID == TsDaProperty.TIMESTAMP)
|
||||
{
|
||||
if (input.GetType() == typeof(DateTime))
|
||||
{
|
||||
var dateTime = (DateTime)input;
|
||||
|
||||
if (dateTime != DateTime.MinValue)
|
||||
{
|
||||
return dateTime.ToUniversalTime();
|
||||
}
|
||||
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of item values to an array of OPCITEMVQT objects.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCITEMVQT[] GetOPCITEMVQTs(TsCDaItemValue[] input)
|
||||
{
|
||||
OpcRcw.Da.OPCITEMVQT[] output = null;
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output = new OpcRcw.Da.OPCITEMVQT[input.Length];
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = new OpcRcw.Da.OPCITEMVQT();
|
||||
|
||||
var timestamp = (input[ii].TimestampSpecified) ? input[ii].Timestamp : DateTime.MinValue;
|
||||
|
||||
output[ii].vDataValue = Technosoftware.DaAeHdaClient.Com.Interop.GetVARIANT(input[ii].Value);
|
||||
output[ii].bQualitySpecified = (input[ii].QualitySpecified) ? 1 : 0;
|
||||
output[ii].wQuality = (input[ii].QualitySpecified) ? input[ii].Quality.GetCode() : (short)0;
|
||||
output[ii].bTimeStampSpecified = (input[ii].TimestampSpecified) ? 1 : 0;
|
||||
output[ii].ftTimeStamp = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(timestamp));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of item objects to an array of GetOPCITEMDEF objects.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCITEMDEF[] GetOPCITEMDEFs(TsCDaItem[] input)
|
||||
{
|
||||
OpcRcw.Da.OPCITEMDEF[] output = null;
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output = new OpcRcw.Da.OPCITEMDEF[input.Length];
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = new OpcRcw.Da.OPCITEMDEF();
|
||||
|
||||
output[ii].szItemID = input[ii].ItemName;
|
||||
output[ii].szAccessPath = (input[ii].ItemPath == null) ? string.Empty : input[ii].ItemPath;
|
||||
output[ii].bActive = (input[ii].ActiveSpecified) ? ((input[ii].Active) ? 1 : 0) : 1;
|
||||
output[ii].vtRequestedDataType = (short)Technosoftware.DaAeHdaClient.Com.Interop.GetType(input[ii].ReqType);
|
||||
output[ii].hClient = 0;
|
||||
output[ii].dwBlobSize = 0;
|
||||
output[ii].pBlob = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCITEMSTATE structures.
|
||||
/// </summary>
|
||||
internal static TsCDaItemValue[] GetItemValues(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
TsCDaItemValue[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new TsCDaItemValue[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
var result = (OpcRcw.Da.OPCITEMSTATE)Marshal.PtrToStructure(pos, typeof(OpcRcw.Da.OPCITEMSTATE));
|
||||
|
||||
output[ii] = new TsCDaItemValue();
|
||||
output[ii].ClientHandle = result.hClient;
|
||||
output[ii].Value = result.vDataValue;
|
||||
output[ii].Quality = new TsCDaQuality(result.wQuality);
|
||||
output[ii].QualitySpecified = true;
|
||||
output[ii].Timestamp = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(result.ftTimeStamp));
|
||||
output[ii].TimestampSpecified = output[ii].Timestamp != DateTime.MinValue;
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Da.OPCITEMSTATE));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMSTATE)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCITEMRESULT structures.
|
||||
/// </summary>
|
||||
internal static int[] GetItemResults(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
int[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new int[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
var result = (OpcRcw.Da.OPCITEMRESULT)Marshal.PtrToStructure(pos, typeof(OpcRcw.Da.OPCITEMRESULT));
|
||||
|
||||
output[ii] = result.hServer;
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(result.pBlob);
|
||||
result.pBlob = IntPtr.Zero;
|
||||
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Da.OPCITEMRESULT));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMRESULT)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of OPCBROWSEELEMENT structures.
|
||||
/// </summary>
|
||||
internal static IntPtr GetItemStates(TsCDaItemValueResult[] input)
|
||||
{
|
||||
var output = IntPtr.Zero;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMSTATE)) * input.Length);
|
||||
|
||||
var pos = output;
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
var item = new OpcRcw.Da.OPCITEMSTATE();
|
||||
|
||||
item.hClient = System.Convert.ToInt32(input[ii].ClientHandle);
|
||||
item.vDataValue = input[ii].Value;
|
||||
item.wQuality = (input[ii].QualitySpecified) ? input[ii].Quality.GetCode() : (short)0;
|
||||
item.ftTimeStamp = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(input[ii].Timestamp));
|
||||
item.wReserved = 0;
|
||||
|
||||
Marshal.StructureToPtr(item, pos, false);
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMSTATE)));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Technosoftware/DaAeHdaClient.Com/Da/Result.cs
Normal file
128
Technosoftware/DaAeHdaClient.Com/Da/Result.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
#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
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com
|
||||
{
|
||||
namespace Da
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines all well known COM DA HRESULT codes.
|
||||
/// </summary>
|
||||
internal struct Result
|
||||
{
|
||||
/// <remarks/>
|
||||
public const int S_OK = +0x00000000; // 0x00000000
|
||||
/// <remarks/>
|
||||
public const int S_FALSE = +0x00000001; // 0x00000001
|
||||
/// <remarks/>
|
||||
public const int E_NOTIMPL = -0x7FFFBFFF; // 0x80004001
|
||||
/// <remarks/>
|
||||
public const int E_OUTOFMEMORY = -0x7FF8FFF2; // 0x8007000E
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDARG = -0x7FF8FFA9; // 0x80070057
|
||||
/// <remarks/>
|
||||
public const int E_NOINTERFACE = -0x7FFFBFFE; // 0x80004002
|
||||
/// <remarks/>
|
||||
public const int E_POINTER = -0x7FFFBFFD; // 0x80004003
|
||||
/// <remarks/>
|
||||
public const int E_FAIL = -0x7FFFBFFB; // 0x80004005
|
||||
/// <remarks/>
|
||||
public const int CONNECT_E_NOCONNECTION = -0x7FFBFE00; // 0x80040200
|
||||
/// <remarks/>
|
||||
public const int CONNECT_E_ADVISELIMIT = -0x7FFBFDFF; // 0x80040201
|
||||
/// <remarks/>
|
||||
public const int DISP_E_TYPEMISMATCH = -0x7FFDFFFB; // 0x80020005
|
||||
/// <remarks/>
|
||||
public const int DISP_E_OVERFLOW = -0x7FFDFFF6; // 0x8002000A
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDHANDLE = -0x3FFBFFFF; // 0xC0040001
|
||||
/// <remarks/>
|
||||
public const int E_BADTYPE = -0x3FFBFFFC; // 0xC0040004
|
||||
/// <remarks/>
|
||||
public const int E_PUBLIC = -0x3FFBFFFB; // 0xC0040005
|
||||
/// <remarks/>
|
||||
public const int E_BADRIGHTS = -0x3FFBFFFA; // 0xC0040006
|
||||
/// <remarks/>
|
||||
public const int E_UNKNOWNITEMID = -0x3FFBFFF9; // 0xC0040007
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDITEMID = -0x3FFBFFF8; // 0xC0040008
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDFILTER = -0x3FFBFFF7; // 0xC0040009
|
||||
/// <remarks/>
|
||||
public const int E_UNKNOWNPATH = -0x3FFBFFF6; // 0xC004000A
|
||||
/// <remarks/>
|
||||
public const int E_RANGE = -0x3FFBFFF5; // 0xC004000B
|
||||
/// <remarks/>
|
||||
public const int E_DUPLICATENAME = -0x3FFBFFF4; // 0xC004000C
|
||||
/// <remarks/>
|
||||
public const int S_UNSUPPORTEDRATE = +0x0004000D; // 0x0004000D
|
||||
/// <remarks/>
|
||||
public const int S_CLAMP = +0x0004000E; // 0x0004000E
|
||||
/// <remarks/>
|
||||
public const int S_INUSE = +0x0004000F; // 0x0004000F
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDCONFIGFILE = -0x3FFBFFF0; // 0xC0040010
|
||||
/// <remarks/>
|
||||
public const int E_NOTFOUND = -0x3FFBFFEF; // 0xC0040011
|
||||
/// <remarks/>
|
||||
public const int E_INVALID_PID = -0x3FFBFDFD; // 0xC0040203
|
||||
/// <remarks/>
|
||||
public const int E_DEADBANDNOTSET = -0x3FFBFC00; // 0xC0040400
|
||||
/// <remarks/>
|
||||
public const int E_DEADBANDNOTSUPPORTED = -0x3FFBFBFF; // 0xC0040401
|
||||
/// <remarks/>
|
||||
public const int E_NOBUFFERING = -0x3FFBFBFE; // 0xC0040402
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDCONTINUATIONPOINT = -0x3FFBFBFD; // 0xC0040403
|
||||
/// <remarks/>
|
||||
public const int S_DATAQUEUEOVERFLOW = +0x00040404; // 0x00040404
|
||||
/// <remarks/>
|
||||
public const int E_RATENOTSET = -0x3FFBFBFB; // 0xC0040405
|
||||
/// <remarks/>
|
||||
public const int E_NOTSUPPORTED = -0x3FFBFBFA; // 0xC0040406
|
||||
}
|
||||
}
|
||||
|
||||
namespace Cpx
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines all well known Complex Data HRESULT codes.
|
||||
/// </summary>
|
||||
internal struct Result
|
||||
{
|
||||
/// <remarks/>
|
||||
public const int E_TYPE_CHANGED = -0x3FFBFBF9; // 0xC0040407
|
||||
/// <remarks/>
|
||||
public const int E_FILTER_DUPLICATE = -0x3FFBFBF8; // 0xC0040408
|
||||
/// <remarks/>
|
||||
public const int E_FILTER_INVALID = -0x3FFBFBF7; // 0xC0040409
|
||||
/// <remarks/>
|
||||
public const int E_FILTER_ERROR = -0x3FFBFBF6; // 0xC004040A
|
||||
/// <remarks/>
|
||||
public const int S_FILTER_NO_DATA = +0x0004040B; // 0xC004040B
|
||||
}
|
||||
}
|
||||
}
|
||||
984
Technosoftware/DaAeHdaClient.Com/Da/Server.cs
Normal file
984
Technosoftware/DaAeHdaClient.Com/Da/Server.cs
Normal file
@@ -0,0 +1,984 @@
|
||||
#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.Threading;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Technosoftware.DaAeHdaClient.Da;
|
||||
using Technosoftware.DaAeHdaClient.Utilities;
|
||||
|
||||
using Technosoftware.OpcRcw.Da;
|
||||
using Technosoftware.DaAeHdaClient.Com.Utilities;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Da
|
||||
{
|
||||
/// <summary>
|
||||
/// A .NET wrapper for a COM server that implements the DA server interfaces.
|
||||
/// </summary>
|
||||
internal class Server : Com.Server, ITsDaServer
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// The default result filters for the server.
|
||||
/// </summary>
|
||||
private int filters_ = (int)TsCDaResultFilter.All | (int)TsCDaResultFilter.ClientHandle;
|
||||
|
||||
/// <summary>
|
||||
/// A table of active subscriptions for the server.
|
||||
/// </summary>
|
||||
private readonly Hashtable subscriptions_ = new Hashtable();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the object.
|
||||
/// </summary>
|
||||
internal Server() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the object with the specified COM server.
|
||||
/// </summary>
|
||||
internal Server(OpcUrl url, object server)
|
||||
{
|
||||
if (url == null) throw new ArgumentNullException(nameof(url));
|
||||
|
||||
url_ = (OpcUrl)url.Clone();
|
||||
server_ = server;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">If true managed and unmanaged resources can be disposed. If false only unmanaged resources.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed_)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Release managed resources.
|
||||
if (server_ != null)
|
||||
{
|
||||
// release all groups.
|
||||
foreach (Subscription subscription in subscriptions_.Values)
|
||||
{
|
||||
var methodName = "IOPCServer.RemoveGroup";
|
||||
|
||||
// remove subscription from server.
|
||||
try
|
||||
{
|
||||
var state = subscription.GetState();
|
||||
if (state != null)
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
server?.RemoveGroup((int)state.ServerHandle, 0);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore error during Dispose
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
// dispose of the subscription object (disconnects all subscription connections).
|
||||
subscription.Dispose();
|
||||
}
|
||||
|
||||
// clear subscription table.
|
||||
subscriptions_.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Release unmanaged resources.
|
||||
// Set large fields to null.
|
||||
|
||||
if (server_ != null)
|
||||
{
|
||||
// release the COM server.
|
||||
Technosoftware.DaAeHdaClient.Com.Interop.ReleaseServer(server_);
|
||||
server_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Call Dispose on your base class.
|
||||
disposed_ = true;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private bool disposed_;
|
||||
#endregion
|
||||
|
||||
#region Technosoftware.DaAeHdaClient.Com.Server Overrides
|
||||
/// <summary>
|
||||
/// Returns the localized text for the specified result code.
|
||||
/// </summary>
|
||||
/// <param name="locale">The locale name in the format "[languagecode]-[country/regioncode]".</param>
|
||||
/// <param name="resultId">The result code identifier.</param>
|
||||
/// <returns>A message localized for the best match for the requested locale.</returns>
|
||||
public override string GetErrorText(string locale, OpcResult resultId)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCServer.GetErrorString";
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
|
||||
(server).GetErrorString(
|
||||
resultId.Code,
|
||||
Technosoftware.DaAeHdaClient.Com.Interop.GetLocale(locale),
|
||||
out var errorText);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
return errorText;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCServer.GetErrorString", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Technosoftware.DaAeHdaClient.IOpcServer Members
|
||||
/// <summary>
|
||||
/// Returns the filters applied by the server to any item results returned to the client.
|
||||
/// </summary>
|
||||
/// <returns>A bit mask indicating which fields should be returned in any item results.</returns>
|
||||
public int GetResultFilters()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
return filters_;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the filters applied by the server to any item results returned to the client.
|
||||
/// </summary>
|
||||
/// <param name="filters">A bit mask indicating which fields should be returned in any item results.</param>
|
||||
public void SetResultFilters(int filters)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
filters_ = filters;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current server status.
|
||||
/// </summary>
|
||||
/// <returns>The current server status.</returns>
|
||||
public OpcServerStatus GetServerStatus()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCServer.GetStatus";
|
||||
|
||||
// initialize arguments.
|
||||
IntPtr pStatus;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
(server).GetStatus(out pStatus);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// return status.
|
||||
return Interop.GetServerStatus(ref pStatus, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the current values for a set of items.
|
||||
/// </summary>
|
||||
/// <param name="items">The set of items to read.</param>
|
||||
/// <returns>The results of the read operation for each item.</returns>
|
||||
public virtual TsCDaItemValueResult[] Read(TsCDaItem[] items)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException(nameof(items));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
var methodName = "IOPCItemIO.Read";
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
|
||||
var count = items.Length;
|
||||
if (count == 0) throw new ArgumentOutOfRangeException(nameof(items.Length), @"0");
|
||||
|
||||
// initialize arguments.
|
||||
var itemIDs = new string[count];
|
||||
var maxAges = new int[count];
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
itemIDs[ii] = items[ii].ItemName;
|
||||
maxAges[ii] = (items[ii].MaxAgeSpecified) ? items[ii].MaxAge : 0;
|
||||
}
|
||||
|
||||
var pValues = IntPtr.Zero;
|
||||
var pQualities = IntPtr.Zero;
|
||||
var pTimestamps = IntPtr.Zero;
|
||||
var pErrors = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCItemIO>(methodName, true);
|
||||
server.Read(
|
||||
count,
|
||||
itemIDs,
|
||||
maxAges,
|
||||
out pValues,
|
||||
out pQualities,
|
||||
out pTimestamps,
|
||||
out pErrors);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var values = Technosoftware.DaAeHdaClient.Com.Interop.GetVARIANTs(ref pValues, count, true);
|
||||
var qualities = Technosoftware.DaAeHdaClient.Com.Interop.GetInt16s(ref pQualities, count, true);
|
||||
var timestamps = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIMEs(ref pTimestamps, count, true);
|
||||
var errors = Technosoftware.DaAeHdaClient.Com.Interop.GetInt32s(ref pErrors, count, true);
|
||||
|
||||
// pre-fetch the current locale to use for data conversions.
|
||||
var locale = GetLocale();
|
||||
|
||||
// construct result array.
|
||||
var results = new TsCDaItemValueResult[count];
|
||||
|
||||
for (var ii = 0; ii < results.Length; ii++)
|
||||
{
|
||||
results[ii] = new TsCDaItemValueResult(items[ii]);
|
||||
|
||||
results[ii].ServerHandle = null;
|
||||
results[ii].Value = values[ii];
|
||||
results[ii].Quality = new TsCDaQuality(qualities[ii]);
|
||||
results[ii].QualitySpecified = true;
|
||||
results[ii].Timestamp = timestamps[ii];
|
||||
results[ii].TimestampSpecified = timestamps[ii] != DateTime.MinValue;
|
||||
results[ii].Result = Utilities.Interop.GetResultId(errors[ii]);
|
||||
results[ii].DiagnosticInfo = null;
|
||||
|
||||
// convert COM code to unified DA code.
|
||||
if (errors[ii] == Result.E_BADRIGHTS) { results[ii].Result = new OpcResult(OpcResult.Da.E_WRITEONLY, Result.E_BADRIGHTS); }
|
||||
|
||||
// convert the data type since the server does not support the feature.
|
||||
if (results[ii].Value != null && items[ii].ReqType != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
results[ii].Value = ChangeType(values[ii], items[ii].ReqType, locale);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
results[ii].Value = null;
|
||||
results[ii].Quality = TsCDaQuality.Bad;
|
||||
results[ii].QualitySpecified = true;
|
||||
results[ii].Timestamp = DateTime.MinValue;
|
||||
results[ii].TimestampSpecified = false;
|
||||
|
||||
if (e.GetType() == typeof(OverflowException))
|
||||
{
|
||||
results[ii].Result = Utilities.Interop.GetResultId(Result.E_RANGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
results[ii].Result = Utilities.Interop.GetResultId(Result.E_BADTYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply request options.
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemName) == 0) results[ii].ItemName = null;
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemPath) == 0) results[ii].ItemPath = null;
|
||||
if ((filters_ & (int)TsCDaResultFilter.ClientHandle) == 0) results[ii].ClientHandle = null;
|
||||
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemTime) == 0)
|
||||
{
|
||||
results[ii].Timestamp = DateTime.MinValue;
|
||||
results[ii].TimestampSpecified = false;
|
||||
}
|
||||
}
|
||||
|
||||
// return results.
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the value, quality and timestamp for a set of items.
|
||||
/// </summary>
|
||||
/// <param name="items">The set of item values to write.</param>
|
||||
/// <returns>The results of the write operation for each item.</returns>
|
||||
public virtual OpcItemResult[] Write(TsCDaItemValue[] items)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException(nameof(items));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCItemIO.WriteVQT";
|
||||
|
||||
var count = items.Length;
|
||||
if (count == 0) throw new ArgumentOutOfRangeException("items.Length", "0");
|
||||
|
||||
// initialize arguments.
|
||||
var itemIDs = new string[count];
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
itemIDs[ii] = items[ii].ItemName;
|
||||
}
|
||||
|
||||
var values = Interop.GetOPCITEMVQTs(items);
|
||||
|
||||
var pErrors = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCItemIO>(methodName, true);
|
||||
server.WriteVQT(
|
||||
count,
|
||||
itemIDs,
|
||||
values,
|
||||
out pErrors);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var errors = Utilities.Interop.GetInt32s(ref pErrors, count, true);
|
||||
|
||||
// construct result array.
|
||||
var results = new OpcItemResult[count];
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
results[ii] = new OpcItemResult(items[ii]);
|
||||
|
||||
results[ii].ServerHandle = null;
|
||||
results[ii].Result = Utilities.Interop.GetResultId(errors[ii]);
|
||||
results[ii].DiagnosticInfo = null;
|
||||
|
||||
// convert COM code to unified DA code.
|
||||
if (errors[ii] == Result.E_BADRIGHTS) { results[ii].Result = new OpcResult(OpcResult.Da.E_READONLY, Result.E_BADRIGHTS); }
|
||||
|
||||
// apply request options.
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemName) == 0) results[ii].ItemName = null;
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemPath) == 0) results[ii].ItemPath = null;
|
||||
if ((filters_ & (int)TsCDaResultFilter.ClientHandle) == 0) results[ii].ClientHandle = null;
|
||||
}
|
||||
|
||||
// return results.
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new subscription.
|
||||
/// </summary>
|
||||
/// <param name="state">The initial state of the subscription.</param>
|
||||
/// <returns>The new subscription object.</returns>
|
||||
public ITsCDaSubscription CreateSubscription(TsCDaSubscriptionState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCServer.AddGroup";
|
||||
|
||||
// copy the subscription state.
|
||||
var result = (TsCDaSubscriptionState)state.Clone();
|
||||
|
||||
// initialize arguments.
|
||||
var iid = typeof(IOPCItemMgt).GUID;
|
||||
object group = null;
|
||||
|
||||
var serverHandle = 0;
|
||||
var revisedUpdateRate = 0;
|
||||
|
||||
var hDeadband = GCHandle.Alloc(result.Deadband, GCHandleType.Pinned);
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
server.AddGroup(
|
||||
(result.Name != null) ? result.Name : "",
|
||||
(result.Active) ? 1 : 0,
|
||||
result.UpdateRate,
|
||||
0,
|
||||
IntPtr.Zero,
|
||||
hDeadband.AddrOfPinnedObject(),
|
||||
Technosoftware.DaAeHdaClient.Com.Interop.GetLocale(result.Locale),
|
||||
out serverHandle,
|
||||
out revisedUpdateRate,
|
||||
ref iid,
|
||||
out group);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (hDeadband.IsAllocated)
|
||||
{
|
||||
hDeadband.Free();
|
||||
}
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
if (group == null) throw new OpcResultException(OpcResult.E_FAIL, "The subscription was not created.");
|
||||
|
||||
methodName = "IOPCGroupStateMgt2.SetKeepAlive";
|
||||
|
||||
// set the keep alive rate if requested.
|
||||
try
|
||||
{
|
||||
var keepAlive = 0;
|
||||
var comObject = BeginComCall<IOPCGroupStateMgt2>(group, methodName, true);
|
||||
comObject.SetKeepAlive(result.KeepAlive, out keepAlive);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
result.KeepAlive = keepAlive;
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
result.KeepAlive = 0;
|
||||
ComCallError(methodName, e1);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// save server handle.
|
||||
result.ServerHandle = serverHandle;
|
||||
|
||||
// set the revised update rate.
|
||||
if (revisedUpdateRate > result.UpdateRate)
|
||||
{
|
||||
result.UpdateRate = revisedUpdateRate;
|
||||
}
|
||||
|
||||
// create the subscription object.
|
||||
var subscription = CreateSubscription(group, result, filters_);
|
||||
|
||||
// index by server handle.
|
||||
subscriptions_[serverHandle] = subscription;
|
||||
|
||||
// return subscription.
|
||||
return subscription;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels a subscription and releases all resources allocated for it.
|
||||
/// </summary>
|
||||
/// <param name="subscription">The subscription to cancel.</param>
|
||||
public void CancelSubscription(ITsCDaSubscription subscription)
|
||||
{
|
||||
if (subscription == null) throw new ArgumentNullException(nameof(subscription));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCServer.RemoveGroup";
|
||||
|
||||
// validate argument.
|
||||
if (!typeof(Subscription).IsInstanceOfType(subscription))
|
||||
{
|
||||
throw new ArgumentException("Incorrect object type.", nameof(subscription));
|
||||
}
|
||||
|
||||
// get the subscription state.
|
||||
var state = subscription.GetState();
|
||||
|
||||
if (!subscriptions_.ContainsKey(state.ServerHandle))
|
||||
{
|
||||
throw new ArgumentException("Handle not found.", nameof(subscription));
|
||||
}
|
||||
|
||||
subscriptions_.Remove(state.ServerHandle);
|
||||
|
||||
// release all subscription resources.
|
||||
subscription.Dispose();
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
server.RemoveGroup((int)state.ServerHandle, 0);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the children of a branch that meet the filter criteria.
|
||||
/// </summary>
|
||||
/// <param name="itemId">The identifier of branch which is the target of the search.</param>
|
||||
/// <param name="filters">The filters to use to limit the set of child elements returned.</param>
|
||||
/// <param name="position">An object used to continue a browse that could not be completed.</param>
|
||||
/// <returns>The set of elements found.</returns>
|
||||
public virtual TsCDaBrowseElement[] Browse(
|
||||
OpcItem itemId,
|
||||
TsCDaBrowseFilters filters,
|
||||
out TsCDaBrowsePosition position)
|
||||
{
|
||||
if (filters == null) throw new ArgumentNullException(nameof(filters));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCBrowse.Browse";
|
||||
|
||||
position = null;
|
||||
|
||||
// initialize arguments.
|
||||
var count = 0;
|
||||
var moreElements = 0;
|
||||
|
||||
var pContinuationPoint = IntPtr.Zero;
|
||||
var pElements = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCBrowse>(methodName, true);
|
||||
server.Browse(
|
||||
(itemId != null && itemId.ItemName != null) ? itemId.ItemName : "",
|
||||
ref pContinuationPoint,
|
||||
filters.MaxElementsReturned,
|
||||
Interop.GetBrowseFilter(filters.BrowseFilter),
|
||||
(filters.ElementNameFilter != null) ? filters.ElementNameFilter : "",
|
||||
(filters.VendorFilter != null) ? filters.VendorFilter : "",
|
||||
(filters.ReturnAllProperties) ? 1 : 0,
|
||||
(filters.ReturnPropertyValues) ? 1 : 0,
|
||||
(filters.PropertyIDs != null) ? filters.PropertyIDs.Length : 0,
|
||||
Interop.GetPropertyIDs(filters.PropertyIDs),
|
||||
out moreElements,
|
||||
out count,
|
||||
out pElements);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var elements = Interop.GetBrowseElements(ref pElements, count, true);
|
||||
|
||||
var continuationPoint = Marshal.PtrToStringUni(pContinuationPoint);
|
||||
Marshal.FreeCoTaskMem(pContinuationPoint);
|
||||
|
||||
// check if more results exist.
|
||||
if (moreElements != 0 || (continuationPoint != null && continuationPoint != ""))
|
||||
{
|
||||
// allocate new browse position object.
|
||||
position = new BrowsePosition(itemId, filters, continuationPoint);
|
||||
}
|
||||
|
||||
// process results.
|
||||
ProcessResults(elements, filters.PropertyIDs);
|
||||
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continues a browse operation with previously specified search criteria.
|
||||
/// </summary>
|
||||
/// <param name="position">An object containing the browse operation state information.</param>
|
||||
/// <returns>The set of elements found.</returns>
|
||||
public virtual TsCDaBrowseElement[] BrowseNext(ref TsCDaBrowsePosition position)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCBrowse.Browse";
|
||||
|
||||
// check for valid position object.
|
||||
if (position == null || position.GetType() != typeof(BrowsePosition))
|
||||
{
|
||||
throw new BrowseCannotContinueException();
|
||||
}
|
||||
|
||||
var pos = (BrowsePosition)position;
|
||||
|
||||
// check for valid continuation point.
|
||||
if (pos == null || pos.ContinuationPoint == null || pos.ContinuationPoint == "")
|
||||
{
|
||||
throw new BrowseCannotContinueException();
|
||||
}
|
||||
|
||||
// initialize arguments.
|
||||
var count = 0;
|
||||
var moreElements = 0;
|
||||
|
||||
var itemID = ((BrowsePosition)position).ItemID;
|
||||
var filters = ((BrowsePosition)position).Filters;
|
||||
|
||||
var pContinuationPoint = Marshal.StringToCoTaskMemUni(pos.ContinuationPoint);
|
||||
var pElements = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCBrowse>(methodName, true);
|
||||
server.Browse(
|
||||
(itemID != null && itemID.ItemName != null) ? itemID.ItemName : "",
|
||||
ref pContinuationPoint,
|
||||
filters.MaxElementsReturned,
|
||||
Interop.GetBrowseFilter(filters.BrowseFilter),
|
||||
(filters.ElementNameFilter != null) ? filters.ElementNameFilter : "",
|
||||
(filters.VendorFilter != null) ? filters.VendorFilter : "",
|
||||
(filters.ReturnAllProperties) ? 1 : 0,
|
||||
(filters.ReturnPropertyValues) ? 1 : 0,
|
||||
(filters.PropertyIDs != null) ? filters.PropertyIDs.Length : 0,
|
||||
Interop.GetPropertyIDs(filters.PropertyIDs),
|
||||
out moreElements,
|
||||
out count,
|
||||
out pElements);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var elements = Interop.GetBrowseElements(ref pElements, count, true);
|
||||
|
||||
pos.ContinuationPoint = Marshal.PtrToStringUni(pContinuationPoint);
|
||||
Marshal.FreeCoTaskMem(pContinuationPoint);
|
||||
|
||||
// check if more no results exist.
|
||||
if (moreElements == 0 && (pos.ContinuationPoint == null || pos.ContinuationPoint == ""))
|
||||
{
|
||||
position = null;
|
||||
}
|
||||
|
||||
// process results.
|
||||
ProcessResults(elements, filters.PropertyIDs);
|
||||
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the item properties for a set of items.
|
||||
/// </summary>
|
||||
/// <param name="itemIds">A list of item identifiers.</param>
|
||||
/// <param name="propertyIDs">A list of properties to fetch for each item.</param>
|
||||
/// <param name="returnValues">Whether the property values should be returned with the properties.</param>
|
||||
/// <returns>A list of properties for each item.</returns>
|
||||
public virtual TsCDaItemPropertyCollection[] GetProperties(
|
||||
OpcItem[] itemIds,
|
||||
TsDaPropertyID[] propertyIDs,
|
||||
bool returnValues)
|
||||
{
|
||||
if (itemIds == null) throw new ArgumentNullException(nameof(itemIds));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCBrowse.GetProperties";
|
||||
|
||||
// initialize arguments.
|
||||
var pItemIDs = new string[itemIds.Length];
|
||||
|
||||
for (var ii = 0; ii < itemIds.Length; ii++)
|
||||
{
|
||||
pItemIDs[ii] = itemIds[ii].ItemName;
|
||||
}
|
||||
|
||||
var pPropertyLists = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCBrowse>(methodName, true);
|
||||
server.GetProperties(
|
||||
itemIds.Length,
|
||||
pItemIDs,
|
||||
(returnValues) ? 1 : 0,
|
||||
(propertyIDs != null) ? propertyIDs.Length : 0,
|
||||
Interop.GetPropertyIDs(propertyIDs),
|
||||
out pPropertyLists);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var resultLists = Interop.GetItemPropertyCollections(ref pPropertyLists, itemIds.Length, true);
|
||||
|
||||
// replace integer codes with qnames passed in.
|
||||
if (propertyIDs != null && propertyIDs.Length > 0)
|
||||
{
|
||||
foreach (var resultList in resultLists)
|
||||
{
|
||||
for (var ii = 0; ii < resultList.Count; ii++)
|
||||
{
|
||||
resultList[ii].ID = propertyIDs[ii];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return the results.
|
||||
return resultLists;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to the specified type using the specified locale.
|
||||
/// </summary>
|
||||
protected object ChangeType(object source, Type type, string locale)
|
||||
{
|
||||
var culture = Thread.CurrentThread.CurrentCulture;
|
||||
|
||||
// override the current thread culture to ensure conversions happen correctly.
|
||||
try
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = new CultureInfo(locale);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = OpcConvert.ChangeType(source, type);
|
||||
|
||||
// check for overflow converting to float.
|
||||
if (typeof(float) == type)
|
||||
{
|
||||
if (float.IsInfinity(Convert.ToSingle(result)))
|
||||
{
|
||||
throw new OverflowException();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// restore the current thread culture after conversion.
|
||||
finally
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = culture;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a subscription.
|
||||
/// </summary>
|
||||
protected virtual Subscription CreateSubscription(
|
||||
object group,
|
||||
TsCDaSubscriptionState state,
|
||||
int filters)
|
||||
{
|
||||
return new Subscription(group, state, filters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the properties to convert COM values to OPC .NET API results.
|
||||
/// </summary>
|
||||
private void ProcessResults(TsCDaBrowseElement[] elements, TsDaPropertyID[] propertyIds)
|
||||
{
|
||||
// check for null.
|
||||
if (elements == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// process each element.
|
||||
foreach (var element in elements)
|
||||
{
|
||||
// check if no properties.
|
||||
if (element.Properties == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// process each property.
|
||||
foreach (var property in element.Properties)
|
||||
{
|
||||
// replace the property ids which on contain the codes with the proper qualified names passed in.
|
||||
if (propertyIds != null)
|
||||
{
|
||||
foreach (var propertyId in propertyIds)
|
||||
{
|
||||
if (property.ID.Code == propertyId.Code)
|
||||
{
|
||||
property.ID = propertyId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2942
Technosoftware/DaAeHdaClient.Com/Da/Subscription.cs
Normal file
2942
Technosoftware/DaAeHdaClient.Com/Da/Subscription.cs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user