HEX
Server: Apache
System: Windows NT MAGNETO-ARM 10.0 build 22000 (Windows 10) AMD64
User: Michel (0)
PHP: 7.4.7
Disabled: NONE
Upload Files
File: C:/Redmine-4.x/redmine-4.2.9/files/180306055903_OpenOpcUaClientLib.cpp
/*****************************************************************************
	  Author
		©. Michel Condemine, 4CE Industry (2010-2013)
	  
	  Contributors


	This software is a computer program whose purpose is to 
			implement behavior describe in the OPC UA specification.
		see wwww.opcfoundation.org for more details about OPC.
	This software is governed by the CeCILL-C license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL-C
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	As a counterpart to the access to the source code and  rights to copy,
	modify and redistribute granted by the license, users are provided only
	with a limited warranty  and the software's author,  the holder of the
	economic rights,  and the successive licensors  have only  limited
	liability. 

	In this respect, the user's attention is drawn to the risks associated
	with loading,  using,  modifying and/or developing or reproducing the
	software by the user in light of its specific status of free software,
	that may mean  that it is complicated to manipulate,  and  that  also
	therefore means  that it is reserved for developers  and  experienced
	professionals having in-depth computer knowledge. Users are therefore
	encouraged to load and test the software's suitability as regards their
	requirements in conditions enabling the security of their systems and/or 
	data to be ensured and,  more generally, to use and operate it in the 
	same conditions as regards security. 

	The fact that you are presently reading this means that you have had
		knowledge of the CeCILL-C license and that you accept its terms.

*****************************************************************************/
#include "stdafx.h"
#include "Application.h"
#include "OpenOpcUaClientLib.h"

#include "MonitoredItemClient.h"
#include "MonitoredItemsNotification.h"
#include "SubscriptionClient.h"
#include "ClientSession.h"
#include "LoggerMessage.h"
#include "ClientApplication.h"
#include "ClientAttribute.h" // Attribute structure used for lookup
#include "UABuiltInType.h" // BuiltIn type structure used for lookup
#include "opcua_base64.h"
#include "XmlParser.h"

using namespace OpenOpcUa;
using namespace UACoreClient;
using namespace UASharedLib;
CClientApplicationList	g_pUaClientApplicationList;
OpcUa_Boolean g_bAbstractionLayerInitialized=OpcUa_False;
OpcUa_UInt32 g_ServiceCallTimeout = CLIENTLIB_DEFAULT_TIMEOUT;
OpcUa_UInt32 CClientApplication::m_uiRequestHandle = 1;
OpcUa_Boolean g_bAutoConnectMecanism=OpcUa_True;
void OpenOpcUa_AutoConnectMecanismState(OpcUa_Boolean bActive)
{
	g_bAutoConnectMecanism = bActive;
}
///-------------------------------------------------------------------------------------------------
/// <summary>	Initialisation de la bibliothèque. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 02/01/2018. </remarks>
///
/// <param name="szApplicationName">	[in,out] If non-null, name of the application. </param>
/// <param name="hApplication">			[in,out] If non-null, the application. </param>
/// <param name="uiDefaultTimeout"> 	This timeout in milliseconds is used in the Client side 
/// 									Communication Stack to set the timeout on a per-call base. 
/// 									See (timeoutHint) OPC UA Part 4. §7.28 for detail </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_InitializeAbstractionLayer(OpcUa_CharA* szApplicationName, OpcUa_Handle* hApplication, OpcUa_UInt32 uiDefaultTimeout)
{
	OpcUa_Trace(OpcUa_Null, OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_InitializeAbstractionLayer\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (!szApplicationName)
		uStatus=OpcUa_BadInvalidArgument;
	else
	{
		//if (!g_bAbstractionLayerInitialized)
		{
			CClientApplication* g_pUaClientApplication=new CClientApplication();
			g_pUaClientApplicationList.push_back(g_pUaClientApplication);
			*hApplication=(OpcUa_Handle)g_pUaClientApplication;
			OpcUa_LocalizedText aAppName;
			OpcUa_LocalizedText_Initialize(&aAppName);
			OpcUa_String_AttachCopy(&(aAppName.Text),szApplicationName);
			OpcUa_String_AttachCopy(&(aAppName.Locale),(OpcUa_CharA*)"en-US");
			g_pUaClientApplication->SetApplicationName(&aAppName);
			OpcUa_ProxyStubConfiguration* pTraceConfiguration = g_pUaClientApplication->GetTraceConfiguration();
			uStatus=OpcUa_Good;
			g_bAbstractionLayerInitialized=OpcUa_True;
			// Save the function timeout
			g_ServiceCallTimeout = uiDefaultTimeout;
			// Il restera a initialiser l'ApplicationDescription du client
			// Elle sera mise en place lors de l'appel à OpenOpcUa_InitializeSecurity

			// On place le niveau de trace
			// Mise en place du nom du fichier de sortie
			OpcUa_LocalizedText* sApplicationName=OpcUa_Null;
			OpcUa_String strFileName;
			OpcUa_String_Initialize(&strFileName);
			OpcUa_String strBakFileName;
			OpcUa_String_Initialize(&strBakFileName);
			// extension du fichier
			OpcUa_String strExtension;
			OpcUa_String_Initialize(&strExtension);
			OpcUa_String_AttachCopy(&strExtension,".log");
			// extension de la sauvegarde
			OpcUa_String strBakExtension;
			OpcUa_String_Initialize(&strBakExtension);
			OpcUa_String_AttachCopy(&strBakExtension,".bak");
			sApplicationName=g_pUaClientApplication->GetApplicationName();
			if (sApplicationName)
			{
				OpcUa_String_CopyTo(&(sApplicationName->Text),&strFileName);
				OpcUa_String_CopyTo(&(sApplicationName->Text),&strBakFileName);

				// creation du nom du fichier de log
				OpcUa_String_StrnCat(&strFileName,&strExtension,OpcUa_String_StrLen(&strExtension));
				// creation du nom du fichier de sauvegarde
				OpcUa_String_StrnCat(&strBakFileName,&strBakExtension,OpcUa_String_StrLen(&strBakExtension));
				OpcUa_Trace_SetTraceFile(pTraceConfiguration,strFileName);
				//
				// On sauvegarde une eventuelle trace existante
				FILE* hFile=OpcUa_Null;
				OpcUa_CharA* szFileName = OpcUa_String_GetRawString(&strFileName);
				hFile=fopen(szFileName, "r");
				if (hFile)
				{
					// Ouverture du fichier de sauvegarde
					FILE* hFileBak=OpcUa_Null;
					OpcUa_CharA* szBakFileName = OpcUa_String_GetRawString(&strBakFileName);
					hFileBak=fopen(szBakFileName, "w");
					char bakBuffer;
					// On lit l'ensemble du fichier dans le buffer
					// On copie dans la sauvegarde
					while (fread(&bakBuffer,sizeof(char),1,hFile)==1)
						fwrite(&bakBuffer,sizeof(char),1,hFileBak);
					fclose(hFileBak);
					hFileBak = OpcUa_Null;
					fclose(hFile);
					// On supprime l'ancien fichier 
					hFile = OpcUa_Null;
					hFile = fopen(szFileName, "w");
					if (hFile)
						fclose(hFile);
				}
				g_pUaClientApplication->SetTraceLevel(OPCUA_TRACE_CLIENT_LEVEL_ERROR);
				g_pUaClientApplication->SetTraceOutput(OPCUA_TRACE_OUTPUT_FILE);
				OpcUa_Trace_SetTraceFile(pTraceConfiguration,strFileName);
			}
			OpcUa_String_Clear(&strExtension);
			OpcUa_String_Clear(&strFileName);
			OpcUa_String_Clear(&strBakExtension);
			OpcUa_String_Clear(&strBakFileName);
			OpcUa_LocalizedText_Clear(&aAppName);
		}
	}
	return uStatus;
}
OpcUa_StatusCode OpenOpcUa_SetTimeoutHint(OpcUa_UInt32 uiDefaultTimeout)
{
	OpcUa_StatusCode uStatus=OpcUa_Good;
	OpcUa_Trace(OpcUa_Null, OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_SetTimeoutHint\n");
	if (g_bAbstractionLayerInitialized)
	{
		if (uiDefaultTimeout > 15000)
			g_ServiceCallTimeout = uiDefaultTimeout;
		else
			uStatus = OpcUa_BadInvalidArgument;
	}
	else
		uStatus = OpcUa_BadInternalError;
	return uStatus;
}
OpcUa_StatusCode OpenOpcUa_ClearAbstractionLayer(OpcUa_Handle hApplication)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_ClearAbstractionLayer\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	OpcUa_LocalizedText* sApplicationName=OpcUa_Null;
	if (g_bAbstractionLayerInitialized)
	{
		OpcUa_LocalizedText_Initialize(sApplicationName);
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		for (CClientApplicationList::iterator it=g_pUaClientApplicationList.begin();it!=g_pUaClientApplicationList.end();it++)
		{
			if (*it==pUaClientApplication)
			{
				delete pUaClientApplication;
				pUaClientApplication = OpcUa_Null;
				g_pUaClientApplicationList.erase(it);
				break;
			}
		}
		uStatus=OpcUa_Good;
	}
	return uStatus;
}
OpcUa_StatusCode OpenOpcUa_InitializeSecurity(OpcUa_Handle hApplication,OpcUa_String szCertificateStore)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_InitializeSecurity\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	OpcUa_String sApplicationUri;
	OpcUa_LocalizedText* pApplicationName=OpcUa_Null;
	if (g_bAbstractionLayerInitialized)
	{
		OpcUa_LocalizedText_Initialize(pApplicationName);
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		pApplicationName=pUaClientApplication->GetApplicationName();
		// L'application URI est fabriquée a partifir de l'applicationName
		OpcUa_String_Initialize(&sApplicationUri);
		OpcUa_String_AttachCopy(
			&(sApplicationUri),(OpcUa_CharA*)"http://www.OpenOpcUa.org/UAClientLib/");
		OpcUa_String_StrnCat(&(sApplicationUri), &(pApplicationName->Text), OpcUa_String_StrLen(&(pApplicationName->Text)));
		//
		//uStatus=pUaClientApplication->InitializeSecurity(
		//	szCertificateStore, 
		//	&sApplicationUri, 
		//	sApplicationName,
		//	OpcUa_False); // will use the DER
		pUaClientApplication->SetCertificateStorePath(szCertificateStore);
		uStatus=pUaClientApplication->LoadPFXCertificate();
		if (uStatus==OpcUa_Good)
		{
			// on va tenter de lire le fichier DER afin de vérifier qu'il s'agit du bon DER
			// a savoir : 
			//	1- il existe
			//	2- sa clé correpsond acelle du pfx
			uStatus=pUaClientApplication->LoadDERCertificate();
			if (uStatus!=OpcUa_Good)
				uStatus=pUaClientApplication->CreateCertificate();
		}
		else
		{
			uStatus=pUaClientApplication->CreateCertificate();
		}
		uStatus = pUaClientApplication->InitializeSecurity(&sApplicationUri, pApplicationName);
		if (uStatus==OpcUa_Good)
		{
			// Maintenant que tout est pret on va remplir l'ApplicationDescription de ce client
			// Creation et remplissage d'un OpcUa_ApplicationDescription.
			// Il contient l'ensemble des informations relative a cette application UA (client)
			OpcUa_ApplicationDescription* pAppDescription=(OpcUa_ApplicationDescription*)OpcUa_Alloc(sizeof(OpcUa_ApplicationDescription));
			OpcUa_ApplicationDescription_Initialize(pAppDescription);
			pAppDescription->ApplicationType=OpcUa_ApplicationType_Client;
			// ApplicationUri
			OpcUa_String_CopyTo(&sApplicationUri, &(pAppDescription->ApplicationUri));
			// ApplicationName
			OpcUa_LocalizedText_CopyTo(pApplicationName, &(pAppDescription->ApplicationName));
			// ProductUri
			OpcUa_String_CopyTo(&sApplicationUri, &(pAppDescription->ProductUri));
			pUaClientApplication->SetApplicationDescription(pAppDescription);
			OpcUa_ApplicationDescription_Clear(pAppDescription);
			OpcUa_Free(pAppDescription);
		}
	}
	OpcUa_String_Clear(&sApplicationUri);
	//if (pApplicationName)
	//{
	//	OpcUa_LocalizedText_Clear(pApplicationName);
	//	OpcUa_Free(pApplicationName);
	//}
	return uStatus;
}
// FindServers
// L'usage classique de cette fonction consiste a demander au LDS ou au server ses ApplicationDescription
OpcUa_StatusCode OpenOpcUa_FindServers(OpcUa_Handle hApplication,OpcUa_String* hostName,OpcUa_Int32* uiNoOfServer,OpcUa_ApplicationDescription** pServers)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_FindServers\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		OpcUa_RequestHeader tRequestHeader;
		OpcUa_String sDiscoveryUrl;
		OpcUa_String sLocaleIds;
		OpcUa_String sServerUris;
		OpcUa_ResponseHeader tResponseHeader;
		
		OpcUa_RequestHeader_Initialize(&tRequestHeader);
		OpcUa_String_Initialize(&sDiscoveryUrl);
		OpcUa_String_Initialize(&sLocaleIds);
		OpcUa_String_Initialize(&sServerUris);
		OpcUa_ResponseHeader_Initialize(&tResponseHeader);
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			// Construction de la discoveryUrl
			std::string tmpDiscoveryUrl;
			// construct the discovery url from the host name.	
			tmpDiscoveryUrl.append("opc.tcp://");
			if (OpcUa_String_StrLen(hostName)>0)
				tmpDiscoveryUrl.append(OpcUa_String_GetRawString(hostName));
			else
				tmpDiscoveryUrl.append("localhost");
			tmpDiscoveryUrl.append(":4840");
			uStatus=OpcUa_String_AttachCopy(&sDiscoveryUrl,(OpcUa_CharA*)tmpDiscoveryUrl.c_str());
			// 
			tmpDiscoveryUrl.clear();
			CChannel channel(pUaClientApplication);
			OpcUa_MessageSecurityMode eSecurityMode = OpcUa_MessageSecurityMode_None;
			OpcUa_String szSecurityPolicy;
			OpcUa_String_Initialize(&szSecurityPolicy);
			uStatus = channel.Connect(sDiscoveryUrl,eSecurityMode,szSecurityPolicy);
			if (uStatus==OpcUa_Good)
			{
				// find the servers.
				tRequestHeader.TimeoutHint = g_ServiceCallTimeout;// UTILS_DEFAULT_TIMEOUT;
				tRequestHeader.Timestamp   = OpcUa_DateTime_UtcNow();
				tRequestHeader.RequestHandle = CClientApplication::m_uiRequestHandle++;
				// find the servers.
				uStatus = OpcUa_ClientApi_FindServers(  
					channel.GetInternalHandle(), 
					&tRequestHeader,
					&sDiscoveryUrl,
					0,
					&sLocaleIds, 
					0,
					&sServerUris, 
					&tResponseHeader,
					uiNoOfServer,
					pServers);
			}
			tmpDiscoveryUrl.clear();
		}
		OpcUa_String_Clear(&sDiscoveryUrl);
		OpcUa_String_Clear(&sLocaleIds);
		OpcUa_String_Clear(&sServerUris);
	}
	return uStatus;
}

// GetEndpoints
OpcUa_StatusCode OpenOpcUa_GetEndpoints(OpcUa_Handle hApplication,OpcUa_String* discoveryUrl,OpcUa_UInt32* uiNoOfEndpointDescription,OpcUa_EndpointDescription** ppEndpointDescription)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetEndpoints\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CEndpointDescriptionList* pEndpoints=new CEndpointDescriptionList();
			if (pEndpoints)
			{
				uStatus = pUaClientApplication->DiscoverEndpoints(*discoveryUrl, pEndpoints);
				if (uStatus == OpcUa_Good)
				{
					*uiNoOfEndpointDescription = pEndpoints->size();

					OpcUa_EndpointDescription* pTmpEndpointDescription = (OpcUa_EndpointDescription*)OpcUa_Alloc((*uiNoOfEndpointDescription)*sizeof(OpcUa_EndpointDescription));
					*ppEndpointDescription = pTmpEndpointDescription;

					for (unsigned int ii = 0; ii < (*uiNoOfEndpointDescription); ii++)
					{
						CEndpointDescription* pEndpointDescription = pEndpoints->at(ii);
						if (pEndpointDescription)
						{
							OpcUa_EndpointDescription_Initialize(&pTmpEndpointDescription[ii]);
							OpcUa_EndpointDescription* pInternalEndpointDescription = pEndpointDescription->GetInternalEndPointDescription();
							if (pInternalEndpointDescription)
							{
								///////////////////////////////////////////////////////////////////////////////////////
								// Copy the EndpointUrl
								if (OpcUa_String_Compare(discoveryUrl, &(pInternalEndpointDescription->EndpointUrl)) == 0)
									OpcUa_String_CopyTo(&(pInternalEndpointDescription->EndpointUrl), &(pTmpEndpointDescription[ii].EndpointUrl));
								else
									OpcUa_String_CopyTo(discoveryUrl, &(pTmpEndpointDescription[ii].EndpointUrl));
								// Copy the UserIdentity Tokens
								pTmpEndpointDescription[ii].NoOfUserIdentityTokens = pInternalEndpointDescription->NoOfUserIdentityTokens;
								OpcUa_UInt32 iLen = pTmpEndpointDescription[ii].NoOfUserIdentityTokens;
								if (iLen)
								{
									pTmpEndpointDescription[ii].UserIdentityTokens = (OpcUa_UserTokenPolicy*)OpcUa_Alloc(iLen*sizeof(OpcUa_UserTokenPolicy));
									ZeroMemory(pTmpEndpointDescription[ii].UserIdentityTokens, iLen*sizeof(OpcUa_UserTokenPolicy));
									for (unsigned int iii = 0; iii < iLen; iii++)
									{
										OpcUa_String_CopyTo(&(pInternalEndpointDescription->UserIdentityTokens[iii].IssuedTokenType),
											&(pTmpEndpointDescription[ii].UserIdentityTokens[iii].IssuedTokenType));
										OpcUa_String_CopyTo(&(pInternalEndpointDescription->UserIdentityTokens[iii].IssuerEndpointUrl),
											&(pTmpEndpointDescription[ii].UserIdentityTokens[iii].IssuerEndpointUrl));;
										OpcUa_String_CopyTo(&(pInternalEndpointDescription->UserIdentityTokens[iii].PolicyId),
											&(pTmpEndpointDescription[ii].UserIdentityTokens[iii].PolicyId));
										OpcUa_String_CopyTo(&(pInternalEndpointDescription->UserIdentityTokens[iii].SecurityPolicyUri),
											&(pTmpEndpointDescription[ii].UserIdentityTokens[iii].SecurityPolicyUri));
										pTmpEndpointDescription[ii].UserIdentityTokens[iii].TokenType = pInternalEndpointDescription->UserIdentityTokens[iii].TokenType;
									}
								}
								// Copy SecurityLevel and SecurityMode
								pTmpEndpointDescription[ii].SecurityLevel = pInternalEndpointDescription->SecurityLevel;
								pTmpEndpointDescription[ii].SecurityMode = pInternalEndpointDescription->SecurityMode;
								// Copy SecurityPolicyUri
								OpcUa_String aString = pInternalEndpointDescription->SecurityPolicyUri;
								OpcUa_String_CopyTo(&aString, &(pTmpEndpointDescription[ii].SecurityPolicyUri));
								//Copy ApplicationDescription
								//OpcUa_ApplicationDescription* pApplicationDescription=Utils::Copy(&(pInternalEndpointDescription->Server));	
								// fill the ApplicationDescription
								// ApplicationName
								OpcUa_LocalizedText_CopyTo(&(pInternalEndpointDescription->Server.ApplicationName),
									&(pTmpEndpointDescription[ii].Server.ApplicationName));
								// ApplicationType
								pTmpEndpointDescription[ii].Server.ApplicationType = pInternalEndpointDescription->Server.ApplicationType;
								// ApplicationUri
								OpcUa_String_CopyTo(&(pInternalEndpointDescription->Server.ApplicationUri),
									&(pTmpEndpointDescription[ii].Server.ApplicationUri));
								// DiscoveryProfileUri
								OpcUa_String_CopyTo(&(pInternalEndpointDescription->Server.DiscoveryProfileUri),
									&(pTmpEndpointDescription[ii].Server.DiscoveryProfileUri));
								// DiscoveryUrls
								if (pInternalEndpointDescription->Server.NoOfDiscoveryUrls > 0)
								{
									pTmpEndpointDescription[ii].Server.NoOfDiscoveryUrls = pInternalEndpointDescription->Server.NoOfDiscoveryUrls;
									pTmpEndpointDescription[ii].Server.DiscoveryUrls = (OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String)*pInternalEndpointDescription->Server.NoOfDiscoveryUrls);
									for (OpcUa_Int32 u = 0; u < pInternalEndpointDescription->Server.NoOfDiscoveryUrls; u++)
									{
										OpcUa_String_Initialize(&(pTmpEndpointDescription[ii].Server.DiscoveryUrls[u]));
										OpcUa_String_CopyTo(&(pInternalEndpointDescription->Server.DiscoveryUrls[u]),
											&(pTmpEndpointDescription[ii].Server.DiscoveryUrls[u]));
									}
								}
								// GatewayServerUri
								OpcUa_String_CopyTo(&(pInternalEndpointDescription->Server.GatewayServerUri),
									&(pTmpEndpointDescription[ii].Server.GatewayServerUri));
								// ProductUri
								OpcUa_String_CopyTo(&(pInternalEndpointDescription->Server.ProductUri),
									&(pTmpEndpointDescription[ii].Server.ProductUri));

								// Copy ServerCertificate
								OpcUa_ByteString_CopyTo(&(pInternalEndpointDescription->ServerCertificate), &(pTmpEndpointDescription[ii].ServerCertificate));
								// Copy TransportProfileUri
								OpcUa_String_CopyTo(&(pInternalEndpointDescription->TransportProfileUri), &(pTmpEndpointDescription[ii].TransportProfileUri));
							}
						}
					}

				}
				// clear and release pEndPoints discovered
				CEndpointDescriptionList::iterator it;
				for (it = pEndpoints->begin(); it != pEndpoints->end(); it++)
				{
					CEndpointDescription* pEndpointDescription = *it;
					delete pEndpointDescription;
				}
				pEndpoints->clear();
				delete pEndpoints;
			}
		}
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	recupère une ApplicationDescription en fonction de sont Handle. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">   	The application. </param>
/// <param name="pAppDescription">	[in,out] If non-null, information describing the application.
/// </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetApplicationDescription(OpcUa_Handle hApplication, OpcUa_ApplicationDescription* pAppDescription)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetApplicationDescription\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			pAppDescription=pUaClientApplication->GetApplicationDescription()->GetInternalApplicationDescription();
			uStatus=OpcUa_Good;
		}
	}
	return uStatus;
}// recupère une EndpointDescription en fonction de sont Handle
// Il s'agit des recupérer les informations sur le serveur sur lequel le client est connecté actuellement
OpcUa_StatusCode OpenOpcUa_GetEndpointDescription(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_EndpointDescription** pEndpointDescription)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetEndpointDescription\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				CEndpointDescription* pUAEndpointDescription = pSession->GetEndpointDescription();
				if (pUAEndpointDescription)
				{
					*pEndpointDescription = pUAEndpointDescription->GetInternalEndPointDescription();
					uStatus = OpcUa_Good;
				}
			}
		}
	}
	return uStatus;
}
// Activate Session
/// <summary>
/// Activate the previously created hSession.
/// </summary>
/// <param name="hApplication">Handle of your Application.</param>
/// <param name="hSession">Hadnle of the Session to activcate.</param>
/// <returns></returns>
OpcUa_StatusCode OpenOpcUa_ActivateSession(OpcUa_Handle hApplication, OpcUa_Handle hSession, 
	OpcUa_UserTokenType userTokenType, 
	OpcUa_String* pszPolicyId,
	OpcUa_String* pszEncryptionAlgorithm, 
	OpcUa_String* pszUserName, 
	OpcUa_ByteString* PasswordorCertificateData)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_ActivateSession\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	// Those are the two legal encryption algorythm name
	// So adjust the name receive from the client according to those one
	OpcUa_String szRSA_OAEP;
	OpcUa_String_Initialize(&szRSA_OAEP);
	OpcUa_String_AttachCopy(&szRSA_OAEP, "http://www.w3.org/2001/04/xmlenc#rsa-oaep");
	OpcUa_String szRSA_15;
	OpcUa_String_Initialize(&szRSA_15);
	OpcUa_String_AttachCopy(&szRSA_15, "http://www.w3.org/2001/04/xmlenc#rsa-1_5");
	// expected cypher name
	OpcUa_String basic128RSA15;
	OpcUa_String_Initialize(&basic128RSA15);
	OpcUa_String_AttachCopy(&basic128RSA15, "Basic128Rsa15");
	OpcUa_String basic256;
	OpcUa_String_Initialize(&basic256);
	OpcUa_String_AttachCopy(&basic256, "Basic256");
	OpcUa_String basic256Sha256;
	OpcUa_String_Initialize(&basic256Sha256);
	OpcUa_String_AttachCopy(&basic256Sha256, "Basic256Sha256");
	if (OpcUa_String_Compare(pszEncryptionAlgorithm, &basic128RSA15) == 0)
	{ 
		OpcUa_String_Clear(pszEncryptionAlgorithm);
		OpcUa_String_Initialize(pszEncryptionAlgorithm);
		OpcUa_String_CopyTo(&szRSA_15, pszEncryptionAlgorithm);
	}
	else
	{
		if (OpcUa_String_Compare(pszEncryptionAlgorithm, &basic256) == 0)
		{ 
			OpcUa_String_Clear(pszEncryptionAlgorithm);
			OpcUa_String_Initialize(pszEncryptionAlgorithm);
			OpcUa_String_CopyTo(&szRSA_15, pszEncryptionAlgorithm);
		}
	}
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				uStatus = pSession->Activate(userTokenType,pszPolicyId,pszEncryptionAlgorithm,pszUserName,PasswordorCertificateData);
				if (uStatus == OpcUa_Good)
					pSession->AutoConnectThreadUnlock();
			}
		}
		else
			uStatus=OpcUa_BadInvalidArgument;
	}
	OpcUa_String_Clear(&szRSA_OAEP);
	OpcUa_String_Clear(&szRSA_15);
	OpcUa_String_Clear(&basic128RSA15);
	OpcUa_String_Clear(&basic256);
	OpcUa_String_Clear(&basic256Sha256);
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Create Session. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 03/01/2017. </remarks>
///
/// <param name="hApplication">					 	The application. </param>
/// <param name="pEndpointDescription">			 	[in,out] If non-null, information describing
/// 												the endpoint.
/// </param>
/// <param name="nRequestedClientSessionTimeout">	The requested client session timeout. </param>
/// <param name="aSessionName">					 	Name of the session. </param>
/// <param name="hSession">						 	[in,out] If non-null, the session. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_CreateSession(OpcUa_Handle hApplication,
										 OpcUa_EndpointDescription* pUAEndpointDescription,
										 OpcUa_Double nRequestedClientSessionTimeout,
										 OpcUa_String aSessionName, 
										 OpcUa_Handle* hSession)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_CreateSession\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			if (pUAEndpointDescription->SecurityMode != OpcUa_MessageSecurityMode_None)
				pUaClientApplication->TrustCertificate(&(pUAEndpointDescription->ServerCertificate));

			CSessionClient*  pSession=new CSessionClient(pUaClientApplication);
			if (pSession)
			{
				if (pSession->GetInternalServerStatus() != OpcUa_BadInternalError)
				{
					CEndpointDescription* pEndpointDescription = new CEndpointDescription(pUAEndpointDescription);
					uStatus = pSession->Create(pEndpointDescription, &aSessionName, nRequestedClientSessionTimeout);
					if (uStatus == OpcUa_Good)
					{
						pUaClientApplication->AddSessionClient(pSession);
						*hSession = (OpcUa_Handle)pSession;
						// creation de la thread de surveillance du serveur
						pSession->StartWatchingThread();
						// creation de la thread de  publication
						pSession->StartPublishingThread();
					}
					else
						delete pSession;
					delete pEndpointDescription;
				}
				else
				{
					uStatus=OpcUa_BadInternalError;
					delete pSession;
				}
			}
			else
				uStatus = OpcUa_BadOutOfMemory;
		}
		else
			uStatus=OpcUa_BadInvalidArgument;
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Get the session params for the session associate with a given handle (hSession). </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 03/01/2017. </remarks>
///
/// <param name="hApplication"> 	The application. </param>
/// <param name="hSession">			The session. </param>
/// <param name="pSessionParam">	[in,out] If non-null, the session parameter. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetSessionParams(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpenOpcUa_SessionParam* pSessionParam)
{
	OpcUa_StatusCode uStatus = OpcUa_BadInternalError;
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetSessionParams\n");
	if (pSessionParam)
	{
		if (g_bAbstractionLayerInitialized)
		{
			CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
			if (pUaClientApplication)
			{
				CSessionClient* pSession = OpcUa_Null;// (CSessionClient*)hSession;
				uStatus = pUaClientApplication->GetSession(hSession, &pSession);
				if ((uStatus == OpcUa_Good) && (pSession))
				{
					// SessionTimeout
					pSessionParam->dblSessionTimeout = pSession->GeSessionTimeout();
					// SessionName
					OpcUa_CharA* pszTmpString=OpcUa_Null;
					OpcUa_String* pSessionName = pSession->GetSessionName();
					OpcUa_UInt32 uiLen = OpcUa_String_StrLen(pSessionName);
					pszTmpString = OpcUa_String_GetRawString(pSessionName);
					pSessionParam->szSessionName = (OpcUa_CharA*)OpcUa_Alloc(uiLen + 1);
					ZeroMemory(pSessionParam->szSessionName, uiLen + 1);
					memcpy(pSessionParam->szSessionName, pszTmpString, uiLen);
					// EndpointDescription
					CEndpointDescription* pEndpointDescription=pSession->GetEndpointDescription();
					pSessionParam->pEndpoint = Utils::Copy(pEndpointDescription->GetInternalEndPointDescription());
					// SessionId
					OpcUa_NodeId aNodeId = pSession->GetSessionId();
					OpcUa_NodeId_CopyTo(&aNodeId, &(pSessionParam->sessionId));
					// UserName
					OpcUa_String szUserName = pSession->GetSessionUserName();
					uiLen = OpcUa_String_StrLen(&szUserName);
					pszTmpString = OpcUa_String_GetRawString(&szUserName);
					pSessionParam->szUserName= (OpcUa_CharA*)OpcUa_Alloc(uiLen + 1);
					ZeroMemory(pSessionParam->szUserName, uiLen + 1);
					memcpy(pSessionParam->szUserName, pszTmpString, uiLen);
					// UserTokenType
					pSessionParam->userTokenType=pSession->GetUserTokenType();
					uStatus = OpcUa_Good;
				}
			}
		}
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}
///-------------------------------------------------------------------------------------------------
/// <summary>	Close Session. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 03/01/2017. </remarks>
///
/// <param name="hApplication">	The application. </param>
/// <param name="hSession">	   	The session. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_CloseSession(OpcUa_Handle hApplication,OpcUa_Handle hSession)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_CloseSession\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;

	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			OpcUa_ProxyStubConfiguration* pTraceConfiguration = pUaClientApplication->GetTraceConfiguration();
			CSessionClient* pSession = OpcUa_Null;// (CSessionClient*)hSession;
			uStatus = pUaClientApplication->GetSession(hSession, &pSession);
			if ( (uStatus==OpcUa_Good) && (pSession) )
			{
				if (!pSession->IsWatchingThreadDetectError())
				{
					// First of all we need to stop the watching thread to avoid side effets					
					pSession->StopWatchingThread();
					// 
					uStatus = pSession->DeleteAllSubscriptions(OpcUa_True);
					if (uStatus != OpcUa_Good)
						OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_ERROR, "DeleteAllSubscriptions failed 0x%05x\n", uStatus);
					else
					{
						uStatus = pUaClientApplication->RemoveSessionClient(pSession);
					}
				}
				else
				{
					OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_ERROR, "The Watching Thread DetectError detected an error. The session has been closed that way\n");
					uStatus = OpcUa_Good;
				}
			}
		}
		else
			uStatus=OpcUa_BadInvalidArgument;
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Create Subscription. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 03/01/2017. </remarks>
///
/// <param name="hApplication">				   	The application. </param>
/// <param name="hSession">					   	The session. </param>
/// <param name="dblPublishingInterval">	   	[in,out] If non-null, the publishing interval.
/// </param>
/// <param name="uiLifetimeCount">			   	[in,out] If non-null, number of lifetimes. </param>
/// <param name="uiMaxKeepAliveCount">		   	[in,out] If non-null, number of maximum keep
/// 											alives.
/// </param>
/// <param name="uiMaxNotificationsPerPublish">	The maximum notifications per publish. </param>
/// <param name="bPublishingEnabled">		   	The publishing enabled. </param>
/// <param name="aPriority">				   	The priority. </param>
/// <param name="hSubscription">			   	[in,out] If non-null, the subscription. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_CreateSubscription(OpcUa_Handle hApplication,OpcUa_Handle hSession,
											  OpcUa_Double* dblPublishingInterval, // In/Out param
											  OpcUa_UInt32* uiLifetimeCount, // In/Out param
											  OpcUa_UInt32* uiMaxKeepAliveCount,// In/Out param
											  OpcUa_UInt32 uiMaxNotificationsPerPublish,
											  OpcUa_Boolean bPublishingEnabled,
											  OpcUa_Byte aPriority,
											  OpcUa_Handle* hSubscription)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_ERROR, "Call OpenOpcUa_CreateSubscription\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSessionClient = (CSessionClient*)hSession;
			if (pSessionClient)
			{
				//OpcUa_Mutex_Lock(pSessionClient->m_AutoConnectThreadMutex);
				uStatus = pSessionClient->CreateSubscription(dblPublishingInterval, 
															uiLifetimeCount, 
															uiMaxKeepAliveCount, 
															uiMaxNotificationsPerPublish, 
															bPublishingEnabled, 
															aPriority, hSubscription);
				pSessionClient->SetInternalServerStatus(uStatus);
				pSessionClient->SetInternalPublishStatus(uStatus);
				//OpcUa_Mutex_Unlock(pSessionClient->m_AutoConnectThreadMutex);
			}
		}
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Delete Subscription. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 03/01/2017. </remarks>
///
/// <param name="hApplication"> 	The application. </param>
/// <param name="hSession">			The session. </param>
/// <param name="hSubscription">	The subscription. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_DeleteSubscription(OpcUa_Handle hApplication,
											  OpcUa_Handle hSession,
											  OpcUa_Handle hSubscription)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_ERROR, "Call OpenOpcUa_DeleteSubscription\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSessionClient = (CSessionClient*)hSession;
			if (pSessionClient)
			{
				CSubscriptionClient* pSubscriptionClient=(CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
					pSubscriptionClient->MarkForDeletion(OpcUa_True);
					OpcUa_Mutex_Lock(pSessionClient->m_AutoConnectThreadMutex);
					uStatus = pSessionClient->DeleteSubscription(pSubscriptionClient, OpcUa_True);
					if (uStatus!=OpcUa_Good)
						pSubscriptionClient->MarkForDeletion(OpcUa_False);
					OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_ERROR, "OpenOpcUa_DeleteSubscription DONE 0x%05x\n",uStatus);
					OpcUa_Mutex_Unlock(pSessionClient->m_AutoConnectThreadMutex);
				}
				else
					uStatus=OpcUa_BadInvalidArgument;
			}
			else
				uStatus=OpcUa_BadInvalidArgument;
		}
		else
			uStatus=OpcUa_BadInvalidArgument;
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Modify Subscription. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">				   	The application. </param>
/// <param name="hSession">					   	The session. </param>
/// <param name="hSubscription">			   	The subscription. </param>
/// <param name="dblPublishingInterval">	   	[in,out] If non-null, the publishing interval.
/// </param>
/// <param name="uiLifetimeCount">			   	[in,out] If non-null, number of lifetimes. </param>
/// <param name="uiMaxKeepAliveCount">		   	[in,out] If non-null, number of maximum keep
/// 											alives.
/// </param>
/// <param name="uiMaxNotificationsPerPublish">	The maximum notifications per publish. </param>
/// <param name="aPriority">				   	The priority. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_ModifySubscription(OpcUa_Handle hApplication,
											  OpcUa_Handle hSession,
											  OpcUa_Handle hSubscription,
											  OpcUa_Double* dblPublishingInterval, // In/Out param
											  OpcUa_UInt32* uiLifetimeCount, // In/Out param
											  OpcUa_UInt32* uiMaxKeepAliveCount,// In/Out param
											  OpcUa_UInt32 uiMaxNotificationsPerPublish,
											  OpcUa_Byte aPriority)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_ModifySubscription\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				CSubscriptionClient* pSubscriptionClient=(CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
					uStatus=pSubscriptionClient->ModifySubscription(*dblPublishingInterval,*uiLifetimeCount,*uiMaxKeepAliveCount,uiMaxNotificationsPerPublish,aPriority);
				}
				else
					uStatus=OpcUa_BadInvalidArgument;
			}
			else
				uStatus=OpcUa_BadInvalidArgument;
		}
		else
			uStatus=OpcUa_BadInvalidArgument;
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	retrieve the subscription attached to this monitoredItem. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">  	The application. </param>
/// <param name="hSession">		 	The session. </param>
/// <param name="hMonitoredItem">	The monitored item. </param>
/// <param name="hSubscription"> 	[in,out] If non-null, the subscription. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetSubscriptionOfMonitoredItem(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_Handle hMonitoredItem, OpcUa_Handle* hSubscription)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetSubscriptionOfMonitoredItem\n");
	OpcUa_StatusCode uStatus = OpcUa_BadNotFound;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			if (!hSession)
			{
				// will scan all sessions
				CSubscriptionClient* pSubscriptionClient = OpcUa_Null;
				uStatus=pUaClientApplication->GetSubscriptionOfMonitoredItem(hMonitoredItem,&pSubscriptionClient);
				if (uStatus==OpcUa_Good)
				{
					(*hSubscription) = (OpcUa_Handle)pSubscriptionClient;
				}
			}
			else
			{
				// scan just hSession
				CSessionClient* pSession = OpcUa_Null;
				uStatus = pUaClientApplication->GetSession(hSession, &pSession);
				if (uStatus == OpcUa_Good)
				{
					CSubscriptionClient* pSubscriptionClient = OpcUa_Null;
					uStatus=pSession->GetSubscriptionOfMonitoredItem(hMonitoredItem,&pSubscriptionClient);
					if (uStatus==OpcUa_Good)
					{
						(*hSubscription) = (OpcUa_Handle)pSubscriptionClient;
					}
				}
			}		
		}
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Retrieve the subscription params of the given subscription (hSubscription) </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">		 	The application. </param>
/// <param name="hSession">			 	The session. </param>
/// <param name="hSubscription">	 	The subscription. </param>
/// <param name="pSubscriptionParam">	[in,out] If non-null, the subscription parameter. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetSubscriptionParams(OpcUa_Handle hApplication,
													OpcUa_Handle hSession,
													OpcUa_Handle hSubscription,
													OpenOpcUa_SubscriptionParam* pSubscriptionParam)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetSubscriptionParams\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (pSubscriptionParam)
	{
		if (g_bAbstractionLayerInitialized)
		{
			CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
			if (pUaClientApplication)
			{
				CSessionClient* pSession = (CSessionClient*)hSession;
				if (pSession)
				{
					CSubscriptionClient* pSubscriptionClient = (CSubscriptionClient*)hSubscription;
					if (pSubscriptionClient)
					{
						pSubscriptionParam->uiSubscriptionId = pSubscriptionClient->GetSubscriptionId();
						pSubscriptionParam->bPublishingEnabled = pSubscriptionClient->GetPublishingEnabled();
						pSubscriptionParam->dblPublishingInterval = pSubscriptionClient->GetPublishingInterval();
						pSubscriptionParam->uiLifetimeCount = pSubscriptionClient->GetLifetimeCount();
						pSubscriptionParam->uiMaxKeepAliveCount = pSubscriptionClient->GetMaxKeepAliveCount();
						pSubscriptionParam->aPriority = pSubscriptionClient->GetPriority();
						pSubscriptionParam->uiMaxNotificationsPerPublish = pSubscriptionClient->GetMaxNotificationsPerPublish();
					}
					else
						uStatus = OpcUa_BadInvalidArgument;
				}
				else
					uStatus = OpcUa_BadInvalidArgument;
			}
			else
				uStatus = OpcUa_BadInvalidArgument;
		}
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA set publishing mode. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication"> 	The application. </param>
/// <param name="hSession">			The session. </param>
/// <param name="hSubscription">	The subscription. </param>
/// <param name="bPublishMode"> 	The publish mode. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_SetPublishingMode(OpcUa_Handle hApplication,
											  OpcUa_Handle hSession,
											  OpcUa_Handle hSubscription,
											  OpcUa_Boolean bPublishMode)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_SetPublishingMode\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				CSubscriptionClient* pSubscriptionClient=(CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
					uStatus=pSubscriptionClient->SetPublishingMode(bPublishMode);
				}
				else
					uStatus=OpcUa_BadInvalidArgument;
			}
			else
				uStatus=OpcUa_BadInvalidArgument;
		}
		else
			uStatus=OpcUa_BadInvalidArgument;
	}
	return uStatus;
}
// Create MonitoredItems
OpcUa_StatusCode OpenOpcUa_CreateMonitoredItemsEx(	OpcUa_Handle					hApplication,// in
													OpcUa_Handle					hSession, // in
													OpcUa_Handle					hSubscription,// in
													OpcUa_UInt32					NoOfItemsToCreate, // in
													OpcUa_MonitoredItemToCreate*	pItemsToCreate, // in
													OpcUa_MonitoredItemCreated**	ppItemsCreated) // out

{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_CreateMonitoredItemsEx\n");
	OpcUa_StatusCode uStatus = OpcUa_BadInternalError;

	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession = (CSessionClient*)hSession;
			if (pSession)
			{
				OpcUa_Mutex_Lock(pSession->m_AutoConnectThreadMutex);
				if ((pSession->GetSessionState() == SESSION_STATE_ON_ACTIVE) || (pSession->GetSessionState() == SESSION_STATE_SUBSCRIBED))
				{
					CSubscriptionClient* pSubscriptionClient = (CSubscriptionClient*)hSubscription;
					if (pSubscriptionClient)
					{
						if (NoOfItemsToCreate > 0)
						{
							OpcUa_MonitoredItemCreateRequest* pItemsToCreateRequest = (OpcUa_MonitoredItemCreateRequest*)OpcUa_Alloc(sizeof(OpcUa_MonitoredItemCreateRequest)*NoOfItemsToCreate);
							if (pItemsToCreateRequest)
							{
								//OpcUa_UInt32 uiNoOfMonitoreditem = 0;
								//OpcUa_Handle* phMonitoredItem = OpcUa_Null;
								ZeroMemory(pItemsToCreateRequest, sizeof(OpcUa_MonitoredItemCreateRequest)*NoOfItemsToCreate);
								for (OpcUa_UInt32 i = 0; i < NoOfItemsToCreate; i++)
								{
									pItemsToCreateRequest[i].ItemToMonitor.AttributeId = pItemsToCreate[i].m_AttributeId;
									OpcUa_String_Initialize(&(pItemsToCreateRequest[i].ItemToMonitor.IndexRange));
									//if (OpcUa_String_StrLen(&(pItemsToCreate[i].m_IndexRange))>0)
									//	OpcUa_String_CopyTo(&(pItemsToCreate[i].m_IndexRange), &(pItemsToCreateRequest[i].ItemToMonitor.IndexRange));
									OpcUa_NodeId_CopyTo(&(pItemsToCreate[i].m_NodeId), &(pItemsToCreateRequest[i].ItemToMonitor.NodeId));
									pItemsToCreateRequest[i].MonitoringMode = pItemsToCreate[i].m_MonitoringMode;
									pItemsToCreateRequest[i].RequestedParameters.ClientHandle = pItemsToCreate[i].m_ClientHandle;
									pItemsToCreateRequest[i].RequestedParameters.DiscardOldest = pItemsToCreate[i].m_DiscardOldest;
									// Filter;
									OpcUa_ExtensionObject_Initialize(&(pItemsToCreateRequest[i].RequestedParameters.Filter));
									pItemsToCreateRequest[i].RequestedParameters.Filter.TypeId.NodeId.Identifier.Numeric = OpcUaId_DataChangeFilter_Encoding_DefaultBinary;
									pItemsToCreateRequest[i].RequestedParameters.Filter.Encoding = OpcUa_ExtensionObjectEncoding_EncodeableObject;
									//pItemsToCreateRequest[i].RequestedParameters.Filter.Body.EncodeableObject.Type = (OpcUa_EncodeableType*)OpcUa_Alloc(sizeof(OpcUa_EncodeableType));
									pItemsToCreateRequest[i].RequestedParameters.Filter.Body.EncodeableObject.Type = &OpcUa_DataChangeFilter_EncodeableType;
									OpcUa_DataChangeFilter* pDataChangeFilter = (OpcUa_DataChangeFilter*)OpcUa_Alloc(sizeof(OpcUa_DataChangeFilter));
									if (pDataChangeFilter)
									{
										OpcUa_DataChangeFilter_Initialize(pDataChangeFilter);
										pDataChangeFilter->DeadbandType = pItemsToCreate[i].m_DeadbandType;
										pDataChangeFilter->DeadbandValue = pItemsToCreate[i].m_DeadbandValue;
										pDataChangeFilter->Trigger = (OpcUa_DataChangeTrigger)pItemsToCreate[i].m_DatachangeTrigger;
										pItemsToCreateRequest[i].RequestedParameters.Filter.Body.EncodeableObject.Object = (void*)pDataChangeFilter;
									}
									pItemsToCreateRequest[i].RequestedParameters.QueueSize = pItemsToCreate[i].m_QueueSize;
									pItemsToCreateRequest[i].RequestedParameters.SamplingInterval = pItemsToCreate[i].m_SamplingInterval;
								}
								OpcUa_TimestampsToReturn sharedTimestampToReturn = pItemsToCreate[0].m_TimestampsToReturn;
								OpcUa_MonitoredItemCreateResult* pResult = OpcUa_Null;
								OpcUa_UInt32* hMonitoredItems = OpcUa_Null;
								// We will subscribe to the requested nodes
								uStatus = pSubscriptionClient->CreateMonitoredItems(
									(OpcUa_TimestampsToReturn)sharedTimestampToReturn,
									NoOfItemsToCreate,
									pItemsToCreateRequest,
									&pResult,
									&hMonitoredItems);
								if (uStatus == OpcUa_Good)
								{
									OpcUa_Int32 iNodNodesToRead = 0;
									//OpcUa_DataValue* pResults = OpcUa_Null;
									iNodNodesToRead = NoOfItemsToCreate;
									//////////////
									// Fill the result of the creation
									(*ppItemsCreated) = (OpcUa_MonitoredItemCreated*)OpcUa_Alloc(sizeof(OpcUa_MonitoredItemCreated)*NoOfItemsToCreate);
									ZeroMemory((*ppItemsCreated), sizeof(OpcUa_MonitoredItemCreated)*NoOfItemsToCreate);
									for (OpcUa_UInt32 ii = 0; ii < NoOfItemsToCreate; ii++)
									{
										(*ppItemsCreated)[ii].m_hMonitoredItem = (OpcUa_Handle)hMonitoredItems[ii];
										(*ppItemsCreated)[ii].m_MonitoredItemId = pResult[ii].MonitoredItemId;
										(*ppItemsCreated)[ii].m_pRevisedQueueSize = pResult[ii].RevisedQueueSize;
										(*ppItemsCreated)[ii].m_Result = pResult[ii].StatusCode;
										(*ppItemsCreated)[ii].m_RevisedSamplingInterval = pResult[ii].RevisedSamplingInterval;
										OpcUa_MonitoredItemCreateResult_Clear(&pResult[ii]);
									}
									OpcUa_Free(pResult);
								}
								else
									OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_INFO, "OpenOpcUa_CreateMonitoredItemsEx>CSubscriptionClient::CreateMonitoredItems failed:0x%05x\n", uStatus);
								
								// Free filter used
								for (OpcUa_UInt32 i = 0; i < NoOfItemsToCreate; i++)
								{
									OpcUa_DataChangeFilter* pDataChangeFilter = (OpcUa_DataChangeFilter*) pItemsToCreateRequest[i].RequestedParameters.Filter.Body.EncodeableObject.Object;
									if (pDataChangeFilter != OpcUa_Null)
									{
										OpcUa_DataChangeFilter_Clear(pDataChangeFilter);
										OpcUa_Free(pDataChangeFilter);
									}
								}
								OpcUa_Free(pItemsToCreateRequest);
								OpcUa_Free(hMonitoredItems);
							}
							else
								uStatus = OpcUa_BadOutOfMemory;
						}
						else
							uStatus = OpcUa_BadNothingToDo;
					}
					else
						OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_INFO, "OpenOpcUa_CreateMonitoredItemsEx>Critical error. hSubscription is NULL\n");
				}
				else
				{
					OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_INFO, "OpenOpcUa_CreateMonitoredItemsEx>The session is not in a correct state. %u\n", pSession->GetSessionState());
					uStatus = OpcUa_BadInvalidState;
				}
				OpcUa_Mutex_Unlock(pSession->m_AutoConnectThreadMutex);
			}
			else 
				OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_INFO, "OpenOpcUa_CreateMonitoredItemsEx>Critical error. hSession is NULL\n");
		}
		else
			OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_INFO, "OpenOpcUa_CreateMonitoredItemsEx>Critical error. hApplication is NULL\n");
	}
	else
		OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_INFO, "OpenOpcUa_CreateMonitoredItemsEx>Critical error. AbstractionLayer is not initialized\n");
	return uStatus;
}
// Create MonitoredItems
OpcUa_StatusCode OpenOpcUa_CreateMonitoredItems(OpcUa_Handle hApplication,
												OpcUa_Handle hSession,
												OpcUa_Handle hSubscription,
												OpcUa_Byte aTimestampsToReturn,
												OpcUa_UInt32 NoOfItemsToCreate,
												OpcUa_MonitoredItemCreateRequest* pItemsToCreate,
												OpcUa_MonitoredItemCreateResult** ppResult,
												OpcUa_Handle** hMonitoredItems)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_CreateMonitoredItems\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				CSubscriptionClient* pSubscriptionClient=(CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
					OpcUa_UInt32* puiMonitoredItems = OpcUa_Null; // (OpcUa_UInt32*)OpcUa_Alloc(sizeof(OpcUa_UInt32)*NoOfItemsToCreate);
					//ZeroMemory(puiMonitoredItems, sizeof(OpcUa_UInt32)*NoOfItemsToCreate);
					// We will subscribe to the requested nodes
					uStatus=pSubscriptionClient->CreateMonitoredItems(
						(OpcUa_TimestampsToReturn)aTimestampsToReturn,
						NoOfItemsToCreate,
						pItemsToCreate,
						ppResult,
						&puiMonitoredItems);
					if (uStatus==OpcUa_Good)
					{
						puiMonitoredItems = (OpcUa_UInt32*)OpcUa_Alloc(sizeof(OpcUa_UInt32)*NoOfItemsToCreate);
						ZeroMemory(puiMonitoredItems, sizeof(OpcUa_UInt32)*NoOfItemsToCreate);

						OpcUa_Int32 iNodNodesToRead=0;
						OpcUa_ReadValueId* pNodesToRead=OpcUa_Null;
						OpcUa_DataValue* pResults=OpcUa_Null;
						iNodNodesToRead=NoOfItemsToCreate;
						pNodesToRead=(OpcUa_ReadValueId*)OpcUa_Alloc(iNodNodesToRead*sizeof(OpcUa_ReadValueId));
						// Read BrowseName for all Added Nodes
						for (OpcUa_UInt32 ii=0;ii<NoOfItemsToCreate;ii++)
						{
							OpcUa_ReadValueId_Initialize(&pNodesToRead[ii]);
							pNodesToRead[ii].AttributeId=OpcUa_Attributes_BrowseName;
							OpcUa_NodeId_CopyTo(&(pItemsToCreate[ii].ItemToMonitor.NodeId), &(pNodesToRead[ii].NodeId));
						}
						uStatus=OpenOpcUa_ReadAttributes(hApplication,hSession,OpcUa_TimestampsToReturn_Both,iNodNodesToRead,pNodesToRead,&pResults);
						if (uStatus==OpcUa_Good)
						{
							for (OpcUa_UInt32 ii=0;ii<NoOfItemsToCreate;ii++)
							{
								CMonitoredItemClient* pMonitoredItem = pSubscriptionClient->FindMonitoredItemByHandle((OpcUa_UInt64)puiMonitoredItems[ii]);
								if (pMonitoredItem)
								{
									if (pResults[ii].Value.Value.QualifiedName)
									{
										(*hMonitoredItems)[ii] = (OpcUa_Handle)puiMonitoredItems[ii];
										pMonitoredItem->SetMonitoredItemName(pResults[ii].Value.Value.QualifiedName->Name);
										OpcUa_QualifiedName_Clear(pResults[ii].Value.Value.QualifiedName);
									}
								}
								OpcUa_DataValue_Clear(&pResults[ii]);
							}
						}
						for (OpcUa_UInt32 ii = 0; ii < NoOfItemsToCreate; ii++)
						{
							OpcUa_NodeId_Clear(&(pNodesToRead[ii].NodeId));
						}
						if (pResults)
						{
							OpcUa_Free(pResults);
							pResults = OpcUa_Null;
						}
						// Read Value for all Added Nodes
						for (OpcUa_UInt32 ii=0;ii<NoOfItemsToCreate;ii++)
						{
							OpcUa_ReadValueId_Initialize(&pNodesToRead[ii]);
							pNodesToRead[ii].AttributeId=OpcUa_Attributes_Value;							
							OpcUa_NodeId_CopyTo(&(pItemsToCreate[ii].ItemToMonitor.NodeId),&(pNodesToRead[ii].NodeId));
						}
						uStatus=OpenOpcUa_ReadAttributes(hApplication,hSession,OpcUa_TimestampsToReturn_Both,iNodNodesToRead,pNodesToRead,&pResults);
						if (uStatus==OpcUa_Good)
						{
							for (OpcUa_UInt32 ii=0;ii<NoOfItemsToCreate;ii++)
							{
								CMonitoredItemClient* pMonitoredItem = pSubscriptionClient->FindMonitoredItemByHandle((OpcUa_UInt64)puiMonitoredItems[ii]);// (CMonitoredItemClient*)(*hMonitoredItems)[ii];
								if (pMonitoredItem)
								{
									pMonitoredItem->SetValue(&pResults[ii]);
								}
								OpcUa_ReadValueId_Clear(pNodesToRead);
								OpcUa_DataValue_Clear(&pResults[ii]);
							}
						}
						if (pResults)
						{
							OpcUa_Free(pResults);
							pResults = OpcUa_Null;
						}
						for (OpcUa_UInt32 ii = 0; ii < NoOfItemsToCreate; ii++)
						{
							OpcUa_NodeId_Clear(&(pNodesToRead[ii].NodeId));
						}
						if (pNodesToRead)
						{
							OpcUa_Free(pNodesToRead);
							pNodesToRead = OpcUa_Null;
						}
					}
					if (puiMonitoredItems)
						OpcUa_Free(puiMonitoredItems);
				}
			}
		}
	}
	return uStatus;
}
// Delete MonitoredItems
OpcUa_StatusCode OpenOpcUa_DeleteMonitoredItems(OpcUa_Handle hApplication,
												OpcUa_Handle hSession,
												OpcUa_Handle hSubscription,
												OpcUa_Int32 iNoOfMonitoredItem,
												OpcUa_Handle* hMonitoredItems,
												OpcUa_StatusCode** ppStatusCode)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_DeleteMonitoredItems\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession = (CSessionClient*)hSession;
			if (pSession)
			{
				CSubscriptionClient* pSubscriptionClient = (CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
						// let's prepare the list of monitoredItem to delete
						OpcUa_Mutex monitoredItemListMutex = pSubscriptionClient->GetMonitoredItemListMutex();
						OpcUa_Mutex_Lock(monitoredItemListMutex);
						CMonitoredItemClientList* pMonitoredItemList = new CMonitoredItemClientList();
						for (OpcUa_Int32 i = 0; i < iNoOfMonitoredItem; i++)
						{
							CMonitoredItemClient* pMonitoredItem = pSubscriptionClient->FindMonitoredItemByHandle((OpcUa_UInt64)hMonitoredItems[i]);
							if (pMonitoredItem)
								pMonitoredItemList->push_back(pMonitoredItem);
						}
						OpcUa_Mutex_Unlock(monitoredItemListMutex);
						// Now lets inform the server that we want to delete it
						if (iNoOfMonitoredItem > 0)
							uStatus=pSubscriptionClient->DeleteMonitoredItems(pMonitoredItemList,OpcUa_True);
						else
							uStatus = OpcUa_BadInvalidArgument;
						delete pMonitoredItemList;
				}
			}
			else
				uStatus = OpcUa_BadInvalidArgument;
		}
		else
			uStatus = OpcUa_BadInvalidArgument;
	}
	return uStatus;
}
// Modify MonitoredItems All parameter are input parameters
OpcUa_StatusCode OpenOpcUa_ModifyMonitoredItems(OpcUa_Handle	hApplication,
												OpcUa_Handle	hSession,
												OpcUa_Handle	hSubscription,
												OpcUa_Handle	hMonitoredItem,
												OpcUa_Byte		aTimestampsToReturn,
												OpcUa_Boolean	bDiscardOldest,
												OpcUa_UInt32	uiQueueSize,
												OpcUa_Double	dblSamplingInterval,
												OpcUa_UInt32	DeadbandType,
												OpcUa_Double	DeadbandValue,
												OpcUa_Byte		DatachangeTrigger)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_ModifyMonitoredItems\n");
	OpcUa_StatusCode uStatus = OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession = (CSessionClient*)hSession;
			if (pSession)
			{
				CSubscriptionClient* pSubscriptionClient = (CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
					uStatus = pSubscriptionClient->ModifyMonitoredItems(hMonitoredItem,aTimestampsToReturn,bDiscardOldest,uiQueueSize,dblSamplingInterval,DeadbandType,DeadbandValue,DatachangeTrigger);
				}
			}
		}
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	SetMonitoringMode. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">		 	The application. </param>
/// <param name="hSession">			 	The session. </param>
/// <param name="hSubscription">	 	The subscription. </param>
/// <param name="iNoOfMonitoredItem">	Zero-based index of the no of monitored item. </param>
/// <param name="hMonitoredItems">   	[in,out] If non-null, the monitored items. </param>
/// <param name="eMonitoringMode">   	The monitoring mode. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_SetMonitoringMode(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_Handle hSubscription,
	OpcUa_Int32			iNoOfMonitoredItem,
	OpcUa_Handle*			hMonitoredItems,
	OpcUa_MonitoringMode	eMonitoringMode)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_SetMonitoringMode\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (hMonitoredItems)
	{
		if (g_bAbstractionLayerInitialized)
		{
			CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
			if (pUaClientApplication)
			{
				CSessionClient* pSession = (CSessionClient*)hSession;
				if (pSession)
				{
					CSubscriptionClient* pSubscriptionClient = (CSubscriptionClient*)hSubscription;
					if (pSubscriptionClient)
					{
						pSubscriptionClient->SetDefaultMonitoringMode(eMonitoringMode);
						if (iNoOfMonitoredItem > 0)
							uStatus = pSubscriptionClient->SetMonitoringMode(eMonitoringMode, iNoOfMonitoredItem, (OpcUa_UInt32*)hMonitoredItems);
						else
							uStatus = OpcUa_Good;
					}
				}
			}
		}
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Read Attributes. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">		  	The application. </param>
/// <param name="hSession">			  	The session. </param>
/// <param name="eTimestampsToReturn">	The timestamps to return. </param>
/// <param name="iNoOfNodesToRead">   	Zero-based index of the no of nodes to read. </param>
/// <param name="pNodesToRead">		  	[in,out] If non-null, the nodes to read. </param>
/// <param name="ppResults">		  	[in,out] If non-null, the results. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_ReadAttributes(  OpcUa_Handle hApplication,
											OpcUa_Handle hSession,
											OpcUa_TimestampsToReturn eTimestampsToReturn,
											OpcUa_Int32 iNoOfNodesToRead, 
											OpcUa_ReadValueId* pNodesToRead,
											OpcUa_DataValue** ppResults)
{
	//CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	//OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	//OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_ReadAttributes\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				OpcUa_ReadValueIdList	aReadValueIdList;
				OpcUa_DataValueList Results;
				OpcUa_DataValueList::iterator iteratorResult;
				// on demande les attributs que l'on souhaite lire
				for (int ii = 0;ii<iNoOfNodesToRead;ii++)
				{
					aReadValueIdList.push_back(&pNodesToRead[ii]);
				}
				uStatus=pSession->Read(aReadValueIdList,eTimestampsToReturn,&Results);
				if (uStatus==OpcUa_Good)
				{
					OpcUa_UInt32 iii=0;
					OpcUa_UInt32 iSizeResult = Results.size();
					if (iSizeResult > 0)
					{
						(*ppResults) = (OpcUa_DataValue*)OpcUa_Alloc(iSizeResult*sizeof(OpcUa_DataValue));
						if ((*ppResults))
						{
							for (OpcUa_UInt32 ij = 0; ij < Results.size(); ij++)
							{
								OpcUa_DataValue_Initialize(&((*ppResults)[iii]));
								OpcUa_DataValue* pDataValue = Results.at(ij);
								(*ppResults)[iii].ServerPicoseconds = pDataValue->ServerPicoseconds;
								(*ppResults)[iii].ServerTimestamp = pDataValue->ServerTimestamp;
								(*ppResults)[iii].SourcePicoseconds = pDataValue->SourcePicoseconds;
								(*ppResults)[iii].SourceTimestamp = pDataValue->SourceTimestamp;
								(*ppResults)[iii].StatusCode = pDataValue->StatusCode;
								if (pDataValue->StatusCode == OpcUa_Good)
									OpcUa_Variant_CopyTo(&(pDataValue->Value), &((*ppResults)[iii].Value));
								else
								{
									(*ppResults)[iii].Value.ArrayType = pDataValue->Value.ArrayType;
									(*ppResults)[iii].Value.Datatype = pDataValue->Value.Datatype;
								}
								iii++;
								OpcUa_DataValue_Clear(pDataValue);
								OpcUa_Free(pDataValue);
								pDataValue = OpcUa_Null;
							}
							Results.clear();
						}
					}
					else
						uStatus = OpcUa_BadNothingToDo;
				}
				aReadValueIdList.clear();
			}
		}
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Write Attributes. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">			The application. </param>
/// <param name="hSession">				The session. </param>
/// <param name="iNoOfNodesToWrite">	Zero-based index of the no of nodes to write. </param>
/// <param name="pNodesToWrite">		[in,out] If non-null, the nodes to write. </param>
/// <param name="ppStatusCode">			[in,out] If non-null, the status code. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_WriteAttributes(OpcUa_Handle hApplication,
										   OpcUa_Handle hSession,
										   OpcUa_Int32 iNoOfNodesToWrite, 
										   OpcUa_WriteValue* pNodesToWrite,
										   OpcUa_StatusCode** ppStatusCode)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_WriteAttributes\n");
	OpcUa_StatusCode uStatus=OpcUa_Good;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				uStatus = pSession->Write(iNoOfNodesToWrite, pNodesToWrite, ppStatusCode);
			}
		}
	}
	return uStatus;
}

OpcUa_StatusCode HistoryReadCallback(
	OpcUa_Channel         hChannel,
	OpcUa_Void*           pResponse,
	OpcUa_EncodeableType* pResponseType,
	OpcUa_Void*           pCallbackData,
	OpcUa_StatusCode      StatusCode)
{
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;

	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA history read raw. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">		  	The application. </param>
/// <param name="hSession">			  	The session. </param>
/// <param name="bModified">		  	The modified. </param>
/// <param name="eTimestampsToReturn">	The timestamps to return. </param>
/// <param name="iNoOfNodesToRead">   	Zero-based index of the no of nodes to read. </param>
/// <param name="pNodesToRead">		  	[in,out] If non-null, the nodes to read. </param>
/// <param name="ipNoOfResults">	  	[in,out] If non-null, the IP no of results. </param>
/// <param name="ppHistoryResults">   	[in,out] If non-null, the history results. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_HistoryReadRaw(OpcUa_Handle					hApplication,
										  OpcUa_Handle					hSession,
										  OpcUa_Boolean					bModified,
										  OpcUa_TimestampsToReturn      eTimestampsToReturn,
										  OpcUa_Int32                   iNoOfNodesToRead,
										  OpenOpcUa_HistoryReadValueId* pNodesToRead,
										  OpcUa_Int32*                  ipNoOfResults,
										  OpenOpcUa_HistoryReadResult** ppHistoryResults)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_HistoryReadRaw\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	OpcUa_RequestHeader			tRequestHeader;

	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession = (CSessionClient*)hSession;
			if (pSession)
			{
				OpcUa_RequestHeader_Initialize(&tRequestHeader);
				tRequestHeader.TimeoutHint = g_ServiceCallTimeout;// UTILS_DEFAULT_TIMEOUT;
				tRequestHeader.Timestamp = OpcUa_DateTime_UtcNow();
				tRequestHeader.AuthenticationToken.IdentifierType = OpcUa_IdentifierType_Numeric;
				tRequestHeader.AuthenticationToken.NamespaceIndex = 0;
				OpcUa_NodeId_CopyTo(pSession->GetAuthenticationToken(), &(tRequestHeader.AuthenticationToken));
				tRequestHeader.RequestHandle = CClientApplication::m_uiRequestHandle++;
				// Let's make the asynchronous call
				CChannel* pChannel = pSession->GetChannel();
				OpcUa_Channel aChannel = pChannel->GetInternalHandle();
				OpcUa_ExtensionObject* pHistoryReadDetails=OpcUa_Null;
				OpcUa_Boolean bReleaseContinuationPoints = OpcUa_False;
				OpcUa_HistoryReadValueId* pHistoryReadValueId;
				void* pCallbackData = OpcUa_Null;
				if (iNoOfNodesToRead > 0)
				{
					pHistoryReadValueId = (OpcUa_HistoryReadValueId*)OpcUa_Alloc(sizeof(OpcUa_HistoryReadValueId)*iNoOfNodesToRead);
					if (pHistoryReadValueId)
					{
						uStatus=OpcUa_ClientApi_BeginHistoryRead(aChannel,
							&tRequestHeader,
							pHistoryReadDetails,
							eTimestampsToReturn,
							bReleaseContinuationPoints,
							iNoOfNodesToRead,
							pHistoryReadValueId,
							(OpcUa_Channel_PfnRequestComplete*)&HistoryReadCallback,
							pCallbackData);
					}
				}
				else
					uStatus = OpcUa_BadInvalidArgument;
			}
		}
	}
	return uStatus;
}

// Call
OpcUa_StatusCode OpenOpcUa_Call(OpcUa_Handle hApplication,
								OpcUa_Handle hSession,
								OpcUa_Int32 iNoOfMethodsToCall,
								OpcUa_CallMethodRequest* pMethodsToCall,
								OpcUa_CallMethodResult** ppResults)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_Call\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	OpcUa_RequestHeader	tRequestHeader;
	OpcUa_ResponseHeader* pResponseHeader = (OpcUa_ResponseHeader*)OpcUa_Alloc(sizeof(OpcUa_ResponseHeader));
	OpcUa_ResponseHeader_Initialize(pResponseHeader);
	OpcUa_Int32             nNoOfDiagnosticInfos = 0;
	OpcUa_DiagnosticInfo*   pDiagnosticInfos = OpcUa_Null;
	OpcUa_DiagnosticInfo_Initialize(pDiagnosticInfos);
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession = (CSessionClient*)hSession;
			if (pSession)
			{
				OpcUa_Int32 iNoOfResults=0;
				OpcUa_RequestHeader_Initialize(&tRequestHeader);
				tRequestHeader.TimeoutHint = g_ServiceCallTimeout;// UTILS_DEFAULT_TIMEOUT;
				tRequestHeader.Timestamp = OpcUa_DateTime_UtcNow();
				OpcUa_NodeId* pNodeId = pSession->GetAuthenticationToken();
				OpcUa_NodeId_CopyTo(pNodeId, &(tRequestHeader.AuthenticationToken));
				tRequestHeader.RequestHandle = CClientApplication::m_uiRequestHandle++;
				// Let's make the asynchronous call
				CChannel* pChannel = pSession->GetChannel();
				if (pChannel)
				{
					//OpcUa_CallMethodResult_Initialize(*ppResults);
					OpcUa_Channel aChannel = pChannel->GetInternalHandle();
					uStatus = OpcUa_ClientApi_Call(
						aChannel, 
						&tRequestHeader, 
						iNoOfMethodsToCall, 
						pMethodsToCall, 
						pResponseHeader, 
						&iNoOfResults,
						ppResults, 
						&nNoOfDiagnosticInfos, 
						&pDiagnosticInfos);
				}
			}
		}
	}
	OpcUa_Free(pResponseHeader);
	return uStatus;
}
// Recupère la session a laquelle appartient cette souscription
OpcUa_StatusCode OpenOpcUa_GetSessionOfSubscription(OpcUa_Handle hSubscription,OpcUa_Handle* hSession)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetSessionOfSubscription\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	OpenOpcUa_HandleType aHandleType;
	uStatus=OpenOpcUa_WhatIsIt(hSubscription,&aHandleType);
	if (aHandleType==OPENOPCUA_SUBSCRIPTION)
	{
		CSubscriptionClient* pSubscriptionClient=(CSubscriptionClient*)hSubscription;
		CSessionClient *pSession=pSubscriptionClient->GetSession();
		if (pSession)
				  *hSession=(OpcUa_Handle)pSession;
	}
	else
	{
		*hSession=OpcUa_Null;
		uStatus=OpcUa_BadInvalidArgument;
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>
/// 	Called tby the client application to set the Callback functions.
/// </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication"> 	The application. </param>
/// <param name="hSession">			The session. </param>
/// <param name="hSubscription">	The subscription. </param>
/// <param name="lpCallback">   	The callback. </param>
/// <param name="pCallbackData">	[in,out] If non-null, information describing the callback.
/// </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_SetPublishCallback(OpcUa_Handle hApplication,
																	OpcUa_Handle hSession,
																	OpcUa_Handle hSubscription,
											  PFUNC lpCallback,
											  void* pCallbackData)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_SetPublishCallback\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				CSubscriptionClient* pSubscriptionClient=(CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
					pSubscriptionClient->SetNotificationCallback(lpCallback);
					pSubscriptionClient->SetNotificationCallbackData(pCallbackData);
					uStatus=OpcUa_Good;
				}
			}
		}
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Callback, called when the open opc UA set shutdown. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication"> 	The application. </param>
/// <param name="hSession">			The session. </param>
/// <param name="lpCallback">   	The callback. </param>
/// <param name="pCallbackData">	[in,out] If non-null, information describing the callback.
/// </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_SetShutdownCallback(OpcUa_Handle hApplication,
												OpcUa_Handle hSession,
												PFUNCSHUTDOWN lpCallback, void* pCallbackData)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_SetShutdownCallback\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				pSession->SetShutdownCallback(lpCallback);
				pSession->SetShutdownCallbackData(pCallbackData);
				uStatus=OpcUa_Good;
			}
		}
	}
	return uStatus;
}
OpcUa_StatusCode OpenOpcUa_ReleaseInternalNode(OpenOpcUa_InternalNode* pInternalNode)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_ReleaseInternalNode\n");
	OpcUa_StatusCode uStatus=OpcUa_Good;
	if (pInternalNode)
	{
		OpcUa_NodeId_Clear(&(pInternalNode->m_NodeId));
		OpcUa_String_Clear(&(pInternalNode->m_BrowseName));
		OpcUa_DataValue_Clear(&(pInternalNode->m_DataValue));
		OpcUa_DataValue_Clear(&(pInternalNode->m_DataValue));
		pInternalNode->m_hMonitoredItem=OpcUa_Null;
		if (pInternalNode->m_pMonitoredItemParam)
			OpcUa_Free(pInternalNode->m_pMonitoredItemParam);
		OpcUa_Free(pInternalNode);
	}
	else
		uStatus=OpcUa_BadInvalidArgument;
	return uStatus;
}
OpcUa_StatusCode OpenOpcUa_GetInternalNodeByMonitoredItemId(OpcUa_Handle hApplication,
	OpcUa_Handle hSession,
	OpcUa_Handle hSubscription,
	OpcUa_UInt32 MonitoredItemId,
	OpenOpcUa_InternalNode** pInternalNode)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetInternalNodeByMonitoredItemId\n");
	OpcUa_StatusCode uStatus = OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession = (CSessionClient*)hSession;
			if (pSession)
			{
				// pSession->FindSubscription(hSubscription,&pSubscriptionClient); This call can also works
				CSubscriptionClient* pSubscriptionClient = (CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
					CMonitoredItemClient* pMonitoredItemClient =pSubscriptionClient->FindMonitoredItemById(MonitoredItemId);
					if (pMonitoredItemClient)
					{
						(*pInternalNode) = (OpenOpcUa_InternalNode*)OpcUa_Alloc(sizeof(OpenOpcUa_InternalNode));
						if (*pInternalNode)
						{
							// NodeId
							OpcUa_NodeId_Initialize(&((*pInternalNode)->m_NodeId));
							OpcUa_NodeId aNodeId = pMonitoredItemClient->GetNodeId();
							OpcUa_NodeId_CopyTo(&aNodeId, &((*pInternalNode)->m_NodeId));

							// BrowseName
							OpcUa_String_Initialize(&((*pInternalNode)->m_BrowseName));
							OpcUa_String* pString = pMonitoredItemClient->GetMonitoredItemName();
							if (OpcUa_String_StrLen(pString)>0)
							{
								OpcUa_String_CopyTo(pString, &((*pInternalNode)->m_BrowseName));
							}
							// DataValue
							OpcUa_DataValue_Initialize(&((*pInternalNode)->m_DataValue));
							OpcUa_DataValue* pDataValue = pMonitoredItemClient->GetValue();							
							OpcUa_DataValue_CopyTo(pDataValue, &((*pInternalNode)->m_DataValue));
							// OpcUa_Handle
							(*pInternalNode)->m_hMonitoredItem = (OpcUa_Handle)pMonitoredItemClient->GetClientHandle();
							// Let's fill the OpenOpcUa_MonitoredItemParam* but first we need to allocate it
							(*pInternalNode)->m_pMonitoredItemParam = (OpenOpcUa_MonitoredItemParam*)OpcUa_Alloc(sizeof(OpenOpcUa_MonitoredItemParam));
							if ((*pInternalNode)->m_pMonitoredItemParam)
							{
								// TimestampsToReturn
								(*pInternalNode)->m_pMonitoredItemParam->m_aTimestampsToReturn = (OpcUa_Byte)pMonitoredItemClient->GetTimestampsToReturn();
								// DiscardOldest
								(*pInternalNode)->m_pMonitoredItemParam->m_bDiscardOldest = pMonitoredItemClient->IsDiscardOldest();
								// SamplingInterval
								(*pInternalNode)->m_pMonitoredItemParam->m_dblSamplingInterval = pMonitoredItemClient->GetSamplingInterval();
								// Deadband
								OpcUa_ExtensionObject* pExtensionObject = pMonitoredItemClient->GetFilterToUse();
								if (pExtensionObject)
								{
									// Deadband Default value. 
									(*pInternalNode)->m_pMonitoredItemParam->m_DeadbandType = OpcUa_DeadbandType_None;
									(*pInternalNode)->m_pMonitoredItemParam->m_dblDeadbandValue = 0;
									(*pInternalNode)->m_pMonitoredItemParam->m_DataChangeTrigger = OpcUa_DataChangeTrigger_StatusValueTimestamp;
									if (pExtensionObject->Body.EncodeableObject.Type)
									{
										if (pExtensionObject->Body.EncodeableObject.Type->TypeId == OpcUaId_DataChangeFilter)
										{
											OpcUa_DataChangeFilter* pDataChangeFilter =
												(OpcUa_DataChangeFilter*)pExtensionObject->Body.EncodeableObject.Object;
											if (pDataChangeFilter)
											{
												(*pInternalNode)->m_pMonitoredItemParam->m_DeadbandType = pDataChangeFilter->DeadbandType;
												(*pInternalNode)->m_pMonitoredItemParam->m_dblDeadbandValue = pDataChangeFilter->DeadbandValue;
												(*pInternalNode)->m_pMonitoredItemParam->m_DataChangeTrigger = pDataChangeFilter->Trigger;
											}
										}
									}
								}
								// QueueSize
								(*pInternalNode)->m_pMonitoredItemParam->m_uiQueueSize = pMonitoredItemClient->GetQueueSize();
								uStatus = OpcUa_Good;
							}
							else
								uStatus = OpcUa_BadOutOfMemory;
						}
						else
							uStatus = OpcUa_BadOutOfMemory;
					}
				}
			}
		}
	}
	return uStatus;
}
OpcUa_StatusCode OpenOpcUa_GetInternalNodeByClientHandle(OpcUa_Handle hApplication,
	OpcUa_Handle hSession,
	OpcUa_Handle hSubscription,
	OpcUa_UInt32 ClientHandle,
	OpenOpcUa_InternalNode** pInternalNode)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetInternalNodeByClientHandle\n");
	OpcUa_StatusCode uStatus = OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession = (CSessionClient*)hSession;
			if (pSession)
			{
				// pSession->FindSubscription(hSubscription,&pSubscriptionClient); This call can also works
				CSubscriptionClient* pSubscriptionClient = (CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
					CMonitoredItemClient* pMonitoredItemClient = pSubscriptionClient->FindMonitoredItemByHandle((OpcUa_UInt64)ClientHandle);
					if (pMonitoredItemClient)
					{
						*pInternalNode = (OpenOpcUa_InternalNode*)OpcUa_Alloc(sizeof(OpenOpcUa_InternalNode));
						if (*pInternalNode)
						{
							// NodeId
							OpcUa_NodeId_Initialize(&((*pInternalNode)->m_NodeId));
							OpcUa_NodeId aNodeId = pMonitoredItemClient->GetNodeId();
							OpcUa_NodeId_CopyTo(&aNodeId, &((*pInternalNode)->m_NodeId));

							// BrowseName
							OpcUa_String_Initialize(&((*pInternalNode)->m_BrowseName));
							OpcUa_String* pString = pMonitoredItemClient->GetMonitoredItemName();
							if (OpcUa_String_StrLen(pString)>0)
							{
								OpcUa_String_CopyTo(pString, &((*pInternalNode)->m_BrowseName));
							}
							// DataValue
							OpcUa_DataValue_Initialize(&((*pInternalNode)->m_DataValue));
							OpcUa_DataValue* pDataValue = pMonitoredItemClient->GetValue();
							(*pInternalNode)->m_DataValue = *Utils::Copy(pDataValue);
							// OpcUa_Handle
							(*pInternalNode)->m_hMonitoredItem = (OpcUa_Handle)pMonitoredItemClient->GetClientHandle();
							// Let's fill the OpenOpcUa_MonitoredItemParam* but first we need to allocate it
							(*pInternalNode)->m_pMonitoredItemParam = (OpenOpcUa_MonitoredItemParam*)OpcUa_Alloc(sizeof(OpenOpcUa_MonitoredItemParam));
							if ((*pInternalNode)->m_pMonitoredItemParam)
							{
								// TimestampsToReturn
								(*pInternalNode)->m_pMonitoredItemParam->m_aTimestampsToReturn = (OpcUa_Byte)pMonitoredItemClient->GetTimestampsToReturn();
								// DiscardOldest
								(*pInternalNode)->m_pMonitoredItemParam->m_bDiscardOldest = pMonitoredItemClient->IsDiscardOldest();
								// SamplingInterval
								(*pInternalNode)->m_pMonitoredItemParam->m_dblSamplingInterval = pMonitoredItemClient->GetSamplingInterval();
								// Deadband
								OpcUa_ExtensionObject* pExtensionObject = pMonitoredItemClient->GetFilterToUse();
								if (pExtensionObject)
								{
									// Deadband Default value. 
									(*pInternalNode)->m_pMonitoredItemParam->m_DeadbandType = OpcUa_DeadbandType_None;
									(*pInternalNode)->m_pMonitoredItemParam->m_dblDeadbandValue = 0;
									(*pInternalNode)->m_pMonitoredItemParam->m_DataChangeTrigger = OpcUa_DataChangeTrigger_StatusValueTimestamp;
									if (pExtensionObject->Body.EncodeableObject.Type)
									{
										if (pExtensionObject->Body.EncodeableObject.Type->TypeId == OpcUaId_DataChangeFilter)
										{
											OpcUa_DataChangeFilter* pDataChangeFilter =
												(OpcUa_DataChangeFilter*)pExtensionObject->Body.EncodeableObject.Object;
											if (pDataChangeFilter)
											{
												(*pInternalNode)->m_pMonitoredItemParam->m_DeadbandType = pDataChangeFilter->DeadbandType;
												(*pInternalNode)->m_pMonitoredItemParam->m_dblDeadbandValue = pDataChangeFilter->DeadbandValue;
												(*pInternalNode)->m_pMonitoredItemParam->m_DataChangeTrigger = pDataChangeFilter->Trigger;
											}
										}
									}
								}
								// QueueSize
								(*pInternalNode)->m_pMonitoredItemParam->m_uiQueueSize = pMonitoredItemClient->GetQueueSize();
								uStatus = OpcUa_Good;
							}
							else
								uStatus = OpcUa_BadOutOfMemory;
						}
						else
							uStatus = OpcUa_BadOutOfMemory;
					}
				}
			}
		}
	}
	return uStatus;
}
/// <summary>
/// Opens the opc ua_ get internal node.
/// </summary>
/// <param name="hApplication">The h application.</param>
/// <param name="hSession">The h session.</param>
/// <param name="hSubscription">The h subscription.</param>
/// <param name="hMonitoredItem">The h monitored item.</param>
/// <param name="pInternalNode">The p internal node.</param>
/// <returns></returns>
OpcUa_StatusCode OpenOpcUa_GetInternalNode(OpcUa_Handle hApplication,
										   OpcUa_Handle hSession,
										   OpcUa_Handle hSubscription,
										   OpcUa_Handle hMonitoredItem,
										   OpenOpcUa_InternalNode** pInternalNode)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetInternalNode\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				// pSession->FindSubscription(hSubscription,&pSubscriptionClient); This call can also works
				CSubscriptionClient* pSubscriptionClient = (CSubscriptionClient*)hSubscription;
				if (pSubscriptionClient)
				{
					CMonitoredItemClient* pMonitoredItemClient = pSubscriptionClient->FindMonitoredItemByHandle((OpcUa_UInt64)hMonitoredItem);
					if (pMonitoredItemClient)
					{
						*pInternalNode=( OpenOpcUa_InternalNode*)OpcUa_Alloc(sizeof(OpenOpcUa_InternalNode));
						if (*pInternalNode)
						{
							// NodeId
							OpcUa_NodeId_Initialize(&((*pInternalNode)->m_NodeId));
							OpcUa_NodeId aNodeId = pMonitoredItemClient->GetNodeId();
							OpcUa_NodeId_CopyTo(&aNodeId,&((*pInternalNode)->m_NodeId));
							
							// BrowseName
							OpcUa_String_Initialize(&((*pInternalNode)->m_BrowseName));
							OpcUa_String* pString=pMonitoredItemClient->GetMonitoredItemName();
							if (OpcUa_String_StrLen(pString)>0)
							{	
								OpcUa_String_CopyTo(pString, &((*pInternalNode)->m_BrowseName));
							}
							// DataValue
							OpcUa_DataValue_Initialize(&((*pInternalNode)->m_DataValue));
							OpcUa_DataValue* pDataValue=pMonitoredItemClient->GetValue();
							//(*pInternalNode)->m_DataValue=*Utils::Copy(pDataValue);
							OpcUa_DataValue_CopyTo(pDataValue, &((*pInternalNode)->m_DataValue));
							// OpcUa_Handle
							(*pInternalNode)->m_hMonitoredItem = (OpcUa_Handle)pMonitoredItemClient->GetClientHandle();
							// Let's fill the OpenOpcUa_MonitoredItemParam* but first we need to allocate it
							(*pInternalNode)->m_pMonitoredItemParam=(OpenOpcUa_MonitoredItemParam*)OpcUa_Alloc(sizeof(OpenOpcUa_MonitoredItemParam));
							if ((*pInternalNode)->m_pMonitoredItemParam)
							{
								// TimestampsToReturn
								(*pInternalNode)->m_pMonitoredItemParam->m_aTimestampsToReturn = (OpcUa_Byte)pMonitoredItemClient->GetTimestampsToReturn();
								// DiscardOldest
								(*pInternalNode)->m_pMonitoredItemParam->m_bDiscardOldest = pMonitoredItemClient->IsDiscardOldest();
								// SamplingInterval
								(*pInternalNode)->m_pMonitoredItemParam->m_dblSamplingInterval = pMonitoredItemClient->GetSamplingInterval();
								// Deadband
								OpcUa_ExtensionObject* pExtensionObject = pMonitoredItemClient->GetFilterToUse();
								{
									// Deadband Default value. 
									(*pInternalNode)->m_pMonitoredItemParam->m_DeadbandType = OpcUa_DeadbandType_None;
									(*pInternalNode)->m_pMonitoredItemParam->m_dblDeadbandValue = 0;
									(*pInternalNode)->m_pMonitoredItemParam->m_DataChangeTrigger = OpcUa_DataChangeTrigger_StatusValueTimestamp;
									if (pExtensionObject->Body.EncodeableObject.Type)
									{
										if (pExtensionObject->Body.EncodeableObject.Type->TypeId == OpcUaId_DataChangeFilter)
										{
											OpcUa_DataChangeFilter* pDataChangeFilter =
												(OpcUa_DataChangeFilter*)pExtensionObject->Body.EncodeableObject.Object;
											if (pDataChangeFilter)
											{
												(*pInternalNode)->m_pMonitoredItemParam->m_DeadbandType = pDataChangeFilter->DeadbandType;
												(*pInternalNode)->m_pMonitoredItemParam->m_dblDeadbandValue = pDataChangeFilter->DeadbandValue;
												(*pInternalNode)->m_pMonitoredItemParam->m_DataChangeTrigger = pDataChangeFilter->Trigger;
											}
										}
									}
								}
								// QueueSize
								(*pInternalNode)->m_pMonitoredItemParam->m_uiQueueSize = pMonitoredItemClient->GetQueueSize();
								uStatus = OpcUa_Good;
							}
							else
								uStatus=OpcUa_BadOutOfMemory;
						}
						else
							uStatus=OpcUa_BadOutOfMemory;
					}
				}
			}
		}
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA what is iterator. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hHandle">	  	The handle. </param>
/// <param name="aHandleType">	[in,out] If non-null, type of the handle. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_WhatIsIt(OpcUa_Handle hHandle, OpenOpcUa_HandleType* aHandleType)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_WhatIsIt\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInvalidArgument;
	*aHandleType=OPENOPCUA_UNKNOWN;
	if (hHandle)
	{
		// Check s'il s'agit d'une Application
		for (OpcUa_UInt32 ii=0;ii<g_pUaClientApplicationList.size();ii++)
		{
			CClientApplication* pApplication = g_pUaClientApplicationList.at(ii);
			if (pApplication==(CClientApplication*)hHandle)
			{
				*aHandleType=OPENOPCUA_APPLICATION;
				uStatus=OpcUa_Good;
				break;
			}
		}
		if (uStatus!=OpcUa_Good)
		{
			// check s'il s'agit d'une session
			for (OpcUa_UInt32 ii=0;ii<g_pUaClientApplicationList.size();ii++)
			{
				CClientApplication* pApplication = g_pUaClientApplicationList.at(ii);
				CSessionClient* pSession=OpcUa_Null;
				uStatus=pApplication->GetSession(hHandle,&pSession);
				if (uStatus==OpcUa_Good)
				{
					*aHandleType=OPENOPCUA_SESSION;
					break;
				}
			}
			if (uStatus!=OpcUa_Good)
			{
				// check s'il s'agit d'une souscription
				for (OpcUa_UInt32 ii=0;ii<g_pUaClientApplicationList.size();ii++)
				{
					CClientApplication* pApplication = g_pUaClientApplicationList.at(ii);
					CSubscriptionClient* pSubscription=OpcUa_Null;
					uStatus=pApplication->GetSubscription(hHandle,&pSubscription);
					if (uStatus==OpcUa_Good)
					{
						*aHandleType=OPENOPCUA_SUBSCRIPTION;
						break;
					}
				}
			}			
		}
	
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>
/// 	Transform a Variant in a String.
/// </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 15/01/2017. </remarks>
///
/// <param name="Var">	   	[in,out] The variable. </param>
/// <param name="strValue">	[in,out] If non-null, the value. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_VariantToString(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_Variant Var, OpcUa_String** strValue)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_VariantToString\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (strValue)
	{
		//OpcUa_ByteString byteString; // This is the base64 Encoded DataTypedictionnary
		//OpcUa_ByteString_Initialize(&byteString);
		if (Var.Datatype == OpcUaType_ExtensionObject)
		{		
			if (Var.ArrayType==OpcUa_VariantArrayType_Scalar)
				uStatus=OpenOpcUa_ExtensionObjectToString(hApplication, hSession,Var.Value.ExtensionObject, strValue);
			else
			{
				if (Var.ArrayType == OpcUa_VariantArrayType_Array)
				{
					if (Var.Value.Array.Length == 0)
						OpcUa_String_AttachCopy((*strValue), "Empty ExtensionObject");
					for (OpcUa_Int32 i = 0 ; i < Var.Value.Array.Length; i++)
					{
						uStatus = OpenOpcUa_ExtensionObjectToString(hApplication, hSession, /*byteString,*/ &Var.Value.Array.Value.ExtensionObjectArray[i], strValue);
					}
				}
				else
					uStatus = OpcUa_BadNotImplemented;
			}
		}
		else
			uStatus=Utils::OpcUaVariantToString(Var,strValue);
	}
	else
		uStatus=OpcUa_BadInvalidArgument;

	return uStatus;
}
OpcUa_StatusCode OpenOpcUa_DateTimeToString(OpcUa_DateTime aDateTime,OpcUa_String** strTime)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_DateTimeToString\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	uStatus=Utils::OpcUaDateTimeToString(aDateTime,strTime);

	return uStatus;
}
// Transforme un chaine en nodeId.
// pour que la transformation fonctionne la chaine doit avoir la forme ns=x;i=yyy ou ns=x;s=zzz
OpcUa_StatusCode OpenOpcUa_StringToNodeId(OpcUa_String strNodeId,OpcUa_NodeId* pNodeId)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	//OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_StringToNodeId\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (!pNodeId)
		uStatus=OpcUa_BadInvalidArgument;
	else
	{
		OpcUa_NodeId_Initialize(pNodeId);
		OpcUa_UInt32 iNs=0,iId=0;
		char* pBuff=OpcUa_String_GetRawString(&strNodeId);
		if (pBuff)
		{
			int iRes = sscanf(pBuff, "ns=%u;i=%u", (unsigned int*)&iNs, (unsigned int*)&iId);
			if (iRes == 2)
			{
				pNodeId->IdentifierType = OpcUa_IdentifierType_Numeric;
				pNodeId->NamespaceIndex = (OpcUa_UInt16)iNs;
				pNodeId->Identifier.Numeric = iId;
				uStatus = OpcUa_Good;
			}
			else
			{
				if (iRes == 1)
				{
					// probably another format
					OpcUa_CharA* strId = (OpcUa_CharA*)OpcUa_Alloc(256);
					if (strId)
					{
						char* ptr = strchr(pBuff, ';');
						if (ptr)
						{
							//char* subStr = strchr(ptr, '=');
							ZeroMemory(strId, 256);
							int iRes = sscanf(pBuff, "ns=%u;s=%255s", (unsigned int*)&iNs, strId);
							if (iRes == 2)
							{
								pNodeId->IdentifierType = OpcUa_IdentifierType_String;
								pNodeId->NamespaceIndex = (OpcUa_UInt16)iNs;
								uStatus = OpcUa_String_AttachCopy(&(pNodeId->Identifier.String), strId); //++subStr
							}
							else
							{
								iRes = sscanf(pBuff, "ns=%u;g=%255s", (unsigned int*)&iNs, strId);
								if (iRes == 2)
								{
									pNodeId->Identifier.Guid = (OpcUa_Guid*)OpcUa_Alloc(sizeof(OpcUa_Guid));
									if (pNodeId->Identifier.Guid)
									{
										pNodeId->IdentifierType = OpcUa_IdentifierType_Guid;
										pNodeId->NamespaceIndex = (OpcUa_UInt16)iNs;
										uStatus = OpcUa_Guid_FromString(strId, pNodeId->Identifier.Guid);
									}
								}
								else
								{
									OpcUa_CharA* bstrId = (OpcUa_CharA*)OpcUa_Alloc(1024);
									if (bstrId)
									{
										iRes = sscanf(pBuff, "ns=%u;b=%1023s", (unsigned int*)&iNs, bstrId);
										if (iRes == 2)
										{
											pNodeId->IdentifierType = OpcUa_IdentifierType_Opaque;
											pNodeId->NamespaceIndex = (OpcUa_UInt16)iNs;
											OpcUa_ByteString_Initialize(&pNodeId->Identifier.ByteString);
											uStatus = OpcUa_Base64_Decode(bstrId, &pNodeId->Identifier.ByteString.Length, &pNodeId->Identifier.ByteString.Data);
										}
										else
											uStatus = OpcUa_BadInvalidArgument;
										OpcUa_Free(bstrId);
									}
								}
							}
						}
						else
							OpcUa_Trace(pTraceConfiguration, OPCUA_TRACE_CLIENT_LEVEL_ERROR, "Error>OpenOpcUa_StringToNodeId was called with an inconsistent string: %s\n", OpcUa_String_GetRawString(&strNodeId));
						OpcUa_Free(strId);
					}
				}
			}
		}
		else
			uStatus = OpcUa_BadInvalidArgument;
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>
/// 	Transform a NodeId in its string representation according the the UA Spec rules.
/// </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="aNodeId">  	Identifier for the node. </param>
/// <param name="strNodeId">	[in,out] If non-null, identifier for the node. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_NodeIdToString(OpcUa_NodeId aNodeId,OpcUa_String* strNodeId)
{
	//CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	//OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	//OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_NodeIdToString\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (strNodeId)
	{
		switch (aNodeId.IdentifierType)
		{
			case OpcUa_IdentifierType_Numeric: // Numeric
			{
				char* buffer=(char*)malloc(20);
				if (buffer)
				{
					memset(buffer, 0, 20);
					sprintf(buffer, "ns=%u;i=%u", aNodeId.NamespaceIndex, (unsigned int)aNodeId.Identifier.Numeric);
					OpcUa_String_AttachCopy(strNodeId, buffer);
					uStatus = OpcUa_Good;
					memset(buffer, 0, 20);
					free(buffer);
				}
				else
					uStatus = OpcUa_BadOutOfMemory;
			}
			break;
			case OpcUa_IdentifierType_String: // string
			{
				char* buffer=(char*)malloc(512);
				if (buffer)
				{
					ZeroMemory(buffer, 512);
					sprintf(buffer, "ns=%u;s=%s", aNodeId.NamespaceIndex, OpcUa_String_GetRawString(&(aNodeId.Identifier.String)));
					OpcUa_String_AttachCopy(strNodeId, buffer);
					uStatus = OpcUa_Good;
					ZeroMemory(buffer, 512);
					free(buffer);
				}
				else
					uStatus = OpcUa_BadOutOfMemory;
			}
			break;
			case OpcUa_IdentifierType_Guid: // guid
			{
				uStatus=OpcUa_BadNotSupported;
			}
			break;
			case OpcUa_IdentifierType_Opaque: // opaque (ByteString)
			{
				uStatus=OpcUa_BadNotSupported;
			}
			break;
			default:
				uStatus=OpcUa_BadInvalidArgument;
				break;
		}
	}
	else
		uStatus=OpcUa_BadInvalidArgument;
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA browse ex. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 25/09/2017. </remarks>
///
/// <param name="hApplication">					The application. </param>
/// <param name="hSession">						The session. </param>
/// <param name="iNoOfNodesToBrowse">			Zero-based index of the no of nodes to browse.
/// </param>
/// <param name="pNodesToBrowse">				The nodes to browse. </param>
/// <param name="iNoOfReferenceDescription">	[in,out] If non-null, information describing the
/// 											no of reference.
/// </param>
/// <param name="pReferenceList">				[in,out] If non-null, list of references. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_BrowseEx(OpcUa_Handle hApplication,
	OpcUa_Handle hSession,
	OpcUa_Int32 iNoOfNodesToBrowse,
	const OpcUa_BrowseDescription* pNodesToBrowse,
	OpcUa_Int32* iNoOfReferenceDescription,
	OpenOpcUa_ReferenceDescription** ppReferenceList)
{
	//CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	//OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	//OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_BrowseEx\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	OpcUa_NodeId parentNodeId;
	OpcUa_NodeId_Initialize(&parentNodeId);
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession = (CSessionClient*)hSession;
			if (pSession)
			{
				OpcUa_ReferenceDescriptionList aReferences;
				uStatus = pSession->Browse(iNoOfNodesToBrowse, pNodesToBrowse, &aReferences);
				if (uStatus == OpcUa_Good)
				{
					OpcUa_UInt32 uiNoOfReferencesDescrition= aReferences.size();
					if (uiNoOfReferencesDescrition > 0)
					{
						*iNoOfReferenceDescription = 1;
						(*ppReferenceList) = (OpenOpcUa_ReferenceDescription*)OpcUa_Alloc(sizeof(OpenOpcUa_ReferenceDescription));
						OpenOpcUa_ReferenceDescription* pReferenceList = (*ppReferenceList);
						pReferenceList->m_pOpcUa_ReferenceDescription = OpcUa_Null;
						pReferenceList->m_pOpcUa_ReferenceDescription = (OpcUa_ReferenceDescription*)OpcUa_Alloc(uiNoOfReferencesDescrition*sizeof(OpcUa_ReferenceDescription));
						pReferenceList->m_noOfOpcUa_ReferenceDescription = uiNoOfReferencesDescrition;
						OpcUa_NodeId_Initialize(&pReferenceList->m_ParentNodeId);
						OpcUa_NodeId_CopyTo(&pNodesToBrowse[0].NodeId, &parentNodeId); // for initialization purpose
						for (OpcUa_UInt32 ii = 0; ii < uiNoOfReferencesDescrition; ii++)
						{
							OpcUa_ReferenceDescription* pReferenceDescription = aReferences.at(ii);
							if (pReferenceDescription)
							{
								if ((!pReferenceDescription->IsForward) && (pReferenceDescription->NodeId.NodeId.Identifier.Numeric!= 0))
								{
									OpcUa_NodeId_Initialize(&pReferenceList->m_ParentNodeId);
									OpcUa_NodeId_CopyTo(&pReferenceDescription->NodeId.NodeId, &parentNodeId);
								}
								OpcUa_NodeId_Clear(&pReferenceList->m_ParentNodeId); // may overwriting pReferenceList->m_ParentNodeId
								OpcUa_NodeId_CopyTo(&parentNodeId, &(pReferenceList->m_ParentNodeId));

								//
								OpcUa_ReferenceDescription_Initialize(&(pReferenceList->m_pOpcUa_ReferenceDescription[ii]));
								OpcUa_QualifiedName_CopyTo(&(pReferenceDescription->BrowseName), &(pReferenceList->m_pOpcUa_ReferenceDescription[ii].BrowseName));
								OpcUa_LocalizedText_CopyTo(&(pReferenceDescription->DisplayName), &(pReferenceList->m_pOpcUa_ReferenceDescription[ii].DisplayName));
								pReferenceList->m_pOpcUa_ReferenceDescription[ii].IsForward = pReferenceDescription->IsForward;
								pReferenceList->m_pOpcUa_ReferenceDescription[ii].NodeClass = pReferenceDescription->NodeClass;
								OpcUa_ExpandedNodeId_CopyTo(&(pReferenceDescription->NodeId), &(pReferenceList->m_pOpcUa_ReferenceDescription[ii].NodeId));
								OpcUa_NodeId_CopyTo(&(pReferenceDescription->ReferenceTypeId), &(pReferenceList->m_pOpcUa_ReferenceDescription[ii].ReferenceTypeId));
								OpcUa_ExpandedNodeId_CopyTo(&(pReferenceDescription->TypeDefinition), &(pReferenceList->m_pOpcUa_ReferenceDescription[ii].TypeDefinition));
							}
						}
					}
					OpcUa_ReferenceDescriptionList::iterator it;
					for (it = aReferences.begin(); it != aReferences.end();it++)
					{
						OpcUa_ReferenceDescription* pReferenceDescription = *it;
						OpcUa_ReferenceDescription_Clear(pReferenceDescription);
						OpcUa_Free(pReferenceDescription);
					}
					aReferences.clear();
				}
			}
		}
	}

	OpcUa_NodeId_Clear(&parentNodeId);
	return uStatus;
}
///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA browse. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 25/09/2017. </remarks>
///
/// <param name="hApplication">					The application. </param>
/// <param name="hSession">						The session. </param>
/// <param name="iNoOfNodesToBrowse">			Zero-based index of the no of nodes to browse.
/// </param>
/// <param name="pNodesToBrowse">				The nodes to browse. </param>
/// <param name="iNoOfReferenceDescription">	[in,out] If non-null, information describing the
/// 											no of reference.
/// </param>
/// <param name="pReferenceList">				[in,out] If non-null, list of references. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_Browse(OpcUa_Handle hApplication,
								  OpcUa_Handle hSession,
								  OpcUa_Int32 iNoOfNodesToBrowse,
								  const OpcUa_BrowseDescription* pNodesToBrowse,
								  OpcUa_Int32* iNoOfReferenceDescription,
								  OpcUa_ReferenceDescription** pReferenceList)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_Browse\n");
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession=(CSessionClient*)hSession;
			if (pSession)
			{
				OpcUa_ReferenceDescriptionList aReferences;
				uStatus=pSession->Browse(iNoOfNodesToBrowse,pNodesToBrowse,&aReferences);
				if (uStatus==OpcUa_Good)
				{
					*iNoOfReferenceDescription=aReferences.size();
					if (aReferences.size() > 0)
					{
						*pReferenceList = (OpcUa_ReferenceDescription*)OpcUa_Alloc(aReferences.size()*sizeof(OpcUa_ReferenceDescription));
						for (OpcUa_UInt32 ii = 0; ii < aReferences.size(); ii++)
						{
							OpcUa_ReferenceDescription* pReferenceDescription = aReferences.at(ii);
							if (pReferenceDescription)
							{
								OpcUa_ReferenceDescription_Initialize(&((*pReferenceList)[ii]));
								OpcUa_QualifiedName_CopyTo(&(pReferenceDescription->BrowseName), &((*pReferenceList)[ii].BrowseName));
								OpcUa_LocalizedText_CopyTo(&(pReferenceDescription->DisplayName), &((*pReferenceList)[ii].DisplayName));
								(*pReferenceList)[ii].IsForward = pReferenceDescription->IsForward;
								(*pReferenceList)[ii].NodeClass = pReferenceDescription->NodeClass;
								OpcUa_ExpandedNodeId_CopyTo(&(pReferenceDescription->NodeId), &((*pReferenceList)[ii].NodeId));
								OpcUa_NodeId_CopyTo(&(pReferenceDescription->ReferenceTypeId), &((*pReferenceList)[ii].ReferenceTypeId));
								OpcUa_ExpandedNodeId_CopyTo(&(pReferenceDescription->TypeDefinition), &((*pReferenceList)[ii].TypeDefinition));
							}
						}
					}
					OpcUa_ReferenceDescriptionList::iterator it;
					while (!aReferences.empty())
					{
						it=aReferences.begin();
						OpcUa_ReferenceDescription* pReferenceDescription=*it;
						OpcUa_ReferenceDescription_Clear(pReferenceDescription);
						OpcUa_Free(pReferenceDescription);
						aReferences.erase(it);
					}
				}
			}
		}
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA get trace file. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 04/01/2017. </remarks>
///
/// <param name="strFileName">	[in,out] If non-null, filename of the file. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetTraceFile(OpcUa_String* strFileName)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetTraceFile\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (strFileName)
	{
		for (OpcUa_UInt32 ii = 0; ii < g_pUaClientApplicationList.size(); ii++)
		{
			CClientApplication* pApplication = g_pUaClientApplicationList.at(ii);
			OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
			OpcUa_Trace_GetTraceFile(pTraceConfiguration,strFileName);
		}
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA get trace level. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 04/01/2017. </remarks>
///
/// <returns>	An OpcUa_UInt32. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_UInt32 OpenOpcUa_GetTraceLevel()
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetTraceLevel\n");
	return OpcUa_Trace_GetTraceLevel(pTraceConfiguration);
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA set trace level. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 04/01/2017. </remarks>
///
/// <param name="iTraceLevel">	Zero-based index of the trace level. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_SetTraceLevel(OpcUa_UInt32 iTraceLevel)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_SetTraceLevel\n");
	OpcUa_StatusCode uStatus=OpcUa_Good;
	switch (iTraceLevel)
	{
	case OPCUA_TRACE_CLIENT_LEVEL_DEBUG:
	case OPCUA_TRACE_CLIENT_LEVEL_ERROR:
	case OPCUA_TRACE_CLIENT_LEVEL_WARNING:
	case OPCUA_TRACE_CLIENT_LEVEL_INFO:
	{
		for (OpcUa_UInt32 ii = 0; ii < g_pUaClientApplicationList.size(); ii++)
		{
			CClientApplication* pApplication = g_pUaClientApplicationList.at(ii);
			OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
			OpcUa_Trace_SetTraceLevel(pTraceConfiguration,iTraceLevel);
		}
	}
	break;
	default:
		uStatus = OpcUa_BadInvalidArgument;
		break;
	}
	return uStatus;
}
//#define OPCUA_TRACE_LEVEL_ERROR         0x00000020 /* in-system errors, which require bugfixing        */
//#define OPCUA_TRACE_LEVEL_WARNING       0x00000010 /* in-system warnings and extern errors             */
//#define OPCUA_TRACE_LEVEL_SYSTEM        0x00000008 /* rare system messages (start, stop, connect)      */
//#define OPCUA_TRACE_LEVEL_INFO          0x00000004 /* more detailed information about system events    */
//#define OPCUA_TRACE_LEVEL_DEBUG         0x00000002 /* information needed for debug reasons             */
//#define OPCUA_TRACE_LEVEL_CONTENT       0x00000001 /* all message content */
OpcUa_StatusCode OpenOpcUa_Trace(OpcUa_Handle hApplication, OpcUa_UInt32 a_uTraceLevel, const OpcUa_CharA* format,...)
{
	OpcUa_StatusCode uStatus=OpcUa_BadInternalError;
	char* outBuffer=(char*)malloc(1024);
	ZeroMemory(outBuffer, 1024);	
	char* outTmpBuffer = (char*)malloc(1024);
	ZeroMemory(outTmpBuffer, 1024);
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication=(CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			va_list args;
			va_start(args, format);
			//char* tmpStr=va_arg( args, char* );
			
			CLoggerMessage* pLoggerMessage=new CLoggerMessage();
			pLoggerMessage->m_pString=(OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String));
			// recherche des caractères  % dans format afin de déterminer ce qui vient de nous être passé
			if (format)
			{
				int iSize=strlen(format);
				// get first position of %
				int iFirstPos=-1;
				//save the first pos
				memcpy(outBuffer, format, iSize);
				
				for (int ii=0;ii<iSize;ii++)
				{
					if (format[ii]==0x25)
					{
						if (iFirstPos==-1)
							iFirstPos=ii;
						// analyse du contenu de l'octet suivant
						switch (format[ii+1])
						{
							case 'u':
							{
								unsigned int uiVal=va_arg(args,unsigned int);
								// Concatenation dans l'outBuffer
								sprintf(outTmpBuffer, "%s %u", outBuffer, uiVal);
							}
							break;
							case 'i':
							case 'd':
							{
								int uiVal=va_arg(args,int);
								// Concatenation dans l'outBuffer
								sprintf(outTmpBuffer, "%s %u", outBuffer, uiVal);
							}
							break;
							case 'e':
							case 'f':
							{
								double fltVal=va_arg(args,double);
								sprintf(outTmpBuffer, "%s %f", outBuffer, fltVal);
							}
							break;
							case 's':
							{
								char* cVal=va_arg(args,char*);
								// Concatenation dans l'outBuffer
								sprintf(outTmpBuffer, "%s %s", outBuffer, cVal);
							}
							break;
							case 'X':
							case 'x':
							{
								unsigned int uiVal=va_arg(args,unsigned int);
								// Concatenation dans l'outBuffer
								sprintf(outTmpBuffer, "%s %x", outBuffer, uiVal);
							}
							break;
							default:
								break;
						}
						strcpy(outBuffer, outTmpBuffer);
					}
				}
			}			
			OpcUa_String_AttachCopy(pLoggerMessage->m_pString,outBuffer);
			pLoggerMessage->m_uiLevel=a_uTraceLevel;
			pUaClientApplication->AddLoggerMessage(pLoggerMessage);
			va_end(args);
		}
	}
	free(outBuffer);
	free(outTmpBuffer);
	return uStatus;
}
// this function will provide the details for an attributes. This is not related to hApplication or hSession 
// This function is just an helper
OpcUa_StatusCode OpenOpcUa_GetAttributeDetails(OpcUa_Int32 iAttributeId, OpcUa_String** szAttributeName, OpcUa_String** szAttributeDescription)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetAttributeDetails\n");
	OpcUa_StatusCode uStatus=OpcUa_Good;
	if (szAttributeName && szAttributeDescription)
	{
		if ( (iAttributeId>0) && (iAttributeId<23) )
		{
			// AttributeName
			*szAttributeName=(OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String));
			OpcUa_String_Initialize(*szAttributeName);
			OpcUa_String_AttachCopy(*szAttributeName, OpcUa_Attributes[iAttributeId-1].szAttribute);
			// Attribute description
			*szAttributeDescription=(OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String));
			OpcUa_String_Initialize(*szAttributeDescription);
			OpcUa_String_AttachCopy(*szAttributeDescription, OpcUa_Attributes[iAttributeId-1].szAttributeText);
		}
		else
			uStatus=OpcUa_BadInvalidArgument;
	}
	else
		uStatus=OpcUa_BadInvalidArgument;
	return uStatus;
}

// Function name   : OpenOpcUa_GetBuiltInTypeDetails
// Description     : 
// This function will provide detail for BuiltInTypes. This is just an helper function
// Application have to release allocated ressources, szBuiltInTypeName, szBuiltInTypeDescription
// Return type     : OpcUa_StatusCode 
// Argument        : OpcUa_Int32 iBuiltInTypeId
// Argument        : OpcUa_String** szBuiltInTypeName output param
// Argument        : OpcUa_String** szBuiltInTypeDescription output param

OpcUa_StatusCode OpenOpcUa_GetBuiltInTypeDetails(OpcUa_Int32 iBuiltInTypeId, OpcUa_String** szBuiltInTypeName, OpcUa_String** szBuiltInTypeDescription)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetBuiltInTypeDetails\n");
	OpcUa_StatusCode uStatus=OpcUa_Good;
	if (szBuiltInTypeName && szBuiltInTypeDescription)
	{
		if ( (iBuiltInTypeId>=0) && (iBuiltInTypeId<26) )
		{
			// BuilInTypeName
			*szBuiltInTypeName=(OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String));
			OpcUa_String_Initialize(*szBuiltInTypeName);
			OpcUa_String_AttachCopy(*szBuiltInTypeName, OpcUa_BuiltInTypes[iBuiltInTypeId].szBuilInType);
			// Attribute description
			*szBuiltInTypeDescription=(OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String));
			OpcUa_String_Initialize(*szBuiltInTypeDescription);
			OpcUa_String_AttachCopy(*szBuiltInTypeDescription, OpcUa_BuiltInTypes[iBuiltInTypeId].szBuilInTypeText);
		}
		else
			uStatus=OpcUa_BadInvalidArgument;
	}
	else
		uStatus=OpcUa_BadInvalidArgument;
	return uStatus;
}
// Get the NodeClassName based ont the NodeClassId
OpcUa_StatusCode OpenOpcUa_GetNodeClassDetails(OpcUa_Int32 iNodeClass, OpcUa_String** szNodeClassName)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetNodeClassDetails\n");
	OpcUa_StatusCode uStatus=OpcUa_Good;
	*szNodeClassName=(OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String));
	OpcUa_String_Initialize(*szNodeClassName);
	switch (iNodeClass)
	{
	case 0:
		OpcUa_String_AttachCopy(*szNodeClassName,"Unspecified");
		break;
	case 1:
		OpcUa_String_AttachCopy(*szNodeClassName,"Object");
		break;
	case 2:
		OpcUa_String_AttachCopy(*szNodeClassName,"Variable");
		break;
	case 4:
		OpcUa_String_AttachCopy(*szNodeClassName,"Method");
		break;
	case 8:
		OpcUa_String_AttachCopy(*szNodeClassName,"ObjectType");
		break;
	case 16:
		OpcUa_String_AttachCopy(*szNodeClassName,"VariableType");
		break;
	case 32:
		OpcUa_String_AttachCopy(*szNodeClassName,"ReferenceType");
		break;
	case 64:
		OpcUa_String_AttachCopy(*szNodeClassName,"DataType");
		break;
	case 128:
		OpcUa_String_AttachCopy(*szNodeClassName,"View");
		break;
	default:
		uStatus=OpcUa_BadInvalidArgument;
		OpcUa_String_Clear(*szNodeClassName);
		break;
	}
	return uStatus;
}
OpcUa_StatusCode OpenOpcUa_GetAccessLevel(OpcUa_Byte AccessLevel, OpcUa_String** szAccessLevel)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetAccessLevel\n");
	OpcUa_StatusCode uStatus=OpcUa_Good;
	*szAccessLevel = (OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String));
	if (*szAccessLevel)
	{
		OpcUa_String StreamAccess, HistoryAccess;
		OpcUa_String_Initialize(&StreamAccess);
		OpcUa_String_Initialize(&HistoryAccess);
		OpcUa_String_Initialize((*szAccessLevel));
		if (AccessLevel == OpcUa_AccessLevels_None)
			OpcUa_String_AttachCopy(&StreamAccess, "None");
		if ((AccessLevel&OpcUa_AccessLevels_CurrentRead) == OpcUa_AccessLevels_CurrentRead)
			OpcUa_String_AttachCopy(&StreamAccess, "Read");
		if ((AccessLevel&OpcUa_AccessLevels_CurrentWrite) == OpcUa_AccessLevels_CurrentWrite)
			OpcUa_String_AttachCopy(&StreamAccess, "Write");
		if ((AccessLevel&OpcUa_AccessLevels_CurrentReadOrWrite) == OpcUa_AccessLevels_CurrentReadOrWrite)
			OpcUa_String_AttachCopy(&StreamAccess, "Read or Write");

		if ((AccessLevel&OpcUa_AccessLevels_HistoryRead) == OpcUa_AccessLevels_HistoryRead)
			OpcUa_String_AttachCopy(&HistoryAccess, "History Read");
		if ((AccessLevel&OpcUa_AccessLevels_HistoryWrite) == OpcUa_AccessLevels_HistoryWrite)
			OpcUa_String_AttachCopy(&HistoryAccess, "History Write");
		if ((AccessLevel&OpcUa_AccessLevels_HistoryReadOrWrite) == OpcUa_AccessLevels_HistoryReadOrWrite)
			OpcUa_String_AttachCopy(&HistoryAccess, "History Read and Write");
		//OpcUa_String_StrnCpy((*szAccessLevel), &StreamAccess, OpcUa_String_StrLen(&StreamAccess));
		OpcUa_String_CopyTo(&StreamAccess,(*szAccessLevel));
		if (OpcUa_String_StrLen(&HistoryAccess) > 0)
			OpcUa_String_StrnCat((*szAccessLevel), &HistoryAccess, OpcUa_String_StrLen(&HistoryAccess));
		OpcUa_String_Clear(&StreamAccess);
		OpcUa_String_Clear(&HistoryAccess);

	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Get the UserAccessLevel. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="AccessLevel">			The access level. </param>
/// <param name="szUserAccessLevel">	[in,out] If non-null, the user access level. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetUserAccessLevel(OpcUa_Byte AccessLevel, OpcUa_String** szUserAccessLevel)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetUserAccessLevel\n");
	return OpenOpcUa_GetAccessLevel(AccessLevel, szUserAccessLevel);
}

///-------------------------------------------------------------------------------------------------
/// <summary>
/// 	Transform an OpcUa_String to OpcUa_Variant according to the requested DataType.
/// </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 17/02/2017. </remarks>
///
/// <param name="pString">  	[in,out] If non-null, the string. </param>
/// <param name="iDataType">	Type of the data. </param>
/// <param name="pVariant"> 	[in,out] If non-null, the variant. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_StringToVariant(OpcUa_String* pString, OpcUa_Int32 iDataType,OpcUa_Variant** pVariant)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_StringToVariant\n");
	OpcUa_StatusCode uStatus=OpcUa_Good;
	if (pString)
	{
		if (OpcUa_String_StrLen(pString) > 0)
		{
			*pVariant = (OpcUa_Variant*)OpcUa_Alloc(sizeof(OpcUa_Variant));
			OpcUa_Variant_Initialize(*pVariant);
			switch (iDataType)
			{
			case OpcUaType_Boolean:
				(*pVariant)->Datatype = OpcUaType_Boolean;
				(*pVariant)->Value.Boolean = (OpcUa_Boolean)atoi(OpcUa_String_GetRawString(pString));
				break;
			case OpcUaType_Byte:
				(*pVariant)->Datatype = OpcUaType_Byte;
				(*pVariant)->Value.Byte = (OpcUa_Byte)atoi(OpcUa_String_GetRawString(pString));
				break;
			case OpcUaType_ByteString:
				break;
			case OpcUaType_DataValue:
				break;
			case OpcUaType_DateTime:
				break;
			case OpcUaType_DiagnosticInfo:
				uStatus = OpcUa_BadNotSupported;
				break;
			case OpcUaType_Double:
			{
				(*pVariant)->Datatype = OpcUaType_Double;
				OpcUa_Double dblVal = 0.0;
				if (sscanf(OpcUa_String_GetRawString(pString), "%lf", &dblVal) == 1)
					(*pVariant)->Value.Double = dblVal;
				else
					uStatus = OpcUa_BadInvalidArgument;
			}
				break;
			case OpcUaType_ExpandedNodeId:
				uStatus = OpcUa_BadNotSupported;
				break;
			case OpcUaType_ExtensionObject:
				uStatus = OpcUa_BadNotSupported;
				break;
			case OpcUaType_Float:
			{
				(*pVariant)->Datatype = OpcUaType_Float;
				OpcUa_Float fltVal = 0.0;
				if (sscanf(OpcUa_String_GetRawString(pString), "%f", &fltVal) == 1)
					(*pVariant)->Value.Float = fltVal;
				else
					uStatus = OpcUa_BadInvalidArgument;
			}
				break;
			case OpcUaType_Guid:
				uStatus = OpcUa_BadNotSupported;
				break;
			case OpcUaType_Int16:
				(*pVariant)->Datatype = OpcUaType_Int16;
				(*pVariant)->Value.Int16 = (OpcUa_Int16)atoi(OpcUa_String_GetRawString(pString));
				break;
			case OpcUaType_Int32:
				(*pVariant)->Datatype = OpcUaType_Int32;
				(*pVariant)->Value.Int32 = (OpcUa_Int32)atoi(OpcUa_String_GetRawString(pString));
				break;
			case OpcUaType_Int64:
				break;
			case OpcUaType_LocalizedText:
				break;
			case OpcUaType_NodeId:
			{
				OpcUa_NodeId nodeId;
				OpcUa_NodeId_Initialize(&nodeId);
				OpcUa_String szVal;
				OpcUa_String_Initialize(&szVal);
				if (pString)
				{
					OpcUa_String_CopyTo(pString, &szVal);
					uStatus = OpenOpcUa_StringToNodeId(szVal, &nodeId);
					if (uStatus == OpcUa_Good)
					{
						(*pVariant)->Value.NodeId = (OpcUa_NodeId*)OpcUa_Alloc(sizeof(OpcUa_NodeId));
						OpcUa_NodeId_Initialize((*pVariant)->Value.NodeId);
						(*pVariant)->Datatype = OpcUaType_NodeId;
						OpcUa_NodeId_CopyTo(&nodeId, (*pVariant)->Value.NodeId);;
					}
				}
				OpcUa_String_Clear(&szVal);
				OpcUa_NodeId_Clear(&nodeId);
			}
				break;
			case OpcUaType_Null:
				uStatus = OpcUa_BadNotSupported;
				break;
			case OpcUaType_QualifiedName:
				break;
			case OpcUaType_SByte:
				break;
			case OpcUaType_StatusCode:
				break;
			case OpcUaType_String:
				(*pVariant)->Datatype = OpcUaType_String;
				OpcUa_String_StrnCpy(&((*pVariant)->Value.String), pString, OpcUa_String_StrLen(pString));
				break;
			case OpcUaType_UInt16:
				(*pVariant)->Datatype = OpcUaType_UInt16;
				(*pVariant)->Value.UInt16 = (OpcUa_UInt16)atoi(OpcUa_String_GetRawString(pString));
				break;
			case OpcUaType_UInt32:
				(*pVariant)->Datatype = OpcUaType_UInt32;
				(*pVariant)->Value.UInt32 = (OpcUa_UInt32)atoi(OpcUa_String_GetRawString(pString));
				break;
			case OpcUaType_UInt64:
				break;
			case OpcUaType_Variant:
				uStatus = OpcUa_BadNotSupported;
				break;
			case OpcUaType_XmlElement:
				uStatus = OpcUa_BadNotSupported;
				break;
			default:
				uStatus = OpcUa_BadDataTypeIdUnknown;
			}
			if (uStatus != OpcUa_Good)
				OpcUa_Variant_Clear(*pVariant);
		}
		else
			uStatus = OpcUa_BadInvalidArgument;
	}
	else
		uStatus=OpcUa_BadInvalidArgument;
	return uStatus;
}

// Function for configuration file manipulation. Configuration file are conform to OpenOpcUaClientConfig.xsd
OpcUa_StatusCode OpenOpcUa_LoadConfig(OpcUa_Handle hApplication, OpcUa_String szConfigFileName)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_LoadConfig\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			OpcUa_CharA* localPath = OpcUa_Null;
			OpcUa_CharA* fileName = OpcUa_Null;
			OpcUa_CharA* chFileName = OpcUa_String_GetRawString(&szConfigFileName);
			// séparation du path et du nom de fichier

			basic_string<char> myString(chFileName);
			basic_string<char>::size_type index = 0;
#ifdef _GNUC_
			index = myString.rfind("/");
#else
#ifdef WIN32
			index = myString.rfind("\\");
#endif
#endif

			if (index != basic_string<char>::npos)
			{
				// path
				basic_string<char> tmpStr = myString.substr(0, index + 1);

				localPath = (char*)OpcUa_Alloc(tmpStr.size() + 1);
				if (localPath)
				{
					ZeroMemory(localPath, tmpStr.size() + 1);
					memcpy(localPath, tmpStr.c_str(), tmpStr.size());
					// fileName
					tmpStr = myString.substr(index + 1, myString.size() - index);

					fileName = (char*)OpcUa_Alloc(tmpStr.size() + 1);
					if (fileName)
					{
						ZeroMemory(fileName, tmpStr.size() + 1);
						memcpy(fileName, tmpStr.c_str(), tmpStr.size());

						if ((localPath) && (fileName))
						{
							uStatus = pUaClientApplication->LoadUaClientConfiguration(localPath, fileName);
							if (uStatus != OpcUa_Good)
								OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_ERROR, "Critical error>Cannot load ClientConfiguration file\n");
						}
						else
							OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_ERROR, "Critical error>Memory error, ClientConfiguration corrupted\n");
						if (fileName)
							OpcUa_Free(fileName);
					}
					if (localPath)
						OpcUa_Free(localPath);
				}
				else
				{
					OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_ERROR, "OpenOpcUa_LoadConfig failed. Not enough memory\n");
					uStatus = OpcUa_BadOutOfMemory;
				}
			}
			else
			{
				uStatus = OpcUa_BadFileNotFound;
				OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_ERROR, "Critical error>Full ClientConfiguration filename is corrupted\n");
			}
		}
		else
		{
			OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_ERROR, "Your Application handle is invalid\n");
			uStatus = OpcUa_BadInvalidArgument;
		}
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA save configuration. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">	   	The application. </param>
/// <param name="szConfigFileName">	Filename of the configuration file. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_SaveConfig(OpcUa_Handle hApplication, OpcUa_String szConfigFileName)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_SaveConfig\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			OpcUa_CharA* localPath = OpcUa_Null;
			OpcUa_CharA* fileName = OpcUa_Null;
			OpcUa_CharA* chFileName = OpcUa_String_GetRawString(&szConfigFileName);
			// séparation du path et du nom de fichier

			basic_string<char> myString(chFileName);
			basic_string<char>::size_type index = 0;
#ifdef _GNUC_
			index = myString.rfind("/");
#else
#ifdef WIN32
			index = myString.rfind("\\");
#endif
#endif

			if (index != basic_string<char>::npos)
			{
				// path
				basic_string<char> tmpStr = myString.substr(0, index + 1);

				localPath = (char*)OpcUa_Alloc(tmpStr.size() + 1);
				if (localPath)
				{
					ZeroMemory(localPath, tmpStr.size() + 1);
					memcpy(localPath, tmpStr.c_str(), index);
					// Path
					//localPath = myString.substr(0, index);
					// fileName
					tmpStr = myString.substr(index + 1, myString.size() - index);

					fileName = (char*)OpcUa_Alloc(tmpStr.size() + 1);
					if (fileName)
					{
						ZeroMemory(fileName, tmpStr.size() + 1);
						memcpy(fileName, tmpStr.c_str(), tmpStr.size());

						if ((localPath) && (fileName))
						{
							uStatus=pUaClientApplication->SaveUaClientConfiguration(localPath, fileName);

						}
					}
				}
			}
		}
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Retrive sessions for a hApplication. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">  	The application. </param>
/// <param name="uiNoOfSessions">	[in,out] If non-null, the no of sessions. </param>
/// <param name="hSessions">	 	[in,out] If non-null, the sessions. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetSessions(OpcUa_Handle hApplication,
	OpcUa_UInt32* uiNoOfSessions, // out
	OpcUa_Handle** hSessions) // out
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetSessions\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			uStatus = pUaClientApplication->GetSessions(uiNoOfSessions,hSessions);
		}
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA get subscriptions. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 05/01/2017. </remarks>
///
/// <param name="hApplication">		 	The application. </param>
/// <param name="hSession">			 	The session. </param>
/// <param name="uiNoOfSubscription">	[in,out] If non-null, the no of subscription. </param>
/// <param name="hSubscription">	 	[in,out] If non-null, the subscription. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetSubscriptions(OpcUa_Handle hApplication,
	OpcUa_Handle hSession,
	OpcUa_UInt32* uiNoOfSubscription, // out
	OpcUa_Handle** hSubscription) // out
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetSubscriptions\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			CSessionClient* pSession = (CSessionClient*)hSession;
			if (pSession)
			{
				uStatus=pSession->GetSubscriptions(uiNoOfSubscription,hSubscription);
			}
		}
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}
// retrieve the hMonitoredItems for a hSubscription
/// <summary>
/// Opens the opc ua_ get monitored items.
/// </summary>
/// <param name="hApplication">The h application.</param>
/// <param name="hSession">The h session.</param>
/// <param name="hSubscription">The h subscription.</param>
/// <param name="uiNoOfMonitoredItems">The UI no of monitored items.</param>
/// <param name="hMonitoredItems">hMonitoredItems is an input parameter it should be null in the call. It will be release by the caller</param>
/// <returns></returns>
OpcUa_StatusCode OpenOpcUa_GetMonitoredItems(OpcUa_Handle hApplication,
	OpcUa_Handle hSession,
	OpcUa_Handle hSubscription,
	OpcUa_UInt32* uiNoOfMonitoredItems, // out
	OpcUa_Handle** hMonitoredItems) // out
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetMonitoredItems\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (hMonitoredItems)
	{
		if (g_bAbstractionLayerInitialized)
		{
			CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
			if (pUaClientApplication)
			{
				CSessionClient* pSession = (CSessionClient*)hSession;
				if (pSession)
				{
					CSubscriptionClient* pSubscriptionClient = (CSubscriptionClient*)hSubscription;
					if (pSubscriptionClient)
					{

						uStatus = pSubscriptionClient->GetMonitoredItems(uiNoOfMonitoredItems, hMonitoredItems);
					}
				}
			}
		}
		else
			uStatus = OpcUa_BadInvalidArgument;
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

// Retrieve the dataType of the UAVariable (aNodeId)  
OpcUa_StatusCode OpenOpcUa_GetUAVariableDatatype(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_NodeId aNodeId, OpcUa_NodeId* pDataType)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_GetUAVariableDatatype\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	OpcUa_DataValue* pResults = OpcUa_Null;
	OpcUa_ReadValueId* pNodesToRead = OpcUa_Null;
	if (g_bAbstractionLayerInitialized)
	{
		CClientApplication* pUaClientApplication = (CClientApplication*)hApplication;
		if (pUaClientApplication)
		{
			OpcUa_Int32 iNodNodesToRead = 1;
			pNodesToRead = (OpcUa_ReadValueId*)OpcUa_Alloc(iNodNodesToRead*sizeof(OpcUa_ReadValueId));
			OpcUa_ReadValueId_Initialize(&pNodesToRead[0]);
			pNodesToRead[0].AttributeId = OpcUa_Attributes_DataType;
			pNodesToRead[0].NodeId = aNodeId;
			OpcUa_NodeId_CopyTo(&aNodeId, &(pNodesToRead[0].NodeId));
			uStatus = OpenOpcUa_ReadAttributes(hApplication, hSession, OpcUa_TimestampsToReturn_Both, iNodNodesToRead, pNodesToRead, &pResults);
			if (uStatus == OpcUa_Good)
			{
				// dans le cas du DataType le type est dans un NodeId
				if (pResults[0].StatusCode == OpcUa_Good)
				{
					if (!pDataType)
						pDataType=(OpcUa_NodeId*)OpcUa_Alloc(sizeof(OpcUa_NodeId));
					OpcUa_NodeId_Initialize(pDataType);
					OpcUa_NodeId_CopyTo(pResults[0].Value.Value.NodeId, pDataType);
				}
				if (pResults)
				{
					OpcUa_DataValue_Clear(pResults);
					OpcUa_Free(pResults);
				}
			}
			OpcUa_ReadValueId_Clear(pNodesToRead);
			OpcUa_Free(pNodesToRead);
		}
	}
	return uStatus;
}
// Caller must clear and release the extensionObject
OpcUa_StatusCode OpenOpcUa_CreateFilterObject(OpcUa_UInt32    DeadbandType,
	OpcUa_Double DeadbandValue,
	OpcUa_Byte   DatachangeTrigger,
	OpcUa_ExtensionObject** pExtensionObject)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_CreateFilterObject\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (!(*pExtensionObject))
	{
		(*pExtensionObject) = (OpcUa_ExtensionObject*)OpcUa_Alloc(sizeof(OpcUa_ExtensionObject));
		OpcUa_ExtensionObject_Initialize((*pExtensionObject));
		(*pExtensionObject)->TypeId.NodeId.Identifier.Numeric = OpcUaId_DataChangeFilter_Encoding_DefaultBinary;
		(*pExtensionObject)->Encoding = OpcUa_ExtensionObjectEncoding_EncodeableObject;
		(*pExtensionObject)->Body.EncodeableObject.Type = (OpcUa_EncodeableType*)OpcUa_Alloc(sizeof(OpcUa_EncodeableType));
		(*pExtensionObject)->Body.EncodeableObject.Type = &OpcUa_DataChangeFilter_EncodeableType;
		//DataChangeFilter.Body.EncodeableObject.Type->TypeId = OpcUaId_DataChangeFilter;
		OpcUa_DataChangeFilter* pDataChangeFilter = (OpcUa_DataChangeFilter*)OpcUa_Alloc(sizeof(OpcUa_DataChangeFilter));
		pDataChangeFilter->DeadbandType = DeadbandType;
		pDataChangeFilter->DeadbandValue = DeadbandValue;
		pDataChangeFilter->Trigger = (OpcUa_DataChangeTrigger)DatachangeTrigger;
		(*pExtensionObject)->Body.EncodeableObject.Object = (void*)pDataChangeFilter;
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}
// Give a string representation of an StatusCode
OpcUa_StatusCode OpenOpcUa_StatusCodeToString(OpcUa_StatusCode inStatus, OpcUa_String** pszStatusCode)
{
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "Call OpenOpcUa_StatusCodeToString\n");
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (!(*pszStatusCode))
	{
		(*pszStatusCode) = (OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String));
		if ((*pszStatusCode))
		{
			OpcUa_String_Initialize((*pszStatusCode));
			switch (inStatus)
			{
				case OpcUa_GoodCallAgain:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodCallAgain");
					break;
				case OpcUa_GoodClamped:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodClamped");
					break;
				case OpcUa_GoodCommunicationEvent:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodCommunicationEvent");
					break;
				case OpcUa_GoodCompletesAsynchronously:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodCompletesAsynchronously");
					break;
				case OpcUa_GoodDataIgnored:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodDataIgnored");
					break;
				case OpcUa_GoodEntryInserted:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodEntryInserted");
					break;
				case OpcUa_GoodEntryReplaced:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodEntryReplaced");
					break;
				case OpcUa_GoodLocalOverride:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodLocalOverride");
					break;
				case OpcUa_GoodMoreData:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodMoreData");
					break;
				case OpcUa_GoodNoData:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodNoData");
					break;
				case OpcUa_GoodNonCriticalTimeout:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodNonCriticalTimeout");
					break;
				case OpcUa_GoodOverload:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodOverload");
					break;
				case OpcUa_GoodResultsMayBeIncomplete:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodResultsMayBeIncomplete");
					break;
				case OpcUa_GoodShutdownEvent:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodShutdownEvent");
					break;
				case OpcUa_GoodSubscriptionTransferred:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodSubscriptionTransferred");
					break;
				case OpcUa_GoodTimeout:
					OpcUa_String_AttachCopy((*pszStatusCode), "GoodTimeout");
					break;
				case OpcUa_Good:
					OpcUa_String_AttachCopy((*pszStatusCode), "Good");
					break;
				case OpcUa_Uncertain:
					OpcUa_String_AttachCopy((*pszStatusCode), "Uncertain");
					break;
				case OpcUa_BadTimeout:
					OpcUa_String_AttachCopy((*pszStatusCode), "BadTimeout");
					break;
				case OpcUa_BadCommunicationError:
					OpcUa_String_AttachCopy((*pszStatusCode), "BadCommunicationError");
					break;
				case OpcUa_BadConnectionClosed:
					OpcUa_String_AttachCopy((*pszStatusCode), "BadCertificateIssuerRevocationUnknown");
					break;
				case OpcUa_BadCertificateInvalid:
					OpcUa_String_AttachCopy((*pszStatusCode), "BadCertificateInvalid");
					break;
				case OpcUa_BadCertificateTimeInvalid:
					OpcUa_String_AttachCopy((*pszStatusCode), "BadCertificateTimeInvalid");
					break;
				case OpcUa_BadCertificateRevoked:
					OpcUa_String_AttachCopy((*pszStatusCode), "BadCertificateRevoked");
					break;
				case OpcUa_BadCertificateUntrusted:
					OpcUa_String_AttachCopy((*pszStatusCode), "BadCertificateUntrusted");
					break;
				case OpcUa_BadCertificateIssuerRevocationUnknown:
					OpcUa_String_AttachCopy((*pszStatusCode), "BadCertificateIssuerRevocationUnknown");
					break;
				case OpcUa_BadConnectionRejected:
					OpcUa_String_AttachCopy((*pszStatusCode), "BadConnectionRejected");
					break;
				default:
					{
						OpcUa_CharA* buf=(OpcUa_CharA*)OpcUa_Alloc(15);
						ZeroMemory(buf, 15);
						sprintf(buf, "0x%05x", (unsigned int)inStatus);
						OpcUa_String_AttachCopy((*pszStatusCode), buf);
						free(buf);
					}
					break;
			}
		}
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Decode from DataTypeDictionnaries of the connected server associated with hSession
/// 			and transfert in a OpenOpcUa_Definition.
/// 			The Datatype definition to retrive is pointed by NodeId We will extract its browseName in order to retrive the Datatype. 
/// 			</summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 17/01/2017. </remarks>
///
/// <param name="hApplication">	[Input] The application handle. </param>
/// <param name="hSession">	   	[Input] The session handle. </param>
/// <param name="nodeId">	   	[Input] Identifier for the node containing the DataType to decode. </param>
/// <param name="pDefinition"> 	[out] The OpenOpcUa_Definition that contains the found definition. </param>
///
/// <returns>	An OpcUa_StatusCode OpcUa_Good if works properly. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetDataTypeDefinition(OpcUa_Handle hApplication,OpcUa_Handle hSession, OpcUa_NodeId nodeId, OpenOpcUa_Definition** ppDefinition)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	OpcUa_UInt32 uiNoOfDataTypeDictionnary = 0;
	OpcUa_ByteString* dataTypeDictionnaries = OpcUa_Null; 
	// Find the DataType dictionnaries for this server
	uStatus = OpenOpcUa_GetBinaryDataTypeDictionnaries(hApplication, hSession, &uiNoOfDataTypeDictionnary, &dataTypeDictionnaries);
	if (uStatus == OpcUa_Good)
	{
		OpcUa_QualifiedName dataTypeBrowseName;
		OpcUa_QualifiedName_Initialize(&dataTypeBrowseName);
		///////////////////////////////////////////////////
		// Search BrowseName and NodeClass for nodeId
		// 
		OpcUa_Int32 iNodNodesToRead = 2;
		OpcUa_ReadValueId* pNodesToRead = (OpcUa_ReadValueId*)OpcUa_Alloc(iNodNodesToRead*sizeof(OpcUa_ReadValueId));
		OpcUa_ReadValueId_Initialize(&pNodesToRead[0]);
		OpcUa_ReadValueId_Initialize(&pNodesToRead[1]);
		OpcUa_DataValue* pResults = OpcUa_Null;
		pNodesToRead[0].AttributeId = OpcUa_Attributes_BrowseName;
		OpcUa_NodeId_CopyTo(&nodeId,&pNodesToRead[0].NodeId);
		pNodesToRead[1].AttributeId = OpcUa_Attributes_NodeClass;
		OpcUa_NodeId_CopyTo(&nodeId,&pNodesToRead[1].NodeId);
		// Read attribute (value)
		//CSessionClient* pSession = (CSessionClient*)hSession;
		uStatus = OpenOpcUa_ReadAttributes(
			hApplication,
			hSession,
			OpcUa_TimestampsToReturn_Both,
			iNodNodesToRead,
			pNodesToRead,
			&pResults);
		// If the read succeed why then we can continue the DataType extraction based on the dataTypeBrowseName
		if (uStatus == OpcUa_Good)
		{
			if (pResults)
			{
				if (pResults[0].Value.Datatype == OpcUaType_QualifiedName)
					OpcUa_QualifiedName_CopyTo(pResults[0].Value.Value.QualifiedName, &dataTypeBrowseName);
				if (pResults[1].Value.Value.UInt32 == OpcUa_NodeClass_DataType)
				{
					for (OpcUa_UInt32 i = 0; i < uiNoOfDataTypeDictionnary; i++)
					{
						if (dataTypeDictionnaries[i].Length > 0)
						{
							OpcUa_StringA pEncodedData = (OpcUa_StringA)OpcUa_Alloc(dataTypeDictionnaries[i].Length + 1);
							if (pEncodedData)
							{
								ZeroMemory(pEncodedData, dataTypeDictionnaries[i].Length + 1);
								memcpy(pEncodedData, dataTypeDictionnaries[i].Data, dataTypeDictionnaries[i].Length);
								OpcUa_Byte* pBytes = OpcUa_Null; // Base64 decoded XML Schema
								OpcUa_Int32 iByteCount = 0; // size in byte of the Base64 decoded XML Schema
								uStatus = OpcUa_Base64_Decode(pEncodedData, &iByteCount, &pBytes);
								if (uStatus == OpcUa_Good)
								{
									CXmlParser* pXmlParser = new CXmlParser(pBytes, iByteCount);
									if (pXmlParser)
									{
										if (pXmlParser->IsDataTypeDictionnary())
										{
											OpenOpcUa_Definition* pDefinition = (OpenOpcUa_Definition*)OpcUa_Alloc(sizeof(OpenOpcUa_Definition));
											OpenOpcUa_Definition_Initialize(pDefinition);
											
											// Fill the NodeId of the definition. This is the m_Type Attribute
											OpcUa_NodeId_CopyTo(&nodeId, &pDefinition->m_Type);
											uStatus = pXmlParser->SearchDataTypeDefinition(dataTypeBrowseName.Name, &pDefinition);
											if (uStatus == OpcUa_Good)
											{
												(*ppDefinition) = pDefinition;
												delete pXmlParser;
												// clean up
												OpcUa_Free(pBytes);
												pBytes = OpcUa_Null;
												OpcUa_Free(pEncodedData);
												break;
											}
											else
											{
												OpenOpcUa_Definition_Clear(pDefinition);
												OpcUa_Free(pDefinition);
												pDefinition = OpcUa_Null;
											}
										}
										delete pXmlParser;
									}
									OpcUa_Free(pBytes);
									pBytes = OpcUa_Null;
								}
								OpcUa_Free(pEncodedData);
							}
						}
					}
				}
				OpcUa_DataValue_Clear(&pResults[0]);
				OpcUa_DataValue_Clear(&pResults[1]);
				OpcUa_Free(pResults);
				pResults = OpcUa_Null;
			}
			else
				uStatus = OpcUa_BadInternalError;
		}
		OpcUa_ReadValueId_Clear(&pNodesToRead[0]);
		OpcUa_ReadValueId_Clear(&pNodesToRead[1]);
		OpcUa_Free(pNodesToRead);
		// clean up
		OpcUa_QualifiedName_Clear(&dataTypeBrowseName);
		for (OpcUa_UInt32 i = 0; i < uiNoOfDataTypeDictionnary; i++)
			OpcUa_ByteString_Clear(&dataTypeDictionnaries[i]);
		OpcUa_Free(dataTypeDictionnaries);
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	This function parse an XML Element and its attributes :
/// 			ELEMENT attr1 attr2 attr3
/// 										</summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 17/01/2017. </remarks>
///
/// <param name="rawBuffer">			[in] If non-null, buffer for raw data. </param>
/// <param name="pszElement">			[out] If non-null, the element. </param>
/// <param name="iNoOfAttributes">  	[out] number of attribute extracted.
/// </param>
/// <param name="ppszAttributes">   	[in,out] Attributes name it always contains iNoOfAttributes attribute name. </param>
/// <param name="ppAttributeValues">	[in,out] Attributes value it always contains iNoOfAttributes attribute value. </param>
/// <param name="pbOpen">				[in,out] Report if the XMLElement is close of not. </param>
///
/// <returns>	An OpcUa_StatusCode. OpcUa_Good if it works properly. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode XMLParseElement(OpcUa_CharA* rawBuffer, OpcUa_String* pszElement, OpcUa_Int32* iNoOfAttributes, OpcUa_String** ppszAttributes, OpcUa_String** ppAttributeValues, OpcUa_Boolean* pbOpen)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	CClientApplication* pApplication = g_pUaClientApplicationList.at(0);
	OpcUa_ProxyStubConfiguration* pTraceConfiguration = pApplication->GetTraceConfiguration();
	OpcUa_Trace(pTraceConfiguration,OPCUA_TRACE_CLIENT_LEVEL_INFO, "%s\n", rawBuffer);
	OpcUa_Int32 i = 0;
	OpcUa_Int32 nIndex = 0;
	OpcUa_Boolean bEndElement = OpcUa_False; // mark end of XML element (/ found)
	std::vector<tAttribute*> Attributes;
	OpcUa_String_Initialize(pszElement);
	while (rawBuffer[i] != '<')
		i ++ ;
	// Jump <
	if (rawBuffer[i] == '<')
		i++;
	OpcUa_CharA* pszLocalElement = (OpcUa_CharA*)OpcUa_Alloc(1024);
	OpcUa_CharA* pszLocalAttribute = (OpcUa_CharA*)OpcUa_Alloc(1024);
	ZeroMemory(pszLocalElement, 1024);
	ZeroMemory(pszLocalAttribute, 1024);
	tAttribute* pAttribute = (tAttribute*)OpcUa_Alloc(sizeof(tAttribute));
	while (rawBuffer[i] != 0)
	{
		OpcUa_String_Initialize(&pAttribute->szAttr);
		OpcUa_String_Initialize(&pAttribute->szVal);

		// Step 1. Extract the element
		nIndex = 0;
		while (rawBuffer[i] != 0x20)
		{
			if ((rawBuffer[i] != 0x0a) && (rawBuffer[i] != 0x0d) && (rawBuffer[i] != '>'))
				pszLocalElement[nIndex++] = rawBuffer[i];
			if (rawBuffer[i] != '>')
				break;
			else
				i++;
		}
		if (nIndex > 0)
			OpcUa_String_AttachCopy(pszElement, pszLocalElement);
		// Step 2. Extract the attributes
		// Jump the space (0x20)
		while (rawBuffer[i] == 0x20)
			i++;
		// Attribute itself
		nIndex = 0;
		ZeroMemory(pszLocalAttribute, 1024);
		OpcUa_Boolean bNameAttributeParsingStarted = OpcUa_False;
		OpcUa_Boolean bValueAttributeParsingStarted = OpcUa_False;
		while ((rawBuffer[i] != '>') && (rawBuffer[i] != 0) )
		{
			switch (rawBuffer[i])
			{
			case 0x20: // SPACE
				if ((bNameAttributeParsingStarted) || (bValueAttributeParsingStarted) )
					pszLocalAttribute[nIndex++] = rawBuffer[i++];
				if ((OpcUa_String_StrLen(&pAttribute->szAttr)>0) && (OpcUa_String_StrLen(&pAttribute->szVal)>0) )
				{
					Attributes.push_back(pAttribute);
					pAttribute = (tAttribute*)OpcUa_Alloc(sizeof(tAttribute));
					OpcUa_String_Initialize(&pAttribute->szAttr);
					OpcUa_String_Initialize(&pAttribute->szVal);
				}
				pszLocalAttribute[nIndex++] = rawBuffer[i];
				i++;
				break;
			case 0x22: // "
				// End parsing Value
				if ((!bNameAttributeParsingStarted) && (bValueAttributeParsingStarted) )
				{
					OpcUa_String_AttachCopy(&pAttribute->szVal, pszLocalAttribute);
					bValueAttributeParsingStarted = OpcUa_False;
				}
				else
				{
					// Start parsing Value
					if ((!bNameAttributeParsingStarted) && (!bValueAttributeParsingStarted))
					{
						if (OpcUa_String_StrLen(&pAttribute->szAttr) > 0)
							bValueAttributeParsingStarted = OpcUa_True;
						else
							bNameAttributeParsingStarted = OpcUa_True;
					}
				}
				nIndex = 0;
				ZeroMemory(pszLocalAttribute, 1024);
				//pszLocalAttribute[nIndex++] = rawBuffer[i];
				i++;
				break;
			case 0x3D: // =
				if ((bNameAttributeParsingStarted) && (!bValueAttributeParsingStarted))
				{
					if (pszLocalAttribute[0])
						OpcUa_String_AttachCopy(&pAttribute->szAttr, pszLocalAttribute);
					bNameAttributeParsingStarted = OpcUa_False;
					nIndex = 0;
					ZeroMemory(pszLocalAttribute, 1024);
				}
				bValueAttributeParsingStarted = OpcUa_False;
				//pszLocalAttribute[nIndex++] = rawBuffer[i];
				i++;
				break;
			case 0x5c: // '\'
				i++;
				break;
			case 0x2F: // /
				pszLocalAttribute[nIndex++] = rawBuffer[i];
				bEndElement = OpcUa_True;
				i++;
				break;
			case 0x3E: // >
				if (bEndElement)
					(*pbOpen) = bEndElement;
				nIndex = 0;
				ZeroMemory(pszLocalAttribute, 1024);
				i++;
				break;
			default:
				if ((!bNameAttributeParsingStarted) && (!bValueAttributeParsingStarted))
					bNameAttributeParsingStarted = OpcUa_True;
				if ((rawBuffer[i] != 0x0a) && (rawBuffer[i] != 0x0d))
					pszLocalAttribute[nIndex++] = rawBuffer[i];
				bEndElement = OpcUa_False;
				i++;
				break;
			}
		}
		i++;
	}
	// Transfort from Attributes
	(*iNoOfAttributes)= Attributes.size();
	if ((*iNoOfAttributes) > 0)
	{
		(*ppszAttributes) = (OpcUa_String*)OpcUa_Alloc((*iNoOfAttributes)*sizeof(OpcUa_String));
		(*ppAttributeValues) = (OpcUa_String*)OpcUa_Alloc((*iNoOfAttributes)*sizeof(OpcUa_String));
	}
	for (OpcUa_UInt32 iv = 0; iv < Attributes.size(); iv++)
	{
		OpcUa_String_Initialize(&(*ppszAttributes)[iv]);
		OpcUa_String_Initialize(&(*ppAttributeValues)[iv]);
		tAttribute* pAttribute = Attributes.at(iv);
		OpcUa_String_CopyTo(&pAttribute->szAttr, &(*ppszAttributes)[iv]);
		OpcUa_String_CopyTo(&pAttribute->szVal, &(*ppAttributeValues)[iv]);
	}
	return uStatus;
}
///-------------------------------------------------------------------------------------------------
/// <summary>	Represent any extensionObject asa sequence of byte put in a OpcUa_String. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 15/01/2017. </remarks>
///
/// <param name="byteString">	  	The byte string. </param>
/// <param name="extensionObject">	The extension object. </param>
/// <param name="ppStrValue">	  	[in,out] If non-null, the string value. </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_ExtensionObjectToString(OpcUa_Handle hApplication, OpcUa_Handle hSession,OpcUa_ExtensionObject* pExtensionObject,OpcUa_String** ppStrValue)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	OpenOpcUa_Definition* pDefinition = OpcUa_Null;	
	// first check if the dataTypeDictonnary is provided or not
	//if (byteString.Length != 0)
	{
		OpcUa_NodeId aNodeId;
		OpcUa_NodeId_Initialize(&aNodeId);
		// First step we will find the namespaceIndex of the ExtensionObject to read
		// Read 2255 NamespaceArray
		OpcUa_NodeId nodeId;
		OpcUa_NodeId_Initialize(&nodeId);
		nodeId.IdentifierType = OpcUa_IdentifierType_Numeric;
		nodeId.Identifier.Numeric = OpcUaId_Server_NamespaceArray; 
		OpcUa_Int32 iNodNodesToRead = 1;
		OpcUa_ReadValueId* pNodesToRead = (OpcUa_ReadValueId*)OpcUa_Alloc(iNodNodesToRead*sizeof(OpcUa_ReadValueId));
		OpcUa_ReadValueId_Initialize(&pNodesToRead[0]);
		OpcUa_DataValue* pResults = OpcUa_Null;
		pNodesToRead[0].AttributeId = OpcUa_Attributes_Value;
		OpcUa_NodeId_CopyTo(&nodeId, &pNodesToRead[0].NodeId);
		// Read attribute (value)
		//CSessionClient* pSession = (CSessionClient*)hSession;
		uStatus = OpenOpcUa_ReadAttributes(
			hApplication,
			hSession,
			OpcUa_TimestampsToReturn_Both,
			iNodNodesToRead,
			pNodesToRead,
			&pResults);
		if (uStatus == OpcUa_Good)
		{
			if (pResults)
			{
				if ((pResults[0].Value.Datatype == OpcUaType_String) && (pResults[0].Value.ArrayType == OpcUa_VariantArrayType_Array))
				{
					OpcUa_Boolean bFound = OpcUa_False;
					if (pExtensionObject->Body.EncodeableObject.Type->NamespaceUri)
					{
						OpcUa_String szUri;
						OpcUa_String_Initialize(&szUri);
						OpcUa_String_AttachCopy(&szUri, pExtensionObject->Body.EncodeableObject.Type->NamespaceUri);
						for (OpcUa_Int32 i = 0; i < pResults[0].Value.Value.Array.Length; i++)
						{
							OpcUa_String szValue = pResults[0].Value.Value.Array.Value.StringArray[i];
							if (OpcUa_String_Compare(&szValue, &szUri) == 0)
							{
								bFound = OpcUa_True;
								aNodeId.NamespaceIndex = (OpcUa_UInt16)i;
								aNodeId.Identifier.Numeric = pExtensionObject->Body.EncodeableObject.Type->TypeId;
								break;
							}
						}
						OpcUa_String_Clear(&szUri);
					}
					else
					{
						bFound = OpcUa_True;
						aNodeId.Identifier.Numeric = pExtensionObject->Body.EncodeableObject.Type->TypeId;
					}
					OpcUa_DataValue_Clear(pResults);
					OpcUa_Free(pResults);
					if (bFound)
					{
						uStatus = OpenOpcUa_GetDataTypeDefinition(hApplication, hSession, aNodeId, &pDefinition);
						if (uStatus == OpcUa_Good)
						{
							uStatus =InternalExtensionObjectToString(pDefinition,pExtensionObject->Body.EncodeableObject.Object, ppStrValue);
							OpenOpcUa_Definition_Clear(pDefinition);
						}
					}
				}
			}
		}
		OpcUa_ReadValueId_Clear(&pNodesToRead[0]);
		OpcUa_Free(pNodesToRead);
	}
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>
/// 	This function return the Binary representation of the Datatype Dictionnary available in
/// 	the server associate with the session handle.
/// </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 15/01/2017. </remarks>
///
/// <param name="hApplication">				   	The data type dictionnaries. </param>
/// <param name="hSession">					   	The session. </param>
/// <param name="puiNodeOfDataTypeDictionnary">	[out] Contains the number of DatatypeDictionnary return.
/// </param>
/// <param name="ppDataTypeDictionnaries">	   	[out] Contains the dataType dictionnaries.
/// </param>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_GetBinaryDataTypeDictionnaries(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_UInt32* puiNodeOfDataTypeDictionnary, OpcUa_ByteString** ppDataTypeDictionnaries)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	std::vector<OpcUa_ByteString*> dataTypeDictionnaries;
	OpcUa_Int32 iNoOfReferenceDescription = 0;
	OpcUa_ReferenceDescription* pReferenceList = OpcUa_Null;
	OpcUa_BrowseDescription* pNodesToBrowse = OpcUa_Null;
	OpcUa_Int32 a_nNoOfNodesToBrowse = 1;
	pNodesToBrowse = (OpcUa_BrowseDescription*)OpcUa_Alloc((sizeof(OpcUa_BrowseDescription)*a_nNoOfNodesToBrowse));
	OpcUa_BrowseDescription_Initialize(&pNodesToBrowse[0]);
	pNodesToBrowse[0].BrowseDirection = OpcUa_BrowseDirection_Forward;
	pNodesToBrowse[0].IncludeSubtypes = true;
	pNodesToBrowse[0].NodeClassMask = OpcUa_NodeClass_Variable; //
	pNodesToBrowse[0].ResultMask = OpcUa_BrowseResultMask_All;

	//Start browsing from pFromNodeId; nodeId i=93 (Opc Binary) this is a UAObject.
	// It contains UAVariable of type ByteString. The ByteString value is a Base64 xsd encoded 
	OpcUa_NodeId* pFromNodeId = (OpcUa_NodeId*)OpcUa_Alloc(sizeof(OpcUa_NodeId));
	OpcUa_NodeId_Initialize(pFromNodeId);
	pFromNodeId->Identifier.Numeric = 93; // Opc Binary
	OpcUa_NodeId_CopyTo(pFromNodeId, &(pNodesToBrowse[0].NodeId));
	OpcUa_NodeId_Initialize(&(pNodesToBrowse[0].ReferenceTypeId));
	pNodesToBrowse[0].ReferenceTypeId.IdentifierType = OpcUa_IdentifierType_Numeric;
	pNodesToBrowse[0].ReferenceTypeId.NamespaceIndex = 0;
	pNodesToBrowse[0].ReferenceTypeId.Identifier.Numeric = OpcUaId_HierarchicalReferences; //OpcUaId_References;
	uStatus = OpenOpcUa_Browse(hApplication, hSession, a_nNoOfNodesToBrowse, pNodesToBrowse, &iNoOfReferenceDescription, &pReferenceList);
	if (uStatus == OpcUa_Good)
	{
		for (OpcUa_Int32 i = 0; i < iNoOfReferenceDescription; i++)
		{
			if (pReferenceList[i].NodeClass == OpcUa_NodeClass_Variable)
			{
				// Get the ByteString from the UAVariable
				// Decode with OpcUa_Base64_Decode
				OpcUa_DataValue* pResults = OpcUa_Null;
				OpcUa_ReadValueId* pNodesToRead = OpcUa_Null;
				OpcUa_Int32 iNodNodesToRead = 0;
				;
				iNodNodesToRead = 1;
				pNodesToRead = (OpcUa_ReadValueId*)OpcUa_Alloc(iNodNodesToRead*sizeof(OpcUa_ReadValueId));
				OpcUa_ReadValueId_Initialize(&pNodesToRead[0]);
				pNodesToRead[0].AttributeId = OpcUa_Attributes_Value;
				OpcUa_NodeId_CopyTo(&pReferenceList[i].NodeId.NodeId,&pNodesToRead[0].NodeId);
				// Read attribute (value)
				uStatus = OpenOpcUa_ReadAttributes(
					hApplication,
					hSession,
					OpcUa_TimestampsToReturn_Both,
					iNodNodesToRead,
					pNodesToRead,
					&pResults);
				// si la lecture aboutie on attends
				if (uStatus == OpcUa_Good)
				{
					if (pResults)
					{
						// Extract the value
						if (pResults[0].Value.Datatype != OpcUaType_ByteString)
							uStatus = OpcUa_BadInternalError; // Read ok but wrong DataType 
						else
						{
							if (pResults[0].Value.ArrayType == OpcUa_VariantArrayType_Scalar)
							{
								OpcUa_ByteString* pDataTypeDictonnary = (OpcUa_ByteString*)OpcUa_Alloc(sizeof(OpcUa_ByteString));
								OpcUa_ByteString_Initialize(pDataTypeDictonnary);
								OpcUa_ByteString_CopyTo(&pResults[0].Value.Value.ByteString, pDataTypeDictonnary);
								dataTypeDictionnaries.push_back(pDataTypeDictonnary);
							}
							else
								uStatus = OpcUa_BadInternalError; // Read ok but wrong ArrayType 
						}
						OpcUa_DataValue_Clear(pResults);
						OpcUa_Free(pResults);
						pResults = OpcUa_Null;
					}
				}
				OpcUa_ReadValueId_Clear(pNodesToRead);
				OpcUa_Free(pNodesToRead);
			}
			OpcUa_ReferenceDescription_Clear(&pReferenceList[i]);
		}
		OpcUa_Free(pReferenceList);
	}
	// Transfert from dataTypeDictionnary to OpcUa_ByteString*
	if (dataTypeDictionnaries.size() > 0)
	{
		(*puiNodeOfDataTypeDictionnary) = dataTypeDictionnaries.size();
		(*ppDataTypeDictionnaries) = (OpcUa_ByteString*)OpcUa_Alloc((*puiNodeOfDataTypeDictionnary) * sizeof(OpcUa_ByteString));
		for (OpcUa_UInt32 i = 0; i < dataTypeDictionnaries.size(); i++)
		{
			OpcUa_ByteString_Initialize(&(*ppDataTypeDictionnaries)[i]);
			OpcUa_ByteString* pdataTypeDisctionnary = dataTypeDictionnaries.at(i);
			OpcUa_ByteString_CopyTo(pdataTypeDisctionnary, &(*ppDataTypeDictionnaries)[i]);
			//
			OpcUa_ByteString_Clear(pdataTypeDisctionnary);
			OpcUa_Free(pdataTypeDisctionnary);
		}
	}
	OpcUa_BrowseDescription_Clear(pNodesToBrowse);
	OpcUa_Free(pNodesToBrowse);
	OpcUa_NodeId_Clear(pFromNodeId);
	OpcUa_Free(pFromNodeId);
	return uStatus;
}

#define VarriantArray_SetAt(xArray, nIndex, xValue, xType)			\
{																	\
	(xArray)[nIndex].Datatype = (OpcUa_Byte) OpcUaType_##xType;		\
	OpcUa_##xType##_Initialize(&(xArray)[nIndex].Value.xType);		\
	OpcUa_##xType##_CopyTo(xValue, &(xArray)[nIndex].Value.xType);	\
}

#define VarriantArray_SetAtP(xArray, nIndex, xValue, xType)		\
{																	\
	if (OpcUa_Null == (xArray)[nIndex].Value.xType)					\
		(xArray)[nIndex].Value.xType = (OpcUa_##xType*) OpcUa_Alloc(sizeof(OpcUa_##xType));	\
	(xArray)[nIndex].Datatype = (OpcUa_Byte) OpcUaType_##xType;		\
	OpcUa_##xType##_Initialize((xArray)[nIndex].Value.xType);		\
	OpcUa_##xType##_CopyTo(xValue, (xArray)[nIndex].Value.xType);	\
}

OpcUa_StatusCode OpenOpcUa_GetInstanceValue(OpcUa_NodeId* pSourceNodeId, OpenOpcUa_Definition* pDefinition, OpcUa_Variant rawValue,OpcUa_Variant** ppVariantVal)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (pSourceNodeId)
	{
		if (pDefinition)
		{
			if (pDefinition->m_baseType.Identifier.Numeric == OpcUaId_Structure)
			{
				// Expand the extensionObject in the ppVariantVal
				OpcUa_UInt32 uiSize = pDefinition->m_uiNoOfField;
				if (rawValue.ArrayType == OpcUa_VariantArrayType_Scalar)
				{
					(*ppVariantVal) = (OpcUa_Variant*)OpcUa_Alloc(uiSize*sizeof(OpcUa_Variant)); // Regular value
					if (rawValue.Value.ExtensionObject)
					{
						OpcUa_Boolean bHandled = OpcUa_False;
						void* pObject = rawValue.Value.ExtensionObject->Body.EncodeableObject.Object;
						if (rawValue.Value.ExtensionObject->Body.EncodeableObject.Type)
						{
							if (OpcUaId_ServerStatusDataType == rawValue.Value.ExtensionObject->Body.EncodeableObject.Type->TypeId)
							{
								OpcUa_ServerStatusDataType* pStatus = (OpcUa_ServerStatusDataType*) pObject;
								VarriantArray_SetAt(*ppVariantVal, 0, &pStatus->StartTime, DateTime); /* StartTime */
								VarriantArray_SetAt(*ppVariantVal, 1, &pStatus->CurrentTime, DateTime); /* CurrentTime */
								VarriantArray_SetAt(*ppVariantVal, 2, &pStatus->State, Int32); /* State */
								VarriantArray_SetAt(*ppVariantVal, 3, &pStatus->BuildInfo.ProductUri, String); /* BuildInfo.ProductUri */
								VarriantArray_SetAt(*ppVariantVal, 4, &pStatus->BuildInfo.ManufacturerName, String); /* BuildInfo.ManufacturerName */
								VarriantArray_SetAt(*ppVariantVal, 5, &pStatus->BuildInfo.ProductName, String); /* BuildInfo.ProductName */
								VarriantArray_SetAt(*ppVariantVal, 6, &pStatus->BuildInfo.SoftwareVersion, String); /* BuildInfo.SoftwareVersion */
								VarriantArray_SetAt(*ppVariantVal, 7, &pStatus->BuildInfo.BuildNumber, String); /* BuildInfo.BuildNumber */
								VarriantArray_SetAt(*ppVariantVal, 8, &pStatus->BuildInfo.BuildDate, DateTime); /* BuildInfo.BuildDate */
								VarriantArray_SetAt(*ppVariantVal, 9, &pStatus->SecondsTillShutdown, UInt32); /* SecondsTillShutdown */
								VarriantArray_SetAtP(*ppVariantVal, 10, &pStatus->ShutdownReason, LocalizedText); /* ShutdownReason */
								/* clear ServerStatusDataType used */
								OpcUa_ServerStatusDataType_Clear(pStatus);
								bHandled = OpcUa_True;
							}
						}

						if (!bHandled)
						{
							void* pObject = rawValue.Value.ExtensionObject->Body.EncodeableObject.Object;
							for (OpcUa_UInt32 i = 0; i < uiSize; i++)
							{
								OpenOpcUa_Field* pField = pDefinition->m_pFields[i];
								if (pField->m_DataType.NamespaceIndex == 0)
								{
									switch (pField->m_DataType.Identifier.Numeric)
									{
									case OpcUaType_Boolean:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.Boolean), 1, pObject, 1);
										((OpcUa_Byte*&)pObject) += 1; //
									}
									break;
									case OpcUaType_SByte:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.SByte), 1, pObject, 1);
										((OpcUa_Byte*&)pObject) += 1; //
									}
									break;
									case OpcUaType_Byte:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.Byte), 1, pObject, 1);
										((OpcUa_Byte*&)pObject) += 1; //
									}
									break;
									case OpcUaType_Int16:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.Int16), 2, pObject, 2);
										((OpcUa_Byte*&)pObject) += 2; //
									}
									break;
									case OpcUaType_UInt16:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.UInt16), 2, pObject, 2);
										((OpcUa_Byte*&)pObject) += 2; //
									}
									break;
									case OpcUaType_Int32:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.Int32), 4, pObject, 4);
										((OpcUa_Byte*&)pObject) += 4; //
									}
									break;
									case OpcUaType_UInt32:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.UInt32), 4, pObject, 4);
										((OpcUa_Byte*&)pObject) += 4; //
									}
									break;
									case OpcUaType_Int64:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.Int64), 8, pObject, 8);
										((OpcUa_Byte*&)pObject) += 8; //
									}
									break;
									case OpcUaType_UInt64:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.UInt64), 8, pObject, 8);
										((OpcUa_Byte*&)pObject) += 8; //
									}
									break;
									case OpcUaType_Float:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.Float), 4, pObject, 4);
										((OpcUa_Byte*&)pObject) += 4; //
									}
									break;
									case OpcUaType_Double:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.Double), 8, pObject, 8);
										((OpcUa_Byte*&)pObject) += 8; //
									}
									break;
									case OpcUaType_String:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_String_Initialize(&((*ppVariantVal)[i].Value.String));
										uStatus = InternalExtractString(pObject, &((*ppVariantVal)[i].Value.String));
										((OpcUa_Byte*&)pObject) += sizeof(OpcUa_String);
									}
									break;
									case OpcUaType_DateTime:
									{
										(*ppVariantVal)[i].Datatype = (OpcUa_Byte)pField->m_DataType.Identifier.Numeric;
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.DateTime.dwLowDateTime), 4, pObject, 4);
										((OpcUa_Byte*&)pObject) += 4; //
										OpcUa_MemCpy(&((*ppVariantVal)[i].Value.DateTime.dwHighDateTime), 4, pObject, 4);
										((OpcUa_Byte*&)pObject) += 4; //
									}
									break;
									case OpcUaType_Guid:
										break;
									case OpcUaType_ByteString:
										break;
									case OpcUaType_NodeId:
										break;
									case OpcUaType_ExpandedNodeId:
										break;
									case OpcUaType_StatusCode:
										break;
									case OpcUaType_QualifiedName:
										break;
									case OpcUaType_LocalizedText:
										break;
									case OpcUaType_ExtensionObject:
										// Search for this extension object in the DataType dictionnary
										break;
									case OpcUaType_DataValue:
										break;
									case OpcUaType_Variant:
										break;
									case OpcUaType_DiagnosticInfo:
										break;
									default:

										uStatus = OpcUa_BadNotSupported;
										break;
									}
								}
							}
						}
					}
					else
						uStatus = OpcUa_BadInvalidArgument;
				}
				else
				{
					if (rawValue.ArrayType == OpcUa_VariantArrayType_Array)
					{
						for (OpcUa_Int32 i = 0; i < rawValue.Value.Array.Length; i++)
						{
							void* pObject = rawValue.Value.Array.Value.ExtensionObjectArray[i].Body.EncodeableObject.Object;
						}
					}
				}
			}
			else
			{
				if (pDefinition->m_baseType.Identifier.Numeric == OpcUaId_Enumeration)
				{
					(*ppVariantVal) = (OpcUa_Variant*)OpcUa_Alloc(sizeof(OpcUa_Variant)); // Regular value
					OpcUa_Variant_Initialize((*ppVariantVal));
					OpenOpcUa_Field* pField = pDefinition->m_pFields[rawValue.Value.UInt32];
					(*ppVariantVal)->Datatype = OpcUaType_String;
					OpcUa_String_Initialize(&(*ppVariantVal)->Value.String);
					OpcUa_String_CopyTo(&pField->m_Name,&((*ppVariantVal)->Value.String));
				}
				else
				{
					(*ppVariantVal) = (OpcUa_Variant*)OpcUa_Alloc(sizeof(OpcUa_Variant)); // Regular value
					OpcUa_Variant_Initialize((*ppVariantVal));
					OpcUa_Variant_CopyTo(&rawValue, (*ppVariantVal));
				}
			}
		}
		else
			uStatus = OpcUa_BadInvalidArgument;
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA definition initialize. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 18/04/2017. </remarks>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_Definition_Initialize(OpenOpcUa_Definition* pDefinition)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (pDefinition)
	{
		ZeroMemory(pDefinition, sizeof(OpenOpcUa_Definition));
		OpcUa_String_Initialize(&pDefinition->m_Description);
		OpcUa_String_Initialize(&pDefinition->m_Name);
		OpcUa_NodeId_Initialize(&pDefinition->m_baseType);
		OpcUa_NodeId_Initialize(&pDefinition->m_Type);
		pDefinition->m_uiNoOfField = 0;
		pDefinition->m_pFields = OpcUa_Null;
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;	
}

///-------------------------------------------------------------------------------------------------
/// <summary>	Opens opc UA definition clear. </summary>
///
/// <remarks>	Michel Condemine - OpenOpcUa, 18/04/2017. </remarks>
///
/// <returns>	An OpcUa_StatusCode. </returns>
///-------------------------------------------------------------------------------------------------

OpcUa_StatusCode OpenOpcUa_Definition_Clear(OpenOpcUa_Definition* pDefinition)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (pDefinition)
	{
		OpcUa_NodeId_Clear(&pDefinition->m_baseType);
		OpcUa_String_Clear(&pDefinition->m_Description);
		OpcUa_String_Clear(&pDefinition->m_Name);
		OpcUa_NodeId_Clear(&pDefinition->m_Type);
		OpenOpcUa_Field** pFields = pDefinition->m_pFields;
		for (OpcUa_UInt32 i = 0; i < pDefinition->m_uiNoOfField; i++)
		{
			OpenOpcUa_Field* pField=pFields[i];
			OpenOpcUa_Field_Clear(pField);
			OpcUa_Free(pField);
		}
		OpcUa_Free(pDefinition->m_pFields);
		pDefinition->m_pFields = OpcUa_Null;
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;		
}
// Not exported
OpenOpcUa_Definition* Copy(OpenOpcUa_Definition* pDefinition);
OpenOpcUa_Field* Copy(OpenOpcUa_Field* pField);
OpcUa_StatusCode OpenOpcUa_Definition_AddField(OpenOpcUa_Definition* pDefinition, OpenOpcUa_Field* pNewField)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	// Save the exisiting Field
	OpenOpcUa_Fields fieldList;
	for (OpcUa_UInt32 i = 0; i < pDefinition->m_uiNoOfField; i++)
	{
		OpenOpcUa_Field* pField = pDefinition->m_pFields[i];
		// Do not copy but use directly
		//fieldList.push_back(Copy(pField));
		fieldList.push_back(pField);
	}
	// Add the newOne
	fieldList.push_back(pNewField);

	if (pDefinition->m_pFields)
	{
		// Expand the pDefinition->m_pFields
		/* No need to free since they were added back to fieldList above
		for (OpcUa_UInt32 ii = 0; ii < pDefinition->m_uiNoOfField; ii++)
		{
			OpenOpcUa_Field_Clear(pDefinition->m_pFields[ii]);
			OpcUa_Free(pDefinition->m_pFields[ii]);
			pDefinition->m_pFields[ii] = OpcUa_Null;
		}
		*/
		OpcUa_Free(pDefinition->m_pFields);
		pDefinition->m_pFields = OpcUa_Null;
	}
	// Allocate for the new size
	if (!pDefinition->m_pFields)
	{
		pDefinition->m_pFields = (OpenOpcUa_Field**)OpcUa_Alloc(fieldList.size()*sizeof(OpenOpcUa_Field*));
		/* no need to allocate memory here wich will be replaced just below
		for (OpcUa_UInt32 ii = 0; ii < fieldList.size(); ii++)
		{
			pDefinition->m_pFields[ii] = (OpenOpcUa_Field*)OpcUa_Alloc(sizeof(OpenOpcUa_Field));
			ZeroMemory(pDefinition->m_pFields[ii], sizeof(OpenOpcUa_Field));
		}
		*/
	}
	pDefinition->m_uiNoOfField = fieldList.size();
	for (OpcUa_UInt32 i = 0; i < fieldList.size(); i++)
	{
		OpenOpcUa_Field* pField = fieldList.at(i);
		pDefinition->m_pFields[i] = pField;
	}
	return uStatus;		
}
OpcUa_StatusCode OpenOpcUa_Field_Initialize(OpenOpcUa_Field* pField)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (pField)
	{
		OpcUa_NodeId_Initialize(&pField->m_DataType);
		OpcUa_String_Initialize(&pField->m_Description);
		pField->m_FieldType = UNKNOWN_FIELD;
		pField->m_iFieldSize = 0;
		OpcUa_String_Initialize(&pField->m_Name);
		pField->m_pDefinition = OpcUa_Null;
		OpcUa_String_Initialize(&pField->m_SymbolicName);
		OpcUa_String_Initialize(&pField->m_TypeName);
		OpcUa_Variant_Initialize(&pField->m_Value);
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}
OpcUa_StatusCode OpenOpcUa_Field_Clear(OpenOpcUa_Field* pField)
{
	OpcUa_StatusCode uStatus = OpcUa_Good;
	if (pField)
	{
		OpcUa_NodeId_Clear(&pField->m_DataType);
		OpcUa_String_Clear(&pField->m_Description);
		pField->m_FieldType = UNKNOWN_FIELD;
		pField->m_iFieldSize = 0;
		OpcUa_String_Clear(&pField->m_Name);
		pField->m_pDefinition = OpcUa_Null;
		OpcUa_String_Clear(&pField->m_SymbolicName);
		OpcUa_String_Clear(&pField->m_TypeName);
		OpcUa_Variant_Clear(&pField->m_Value);
	}
	else
		uStatus = OpcUa_BadInvalidArgument;
	return uStatus;
}

OpenOpcUa_Field* Copy(OpenOpcUa_Field* pField)
{
	OpenOpcUa_Field* pNewField= (OpenOpcUa_Field*)OpcUa_Alloc(sizeof(OpenOpcUa_Field));
	OpenOpcUa_Field_Initialize(pNewField);
	OpcUa_NodeId_CopyTo(&pField->m_DataType,&pNewField->m_DataType);
	OpcUa_String_CopyTo(&pField->m_Description,&pNewField->m_Description);
	pNewField->m_FieldType = pField->m_FieldType;
	pNewField->m_iFieldSize = pField->m_iFieldSize;
	OpcUa_String_CopyTo(&pField->m_Name,&pNewField->m_Name);
	pNewField->m_pDefinition = (void*)pField->m_pDefinition; //Copy((OpenOpcUa_Definition*)pField->m_pDefinition);
	OpcUa_String_CopyTo(&pField->m_SymbolicName, &pNewField->m_SymbolicName);
	OpcUa_String_CopyTo(&pField->m_TypeName, &pNewField->m_TypeName);
	OpcUa_Variant_CopyTo(&pField->m_Value, &pNewField->m_Value);
	return pNewField;
}
OpenOpcUa_Definition* Copy(OpenOpcUa_Definition* pDefinition)
{
	OpenOpcUa_Definition* pNewDefinition = (OpenOpcUa_Definition*)OpcUa_Alloc(sizeof(OpenOpcUa_Definition));
	OpenOpcUa_Definition_Initialize(pNewDefinition);
	OpcUa_NodeId_CopyTo(&pDefinition->m_baseType, &pNewDefinition->m_baseType);
	pNewDefinition->m_bUnion = pDefinition->m_bUnion;
	OpcUa_String_CopyTo(&pDefinition->m_Description,&pNewDefinition->m_Description);
	pNewDefinition->m_LengthInBits = pDefinition->m_LengthInBits;
	OpcUa_String_CopyTo(&pDefinition->m_Name,&pNewDefinition->m_Name);
	for (OpcUa_UInt32 i = 0; i < pDefinition->m_uiNoOfField; i++)
	{
		OpenOpcUa_Field* pField = pDefinition->m_pFields[i];
		OpenOpcUa_Field* pNewField = Copy(pField);
		OpenOpcUa_Definition_AddField(pNewDefinition, pNewField);
	}
	OpcUa_NodeId_CopyTo(&pDefinition->m_Type,&pNewDefinition->m_Type) ;
	pNewDefinition->m_uiNoOfField = pDefinition->m_uiNoOfField;
	return pNewDefinition;
}