<?xml version="1.0" encoding="UTF-8" ?>
<!-- created at 2025-12-16 14:12:31 -->
<UANodeSet xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd">
	<Extensions>
		<atvise Version="3.13"/>
	</Extensions>
	<NamespaceUris>
		<Uri>http://www.atvise.com/atServer/UA/</Uri>
	</NamespaceUris>
	<Aliases>
		<!-- data types -->
		<Alias Alias="Boolean">i=1</Alias>
		<Alias Alias="SByte">i=2</Alias>
		<Alias Alias="Byte">i=3</Alias>
		<Alias Alias="Int16">i=4</Alias>
		<Alias Alias="UInt16">i=5</Alias>
		<Alias Alias="Int32">i=6</Alias>
		<Alias Alias="UInt32">i=7</Alias>
		<Alias Alias="Int64">i=8</Alias>
		<Alias Alias="UInt64">i=9</Alias>
		<Alias Alias="Float">i=10</Alias>
		<Alias Alias="Double">i=11</Alias>
		<Alias Alias="String">i=12</Alias>
		<Alias Alias="DateTime">i=13</Alias>
		<Alias Alias="ByteString">i=15</Alias>
		<Alias Alias="XmlElement">i=16</Alias>
		<Alias Alias="NodeId">i=17</Alias>
		<Alias Alias="LocalizedText">i=21</Alias>
		<!-- references -->
		<Alias Alias="Organizes">i=35</Alias>
		<Alias Alias="HasEventSource">i=36</Alias>
		<Alias Alias="HasModellingRule">i=37</Alias>
		<Alias Alias="HasTypeDefinition">i=40</Alias>
		<Alias Alias="HasSubtype">i=45</Alias>
		<Alias Alias="HasProperty">i=46</Alias>
		<Alias Alias="HasComponent">i=47</Alias>
		<Alias Alias="HasNotifier">i=48</Alias>
		<Alias Alias="HasCondition">i=9006</Alias>
		<Alias Alias="HasHistoricalConfiguration">i=56</Alias>
		<!-- types -->
		<Alias Alias="BaseDataType">i=24</Alias>
		<Alias Alias="BaseObjectType">i=58</Alias>
		<Alias Alias="FolderType">i=61</Alias>
		<Alias Alias="BaseVariableType">i=62</Alias>
		<Alias Alias="BaseDataVariableType">i=63</Alias>
		<Alias Alias="PropertyType">i=68</Alias>
		<Alias Alias="AlarmConditionStateType">ns=1;i=1001</Alias>
		<!-- modelling rules -->
		<Alias Alias="New">i=78</Alias>
		<Alias Alias="Shared">i=79</Alias>
		<Alias Alias="SharedExclusive">i=336</Alias>
		<!-- other -->
		<Alias Alias="Server">i=2253</Alias>
		<Alias Alias="Objects">i=85</Alias>
	</Aliases>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT" BrowseName="1:PROJECT" IsAbstract="true">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">BaseObjectType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=ObjectTypes.PROJECT.EngineType" ExportedBrowseName="1:EngineType" Upstream="true"/>
		</Extensions>
	</UAObjectType>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType" BrowseName="1:EngineType">
		<DisplayName Locale="en">2:EngineType</DisplayName>
		<Description Locale="en">EngineType</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">ns=1;s=ObjectTypes.PROJECT</Reference>
		</References>
	</UAObjectType>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Description" BrowseName="1:Description" DataType="String" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">Description</DisplayName>
		<Description Locale="en">Description</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType</Reference>
			<Reference ReferenceType="HasTypeDefinition">BaseDataVariableType</Reference>
			<Reference ReferenceType="HasModellingRule">New</Reference>
		</References>
		<Value>
			<uax:String></uax:String>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Description.RelMirrorInputOutput" BrowseName="1:RelMirrorInputOutput" DataType="String" ValueRank="1" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RelMirrorInputOutput</DisplayName>
		<Description Locale="en">RelMirrorInputOutput</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType.Description</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.Mirror.Relative.InputOutput</Reference>
			<Reference ReferenceType="HasModellingRule">Shared</Reference>
		</References>
		<Value>
			<uax:ListOfString>
				<uax:String>2:Description</uax:String>
			</uax:ListOfString>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.On" BrowseName="1:On" DataType="Boolean" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">On</DisplayName>
		<Description Locale="en">On</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType</Reference>
			<Reference ReferenceType="HasTypeDefinition">BaseDataVariableType</Reference>
			<Reference ReferenceType="HasModellingRule">New</Reference>
		</References>
		<Value>
			<uax:Boolean>false</uax:Boolean>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.On.RelMirrorInputOutput" BrowseName="1:RelMirrorInputOutput" DataType="String" ValueRank="1" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RelMirrorInputOutput</DisplayName>
		<Description Locale="en">RelMirrorInputOutput</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType.On</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.Mirror.Relative.InputOutput</Reference>
			<Reference ReferenceType="HasModellingRule">Shared</Reference>
		</References>
		<Value>
			<uax:ListOfString>
				<uax:String>2:On</uax:String>
			</uax:ListOfString>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.RPM" BrowseName="1:RPM" DataType="Int32" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RPM</DisplayName>
		<Description Locale="en">RPM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType</Reference>
			<Reference ReferenceType="HasTypeDefinition">BaseDataVariableType</Reference>
			<Reference ReferenceType="HasModellingRule">New</Reference>
		</References>
		<Value>
			<uax:Int32>0</uax:Int32>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.RPM.RelMirrorInputOutput" BrowseName="1:RelMirrorInputOutput" DataType="String" ValueRank="1" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RelMirrorInputOutput</DisplayName>
		<Description Locale="en">RelMirrorInputOutput</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType.RPM</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.Mirror.Relative.InputOutput</Reference>
			<Reference ReferenceType="HasModellingRule">Shared</Reference>
		</References>
		<Value>
			<uax:ListOfString>
				<uax:String>2:RPM</uax:String>
			</uax:ListOfString>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Temperature" BrowseName="1:Temperature" DataType="Float" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">Temperature</DisplayName>
		<Description Locale="en">Temperature</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType</Reference>
			<Reference ReferenceType="HasTypeDefinition">BaseDataVariableType</Reference>
			<Reference ReferenceType="HasModellingRule">New</Reference>
		</References>
		<Value>
			<uax:Float>0</uax:Float>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Temperature.RelMirrorInputOutput" BrowseName="1:RelMirrorInputOutput" DataType="String" ValueRank="1" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RelMirrorInputOutput</DisplayName>
		<Description Locale="en">RelMirrorInputOutput</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType.Temperature</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.Mirror.Relative.InputOutput</Reference>
			<Reference ReferenceType="HasModellingRule">Shared</Reference>
		</References>
		<Value>
			<uax:ListOfString>
				<uax:String>2:Temperature</uax:String>
			</uax:ListOfString>
		</Value>
	</UAVariable>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT" BrowseName="1:PROJECT" IsAbstract="true">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">BaseObjectType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Description" ExportedBrowseName="1:Description" Upstream="true"/>
		</Extensions>
	</UAObjectType>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType" BrowseName="1:EngineType">
		<DisplayName Locale="en">2:EngineType</DisplayName>
		<Description Locale="en">EngineType</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">ns=1;s=ObjectTypes.PROJECT</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObjectType>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Description" BrowseName="1:Description" DataType="String" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">Description</DisplayName>
		<Description Locale="en">Description</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType</Reference>
			<Reference ReferenceType="HasTypeDefinition">BaseDataVariableType</Reference>
			<Reference ReferenceType="HasModellingRule">New</Reference>
		</References>
		<Value>
			<uax:String></uax:String>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Description.RelMirrorInputOutput" BrowseName="1:RelMirrorInputOutput" DataType="String" ValueRank="1" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RelMirrorInputOutput</DisplayName>
		<Description Locale="en">RelMirrorInputOutput</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType.Description</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.Mirror.Relative.InputOutput</Reference>
			<Reference ReferenceType="HasModellingRule">Shared</Reference>
		</References>
		<Value>
			<uax:ListOfString>
				<uax:String>2:Description</uax:String>
			</uax:ListOfString>
		</Value>
	</UAVariable>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT" BrowseName="1:PROJECT" IsAbstract="true">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">BaseObjectType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.On" ExportedBrowseName="1:On" Upstream="true"/>
		</Extensions>
	</UAObjectType>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType" BrowseName="1:EngineType">
		<DisplayName Locale="en">2:EngineType</DisplayName>
		<Description Locale="en">EngineType</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">ns=1;s=ObjectTypes.PROJECT</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObjectType>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.On" BrowseName="1:On" DataType="Boolean" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">On</DisplayName>
		<Description Locale="en">On</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType</Reference>
			<Reference ReferenceType="HasTypeDefinition">BaseDataVariableType</Reference>
			<Reference ReferenceType="HasModellingRule">New</Reference>
		</References>
		<Value>
			<uax:Boolean>false</uax:Boolean>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.On.RelMirrorInputOutput" BrowseName="1:RelMirrorInputOutput" DataType="String" ValueRank="1" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RelMirrorInputOutput</DisplayName>
		<Description Locale="en">RelMirrorInputOutput</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType.On</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.Mirror.Relative.InputOutput</Reference>
			<Reference ReferenceType="HasModellingRule">Shared</Reference>
		</References>
		<Value>
			<uax:ListOfString>
				<uax:String>2:On</uax:String>
			</uax:ListOfString>
		</Value>
	</UAVariable>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT" BrowseName="1:PROJECT" IsAbstract="true">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">BaseObjectType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.RPM" ExportedBrowseName="1:RPM" Upstream="true"/>
		</Extensions>
	</UAObjectType>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType" BrowseName="1:EngineType">
		<DisplayName Locale="en">2:EngineType</DisplayName>
		<Description Locale="en">EngineType</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">ns=1;s=ObjectTypes.PROJECT</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObjectType>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.RPM" BrowseName="1:RPM" DataType="Int32" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RPM</DisplayName>
		<Description Locale="en">RPM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType</Reference>
			<Reference ReferenceType="HasTypeDefinition">BaseDataVariableType</Reference>
			<Reference ReferenceType="HasModellingRule">New</Reference>
		</References>
		<Value>
			<uax:Int32>0</uax:Int32>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.RPM.RelMirrorInputOutput" BrowseName="1:RelMirrorInputOutput" DataType="String" ValueRank="1" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RelMirrorInputOutput</DisplayName>
		<Description Locale="en">RelMirrorInputOutput</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType.RPM</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.Mirror.Relative.InputOutput</Reference>
			<Reference ReferenceType="HasModellingRule">Shared</Reference>
		</References>
		<Value>
			<uax:ListOfString>
				<uax:String>2:RPM</uax:String>
			</uax:ListOfString>
		</Value>
	</UAVariable>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT" BrowseName="1:PROJECT" IsAbstract="true">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">BaseObjectType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Temperature" ExportedBrowseName="1:Temperature" Upstream="true"/>
		</Extensions>
	</UAObjectType>
	<UAObjectType NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType" BrowseName="1:EngineType">
		<DisplayName Locale="en">2:EngineType</DisplayName>
		<Description Locale="en">EngineType</Description>
		<References>
			<Reference ReferenceType="HasSubtype" IsForward="false">ns=1;s=ObjectTypes.PROJECT</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObjectType>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Temperature" BrowseName="1:Temperature" DataType="Float" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">Temperature</DisplayName>
		<Description Locale="en">Temperature</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType</Reference>
			<Reference ReferenceType="HasTypeDefinition">BaseDataVariableType</Reference>
			<Reference ReferenceType="HasModellingRule">New</Reference>
		</References>
		<Value>
			<uax:Float>0</uax:Float>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=ObjectTypes.PROJECT.EngineType.Temperature.RelMirrorInputOutput" BrowseName="1:RelMirrorInputOutput" DataType="String" ValueRank="1" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">RelMirrorInputOutput</DisplayName>
		<Description Locale="en">RelMirrorInputOutput</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=ObjectTypes.PROJECT.EngineType.Temperature</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.Mirror.Relative.InputOutput</Reference>
			<Reference ReferenceType="HasModellingRule">Shared</Reference>
		</References>
		<Value>
			<uax:ListOfString>
				<uax:String>2:Temperature</uax:String>
			</uax:ListOfString>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.MENUSCRIPTS.Example" ExportedBrowseName="1:Example" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.MENUSCRIPTS" BrowseName="1:MENUSCRIPTS">
		<DisplayName Locale="en">MENUSCRIPTS</DisplayName>
		<Description Locale="en">MENUSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.MENUSCRIPTS.Example" BrowseName="1:Example" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">Example</DisplayName>
		<Description Locale="en">Example</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.MENUSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[// LOAD MODULES
const folder = call("Library.objects.folder");
const node = call("Library.objects.node");
const property = call("Library.objects.property");
const objecttypeinstance = call("Library.objects.objecttype");
const variabletypeinstance = call("Library.objects.variabletype");
const datasource = call("Library.mirror.datasource");
const mirror = call("Library.mirror.mirror");
const configuration = call("Library.alarm.configuration");
const condition = call("Library.alarm.condition");
const archive = call("Library.history.archive");
const template = call("Library.history.template");
const aggregate = call("Library.history.aggregate");

/*
	Example: Create folder, array node, datasource, relmirrorbase and instance of object type
*/

// CREATE FOLDER
var ret = folder.create("AGENT.OBJECTS", "AreaA");
console.log(ret);

// CREATE ARRAY NODE
var ret = node.create("AGENT.OBJECTS.AreaA", "boolArray", {
	dataType: Ua.DataType.BOOLEAN,
	valueRank: Ua.ValueRank.ONEDIMENSION,
	value: [true, false, true, false]
});
console.log(ret);

// CREATE INSTANCE OF OBJECTTYPE
var ret = objecttypeinstance.create("AGENT.OBJECTS.AreaA", "Engine001", "ObjectTypes.PROJECT.EngineType");
console.log(ret);

// CREATE DATASOURCE
var ret = datasource.create("OPC UA Testserver", {
	url:  "opc.tcp://localhost:4841",
	typeDefinition: "OpcUa"
});
console.log(ret);

// CREATE RELMIRRORBASE
var ret = mirror.create("AGENT.OBJECTS.AreaA.Engine001", "RelMirrorBase", {
	datasource: "OPC UA Testserver",
	address: "ns=2;s=Engines.Engine001"
});
console.log(ret);





/*
	Example: Create node and absolute mirroring
*/

// CREATE NODE
var ret = node.create("AGENT.OBJECTS.AreaA", "Sin", {
	dataType: Ua.DataType.INT32
});
console.log(ret);

// CREATE ABSOLUTE MIRROR
var ret = mirror.create("AGENT.OBJECTS.AreaA.Sin", "MirrorInput", {
	datasource: "OPC UA Testserver",
	address: "ns=2;s=Simulated.SinInt32"
});
console.log(ret);



/*
	Example: Create node, alarm configuration and condition
*/
var ret = node.create("AGENT.OBJECTS.AreaA", "Status", {
	dataType: Ua.DataType.BOOLEAN
});
console.log(ret);

var config = condition.template(0); //0 = Discret Alarm
config.active_message = "alarm is active!";
config.value = true;
config.value_compare = "==";

var conditions = {
	"myFirstCondition": config
}

var ret = configuration.create("AGENT.OBJECTS.AreaA.Status", "Alarm", conditions);
console.log(ret);


/*
	Example: Create archive based on template
*/

var archiveTemplate = archive.template(0); //
console.log(archiveTemplate);

var ret = archive.create("myArchive", 0, archiveTemplate);
console.log(ret);

var ret = archive.assign("datavalues", "AGENT.OBJECTS.AreaA.Sin");
console.log(ret);]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm.condition" ExportedBrowseName="1:condition" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm" BrowseName="1:alarm">
		<DisplayName Locale="en">alarm</DisplayName>
		<Description Locale="en">alarm</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm.condition" BrowseName="1:condition" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">condition</DisplayName>
		<Description Locale="en">condition</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
const status = call("Library.helper.returnObject");
const param = call("Library.helper.parameterCheck");

/**
* @module condition
* @classdesc The condition class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
* to interact with condition.
* 
* @exports condition
* @constructor
* @example
* const condition = call("Library.alarm.condition");
*/

/**
 * @typedef {object} discreteAlarm
 * @property {number} type
 * @property {string} [active_message]
 * @property {string} [inactive_message]
 * @property {string} [explicit_acknowledgement=false]
 * @property {string} [category=Error]
 * @property {string} [max_time_shelved=0]
 * @property {string} [modify_script=]
 * @property {string} [off_delay=0]
 * @property {string} [on_delay=0]
 * @property {string} [prevention_script]
 * @property {string} [value=false]
 * @property {string} [value_compare===]
 */

 /**
 * @typedef {object} limitAlarm
 * @property {number} type
 * @property {string} [active_message]
 * @property {string} [inactive_message]
 * @property {string} [explicit_acknowledgement=false]
 * @property {string} [category=Error]
 * @property {string} [max_time_shelved=0]
 * @property {string} [modify_script]
 * @property {string} [off_delay=0]
 * @property {string} [on_delay=0]
 * @property {string} [prevention_script]
 * @property {string} [lower_limit=0]
 * @property {string} [lower_limit_compare=>]
 * @property {string} [upper_limit=10]
 * @property {string} [upper_limit_compare=<]
 */

class condition{

	constructor(){
		//todo add all available parameters in array and go through to check -> would me more generic solution
		// constants
		this.iCategoryBase = "AGENT.ALARMING.Categories.";
		this.iConditionBase = "ObjectTypes.ATVISE.AlarmConditionControl.";
	
		this.validFields = {
			"Discrete": {},
			"Discrete.Retrigger": {},
			"Limit": {}
		}
		
		/*
		[
			"ObjectTypes.ATVISE.AlarmConditionControl.Discrete",
			"ObjectTypes.ATVISE.AlarmConditionControl.Discrete.Retrigger",
			"ObjectTypes.ATVISE.AlarmConditionControl.Limit"
		];*/
	
		this.basicValidFields = {
			type: "Discrete",
			active_message: "",
			inactive_message: "",
			explicit_acknowledgement: false,
			category: "Error",
			max_time_shelved: 0,
			modify_script: 0,
			off_delay: 0,
			on_delay: 0,
			prevention_script: 0
		}
	
		this.discreteValidFields = {
			value: false,
			value_compare: "==",
		}
	
		this.limitValidFields = {
			lower_limit: 0,
			lower_limit_compare: ">",
			upper_limit: 10,
			upper_limit_compare: "<"
		}
	
		// set validFields on init
		for(var i in this.validFields){
			switch(i){
				case "Discrete":
					this.validFields[i] = Object.assign({}, this.basicValidFields, this.discreteValidFields);
					break;
				case "Discrete.Retrigger":
					this.validFields[i] = Object.assign({}, this.basicValidFields, this.discreteValidFields);
					break;
				case "Limit":
					this.validFields[i] = Object.assign(this.basicValidFields, this.limitValidFields);
					break;
			}
		}
		
	/*
	
	
		this.iDefaults = {
			type: 0,
			category: "AGENT.ALARMING.Categories.Error",
			value: false,
			off_delay: 0,
			on_delay: 0,
			value_compare: "==",
	
			lower_limit: 0,
			lower_limit_compare: ">",
			upper_limit: 10,
			upper_limit_compare: "<"
		}
	
		this.validFields = {
			value: false,
			value_compare: "==",
	
			lower_limit: 0,
			lower_limit_compare: ">",
			upper_limit: 10,
			upper_limit_compare: "<"
		};
	*/
	}


	/**
	 * Method to create an alarm condition depending on address string, name and options
	 * 
	 * @param {string} configAddress 
	 * @param {string} name 
	 * @param {discreteAlarm|limitAlarm} [options] 
	 */

	create(configAddress, name, options){
		let state = {};
		var conditionConfig = {};
	
		// check mandatory parameters
		if(param.isUndefinedOrEmpty(configAddress))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(name))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(options))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// todo check if type is included and limit nodes
		if(param.isUndefinedOrEmpty(options, "type"))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(configAddress, "string"))  return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(!param.check(name, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(param.check(options.type, "number", {min: 0, max: 2})){
			options.type = Object.keys(this.validFields)[options.type];
		}else if(param.check(options.type, "string")){
			if(!this.validFields.hasOwnProperty(options.type)){
				return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
			}	
		}else{
			return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		}
		
		if(options.type == Object.keys(this.validFields)[2]){
			if(param.isUndefinedOrEmpty(options.lower_limit) && param.isUndefinedOrEmpty(options.upper_limit)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
			if(!param.isUndefinedOrEmpty(options.lower_limit_compare) && options.lower_limit_compare != ">" && options.lower_limit_compare != ">=") return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
			if(!param.isUndefinedOrEmpty(options.upper_limit_compare) && options.upper_limit_compare != "<" && options.upper_limit_compare != "<=") return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}else{
			if(!param.isUndefinedOrEmpty(options.value_compare) && options.value_compare != "==" && options.value_compare != "!=") return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}
		
		var condition = Ua.findNode(configAddress + "." + name);
		if(condition.result){
			state.error = Ua.Status.BADNODEIDEXISTS;
			state.errorstring = Ua.Status(state.error).toString();
			return state;
		}
		
		var configNode = Ua.findNode(configAddress);
		if(!configNode.result){
			state.error = configNode.error;
			state.errorstring = Ua.Status(state.error).toString();
			return state;
		}

		var type = Ua.findNode(configNode.result.nodeId.address.replace("." + configNode.result.browseName.name, "")).result.dataType.toString();
		// todo add check if already existing conditions have same type		? NOAB

		// create basic condition depending on type
		let config = {
			nodeClass: Ua.NodeClass.OBJECT,
			typeDefinition: this.iConditionBase + options.type,
			reference: Ua.Reference.HASEVENTSOURCE,
			modellingRule: Ua.NodeId.MODELLINGRULE_MANDATORYSHARED
		};
		state = node.create(configAddress, name, config);

		if(state.error === 0){
			condition = Ua.findNode(configAddress + "." + name);
			// add category as reference
			if(param.isUndefinedOrEmpty(options, "category")){
				conditionConfig.category = this.iCategoryBase + this.basicValidFields.category;
			} else {
				if(Ua.findNode(this.iCategoryBase + options.category).result){
					conditionConfig.category = this.iCategoryBase + options.category;
				} else {
					conditionConfig.category = this.iCategoryBase + this.basicValidFields.category;
				}
			}
		
			condition.result.addReference(Ua.Reference.HASCOMPONENT, conditionConfig.category);
			delete conditionConfig.category;
			
			// set inactive_message to active_message in case is not defined
			if(options.active_message !== "" && options.inactive_message === ""){
				options.inactive_message = options.active_message;
			}		
		
			// set all other available options
			for(var i in this.validFields) {	
				if(i == options.type){
					conditionConfig = this.validFields[i];
					if(!("lower_limit" in options) || options.lower_limit == ""){
						delete conditionConfig.lower_limit;
					}
					if(!("upper_limit" in options) || options.upper_limit == ""){
						delete conditionConfig.upper_limit;
					}

					for(var j in conditionConfig){
						if(j != "category" && j != "type" ){					
							var condNode = Ua.findNode(configAddress + "." + name+ "." +j);
							var value = (j in options) ? options[j] : conditionConfig[j];
							if(j.includes("compare")) {
								value = value;
							}
							if(j === "on_delay" || j === "off_delay"){
								value = value * 1000;
							}
							if(j === "value" || j === "lower_limit" || j === "upper_limit"){
								condNode.result.value = {type: Ua.DataType[type.toUpperCase()], value: value};
							} else {
								condNode.result.value = value;
							}
						}
					}
				}
			}
		
			state.error = condition.error;
			state.errorstring = condition.errorstring;
		}
		else{
			return state;
		}
		return state;
	}
	
	
	
	/**
	 * Method to delete an alarm condition depending on address
	 * 
	 * @param {string} address 
	 */
	delete(address){
		//todo check if address is string or UaNode object
		//todo check if type is alarmcondition type
		const state = {};
		var condition = Ua.findNode(address);
		
		if(condition.result){
			if(condition.result.typeDefinition.value.indexOf("ObjectTypes.ATVISE.AlarmConditionControl")>= 0){
				return node.delete(address);
			} else {
				state.error = Ua.Status.BADTYPEMISMATCH;
				state.errorstring = Ua.Status(Ua.Status.BADTYPEMISMATCH).toString();
			}
			
		} else {
			state.error = condition.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		
		return state;
	}
	
	/**
	 * Method to return configuration of an alarm condition depending on conditionID
	 * 
	 * @param {string} conditionID 
	 * 
	 * @returns {discreteAlarm|limitAlarm}
	 */
	config(conditionID){
		var config = {};
		const state = {};
		var condition = Ua.findNode(conditionID);
		if(condition.result){
			config.type = condition.result.typeDefinition.value.replace("ns=1;s=ObjectTypes.ATVISE.AlarmConditionControl.","");
			var browseResult = condition.result.browse();

			for(var i = 0; i < browseResult.result.length; i++){
				if(browseResult.result[i].node.typeDefinition.value === "ns=1;s=ObjectTypes.ATVISE.AlarmConditionCategory"){
					config.category = browseResult.result[i].node.browseName.name;
				} else if(browseResult.result[i].node.browseName.name === "active_message" || browseResult.result[i].node.browseName.name === "inactive_message") {
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value[""];
				} else if(browseResult.result[i].node.browseName.name === "on_delay" || browseResult.result[i].node.browseName.name === "off_delay") {
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value / 1000;
				} else {
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value;
				}
			}
			
			return config;
		} else {
			state.error = condition.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state;
	}
	
	/**
	 * Method to update an alarm condition depending on conditionID and options
	 * 
	 * @param {string} conditionID 
	 * @param {discreteAlarm|limitAlarm} options 
	 */
	update(conditionID, options){
		const state = {};
		var condition = Ua.findNode(conditionID);
		if(condition.result){
			for(var i in options){
				if(i === "on_delay" || i === "off_delay"){
					options[i] = options[i] * 1000;
				} else if(i === "active_message"){
					if(!("inactive_message" in options))
						options.inactive_message = options[i];
				} else if(i === "value"){
					var configAddress = conditionID.substr(0, conditionID.lastIndexOf("."))
	
					var configNode = Ua.findNode(configAddress);
					if(!configNode.result){
						state.error = configNode.error;
						state.errorstring = Ua.Status(state.error).toString();
						return state;
					}
			
					var type = Ua.findNode(configNode.result.nodeId.address.replace("." + configNode.result.browseName.name, "")).result.dataType.toString();
					
					options[i] = {type: Ua.DataType[type.toUpperCase()], value: options[i]}
				} else if(i === "category"){
					var category = condition.result.browse({
						typeDefinition: "ObjectTypes.ATVISE.AlarmConditionCategory"
					});
					
					if(category.result.length > 0){
						condition.result.deleteReference(Ua.Reference.HASCOMPONENT, category.result[0].node.nodeId.address);
					}
					if(Ua.findNode("AGENT.ALARMING.Categories." + options.category).result){
						condition.result.addReference(Ua.Reference.HASCOMPONENT, "AGENT.ALARMING.Categories." + options.category);
					} else {
						condition.result.addReference(Ua.Reference.HASCOMPONENT, "AGENT.ALARMING.Categories.Error");
					}
	
					continue;
				}
				if(i != "type"){
					this._setAttribute(conditionID + "." + i, options[i]);
				}
			}
			state.error = condition.error;
			state.errorstring = condition.errorstring;
		} else {
			state.error = condition.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state;
	}
	
	/**
	 * Method to get default configuration for an alarm condition depending on type.
	 * 
	 * @param {*} type 
	 * 
	 * @returns {discreteAlarm|limitAlarm}
	 */
	template(type){
		
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(type))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(param.check(type, "number", {min: 0, max: 2})){
			type = Object.keys(this.validFields)[type];
		}else if(param.check(type, "string")){
			if(!this.validFields.hasOwnProperty(type)){
				return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
			}	
		}else{
			return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		}
	
		var config = {};
	
		var baseConditionType = Ua.findNode("ObjectTypes.ATVISE.AlarmConditionControl");
		var browseResultBase = baseConditionType.result.browse({
			nodeClass: Ua.NodeClass.VARIABLE
		});
		
		var conditionType = Ua.findNode(this.iConditionBase+""+type);
		
		
		var browseResultType = conditionType.result.browse({
			nodeClass: Ua.NodeClass.VARIABLE
		});
		
		//browseResultBase.result.forEach(el=>console.log(el.node.browseName.name));
		
		for(var i = 0; i < browseResultBase.result.length; i++){
			switch(browseResultBase.result[i].node.browseName.name){
				case "active_message":
				case "inactive_message":
					config[browseResultBase.result[i].node.browseName.name] = "";
					break;
				default:
					config[browseResultBase.result[i].node.browseName.name] = browseResultBase.result[i].node.value;
					break;
			}
		}
	
		for(var i = 0; i < browseResultType.result.length; i++){
			config[browseResultType.result[i].node.browseName.name] = browseResultType.result[i].node.value === null ? "" : browseResultType.result[i].node.value;
		}
	
		if(type === 0 || type === 1 || type === 2 || (!param.isUndefinedOrEmpty(type) && type in this.validFields))
			config.type = type;
			
		config.category = "Error";
		
		return config;
	}
	
	_setAttribute(address, value){
		var item = Ua.findNode(address);
		switch(item.result.dataType.toString()){
			case "BaseDataType":
				//todo implementieren von alarmconfigs die nicht unterhalb des eigentlichen elements liegen
	
				item.result.value = value;
				break;
			case "LocalizedText":
				//console.log("L ", item.browsename);
				if(typeof value === "string"){
					item.result.value = {locale: "en", text: value};
				} else {
					// todo error handling
				}
			case "String":
				//console.log("S ", item.browsename);
				if(typeof value === "string"){
					item.result.value = value;
				} else {
					// todo error handling
				}
				break;
			case "Double":
				//console.log("D ", item.browsename);
				if(typeof value === "number"){
					item.result.value = value;
				} else {
					// todo error handling
				}
				break;
			case "Boolean":
				value = JSON.parse(value);
				if(value === true || value === false){
					item.result.value = value;
				} else {
					// todo error handling
				}
				break;
			default:
				console.log("V ", item.browseName.name + " " + item.result.dataType.toString());
				break;
		}
	}

}

return new condition();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm.configuration" ExportedBrowseName="1:configuration" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm" BrowseName="1:alarm">
		<DisplayName Locale="en">alarm</DisplayName>
		<Description Locale="en">alarm</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm.configuration" BrowseName="1:configuration" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">configuration</DisplayName>
		<Description Locale="en">configuration</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
const condition = call("Library.alarm.condition");

/**
* @module configuration
* @classdesc The configuration class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
* to interact with configuration.
* 
* @exports configuration
* @constructor
* @example
* const configuration = call("Library.alarm.configuration");
*/

class configuration {
	/**
	 * 
	 * @param {string} parent 
	 * @param {string} name 
	 * @param {*} options 
	 */
	static create(parent, name, options){
		let state = {};
		let parentNode = Ua.findNode(parent);
		if(parentNode.result){
			let configuration = Ua.findNode(parent + "." + name);
			if(!configuration.result){
				let config = {
					nodeClass: Ua.NodeClass.OBJECT,
					typeDefinition: "ObjectTypes.ATVISE.AlarmConfiguration"
				};
				state = node.create(parent, name, config);
				if(state.error === 0){
					configuration = Ua.findNode(parent + "." + name);
					for(let i in options){
						condition.create(configuration.result.nodeId.address, i, options[i]);
					}
				} else {
					return state;
				}
			} else {
				state.error = Ua.Status.BADNODEIDEXISTS;
				state.errorstring = Ua.Status(state.error).toString();
			}
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();		
		}
		return state;
	}
	
	/**
	 * 
	 * @param {string} configurationID 
	 * @param {*} options 
	 * @param {boolean} overwrite 
	 */
	static update(configurationID, options, overwrite){
		const state = {};
		let configuration = Ua.findNode(configurationID);
		if(configuration.result){
			let allConditions = Object.keys(options);			
			let browseConditions = configuration.result.browse();
			for(let i = 0; i < browseConditions.result.length; i++){	
				if(browseConditions.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.AlarmConditionControl") >= 0 && allConditions.includes(browseConditions.result[i].node.browseName.name)===false){
					allConditions = allConditions.concat(browseConditions.result[i].node.browseName.name);
				}
			}

			for(let i = 0; i < allConditions.length; i++){			
				let conditionNode = Ua.findNode(configurationID + "." + allConditions[i]);
				if(conditionNode.result){
					if(overwrite === true && !(allConditions[i] in options)){
						return condition.delete(conditionNode.result.nodeId.address);
					} else if(allConditions[i] in options){
						return condition.update(conditionNode.result.nodeId.address, options[allConditions[i]]);
					}
				
				} else {	
					return condition.create(configurationID, allConditions[i], options[allConditions[i]]);
				}
			}
		} else {
			state.error = Ua.Status.BADNODEIDUNKNOWN;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state
	}
	
	/**
	 * 
	 * @param {string} configurationID 
	 */
	static delete(configurationID){
		// todo check if node exists & typedefintion is configuration
		// todo implementieren, dass nodeid als auch string verwendet weden kann

		let configuration = Ua.findNode(configurationID);    
		let state = {};
	
		if(configuration.result){
				return node.delete(configurationID);
		} else {
			state.error = configuration.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state
	}

	/**
	 * 
	 * @param {number} type 
	 */
	static template(type){
		return condition.template(type);
	}

	/**
	 * 
	 * @param {string} configurationID 
	 */
	static config(configurationID){
		let config = {};
		const state = {};
		let configuration = Ua.findNode(configurationID);
		if(!configuration.result){
			state.error = configuration.error;
			state.errorstring = Ua.Status(state.error).toString();
			return state;
		}
		let browseResult = configuration.result.browse();		
		for(let i = 0; i < browseResult.result.length; i++){			
			if(browseResult.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.AlarmConditionControl") >= 0){
				config[browseResult.result[i].node.browseName.name] = condition.config(browseResult.result[i].node.nodeId.address);
			}
		}
	
		return config;
	}
}

return configuration;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper.parameterCheck" ExportedBrowseName="1:parameterCheck" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper" BrowseName="1:helper">
		<DisplayName Locale="en">helper</DisplayName>
		<Description Locale="en">helper</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper.parameterCheck" BrowseName="1:parameterCheck" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">parameterCheck</DisplayName>
		<Description Locale="en">parameterCheck</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[class parameterCheck{

	check(value, type, options) {
		let isValid = false;
		if (this.isUndefinedOrEmpty(value)) {
			return isValid;
		}
	
		switch (type) {
			case 'number':
				if (!isNaN(value)) {
					let curValue = Number(value);
	
					let minValid;
					let maxValid;
	
					if ((!this.isUndefinedOrEmpty(options, 'max') && this.check(options.max, 'number') && curValue <= options.max) || this.isUndefinedOrEmpty(options, 'max')) {
						maxValid = true;
					} else if (!this.isUndefinedOrEmpty(options, 'max') && curValue > options.max) {
						maxValid = false;
					}
	
					if ((!this.isUndefinedOrEmpty(options, 'min') && this.check(options.min, 'number') && curValue >= options.min) || this.isUndefinedOrEmpty(options, 'min')) {
						minValid = true;
					} else if (!this.isUndefinedOrEmpty(options, 'min') && curValue < options.min) {
						minValid = false;
					}
	
					if (minValid !== undefined && maxValid !== undefined) {
						if (minValid && maxValid) {
							isValid = true;
						}
	
						switch (minValid && maxValid) {
							case true:
								isValid = true;
								break;
							case false:
								isValid = false;
								break;
						}
					}
	
					if (this.isUndefinedOrEmpty(options)) {
						isValid = true;
					}
				}
				break;
			case 'string':
				let curValue = String(value);
				isValid = true;
				break;
			case 'object':
				isValid = !this.isUndefinedOrEmpty(value);
				break;
			default:
				console.log('TODO doesn´t used type ' + typeof type);
		}
	
		return isValid;
	};
	
	isUndefinedOrEmpty(elem, attribute) {
		let isUndefined = false;
	
		if (elem === undefined || elem === '') {
			isUndefined = true;
		} else if (typeof elem === 'object') {
			if (Object.keys(elem).length === 0) {
				isUndefined = true;
			} else if (!(typeof attribute === 'undefined')) {
				if (!(attribute in elem)) {
					isUndefined = true;
				}
			}
		}
	
		return isUndefined;
	};
}
return new parameterCheck();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper.returnObject" ExportedBrowseName="1:returnObject" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper" BrowseName="1:helper">
		<DisplayName Locale="en">helper</DisplayName>
		<Description Locale="en">helper</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper.returnObject" BrowseName="1:returnObject" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">returnObject</DisplayName>
		<Description Locale="en">returnObject</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[class returnObject{
	constructor(){
		this.i_errormsg = {
			/* GENERAL */
			1: "Parameter [placeholder] invalid",
			2: "Mandatory parameter [placeholder] missing",
	
	
			/* HISTORY */
			1000: "Archive [placeholder] already exists",
			1001: "Couldn´t create archive [placeholder]",
			1002: "Couldn´t set option(s) [placeholder]",
			1003: "Couldn´t delete archive [placeholder]",
			1004: "Archive [placeholder] doesn´t exists",
			1005: "Typedefinition doesn´t match an archive",
	
			1100: "Template [placeholder] already exists",
			1101: "Template [placeholder] doesn´t exists",
			1003: "Couldn´t delete template [placeholder]",
	
	
			1200: "Couldn´t create Aggregate [placeholder]",
			1201: "Couldn´t update Aggregate [placeholder]"
		}
    }
    
	good(){
		return {status: 0};
	}
	
	
	bad(error, name/*, addInfo*/){
		let placeholder = "";
		if(typeof name === "object"){
			placeholder = name.map(function(e){
				return "'" + e + "'";
			}).join(", ");
		} else {
			placeholder = "'" + name + "'";
		}
		return {status: error, errorMsg: this.i_errormsg[error].replace("[placeholder]", placeholder)/* + (addInfo ? " - " + addInfo : "")*/}
	}
}


return new returnObject();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.aggregate" ExportedBrowseName="1:aggregate" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history" BrowseName="1:history">
		<DisplayName Locale="en">history</DisplayName>
		<Description Locale="en">history</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.aggregate" BrowseName="1:aggregate" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">aggregate</DisplayName>
		<Description Locale="en">aggregate</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const param = call("Library.helper.parameterCheck");

/**
 * @module aggregate
 * @classdesc The aggregate class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with aggregate.
 * 
 * @exports aggregate
 * @constructor
 * @example
 * const aggregate = call("Library.history.aggregate");
 */

/**
 * @typedef {object} aggregateConfig
 * @property {string} [interval_unit]
 * @property {number} [interval_value]
 * @property {string} [offset_unit]
 * @property {number} [offset_value]
 * @property {boolean} [stepped]
 * @property {object} [function] //todo
 * @property {object} [archive] //todo
 * @property {number} [PercentDataGood]
 * @property {number} [PercentDataBad]
 * @property {boolean} [TreatUncertainAsBad]
 * @property {boolean} [UseSlopedExtrapolation]
 */

class aggregate {

	constructor(){
		this.i_parent = "AGENT.HISTORY";
	
		this.i_partition_interval = ["n", "y", "q", "m", "w", "d", "h"];
	
		this.i_agg_nodeid = ["i=0"];
		this.i_agg_browsename = ["(None)"];
	
		let node = Ua.findNode("2997");
		let res = node.result.browse();
		for(let i = 0; i < res.result.length; i++){
			this.i_agg_nodeid.push(res.result[i].node.nodeId.value);
			this.i_agg_browsename.push(res.result[i].node.browseName.name);
		}

		this.i_configObject = {
			interval_unit: {
				type: Ua.DataType.STRING,
				defaultValue: "m"
			},
			interval_value: {
				type: Ua.DataType.UINT32,
				defaultValue: 5
			},
			offset_unit: {
				type: Ua.DataType.STRING,
				defaultValue: "m"
			},
			offset_value: {
				type: Ua.DataType.UINT32,
				defaultValue: 0
			},
			stepped: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			},
			function: {
				type: Ua.DataType.NODEID,
				defaultValue: "i=0"
			},
			archive: {
				type: Ua.DataType.NODEID,
				defaultValue: ""
			},
			PercentDataGood: {
				type: Ua.DataType.BYTE,
				defaultValue: 100
			},
			PercentDataBad: {
				type: Ua.DataType.BYTE,
				defaultValue: 100
			},
			TreatUncertainAsBad: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: true
			},
			UseSlopedExtrapolation: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			}
		}
	  } 
		
		
	/**
	 * Method to create an aggregate depending on template, name and optional options.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * @param {aggregateConfig} config
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 *  
	 * //create Aggregate for 1 minute average function
	 * let config = {
	 * 	 function: "Average",
	 * 	 interval_unit: "m",
	 * 	 interval_value: 1,
	 * 	 archive: "aggArchive",
	 * 	 stepped: false
	 * }
	 * aggregate.create("AvgTemplate","avg_1min",config);
	 * 
	 */
	
	create(template, name, options){
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(template)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(options)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(template, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(!param.check(name, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
	
		let keys = [];
		let values = [];
		const state = {};
		for(let i in this.i_configObject) {
			switch(i){
				case "archive":
					try{
						let position = options === null ? -1 : options[i].indexOf("AGENT.HISTORY");
						if(position >= 0 && options[i].length > 0){
							options[i] = position === 0 ? "ns=1;s=" + options[i] : options[i];
						} else if(options[i].length === 0){
							delete options[i];
						} else if(options[i] !== "i=0") {
							options[i] = "ns=1;s=AGENT.HISTORY." + options[i];
						}
					} catch(ex){
						console.log(ex);
					}
					break;
				case "function":
					if(options !== undefined && i in options){
						console.log(options[i]);
						options[i] = this.i_agg_nodeid[this.i_agg_browsename.indexOf(options[i])];
					}
					break;
			}
	
			keys.push(i);
			values.push({
				type: this.i_configObject[i].type,
				value: options !== undefined && i in options ? options[i] : this.i_configObject[i].defaultValue
			});
		}
		
		console.log(keys);
		console.log(values);
	

		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name},
				{type: Ua.DataType.STRING, value: keys},
				{type: Ua.DataType.BASEDATATYPE, value: values}
			]
		});
		
		
		state.error = callResult.error;
		state.errorstring = callResult.errorstring;
		return state;
	}


	
	/**
	 * Method to update a aggregate depending on template, name and options.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * @param {aggregateConfig} config
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * 
	 * let config = {
	 * 	 interval_unit: "d",
	 * 	 interval_value: 2,
	 * }
	 * aggregate.update("AvgTemplate","avg_1min",config);
	 */
	update(template, name, options){
		let keys = [];
		let values = [];
		const state = {};
		for(let i in options) {
			switch(i){
				case "archive":
					let position = options[i].indexOf("AGENT.HISTORY");
					if(position >= 0 && options[i].length > 0){
						options[i] = position === 0 ? "ns=1;s=" + options[i] : options[i];
					} else if(options[i].length === 0){
						delete options[i];
					} else {
						options[i] = "ns=1;s=AGENT.HISTORY." + options[i];
					}
					break;
				case "function":
					options[i] = this.i_agg_nodeid[this.i_agg_browsename.indexOf(options[i])];
					break;
			}
	
			keys.push(i);
			values.push({
				type: this.i_configObject[i].type,
				value: options[i]
			});
		}
		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 2},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name},
				{type: Ua.DataType.STRING, value: keys},
				{type: Ua.DataType.BASEDATATYPE, value: values}
			]
		});
		if(callResult.result==""){
			state.error = callResult.error;
			state.errorstring = callResult.errorstring;
			return state;
		}else{	
			return callResult.result;	
		}
		//todo fehlerhandling
	}
	
	/**
	 * Method to delete a aggregate depending on template and name. Not supported until [AT-D-12115] is fixed.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * aggregate.delete("AvgTemplate","avg_1min");
	 */
	delete(template, name){
		// Both solutions work without problem and the first one was chosen because it uses the same call method as create and update
		var state;

		// Solution 1
		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 4},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name}
			]
		});
		/*
		// Solution 2
		let aggregate = Ua.findNode("AGENT.HISTORY.AGGREGATETEMPLATES." + template + "." + name);
		if(aggregate.result){{
			state=aggregate.result.remove().result;
		} else {
			// todo
		}
	
		return state === 0 ? true : false;
		*/
		if(callResult.result==""){
			state.error = callResult.error;
			state.errorstring = callResult.errorstring;
			return state;
		}else{	
			return callResult.result;	
		}
	}
	
	/**
	 * Method to get config of aggregate depending template and name.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * 
	 * @returns {aggregateConfig}
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * aggregate.config("AvgTemplate","avg_1min");
	 */
	config(template, name){
		let config = {};
		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 3},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name}
			]
		});

		for(let j = 0; j < callResult.result[0].length; j++){
			if(callResult.result[0][j] === "archive"){
				callResult.result[1][j].value = callResult.result[1][j].value.replace("ns=1;s=AGENT.HISTORY.", "");
			} else if (callResult.result[0][j] === "function") {
				if(callResult.result[1][j].value === "i=0"){
					callResult.result[1][j].value = "(None)";
				} else {
					var aggregate = new UaNode(callResult.result[1][j].value);
					callResult.result[1][j].value = aggregate.browsename;
				}
			}
	
			config[callResult.result[0][j]] = callResult.result[1][j].value;
		}

		return config;
	}
	
	/**
	 * Method to get default configuration for an aggregate.
	 * 
	 * @returns {aggregateConfig}
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * aggregate.template(); 
	 */
	template(){
		let config = {};
		for(let i in this.i_configObject) {
			if(i === "function") {
				config[i] = this.i_agg_browsename[0];
			} else {
				config[i] = this.i_configObject[i].defaultValue;
			}
		}
		return config;
	}
}
return new aggregate();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.archive" ExportedBrowseName="1:archive" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history" BrowseName="1:history">
		<DisplayName Locale="en">history</DisplayName>
		<Description Locale="en">history</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.archive" BrowseName="1:archive" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">archive</DisplayName>
		<Description Locale="en">archive</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const param = call("Library.helper.parameterCheck");
const node = call("Library.objects.node");

/**
 * @module archive
 * @classdesc The archive class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with archives.
 * 
 * @exports archive
 * @constructor
 * @example
 * const archive = call("Library.history.archive");
 */

class archive{

	constructor(){
		this.i_debug = false;	
		this.i_parent = "AGENT.HISTORY";
		this.i_types = ["Aggregate", "Data", "Event"];

		this.i_partition_interval = ["n", "y", "q", "m", "w", "d", "h"];

		this.i_configObject = {
			Stepped: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			},
			disable: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			},
			file_limit: {
				type: Ua.DataType.UINT32,
				defaultValue: 0
			},
			partition_interval: {
				type: Ua.DataType.STRING,
				defaultValue: "n"
			}
		};
	}	
	
	
	_setOption(address, key, value){
		let isValid = false;
		let option = Ua.findNode(address + "." + key);
	
		if(option.result){
			switch(key){
				case "partition_interval":
					if(this.i_partition_interval.indexOf(value) >= 0) isValid = true;
					break;
				case "file_limit":
					// TODO check with development
					isValid = true;
					break;
				case "disable": isValid=true; break;
				case "Stepped":
					if(value === true || value === false || value === 1 || value === 0) isValid = true;
					break;
			}
	
			if(isValid){
				option.result.value = value;
				return {error: 0, errorstring: undefined};
			}
		} else {
			return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}
	}
	
	/**
	 * Method to create an archive of all possible types with optional possibility to set
	 * some custom configuration for the archive.
	 * 
	 * @param {string} name 
	 * @param {string} type 0 | 1 | 2
	 * @param {object} [options]
	 * @param {string} [options.disable]
	 * @param {string} [options.partition_interval]
	 * @param {number} [options.file_limit]
	 * @param {boolean} [options.Stepped]
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * 
	 * // Create "Aggregate" archive with default settings
	 * archive.create("aggArchive", "Aggregate");
	 * 
	 * // Create "Data" archive with default config
	 * archive.create("dataArchive", "Data");
	 * 
	 * // Create "Event" archive with default settings
	 * archive.create("eventArchive", "Event");
	 * 
	 * // Create "Data" archive with yearly partition interval and a file limit of 5
	 * let config = {
	 * 	partition_interval: "y",
	 * 	file_limit: 5
	 * }
	 * archive.create("dataArchive2", 1, config);
	 * 
	 */
	create(name, type, options){
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		if(param.isUndefinedOrEmpty(type)) return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
	
		// check parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.check(type, "number", {min: 0, max: 2})) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(!param.isUndefinedOrEmpty(options.partition_interval) && !this.i_partition_interval.includes(options.partition_interval)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.isUndefinedOrEmpty(options.file_limit) && !param.check(options.file_limit, "number")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		
		let state = {};
		state = node.create(this.i_parent, name, {
			nodeClass: Ua.NodeClass.OBJECT,
			typeDefinition: "ObjectTypes.ATVISE.ArchiveGroup." + this.i_types[type]
		});	
		
		if(state.error === 0){
			let node = Ua.findNode(this.i_parent + "." + name);		
			for(let i in options){
				let optionState = this._setOption(node.result.nodeId.address, i, options[i]);
				if(optionState.error !== 0){
					return optionState;
				}
			}
		}

		return state;
	}
	
	/**
	 * Method to update an archive configuration.
	 * 
	 * @param {string} name 
	 * @param {object} options
	 * @param {string} [options.disable]
	 * @param {string} [options.partition_interval]
	 * @param {number} [options.file_limit]
	 * @param {boolean} [options.Stepped]
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * 
	 * // Disable historization for archive
	 * let config = {
	 * 	disable: true
	 * }
	 * archive.update("aggArchive", config);
	 * 
	 * // Change partition interval and file_limit
	 * let config = {
	 * 	partition_interval: "m",
	 * 	file_limit: 15
	 * }
	 * archive.update("aggArchive", config);
	 * 
	 */
	update(name, options){
		// check parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.check(options, "object")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.isUndefinedOrEmpty(options.partition_interval) && !this.i_partition_interval.includes(options.partition_interval)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.isUndefinedOrEmpty(options.file_limit) && !param.check(options.file_limit, "number")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};

		let state = {};
		let archive = Ua.findNode(this.i_parent + "." + name);
		if (archive.result){
			for(let i in options){
				let optionState = this._setOption(archive.result.nodeId.address, i, options[i]);
				if(optionState.error !== 0){
					return optionState;
				}
			}

			state = {error: Ua.Status.GOOD, errorstring: undefined};
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
		
		return state;
	}
	
	/**
	 * Method to delete an archive from atvise project.
	 * 
	 * @param {string} name 
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * archive.delete("aggArchive");
	 */
	delete(name){
		// check parameter
		if(!param.check(name, "string"))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		
		let archive = Ua.findNode(name.indexOf("AGENT.HISTORY.") >= 0 ? name : this.i_parent + "." + name);

		if (archive.result){		
			if(this.i_types.indexOf(archive.result.typeDefinition.toString()) < 0){
				return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
			} else {
				return node.delete(archive.result.nodeId.address);
			}
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
	}
	
	/**
	 * Method to read current configuration for an archive.
	 * 
	 * @param {string} name 
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * let config = archive.config("aggArchive");
	 * 
	 * for(let i in config){
	 * 	console.log("setting: " + i + " - value: " + config[i]);
	 * }
	 */
	config(name){
		let exceptProperties = ["filter"];
	
		// check mandatory parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		let config = {};
		let archive = Ua.findNode(this.i_parent + "." + name);
		if (archive.result){
			let browseResult = archive.result.browse({
				nodeClass: Ua.NodeClass.VARIABLE
			});

			for(let i = 0; i < browseResult.result.length; i++){
				if(exceptProperties.indexOf(browseResult.result[i].node.browseName.name) < 0)
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value;
			}
			
			return config;
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
	}
	
	/**
	 * Method to get whole configuration default configuration for an archive.
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * // Get default configuration
	 * let defaultConfig = archive.template();
	 * 
	 * // Disable archive
	 * defaultConfig.disable = true;
	 * 
	 * // Create archive with modified default config
	 * archive.create("dataArchive", 1, defaultConfig);
	 */
	template(){
		let config = {};
	
		for(let i in this.i_configObject) {
			if(i === "function") {
				config[i] = this.i_agg_browsename[0];
			} else {
				config[i] = this.i_configObject[i].defaultValue;
			}
		}
	
		return config;
	}	
	
	/**
	 * Method to assign archive to one or multiple nodes. Already existing configurations will be removed
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * archive.assign("datavalues", "AGENT.OBJECTS.myBoolean");
	 * 
	 * archive.assign("datavalues", ["AGENT.OBJECTS.myBoolean", "AGENT.OBJECTS.myString"]);
	 */
	assign(archive, nodes){
		let archiveNode = Ua.findNode("AGENT.HISTORY." + archive);
		if(archiveNode.result && archiveNode.result.typeDefinition.value === "ns=1;s=ObjectTypes.ATVISE.ArchiveGroup.Data"){
			if(typeof nodes === "string"){
				var node = Ua.findNode(nodes);
				if(node.result){
					//remove existing configuration
					var historicalReferences = node.result.browse({
						reference: Ua.Reference.HASHISTORICALCONFIGURATION
					});

					for(var i = 0; i < historicalReferences.result.length; i++){
						if(historicalReferences.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.ArchiveGroup.Data") >= 0){
							node.result.deleteReference(Ua.Reference.HASHISTORICALCONFIGURATION, historicalReferences.result[i].node.nodeId.address);
						}
					}

					//assign archive
					node.result.addReference(Ua.Reference.HASHISTORICALCONFIGURATION, archiveNode.result.nodeId.address);
				} else {
					return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
				}
			} else if(typeof nodes === "object"){
				for(var j = 0; j < nodes.length; j++){
					var node = Ua.findNode(nodes[j]);
					if(node.result){
						//remove existing configuration
						var historicalReferences = node.result.browse({
							reference: Ua.Reference.HASHISTORICALCONFIGURATION
						});

						for(var i = 0; i < historicalReferences.result.length; i++){
							if(historicalReferences.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.ArchiveGroup.Data") >= 0){
								node.result.deleteReference(Ua.Reference.HASHISTORICALCONFIGURATION, historicalReferences.result[i].node.nodeId.address);
							}
						}

						//assign archive
						node.result.addReference(Ua.Reference.HASHISTORICALCONFIGURATION, archiveNode.result.nodeId.address);
					} else {
						return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
					}
				}
			}
		} else {
			return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}
	}
}

return new archive();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.template" ExportedBrowseName="1:template" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history" BrowseName="1:history">
		<DisplayName Locale="en">history</DisplayName>
		<Description Locale="en">history</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.template" BrowseName="1:template" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">template</DisplayName>
		<Description Locale="en">template</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const param = call("Library.helper.parameterCheck");
const aggregate = call("Library.history.aggregate");

/**
 * @module template
 * @classdesc The template class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with templates.
 * 
 * @exports template
 * @constructor
 * @example
 * const template = call("Library.history.template");
 */

class template {
	constructor(){
	    this.i_parent = "AGENT.HISTORY.AGGREGATETEMPLATES";
	}
	
	/**
	 * Method to create a template depending name and object of aggregate configurations.
	 * 
	 * @param {string} name 
	 * @param {object} [config] 
	 * @param {aggregateConfig} [config.name]
	 * 
	 * @example
	 * let template = call("Library.history.template");
	 * 
	 * // Create template with no aggregate functions
	 * template.create("T1");
	 * 
	 * // Create template with simple aggregate function
	 * let config = {
	 *  "avg_1min": {
	 *      ...
	 *  }
	 * }
	 * template.create("T2", config);
	 * 
	 * // Create template with cascaded aggregate functions
	 * let config = {
	 *  "avg_1min": {
	 *      ...
	 *  },
	 * "avg_1min.avg_5min": {
	*       ...
	 *  },
	 * }
	 * template.create("T3", config);
	 */
	create(name, config){
		const state = {};
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
	
		let template = Ua.findNode(this.i_parent + "." + name);
		
		if(template.result){
			state.error = Ua.Status.BADNODEIDEXISTS;
			state.errorstring = Ua.Status(state.error).toString();
		}
		else{
			let tempNode = Ua.createNode(this.i_parent + "." + name,{
				nodeClass: Ua.NodeClass.OBJECT,
				parent: this.i_parent,
				typeDefinition: "ObjectTypes.ATVISE.AggregateTemplate",
				reference: Ua.Reference.HASCOMPONENT
			});
			
			// Workaround waiting some time before continue due to problem with OPC UA Method call
			let time = new Date().getTime();
			while(new Date().getTime() - 50 <= time){}
			
			//return state === 0 ? true : false;
			state.error = tempNode.error;
			state.errorstring = tempNode.errorstring;
			
			if(config !== undefined && typeof config === "object"){
				for(let i in config){
					aggregate.create(name, i, config[i]);
				}
			}
		}
		return state;
	}
	
	/**
	 * Method to delete a template depending name.
	 * 
	 * @param {string} name 
	 */
	delete(name){
		const state = {};
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(name, "string"))  return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
	
		let template = Ua.findNode(this.i_parent + "." + name);
		if(template.result){
			return template.result.remove();
		} else {
			state.error = template.error;
			state.errorstring = Ua.Status(template.error).toString();
		}
		return state;
	}
	
	/**
	 * Method to update a template depending on name and object of aggregate configurations.
	 * 
	 * @param {string} name 
	 * @param {object} config 
	 * @param {aggregateConfig} config.name
	 * @param {boolean} remove not supported at the moment
	 */
	update(name, config, remove){
		let configNames = [];
		const state = {};
		let template = Ua.findNode(this.i_parent + "." + name);
		if(!template.result){
			state.error = template.error;
			state.errorstring = Ua.Status(template.error).toString();
			return state;
		}

		configNames = this._browseItem(template.result.nodeId.address, template.result.browseName.name);

		//remove = false; 
		if(remove === true){
			let configurations = this.config(name);
			let allConfigs = configNames.concat(Object.keys(config).filter(seccondArrayItem => !configNames.includes(seccondArrayItem)));

			for(let i = 0; i < allConfigs.length; i++){
				if(allConfigs[i] in config && configNames.indexOf(allConfigs[i]) < 0){
					return aggregate.create(name, configNames[i], config[configNames[i]]);
				} else if(allConfigs[i] in config && configNames.indexOf(allConfigs[i]) >= 0){
					return aggregate.update(name, allConfigs[i], config[allConfigs[i]]);
				} else {
					return aggregate.delete(name, allConfigs[i]);
				}
			}
	
		} else {
			for(let item in config){
				if(configNames.indexOf(item) >= 0){
					return aggregate.update(name, item, config[item]);
				} else {
					let time = new Date().getTime();
					while(new Date().getTime() - 50 <= time){}
					return aggregate.create(name, item, config[item]);
				}
			}
		}
	}
		
	/**
	 * Method to get current configuration of a template depending name.
	 * 
	 * @param {string} name 
	 */
	config(name){
		// todo fehlerhandling bei nicht zurückgeliefertem result der opc ua methode
		let configNames = [];
		let config = {};
		const state = {};
		let template = Ua.findNode(this.i_parent + "." + name);
		// überprüfen ob template existiert
		if(template.result){
			configNames = this._browseItem(template.result.nodeId.address, template.result.browseName.name);
			
			for(let i = 0; i < configNames.length; i++){
				config[configNames[i]] = aggregate.config(name, configNames[i]);
			}
		
			return config;
		} else {
			state.error = template.error;
			state.errorstring = Ua.Status(template.error).toString();
			return state;
		}

	}
		
	/**
	 * Method to get default configuration for an aggregate function.
	 * 
	 * @returns {aggregateConfig}
	 */
	template(){
		return aggregate.template();
	}
	
	_browseItem(nodeid, templateName){
		let configNames = [];
	
		let parentNode = Ua.findNode(nodeid);
		let browseResult = parentNode.result.browse({
			typeDefinition : "ObjectTypes.ATVISE.AggregateFunction"
		});
		
		for(let i = 0; i < browseResult.result.length; i++){
			configNames.push(browseResult.result[i].node.nodeId.address.replace(nodeid + ".", ""));
			let result = this._browseItem(browseResult.result[i].node.nodeId.address, nodeid);
	
			configNames = configNames.concat(result);
		}
	
		return configNames;
	}
}
return new template();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror.datasource" ExportedBrowseName="1:datasource" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror" BrowseName="1:mirror">
		<DisplayName Locale="en">mirror</DisplayName>
		<Description Locale="en">mirror</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror.datasource" BrowseName="1:datasource" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">datasource</DisplayName>
		<Description Locale="en">datasource</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module datasource
 * @classdesc The datasource class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with datasources.
 * 
 * @exports datasource
 * @constructor
 * @example
 * let datasource = call("Library.mirror.datasource");
 */

class datasource extends node{

/**
 * Method to create datasource depending on name without path, options like url and typeDefinition and activated as boolean..
 * 
 * @param {string} name Name of datasource
 * @param {Object} options like url, typeDefinition and disable
 *
 * @example
 * let datasource = call("Library.mirror.datasource");
 * datasource.create("MyDataSource", {
 * 		url:  "opc.tcp://localhost:4841",
 * 		typeDefinition:	"OpcUa",
 * 		disable: false,
 * 		publishing_interval: 1000,
 * 		sampling_interval: 1000,
 * 		user: "root",
 * 		password: "myPassword",
 * 		queue_size: -1,
 * 		connection_timeout: 10000
 * });
 */
	static create(name, options){
		let state = {};
		let objectTypeNode = Ua.findNode("ObjectTypes.ATVISE.Datasource." + options.typeDefinition);
		let parent = "AGENT.DATASOURCES";
		let datasourceAddress = parent + "." + name;
		
		if(objectTypeNode.result){
			options.typeDefinition = objectTypeNode.result.nodeId.address;
			options.nodeClass = Ua.NodeClass.OBJECT;
			let result = node.create(parent, name, options);
			
			// Creating Redundancy to Datasource			
			let redu = node.create(datasourceAddress, "Redundancy",{
				nodeClass: Ua.NodeClass.OBJECT,
				typeDefinition: "ObjectTypes.ATVISE.DatasourceRedundancy"	
			});

			// Activate datasource
			let disableNode = Ua.findNode(datasourceAddress + ".disable");
			if(disableNode.error === 0){
				disableNode.result.value = "disable" in options ? options.disable : false;
			} else {
				return {
					error: disableNode.error,
					errorstring: Ua.Status(disableNode.error).toString()
				}
			}
			
			// Assigning the URL
			let urlNode = Ua.findNode(datasourceAddress + ".url");
			if(urlNode.error === 0){
				urlNode.result.value = options.url;
			} else {
				return {
					error: urlNode.error,
					errorstring: Ua.Status(urlNode.error).toString()
				}
			}
			
			// Sampling & Publish
			let publishNode = Ua.findNode(datasourceAddress + ".publishing_interval");
			
			if(publishNode.error === 0 && "publishing_interval" in options){
				publishNode.result.value = options.publishing_interval;
			} else if(publishNode.error !== 0) {
				return {
					error: publishNode.error,
					errorstring: Ua.Status(publishNode.error).toString()
				}
			}
			let samplingNode = Ua.findNode(datasourceAddress + ".sampling_interval");
			if(samplingNode.error === 0 && "sampling_interval" in options){
				samplingNode.result.value = options.sampling_interval;
			} else if(samplingNode.error !== 0) {
				return {
					error: samplingNode.error,
					errorstring: Ua.Status(samplingNode.error).toString()
				}
			}
			
			// Queue
			let queueNode = Ua.findNode(datasourceAddress + ".queue_size");
			if(queueNode.error === 0 && "queue_size" in options){
				queueNode.result.value = options.queue_size;
			} else if(queueNode.error !== 0) {
				return {
					error: queueNode.error,
					errorstring: Ua.Status(queueNode.error).toString()
				}
			}
			
			// ConnectionTimeout
			let timeoutNode = Ua.findNode(datasourceAddress + ".connection_timeout");
			if(timeoutNode.error === 0 && "connection_timeout" in options){
				timeoutNode.result.value = options.connection_timeout;
			} else if(timeoutNode.error !== 0) {
				return {
					error: timeoutNode.error,
					errorstring: Ua.Status(timeoutNode.error).toString()
				}
			}
			
			// Username
			let userNode = Ua.findNode(datasourceAddress + ".user");
			if(userNode.error === 0 && "user" in options){
				userNode.result.value = options.user;
			} else if(userNode.error !== 0) {
				return {
					error: userNode.error,
					errorstring: Ua.Status(userNode.error).toString()
				}
			}

			// Password
			let pwdNode = Ua.findNode(datasourceAddress + ".password");
			if(pwdNode.error === 0 && "password" in options){
				pwdNode.result.value = options.password;
			} else if(pwdNode.error !== 0) {
				return {
					error: pwdNode.error,
					errorstring: Ua.Status(pwdNode.error).toString()
				}
			}		
			
			state.error = Ua.Status.GOOD;
			state.errorstring = undefined;
		} else {
			state.error = Ua.Status.BADNODEIDUNKNOWN;
			state.errorstring = Ua.Status(state.error).toString();
		}
		
		return state;
	}

/**
 * Method to read current datasource settings
 * 
 * @param {string} name Name of datasource
 *
 * @example
 * let datasource = call("Library.mirror.datasource");
 * let config = datasource.config("MyDataSource");
 */		
	static config(name){
		let exceptProperties = [
			"namespace_array",
			"password",
			"revised_alarm_queue_size",
			"revised_lifetime_count",
			"revised_publishing_interval",
			"revised_queue_size",
			"revised_sampling_interval",
			"data_change_trigger",
			"connection_status",
			"copy_servertime_to_sourcetime",
			"dead_band",
			"history_sync_interval",
			"lifetime_count",
			"connected",
			"string_encoding",
			"variable_type_prefix",
			"variable_type_trim",
			"object_type_trim",
			"object_type_prefix",
			"user",
			"private_key",
			"pki_path",
			"client_certificate",
			"pass_through_event_history",
			"mode",
			"max_notifications_per_publish",
			"policy"
		];
	
		// check mandatory parameter
		//if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		let config = {};
		let datasource = Ua.findNode("AGENT.DATASOURCES." + name);
		if (datasource.result){
			let browseResult = datasource.result.browse({
				nodeClass: Ua.NodeClass.VARIABLE
			});

			for(let i = 0; i < browseResult.result.length; i++){
				if(exceptProperties.indexOf(browseResult.result[i].node.browseName.name) < 0)
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value;
			}
			
			return config;
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
	}
	
/**
 * Method to delete a dataSource depending on an name string.
 * 
 * @param {string} name of dataSource
 * 
 * @example
 * let datasource = call("Library.mirror.datasource");
 * datasource.delete("MyDataSource");
 */
	static delete(name){
		let address = "AGENT.DATASOURCES." + name;
		let dataSource = Ua.findNode(address);    
		const state = {};
		if(dataSource.result){
			return node.delete(dataSource.result.nodeId.address);
		} else {
			state.error = dataSource.error;
			state.errorstring = Ua.Status(dataSource.error).toString();
		}

		return state;
	}
}

return datasource;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror.mirror" ExportedBrowseName="1:mirror" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror" BrowseName="1:mirror">
		<DisplayName Locale="en">mirror</DisplayName>
		<Description Locale="en">mirror</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror.mirror" BrowseName="1:mirror" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">mirror</DisplayName>
		<Description Locale="en">mirror</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module mirror
 * @classdesc The mirror class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with mirrors.
 * 
 * @exports mirror
 * @constructor
 * @example
 * const mirror = call("Library.mirror.mirror");
 */

class mirror extends node {

/**
 * Method to create mirror ..
 * 
 * @param {string} parent 
 * @param {string} name 
 * @param {object} [options] 
 *
 * @example
 * const mirror = call("Library.mirror.mirror");
 * 
 * // Create RelMirrorBase
 * mirror.create("AGENT.OBJECTS.NewCustomFolder.Engine", "RelMirrorBase", {
 * 		datasource: "PLC1",
 * 		address: "ns=2;s=Engines.Engine001"
 * 	});	
 * 
 * // Create RelMirrorBase	
 * 	mirror.create("AGENT.OBJECTS.NewCustomFolder.SinInt32", "MirrorInputOutput", {
 * 		datasource: "PLC1",
 * 		namespace: 2,
 *		address: "Simulated.SinInt32"
 * 	});	
 */
	static create(parent, name, addr){
		let parentNode = Ua.findNode(parent);
		const state = {};
		if(parentNode.result){
			let address = addr.address;
			let source = addr.datasource + "/" + address;
			let options = {
				parent:	parent,
				dataType: Ua.DataType.STRING,
				nodeClass: Ua.NodeClass.VARIABLE,
				value: source
			}
			switch(name){
				case "MirrorInput": 
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Input";
					break;
				case "MirrorOutput": 
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Output";
					break;
				case "MirrorInputOutput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.InputOutput";
					break;
				case "MirrorDisable":
					options.dataType = Ua.DataType.BOOLEAN;
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Disable";
					break;
				case "MirrorOnDemand":
					options.dataType = Ua.DataType.BOOLEAN;
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.OnDemand";
					break;
				case "RelMirrorBase":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Base";
					break;
				case "RelMirrorInput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Input";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorOutput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Output";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorInputOutput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.InputOutput";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorPathFragment":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.PathFragment";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorDisable":
					options.dataType = Ua.DataType.BOOLEAN;
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Disable";
					break;
				default:
					break;
			}
		
			return node.create(parent, name, options);
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();
		}
		return state;
	}


/**
 * Method to delete a dataSource depending on an name string.
 * 
 * @param {string} address
 * 
 * @example
 * const mirror = call("Library.mirror.mirror");
 * mirror.delete("AGENT.OBJECTS.NewCustomFolder.SinInt32.MirrorInputOutput");
 */
	static delete(address){
		let mirror = Ua.findNode(address);    
		const state = {};
		if(mirror.result){
			return node.delete(address);
		}else{
			state.error = mirror.error;
			state.errorstring = Ua.Status(mirror.error).toString();
		}
		return state;
	}
}


return mirror;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.folder" ExportedBrowseName="1:folder" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects" BrowseName="1:objects">
		<DisplayName Locale="en">objects</DisplayName>
		<Description Locale="en">objects</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.folder" BrowseName="1:folder" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">folder</DisplayName>
		<Description Locale="en">folder</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module folder
 * @classdesc The folder class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with folders.
 * 
 * @exports folder
 * @constructor
 * @example
 * let folder = call("Library.objects.folder");
 */

class folder extends node{
/**
 * Method to create a folder depending on parent and name in defined paths. The name of the folder can contain "." in it´s name.
 * 
 * @param {string} parent Address of root element
 * @param {string} name Name of folder
 * 
 * @example
 * let folder = call("Library.objects.folder");
 * folder.create("AGENT.OBJECTS", "folder1");
 */
	static create(parent, name){
		let parentNode = Ua.findNode(parent);
		const state = {};

		if(parentNode.result){
			return node.create(parent, name, {
				nodeClass: Ua.NodeClass.OBJECT,
				typeDefinition: Ua.ObjectType.FOLDERTYPE		
			});
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();
		}
		return state;
	}
	
/**
 * Method to delete a folder depending on an address string.
 * 
 * @param {string} address Address of folder
 * 
 * @example
 * let folder = call("Library.objects.folder");
 * folder.delete("AGENT.OBJECTS.folder1");
 */
	static delete(address){
		var folder = Ua.findNode(address);
		const state = {};
		
		if(folder.result){
			if(folder.result.typeDefinition.value === "i=61"){
				return node.delete(address);
			} else {
				state.error = Ua.Status.BADTYPEMISMATCH;
				state.errorstring = Ua.Status(state.error).toString();
			}
		} else {
			state.error = folder.error;
			state.errorstring = Ua.Status(folder.error).toString();
		}
		return state;
	}
}

return folder;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.node" ExportedBrowseName="1:node" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects" BrowseName="1:objects">
		<DisplayName Locale="en">objects</DisplayName>
		<Description Locale="en">objects</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.node" BrowseName="1:node" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">node</DisplayName>
		<Description Locale="en">node</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[/**
 * @module node
 * @classdesc The node class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with nodes.
 * 
 * @exports node
 * @constructor
 * @example
 * let node = call("Library.objects.node");
 */

class node {
//todo globaler valid path überprüfung für alle datenpunkt aktionen

/**
 * 
 * @param {string} parent 
 * @param {string} name 
 * @param {object} [options] 
 * @param {string} [options.description=[name]] 
 * @param {string} [options.displayName=[name]] 
 * @param {string} [options.browseName=[name]] 
 * @param {string} [options.dataType=Ua.DataType.INT32]
 * @param {string} [options.nodeClass=Ua.NodeClass.VARIABLE]
 * @param {string} [options.valueRank=Ua.ValueRank.SCALAR] 
 * @param {string} [options.modellingRule=null]
 * @param {string} [options.typeDefinition=Ua.VariableType.BASEVARIABLETYPE]
 * @param {string} [options.reference=Ua.Reference.HASCOMPONENT]
 * @param {*} options.value 
 * 
 * @example
 * let node = call("Library.objects.node");
 * 
 * // Create node with default values
 * node.create("AGENT.OBJECTS", "int32");
 * 
 * // Create array bool node and set initial value
 * let config = {
 *  dataType: Ua.DataType.BOOLEAN,
 *  valueRank: Ua.ValueRank.ONEDIMENSION,
 *  value: [true, false, true, false]
 * }
 * node.create("AGENT.OBJECTS", "boolArray", config);
 */
static create(parent, name, options){
    let node = Ua.findNode(parent + "." + name);
    let parentNode = Ua.findNode(parent);
    const state = {};
    
    if(!parentNode.result){
		state.error = parentNode.error;
		state.errorstring = Ua.Status(parentNode.error).toString();
    } else {
		if(!node.result){
			let config = {
				nodeClass:		Ua.NodeClass.VARIABLE,
				parent:			parent,
				typeDefinition: Ua.VariableType.BASEVARIABLETYPE,
				displayName:	name,
				browseName:		name,
				description:	name,
				reference:		Ua.Reference.HASCOMPONENT,
				dataType:		Ua.DataType.INT32,
				valueRank:		Ua.ValueRank.SCALAR,
				value:			0
			};
	 
			for(let i in options){
				switch(i){
					case "displayName":
						config[i] = options[i];
						break;
					case "browseName":
						config[i] = options[i];
						break;
					case "description":
						config[i] = options[i];
						break;
					case "nodeClass": 
						config[i] = options[i];
						break;
					case "typeDefinition": 
						config[i] = options[i];
						break;
					case "value":
						config[i] = options[i];
						break;
					case "dataType":
						config[i] = options[i];
						break;
					case "valueRank":
						config[i] = options[i];
						break;
					case "modellingRule":
						config[i] = options[i];
						break;
					case "reference":
						config[i] = options[i];
						break;
					default:
						break;
				}
			}
		  
			node = Ua.createNode(parent + "." + name, config);
			state.error = node.error;
			state.errorstring = node.errorstring;
		} else {
			state.error = Ua.Status.BADNODEIDEXISTS;
			state.errorstring = Ua.Status(state.error).toString();
		}
	}
    return state;
}

/**
 * Method to delete a node depending on an address string.
 * 
 * @param {string} address 
 * 
 * @example
 * let node = call("Library.objects.node");
 * node.delete("AGENT.OBJECTS.bool");
 */
static delete(address){
	let node = Ua.findNode(address);    
	const state = {};
	if(node.result){
		return node.result.remove();
	} else {
		state.error = node.error;
		state.errorstring = Ua.Status(node.error).toString();
	}

	return state;
}
}

return node;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.objecttype" ExportedBrowseName="1:objecttype" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects" BrowseName="1:objects">
		<DisplayName Locale="en">objects</DisplayName>
		<Description Locale="en">objects</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.objecttype" BrowseName="1:objecttype" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">objecttype</DisplayName>
		<Description Locale="en">objecttype</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module ObjectType Instance
 * @classdesc The objecttype class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with ObjectType Instances.
 * 
 * @exports instance
 * @constructor
 * @example
 * let instance = call("Library.objects.objecttype");
 */

class objecttype extends node{

/**
 * Method to create an objectType instance depending on parent, name in defined paths and objectType Address. The name of the folder can contain "." in it´s name.
 * 
 * @param {string} parent Address of root element
 * @param {string} name Name of folder
 * @param {string} Address of objectType
 *
 * @example
 * let instance = call("Library.objects.objecttype");
 * instance.create("AGENT.OBJECTS", "Instance1", "ObjectTypes.PROJECT.TestObject");
 */
	static create(parent, name, objectType){
		const state = {};
		let objectTypeNode = Ua.findNode(objectType);
		if(objectTypeNode.result){
			let parentNode = Ua.findNode(parent);
			if(parentNode.result){
				return node.create(parent, name, {
					nodeClass: Ua.NodeClass.OBJECT,
					typeDefinition: objectType	
				});
			} else {
				state.error = parentNode.error;
				state.errorstring = Ua.Status(parentNode.error).toString();
			}		
		} else {	
			state.error = objectTypeNode.error;
			state.errorstring = Ua.Status(objectTypeNode.error).toString();
		}
		return state;
	}
	
	
/**
 * Method to delete a instance depending on an address string.
 * 
 * @param {string} address Address of instance
 * 
 * @example
 * let instance = call("Library.objects.objecttype");
 * instance.delete("AGENT.OBJECTS.Instance1");
 */
	static delete(address){
		let instance = Ua.findNode(address);    
		const state = {};
		if(instance.result){
			return node.delete(address);
		} else {
			state.error = instance.error;
			state.errorstring = Ua.Status(instance.error).toString();
		}
		
		return state;
	}
}


return objecttype;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.property" ExportedBrowseName="1:property" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects" BrowseName="1:objects">
		<DisplayName Locale="en">objects</DisplayName>
		<Description Locale="en">objects</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.property" BrowseName="1:property" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">property</DisplayName>
		<Description Locale="en">property</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");

/**
 * @module property
 * @classdesc The property class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with property.
 * 
 * @exports property
 * @constructor
 * @example
 * let property = call("Library.objects.property");
 */

class property extends node {

/**
 * Method to create a property depending on parent, name and optional options in defined paths. The name of the property can contain "." in it´s name.
 * 
 * @param {string} parent 
 * @param {string} name 
 * @param {object} [options] 
 * @param {string} [options.description=[name]] 
 * @param {string} [options.displayName=[name]] 
 * @param {string} [options.browseName=[name]] 
 * @param {string} [options.dataType=Ua.DataType.INT32] 
 * @param {string} [options.valueRank=Ua.ValueRank.SCALAR]
 * @param {string} [options.modellingRule=null]
 * @param {*} [options.value] 
 * 
 * @example
 * let property = call("Library.objects.property");
 * 
 * // Create property with default values
 * property.create("AGENT.OBJECTS", "int32Prop");
 * 
 * // Create bool property and value true
 * let config = {
 *  dataType: Ua.DataType.BOOLEAN
 *  value: true
 * }
 * property.create("AGENT.OBJECTS", "boolProp", config);
 * 
 * // Create array bool property
 * let config = {
 *  dataType: Ua.DataType.BOOLEAN,
 *  valueRank: Ua.ValueRank.ONEDIMENSION
 * }
 * property.create("AGENT.OBJECTS", "boolArrayProp", config);
 */
 
 
	static create(parent, name, options){
		let parentNode = Ua.findNode(parent);
		const state = {};
		if(parentNode.result){
			typeof options === "undefined" ? options = {} : "";
			options.typeDefinition = Ua.VariableType.PROPERTYTYPE;
			return node.create(parent, name, options);
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();
		}
		return state;
	}

/**
 * Method to delete a property depending on an address string.
 * 
 * @param {string} address 
 * 
 * @example
 * let property = call("Library.objects.property");
 * property.delete("AGENT.OBJECTS.boolProp");
 */
	static delete(address){
		let property = Ua.findNode(address);    
		const state = {};
		if(property.result){
			if(property.result.typeDefinition.value === "i=68"){
				return node.delete(address);
			} else {
				state.error = Ua.Status.BADTYPEMISMATCH;
				state.errorstring = Ua.Status(state.error).toString();
			}
		} else {
			state.error = property.error;
			state.errorstring = Ua.Status(property.error).toString();
		}
		return state;
	}
}

return property;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.variabletype" ExportedBrowseName="1:variabletype" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects" BrowseName="1:objects">
		<DisplayName Locale="en">objects</DisplayName>
		<Description Locale="en">objects</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.variabletype" BrowseName="1:variabletype" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">variabletype</DisplayName>
		<Description Locale="en">variabletype</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module VariableType Instance
 * @classdesc The variabletype class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with VariableType Instances.
 * 
 * @exports variabletype
 * @constructor
 * @example
 * let VT_instance = call("Library.objects.variabletype");
 */

class variabletype extends node{

/**
 * Method to create a VariableType instance depending on parent, name in defined paths and options.
 * 
 * @param {string} parent
 * @param {string} name
 * @param {Object} options
 *
 * @example
 * let instance = call("Library.objects.variabletype");
 * instance.create("AGENT.OBJECTS", "Instance2", {dataType:	Ua.DataType.UINT16, typeDefinition:	"VariableTypes.PROJECT.word_to_bits"});
 */
	static create(parent, name, options){
		let VariableTypeNode = Ua.findNode(options.typeDefinition);
		const state = {};
		if(VariableTypeNode.result){
			let parentNode = Ua.findNode(parent);
			if(parentNode.result){
				return node.create(parent, name, options);
			} else {
				state.error = parentNode.error;
				state.errorstring = Ua.Status(parentNode.error).toString();
			}
		} else {
			state.error = VariableTypeNode.error;
			state.errorstring = Ua.Status(VariableTypeNode.error).toString();
		}
		return state;
	}
	
	
/**
 * Method to delete a instance depending on an address string.
 * 
 * @param {string} address
 * 
 * @example
 * let instance = call("Library.objects.variabletype");
 * instance.delete("AGENT.OBJECTS.Instance_2");
 */
	static delete(address){
		let instance = Ua.findNode(address);    
		const state = {};
		if(instance.result){
			return node.delete(address);
		} else {
			state.error = instance.error;
			state.errorstring = Ua.Status(instance.error).toString();
		}
		
		return state;
	}
}


return variabletype;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" ExportedBrowseName="1:Library" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history" BrowseName="1:history">
		<DisplayName Locale="en">history</DisplayName>
		<Description Locale="en">history</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.archive" BrowseName="1:archive" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">archive</DisplayName>
		<Description Locale="en">archive</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const param = call("Library.helper.parameterCheck");
const node = call("Library.objects.node");

/**
 * @module archive
 * @classdesc The archive class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with archives.
 * 
 * @exports archive
 * @constructor
 * @example
 * const archive = call("Library.history.archive");
 */

class archive{

	constructor(){
		this.i_debug = false;	
		this.i_parent = "AGENT.HISTORY";
		this.i_types = ["Aggregate", "Data", "Event"];

		this.i_partition_interval = ["n", "y", "q", "m", "w", "d", "h"];

		this.i_configObject = {
			Stepped: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			},
			disable: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			},
			file_limit: {
				type: Ua.DataType.UINT32,
				defaultValue: 0
			},
			partition_interval: {
				type: Ua.DataType.STRING,
				defaultValue: "n"
			}
		};
	}	
	
	
	_setOption(address, key, value){
		let isValid = false;
		let option = Ua.findNode(address + "." + key);
	
		if(option.result){
			switch(key){
				case "partition_interval":
					if(this.i_partition_interval.indexOf(value) >= 0) isValid = true;
					break;
				case "file_limit":
					// TODO check with development
					isValid = true;
					break;
				case "disable": isValid=true; break;
				case "Stepped":
					if(value === true || value === false || value === 1 || value === 0) isValid = true;
					break;
			}
	
			if(isValid){
				option.result.value = value;
				return {error: 0, errorstring: undefined};
			}
		} else {
			return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}
	}
	
	/**
	 * Method to create an archive of all possible types with optional possibility to set
	 * some custom configuration for the archive.
	 * 
	 * @param {string} name 
	 * @param {string} type 0 | 1 | 2
	 * @param {object} [options]
	 * @param {string} [options.disable]
	 * @param {string} [options.partition_interval]
	 * @param {number} [options.file_limit]
	 * @param {boolean} [options.Stepped]
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * 
	 * // Create "Aggregate" archive with default settings
	 * archive.create("aggArchive", "Aggregate");
	 * 
	 * // Create "Data" archive with default config
	 * archive.create("dataArchive", "Data");
	 * 
	 * // Create "Event" archive with default settings
	 * archive.create("eventArchive", "Event");
	 * 
	 * // Create "Data" archive with yearly partition interval and a file limit of 5
	 * let config = {
	 * 	partition_interval: "y",
	 * 	file_limit: 5
	 * }
	 * archive.create("dataArchive2", 1, config);
	 * 
	 */
	create(name, type, options){
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		if(param.isUndefinedOrEmpty(type)) return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
	
		// check parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.check(type, "number", {min: 0, max: 2})) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(!param.isUndefinedOrEmpty(options.partition_interval) && !this.i_partition_interval.includes(options.partition_interval)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.isUndefinedOrEmpty(options.file_limit) && !param.check(options.file_limit, "number")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		
		let state = {};
		state = node.create(this.i_parent, name, {
			nodeClass: Ua.NodeClass.OBJECT,
			typeDefinition: "ObjectTypes.ATVISE.ArchiveGroup." + this.i_types[type]
		});	
		
		if(state.error === 0){
			let node = Ua.findNode(this.i_parent + "." + name);		
			for(let i in options){
				let optionState = this._setOption(node.result.nodeId.address, i, options[i]);
				if(optionState.error !== 0){
					return optionState;
				}
			}
		}

		return state;
	}
	
	/**
	 * Method to update an archive configuration.
	 * 
	 * @param {string} name 
	 * @param {object} options
	 * @param {string} [options.disable]
	 * @param {string} [options.partition_interval]
	 * @param {number} [options.file_limit]
	 * @param {boolean} [options.Stepped]
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * 
	 * // Disable historization for archive
	 * let config = {
	 * 	disable: true
	 * }
	 * archive.update("aggArchive", config);
	 * 
	 * // Change partition interval and file_limit
	 * let config = {
	 * 	partition_interval: "m",
	 * 	file_limit: 15
	 * }
	 * archive.update("aggArchive", config);
	 * 
	 */
	update(name, options){
		// check parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.check(options, "object")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.isUndefinedOrEmpty(options.partition_interval) && !this.i_partition_interval.includes(options.partition_interval)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.isUndefinedOrEmpty(options.file_limit) && !param.check(options.file_limit, "number")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};

		let state = {};
		let archive = Ua.findNode(this.i_parent + "." + name);
		if (archive.result){
			for(let i in options){
				let optionState = this._setOption(archive.result.nodeId.address, i, options[i]);
				if(optionState.error !== 0){
					return optionState;
				}
			}

			state = {error: Ua.Status.GOOD, errorstring: undefined};
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
		
		return state;
	}
	
	/**
	 * Method to delete an archive from atvise project.
	 * 
	 * @param {string} name 
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * archive.delete("aggArchive");
	 */
	delete(name){
		// check parameter
		if(!param.check(name, "string"))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		
		let archive = Ua.findNode(name.indexOf("AGENT.HISTORY.") >= 0 ? name : this.i_parent + "." + name);

		if (archive.result){		
			if(this.i_types.indexOf(archive.result.typeDefinition.toString()) < 0){
				return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
			} else {
				return node.delete(archive.result.nodeId.address);
			}
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
	}
	
	/**
	 * Method to read current configuration for an archive.
	 * 
	 * @param {string} name 
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * let config = archive.config("aggArchive");
	 * 
	 * for(let i in config){
	 * 	console.log("setting: " + i + " - value: " + config[i]);
	 * }
	 */
	config(name){
		let exceptProperties = ["filter"];
	
		// check mandatory parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		let config = {};
		let archive = Ua.findNode(this.i_parent + "." + name);
		if (archive.result){
			let browseResult = archive.result.browse({
				nodeClass: Ua.NodeClass.VARIABLE
			});

			for(let i = 0; i < browseResult.result.length; i++){
				if(exceptProperties.indexOf(browseResult.result[i].node.browseName.name) < 0)
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value;
			}
			
			return config;
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
	}
	
	/**
	 * Method to get whole configuration default configuration for an archive.
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * // Get default configuration
	 * let defaultConfig = archive.template();
	 * 
	 * // Disable archive
	 * defaultConfig.disable = true;
	 * 
	 * // Create archive with modified default config
	 * archive.create("dataArchive", 1, defaultConfig);
	 */
	template(){
		let config = {};
	
		for(let i in this.i_configObject) {
			if(i === "function") {
				config[i] = this.i_agg_browsename[0];
			} else {
				config[i] = this.i_configObject[i].defaultValue;
			}
		}
	
		return config;
	}	
	
	/**
	 * Method to assign archive to one or multiple nodes. Already existing configurations will be removed
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * archive.assign("datavalues", "AGENT.OBJECTS.myBoolean");
	 * 
	 * archive.assign("datavalues", ["AGENT.OBJECTS.myBoolean", "AGENT.OBJECTS.myString"]);
	 */
	assign(archive, nodes){
		let archiveNode = Ua.findNode("AGENT.HISTORY." + archive);
		if(archiveNode.result && archiveNode.result.typeDefinition.value === "ns=1;s=ObjectTypes.ATVISE.ArchiveGroup.Data"){
			if(typeof nodes === "string"){
				var node = Ua.findNode(nodes);
				if(node.result){
					//remove existing configuration
					var historicalReferences = node.result.browse({
						reference: Ua.Reference.HASHISTORICALCONFIGURATION
					});

					for(var i = 0; i < historicalReferences.result.length; i++){
						if(historicalReferences.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.ArchiveGroup.Data") >= 0){
							node.result.deleteReference(Ua.Reference.HASHISTORICALCONFIGURATION, historicalReferences.result[i].node.nodeId.address);
						}
					}

					//assign archive
					node.result.addReference(Ua.Reference.HASHISTORICALCONFIGURATION, archiveNode.result.nodeId.address);
				} else {
					return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
				}
			} else if(typeof nodes === "object"){
				for(var j = 0; j < nodes.length; j++){
					var node = Ua.findNode(nodes[j]);
					if(node.result){
						//remove existing configuration
						var historicalReferences = node.result.browse({
							reference: Ua.Reference.HASHISTORICALCONFIGURATION
						});

						for(var i = 0; i < historicalReferences.result.length; i++){
							if(historicalReferences.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.ArchiveGroup.Data") >= 0){
								node.result.deleteReference(Ua.Reference.HASHISTORICALCONFIGURATION, historicalReferences.result[i].node.nodeId.address);
							}
						}

						//assign archive
						node.result.addReference(Ua.Reference.HASHISTORICALCONFIGURATION, archiveNode.result.nodeId.address);
					} else {
						return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
					}
				}
			}
		} else {
			return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}
	}
}

return new archive();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.aggregate" BrowseName="1:aggregate" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">aggregate</DisplayName>
		<Description Locale="en">aggregate</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const param = call("Library.helper.parameterCheck");

/**
 * @module aggregate
 * @classdesc The aggregate class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with aggregate.
 * 
 * @exports aggregate
 * @constructor
 * @example
 * const aggregate = call("Library.history.aggregate");
 */

/**
 * @typedef {object} aggregateConfig
 * @property {string} [interval_unit]
 * @property {number} [interval_value]
 * @property {string} [offset_unit]
 * @property {number} [offset_value]
 * @property {boolean} [stepped]
 * @property {object} [function] //todo
 * @property {object} [archive] //todo
 * @property {number} [PercentDataGood]
 * @property {number} [PercentDataBad]
 * @property {boolean} [TreatUncertainAsBad]
 * @property {boolean} [UseSlopedExtrapolation]
 */

class aggregate {

	constructor(){
		this.i_parent = "AGENT.HISTORY";
	
		this.i_partition_interval = ["n", "y", "q", "m", "w", "d", "h"];
	
		this.i_agg_nodeid = ["i=0"];
		this.i_agg_browsename = ["(None)"];
	
		let node = Ua.findNode("2997");
		let res = node.result.browse();
		for(let i = 0; i < res.result.length; i++){
			this.i_agg_nodeid.push(res.result[i].node.nodeId.value);
			this.i_agg_browsename.push(res.result[i].node.browseName.name);
		}

		this.i_configObject = {
			interval_unit: {
				type: Ua.DataType.STRING,
				defaultValue: "m"
			},
			interval_value: {
				type: Ua.DataType.UINT32,
				defaultValue: 5
			},
			offset_unit: {
				type: Ua.DataType.STRING,
				defaultValue: "m"
			},
			offset_value: {
				type: Ua.DataType.UINT32,
				defaultValue: 0
			},
			stepped: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			},
			function: {
				type: Ua.DataType.NODEID,
				defaultValue: "i=0"
			},
			archive: {
				type: Ua.DataType.NODEID,
				defaultValue: ""
			},
			PercentDataGood: {
				type: Ua.DataType.BYTE,
				defaultValue: 100
			},
			PercentDataBad: {
				type: Ua.DataType.BYTE,
				defaultValue: 100
			},
			TreatUncertainAsBad: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: true
			},
			UseSlopedExtrapolation: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			}
		}
	  } 
		
		
	/**
	 * Method to create an aggregate depending on template, name and optional options.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * @param {aggregateConfig} config
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 *  
	 * //create Aggregate for 1 minute average function
	 * let config = {
	 * 	 function: "Average",
	 * 	 interval_unit: "m",
	 * 	 interval_value: 1,
	 * 	 archive: "aggArchive",
	 * 	 stepped: false
	 * }
	 * aggregate.create("AvgTemplate","avg_1min",config);
	 * 
	 */
	
	create(template, name, options){
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(template)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(options)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(template, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(!param.check(name, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
	
		let keys = [];
		let values = [];
		const state = {};
		for(let i in this.i_configObject) {
			switch(i){
				case "archive":
					try{
						let position = options === null ? -1 : options[i].indexOf("AGENT.HISTORY");
						if(position >= 0 && options[i].length > 0){
							options[i] = position === 0 ? "ns=1;s=" + options[i] : options[i];
						} else if(options[i].length === 0){
							delete options[i];
						} else if(options[i] !== "i=0") {
							options[i] = "ns=1;s=AGENT.HISTORY." + options[i];
						}
					} catch(ex){
						console.log(ex);
					}
					break;
				case "function":
					if(options !== undefined && i in options){
						console.log(options[i]);
						options[i] = this.i_agg_nodeid[this.i_agg_browsename.indexOf(options[i])];
					}
					break;
			}
	
			keys.push(i);
			values.push({
				type: this.i_configObject[i].type,
				value: options !== undefined && i in options ? options[i] : this.i_configObject[i].defaultValue
			});
		}
		
		console.log(keys);
		console.log(values);
	

		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name},
				{type: Ua.DataType.STRING, value: keys},
				{type: Ua.DataType.BASEDATATYPE, value: values}
			]
		});
		
		
		state.error = callResult.error;
		state.errorstring = callResult.errorstring;
		return state;
	}


	
	/**
	 * Method to update a aggregate depending on template, name and options.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * @param {aggregateConfig} config
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * 
	 * let config = {
	 * 	 interval_unit: "d",
	 * 	 interval_value: 2,
	 * }
	 * aggregate.update("AvgTemplate","avg_1min",config);
	 */
	update(template, name, options){
		let keys = [];
		let values = [];
		const state = {};
		for(let i in options) {
			switch(i){
				case "archive":
					let position = options[i].indexOf("AGENT.HISTORY");
					if(position >= 0 && options[i].length > 0){
						options[i] = position === 0 ? "ns=1;s=" + options[i] : options[i];
					} else if(options[i].length === 0){
						delete options[i];
					} else {
						options[i] = "ns=1;s=AGENT.HISTORY." + options[i];
					}
					break;
				case "function":
					options[i] = this.i_agg_nodeid[this.i_agg_browsename.indexOf(options[i])];
					break;
			}
	
			keys.push(i);
			values.push({
				type: this.i_configObject[i].type,
				value: options[i]
			});
		}
		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 2},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name},
				{type: Ua.DataType.STRING, value: keys},
				{type: Ua.DataType.BASEDATATYPE, value: values}
			]
		});
		if(callResult.result==""){
			state.error = callResult.error;
			state.errorstring = callResult.errorstring;
			return state;
		}else{	
			return callResult.result;	
		}
		//todo fehlerhandling
	}
	
	/**
	 * Method to delete a aggregate depending on template and name. Not supported until [AT-D-12115] is fixed.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * aggregate.delete("AvgTemplate","avg_1min");
	 */
	delete(template, name){
		// Both solutions work without problem and the first one was chosen because it uses the same call method as create and update
		var state;

		// Solution 1
		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 4},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name}
			]
		});
		/*
		// Solution 2
		let aggregate = Ua.findNode("AGENT.HISTORY.AGGREGATETEMPLATES." + template + "." + name);
		if(aggregate.result){{
			state=aggregate.result.remove().result;
		} else {
			// todo
		}
	
		return state === 0 ? true : false;
		*/
		if(callResult.result==""){
			state.error = callResult.error;
			state.errorstring = callResult.errorstring;
			return state;
		}else{	
			return callResult.result;	
		}
	}
	
	/**
	 * Method to get config of aggregate depending template and name.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * 
	 * @returns {aggregateConfig}
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * aggregate.config("AvgTemplate","avg_1min");
	 */
	config(template, name){
		let config = {};
		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 3},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name}
			]
		});

		for(let j = 0; j < callResult.result[0].length; j++){
			if(callResult.result[0][j] === "archive"){
				callResult.result[1][j].value = callResult.result[1][j].value.replace("ns=1;s=AGENT.HISTORY.", "");
			} else if (callResult.result[0][j] === "function") {
				if(callResult.result[1][j].value === "i=0"){
					callResult.result[1][j].value = "(None)";
				} else {
					var aggregate = new UaNode(callResult.result[1][j].value);
					callResult.result[1][j].value = aggregate.browsename;
				}
			}
	
			config[callResult.result[0][j]] = callResult.result[1][j].value;
		}

		return config;
	}
	
	/**
	 * Method to get default configuration for an aggregate.
	 * 
	 * @returns {aggregateConfig}
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * aggregate.template(); 
	 */
	template(){
		let config = {};
		for(let i in this.i_configObject) {
			if(i === "function") {
				config[i] = this.i_agg_browsename[0];
			} else {
				config[i] = this.i_configObject[i].defaultValue;
			}
		}
		return config;
	}
}
return new aggregate();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.template" BrowseName="1:template" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">template</DisplayName>
		<Description Locale="en">template</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const param = call("Library.helper.parameterCheck");
const aggregate = call("Library.history.aggregate");

/**
 * @module template
 * @classdesc The template class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with templates.
 * 
 * @exports template
 * @constructor
 * @example
 * const template = call("Library.history.template");
 */

class template {
	constructor(){
	    this.i_parent = "AGENT.HISTORY.AGGREGATETEMPLATES";
	}
	
	/**
	 * Method to create a template depending name and object of aggregate configurations.
	 * 
	 * @param {string} name 
	 * @param {object} [config] 
	 * @param {aggregateConfig} [config.name]
	 * 
	 * @example
	 * let template = call("Library.history.template");
	 * 
	 * // Create template with no aggregate functions
	 * template.create("T1");
	 * 
	 * // Create template with simple aggregate function
	 * let config = {
	 *  "avg_1min": {
	 *      ...
	 *  }
	 * }
	 * template.create("T2", config);
	 * 
	 * // Create template with cascaded aggregate functions
	 * let config = {
	 *  "avg_1min": {
	 *      ...
	 *  },
	 * "avg_1min.avg_5min": {
	*       ...
	 *  },
	 * }
	 * template.create("T3", config);
	 */
	create(name, config){
		const state = {};
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
	
		let template = Ua.findNode(this.i_parent + "." + name);
		
		if(template.result){
			state.error = Ua.Status.BADNODEIDEXISTS;
			state.errorstring = Ua.Status(state.error).toString();
		}
		else{
			let tempNode = Ua.createNode(this.i_parent + "." + name,{
				nodeClass: Ua.NodeClass.OBJECT,
				parent: this.i_parent,
				typeDefinition: "ObjectTypes.ATVISE.AggregateTemplate",
				reference: Ua.Reference.HASCOMPONENT
			});
			
			// Workaround waiting some time before continue due to problem with OPC UA Method call
			let time = new Date().getTime();
			while(new Date().getTime() - 50 <= time){}
			
			//return state === 0 ? true : false;
			state.error = tempNode.error;
			state.errorstring = tempNode.errorstring;
			
			if(config !== undefined && typeof config === "object"){
				for(let i in config){
					aggregate.create(name, i, config[i]);
				}
			}
		}
		return state;
	}
	
	/**
	 * Method to delete a template depending name.
	 * 
	 * @param {string} name 
	 */
	delete(name){
		const state = {};
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(name, "string"))  return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
	
		let template = Ua.findNode(this.i_parent + "." + name);
		if(template.result){
			return template.result.remove();
		} else {
			state.error = template.error;
			state.errorstring = Ua.Status(template.error).toString();
		}
		return state;
	}
	
	/**
	 * Method to update a template depending on name and object of aggregate configurations.
	 * 
	 * @param {string} name 
	 * @param {object} config 
	 * @param {aggregateConfig} config.name
	 * @param {boolean} remove not supported at the moment
	 */
	update(name, config, remove){
		let configNames = [];
		const state = {};
		let template = Ua.findNode(this.i_parent + "." + name);
		if(!template.result){
			state.error = template.error;
			state.errorstring = Ua.Status(template.error).toString();
			return state;
		}

		configNames = this._browseItem(template.result.nodeId.address, template.result.browseName.name);

		//remove = false; 
		if(remove === true){
			let configurations = this.config(name);
			let allConfigs = configNames.concat(Object.keys(config).filter(seccondArrayItem => !configNames.includes(seccondArrayItem)));

			for(let i = 0; i < allConfigs.length; i++){
				if(allConfigs[i] in config && configNames.indexOf(allConfigs[i]) < 0){
					return aggregate.create(name, configNames[i], config[configNames[i]]);
				} else if(allConfigs[i] in config && configNames.indexOf(allConfigs[i]) >= 0){
					return aggregate.update(name, allConfigs[i], config[allConfigs[i]]);
				} else {
					return aggregate.delete(name, allConfigs[i]);
				}
			}
	
		} else {
			for(let item in config){
				if(configNames.indexOf(item) >= 0){
					return aggregate.update(name, item, config[item]);
				} else {
					let time = new Date().getTime();
					while(new Date().getTime() - 50 <= time){}
					return aggregate.create(name, item, config[item]);
				}
			}
		}
	}
		
	/**
	 * Method to get current configuration of a template depending name.
	 * 
	 * @param {string} name 
	 */
	config(name){
		// todo fehlerhandling bei nicht zurückgeliefertem result der opc ua methode
		let configNames = [];
		let config = {};
		const state = {};
		let template = Ua.findNode(this.i_parent + "." + name);
		// überprüfen ob template existiert
		if(template.result){
			configNames = this._browseItem(template.result.nodeId.address, template.result.browseName.name);
			
			for(let i = 0; i < configNames.length; i++){
				config[configNames[i]] = aggregate.config(name, configNames[i]);
			}
		
			return config;
		} else {
			state.error = template.error;
			state.errorstring = Ua.Status(template.error).toString();
			return state;
		}

	}
		
	/**
	 * Method to get default configuration for an aggregate function.
	 * 
	 * @returns {aggregateConfig}
	 */
	template(){
		return aggregate.template();
	}
	
	_browseItem(nodeid, templateName){
		let configNames = [];
	
		let parentNode = Ua.findNode(nodeid);
		let browseResult = parentNode.result.browse({
			typeDefinition : "ObjectTypes.ATVISE.AggregateFunction"
		});
		
		for(let i = 0; i < browseResult.result.length; i++){
			configNames.push(browseResult.result[i].node.nodeId.address.replace(nodeid + ".", ""));
			let result = this._browseItem(browseResult.result[i].node.nodeId.address, nodeid);
	
			configNames = configNames.concat(result);
		}
	
		return configNames;
	}
}
return new template();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects" BrowseName="1:objects">
		<DisplayName Locale="en">objects</DisplayName>
		<Description Locale="en">objects</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.node" BrowseName="1:node" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">node</DisplayName>
		<Description Locale="en">node</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[/**
 * @module node
 * @classdesc The node class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with nodes.
 * 
 * @exports node
 * @constructor
 * @example
 * let node = call("Library.objects.node");
 */

class node {
//todo globaler valid path überprüfung für alle datenpunkt aktionen

/**
 * 
 * @param {string} parent 
 * @param {string} name 
 * @param {object} [options] 
 * @param {string} [options.description=[name]] 
 * @param {string} [options.displayName=[name]] 
 * @param {string} [options.browseName=[name]] 
 * @param {string} [options.dataType=Ua.DataType.INT32]
 * @param {string} [options.nodeClass=Ua.NodeClass.VARIABLE]
 * @param {string} [options.valueRank=Ua.ValueRank.SCALAR] 
 * @param {string} [options.modellingRule=null]
 * @param {string} [options.typeDefinition=Ua.VariableType.BASEVARIABLETYPE]
 * @param {string} [options.reference=Ua.Reference.HASCOMPONENT]
 * @param {*} options.value 
 * 
 * @example
 * let node = call("Library.objects.node");
 * 
 * // Create node with default values
 * node.create("AGENT.OBJECTS", "int32");
 * 
 * // Create array bool node and set initial value
 * let config = {
 *  dataType: Ua.DataType.BOOLEAN,
 *  valueRank: Ua.ValueRank.ONEDIMENSION,
 *  value: [true, false, true, false]
 * }
 * node.create("AGENT.OBJECTS", "boolArray", config);
 */
static create(parent, name, options){
    let node = Ua.findNode(parent + "." + name);
    let parentNode = Ua.findNode(parent);
    const state = {};
    
    if(!parentNode.result){
		state.error = parentNode.error;
		state.errorstring = Ua.Status(parentNode.error).toString();
    } else {
		if(!node.result){
			let config = {
				nodeClass:		Ua.NodeClass.VARIABLE,
				parent:			parent,
				typeDefinition: Ua.VariableType.BASEVARIABLETYPE,
				displayName:	name,
				browseName:		name,
				description:	name,
				reference:		Ua.Reference.HASCOMPONENT,
				dataType:		Ua.DataType.INT32,
				valueRank:		Ua.ValueRank.SCALAR,
				value:			0
			};
	 
			for(let i in options){
				switch(i){
					case "displayName":
						config[i] = options[i];
						break;
					case "browseName":
						config[i] = options[i];
						break;
					case "description":
						config[i] = options[i];
						break;
					case "nodeClass": 
						config[i] = options[i];
						break;
					case "typeDefinition": 
						config[i] = options[i];
						break;
					case "value":
						config[i] = options[i];
						break;
					case "dataType":
						config[i] = options[i];
						break;
					case "valueRank":
						config[i] = options[i];
						break;
					case "modellingRule":
						config[i] = options[i];
						break;
					case "reference":
						config[i] = options[i];
						break;
					default:
						break;
				}
			}
		  
			node = Ua.createNode(parent + "." + name, config);
			state.error = node.error;
			state.errorstring = node.errorstring;
		} else {
			state.error = Ua.Status.BADNODEIDEXISTS;
			state.errorstring = Ua.Status(state.error).toString();
		}
	}
    return state;
}

/**
 * Method to delete a node depending on an address string.
 * 
 * @param {string} address 
 * 
 * @example
 * let node = call("Library.objects.node");
 * node.delete("AGENT.OBJECTS.bool");
 */
static delete(address){
	let node = Ua.findNode(address);    
	const state = {};
	if(node.result){
		return node.result.remove();
	} else {
		state.error = node.error;
		state.errorstring = Ua.Status(node.error).toString();
	}

	return state;
}
}

return node;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.property" BrowseName="1:property" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">property</DisplayName>
		<Description Locale="en">property</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");

/**
 * @module property
 * @classdesc The property class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with property.
 * 
 * @exports property
 * @constructor
 * @example
 * let property = call("Library.objects.property");
 */

class property extends node {

/**
 * Method to create a property depending on parent, name and optional options in defined paths. The name of the property can contain "." in it´s name.
 * 
 * @param {string} parent 
 * @param {string} name 
 * @param {object} [options] 
 * @param {string} [options.description=[name]] 
 * @param {string} [options.displayName=[name]] 
 * @param {string} [options.browseName=[name]] 
 * @param {string} [options.dataType=Ua.DataType.INT32] 
 * @param {string} [options.valueRank=Ua.ValueRank.SCALAR]
 * @param {string} [options.modellingRule=null]
 * @param {*} [options.value] 
 * 
 * @example
 * let property = call("Library.objects.property");
 * 
 * // Create property with default values
 * property.create("AGENT.OBJECTS", "int32Prop");
 * 
 * // Create bool property and value true
 * let config = {
 *  dataType: Ua.DataType.BOOLEAN
 *  value: true
 * }
 * property.create("AGENT.OBJECTS", "boolProp", config);
 * 
 * // Create array bool property
 * let config = {
 *  dataType: Ua.DataType.BOOLEAN,
 *  valueRank: Ua.ValueRank.ONEDIMENSION
 * }
 * property.create("AGENT.OBJECTS", "boolArrayProp", config);
 */
 
 
	static create(parent, name, options){
		let parentNode = Ua.findNode(parent);
		const state = {};
		if(parentNode.result){
			typeof options === "undefined" ? options = {} : "";
			options.typeDefinition = Ua.VariableType.PROPERTYTYPE;
			return node.create(parent, name, options);
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();
		}
		return state;
	}

/**
 * Method to delete a property depending on an address string.
 * 
 * @param {string} address 
 * 
 * @example
 * let property = call("Library.objects.property");
 * property.delete("AGENT.OBJECTS.boolProp");
 */
	static delete(address){
		let property = Ua.findNode(address);    
		const state = {};
		if(property.result){
			if(property.result.typeDefinition.value === "i=68"){
				return node.delete(address);
			} else {
				state.error = Ua.Status.BADTYPEMISMATCH;
				state.errorstring = Ua.Status(state.error).toString();
			}
		} else {
			state.error = property.error;
			state.errorstring = Ua.Status(property.error).toString();
		}
		return state;
	}
}

return property;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.folder" BrowseName="1:folder" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">folder</DisplayName>
		<Description Locale="en">folder</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module folder
 * @classdesc The folder class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with folders.
 * 
 * @exports folder
 * @constructor
 * @example
 * let folder = call("Library.objects.folder");
 */

class folder extends node{
/**
 * Method to create a folder depending on parent and name in defined paths. The name of the folder can contain "." in it´s name.
 * 
 * @param {string} parent Address of root element
 * @param {string} name Name of folder
 * 
 * @example
 * let folder = call("Library.objects.folder");
 * folder.create("AGENT.OBJECTS", "folder1");
 */
	static create(parent, name){
		let parentNode = Ua.findNode(parent);
		const state = {};

		if(parentNode.result){
			return node.create(parent, name, {
				nodeClass: Ua.NodeClass.OBJECT,
				typeDefinition: Ua.ObjectType.FOLDERTYPE		
			});
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();
		}
		return state;
	}
	
/**
 * Method to delete a folder depending on an address string.
 * 
 * @param {string} address Address of folder
 * 
 * @example
 * let folder = call("Library.objects.folder");
 * folder.delete("AGENT.OBJECTS.folder1");
 */
	static delete(address){
		var folder = Ua.findNode(address);
		const state = {};
		
		if(folder.result){
			if(folder.result.typeDefinition.value === "i=61"){
				return node.delete(address);
			} else {
				state.error = Ua.Status.BADTYPEMISMATCH;
				state.errorstring = Ua.Status(state.error).toString();
			}
		} else {
			state.error = folder.error;
			state.errorstring = Ua.Status(folder.error).toString();
		}
		return state;
	}
}

return folder;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.variabletype" BrowseName="1:variabletype" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">variabletype</DisplayName>
		<Description Locale="en">variabletype</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module VariableType Instance
 * @classdesc The variabletype class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with VariableType Instances.
 * 
 * @exports variabletype
 * @constructor
 * @example
 * let VT_instance = call("Library.objects.variabletype");
 */

class variabletype extends node{

/**
 * Method to create a VariableType instance depending on parent, name in defined paths and options.
 * 
 * @param {string} parent
 * @param {string} name
 * @param {Object} options
 *
 * @example
 * let instance = call("Library.objects.variabletype");
 * instance.create("AGENT.OBJECTS", "Instance2", {dataType:	Ua.DataType.UINT16, typeDefinition:	"VariableTypes.PROJECT.word_to_bits"});
 */
	static create(parent, name, options){
		let VariableTypeNode = Ua.findNode(options.typeDefinition);
		const state = {};
		if(VariableTypeNode.result){
			let parentNode = Ua.findNode(parent);
			if(parentNode.result){
				return node.create(parent, name, options);
			} else {
				state.error = parentNode.error;
				state.errorstring = Ua.Status(parentNode.error).toString();
			}
		} else {
			state.error = VariableTypeNode.error;
			state.errorstring = Ua.Status(VariableTypeNode.error).toString();
		}
		return state;
	}
	
	
/**
 * Method to delete a instance depending on an address string.
 * 
 * @param {string} address
 * 
 * @example
 * let instance = call("Library.objects.variabletype");
 * instance.delete("AGENT.OBJECTS.Instance_2");
 */
	static delete(address){
		let instance = Ua.findNode(address);    
		const state = {};
		if(instance.result){
			return node.delete(address);
		} else {
			state.error = instance.error;
			state.errorstring = Ua.Status(instance.error).toString();
		}
		
		return state;
	}
}


return variabletype;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.objecttype" BrowseName="1:objecttype" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">objecttype</DisplayName>
		<Description Locale="en">objecttype</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module ObjectType Instance
 * @classdesc The objecttype class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with ObjectType Instances.
 * 
 * @exports instance
 * @constructor
 * @example
 * let instance = call("Library.objects.objecttype");
 */

class objecttype extends node{

/**
 * Method to create an objectType instance depending on parent, name in defined paths and objectType Address. The name of the folder can contain "." in it´s name.
 * 
 * @param {string} parent Address of root element
 * @param {string} name Name of folder
 * @param {string} Address of objectType
 *
 * @example
 * let instance = call("Library.objects.objecttype");
 * instance.create("AGENT.OBJECTS", "Instance1", "ObjectTypes.PROJECT.TestObject");
 */
	static create(parent, name, objectType){
		const state = {};
		let objectTypeNode = Ua.findNode(objectType);
		if(objectTypeNode.result){
			let parentNode = Ua.findNode(parent);
			if(parentNode.result){
				return node.create(parent, name, {
					nodeClass: Ua.NodeClass.OBJECT,
					typeDefinition: objectType	
				});
			} else {
				state.error = parentNode.error;
				state.errorstring = Ua.Status(parentNode.error).toString();
			}		
		} else {	
			state.error = objectTypeNode.error;
			state.errorstring = Ua.Status(objectTypeNode.error).toString();
		}
		return state;
	}
	
	
/**
 * Method to delete a instance depending on an address string.
 * 
 * @param {string} address Address of instance
 * 
 * @example
 * let instance = call("Library.objects.objecttype");
 * instance.delete("AGENT.OBJECTS.Instance1");
 */
	static delete(address){
		let instance = Ua.findNode(address);    
		const state = {};
		if(instance.result){
			return node.delete(address);
		} else {
			state.error = instance.error;
			state.errorstring = Ua.Status(instance.error).toString();
		}
		
		return state;
	}
}


return objecttype;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm" BrowseName="1:alarm">
		<DisplayName Locale="en">alarm</DisplayName>
		<Description Locale="en">alarm</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm.condition" BrowseName="1:condition" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">condition</DisplayName>
		<Description Locale="en">condition</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
const status = call("Library.helper.returnObject");
const param = call("Library.helper.parameterCheck");

/**
* @module condition
* @classdesc The condition class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
* to interact with condition.
* 
* @exports condition
* @constructor
* @example
* const condition = call("Library.alarm.condition");
*/

/**
 * @typedef {object} discreteAlarm
 * @property {number} type
 * @property {string} [active_message]
 * @property {string} [inactive_message]
 * @property {string} [explicit_acknowledgement=false]
 * @property {string} [category=Error]
 * @property {string} [max_time_shelved=0]
 * @property {string} [modify_script=]
 * @property {string} [off_delay=0]
 * @property {string} [on_delay=0]
 * @property {string} [prevention_script]
 * @property {string} [value=false]
 * @property {string} [value_compare===]
 */

 /**
 * @typedef {object} limitAlarm
 * @property {number} type
 * @property {string} [active_message]
 * @property {string} [inactive_message]
 * @property {string} [explicit_acknowledgement=false]
 * @property {string} [category=Error]
 * @property {string} [max_time_shelved=0]
 * @property {string} [modify_script]
 * @property {string} [off_delay=0]
 * @property {string} [on_delay=0]
 * @property {string} [prevention_script]
 * @property {string} [lower_limit=0]
 * @property {string} [lower_limit_compare=>]
 * @property {string} [upper_limit=10]
 * @property {string} [upper_limit_compare=<]
 */

class condition{

	constructor(){
		//todo add all available parameters in array and go through to check -> would me more generic solution
		// constants
		this.iCategoryBase = "AGENT.ALARMING.Categories.";
		this.iConditionBase = "ObjectTypes.ATVISE.AlarmConditionControl.";
	
		this.validFields = {
			"Discrete": {},
			"Discrete.Retrigger": {},
			"Limit": {}
		}
		
		/*
		[
			"ObjectTypes.ATVISE.AlarmConditionControl.Discrete",
			"ObjectTypes.ATVISE.AlarmConditionControl.Discrete.Retrigger",
			"ObjectTypes.ATVISE.AlarmConditionControl.Limit"
		];*/
	
		this.basicValidFields = {
			type: "Discrete",
			active_message: "",
			inactive_message: "",
			explicit_acknowledgement: false,
			category: "Error",
			max_time_shelved: 0,
			modify_script: 0,
			off_delay: 0,
			on_delay: 0,
			prevention_script: 0
		}
	
		this.discreteValidFields = {
			value: false,
			value_compare: "==",
		}
	
		this.limitValidFields = {
			lower_limit: 0,
			lower_limit_compare: ">",
			upper_limit: 10,
			upper_limit_compare: "<"
		}
	
		// set validFields on init
		for(var i in this.validFields){
			switch(i){
				case "Discrete":
					this.validFields[i] = Object.assign({}, this.basicValidFields, this.discreteValidFields);
					break;
				case "Discrete.Retrigger":
					this.validFields[i] = Object.assign({}, this.basicValidFields, this.discreteValidFields);
					break;
				case "Limit":
					this.validFields[i] = Object.assign(this.basicValidFields, this.limitValidFields);
					break;
			}
		}
		
	/*
	
	
		this.iDefaults = {
			type: 0,
			category: "AGENT.ALARMING.Categories.Error",
			value: false,
			off_delay: 0,
			on_delay: 0,
			value_compare: "==",
	
			lower_limit: 0,
			lower_limit_compare: ">",
			upper_limit: 10,
			upper_limit_compare: "<"
		}
	
		this.validFields = {
			value: false,
			value_compare: "==",
	
			lower_limit: 0,
			lower_limit_compare: ">",
			upper_limit: 10,
			upper_limit_compare: "<"
		};
	*/
	}


	/**
	 * Method to create an alarm condition depending on address string, name and options
	 * 
	 * @param {string} configAddress 
	 * @param {string} name 
	 * @param {discreteAlarm|limitAlarm} [options] 
	 */

	create(configAddress, name, options){
		let state = {};
		var conditionConfig = {};
	
		// check mandatory parameters
		if(param.isUndefinedOrEmpty(configAddress))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(name))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(options))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// todo check if type is included and limit nodes
		if(param.isUndefinedOrEmpty(options, "type"))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(configAddress, "string"))  return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(!param.check(name, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(param.check(options.type, "number", {min: 0, max: 2})){
			options.type = Object.keys(this.validFields)[options.type];
		}else if(param.check(options.type, "string")){
			if(!this.validFields.hasOwnProperty(options.type)){
				return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
			}	
		}else{
			return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		}
		
		if(options.type == Object.keys(this.validFields)[2]){
			if(param.isUndefinedOrEmpty(options.lower_limit) && param.isUndefinedOrEmpty(options.upper_limit)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
			if(!param.isUndefinedOrEmpty(options.lower_limit_compare) && options.lower_limit_compare != ">" && options.lower_limit_compare != ">=") return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
			if(!param.isUndefinedOrEmpty(options.upper_limit_compare) && options.upper_limit_compare != "<" && options.upper_limit_compare != "<=") return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}else{
			if(!param.isUndefinedOrEmpty(options.value_compare) && options.value_compare != "==" && options.value_compare != "!=") return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}
		
		var condition = Ua.findNode(configAddress + "." + name);
		if(condition.result){
			state.error = Ua.Status.BADNODEIDEXISTS;
			state.errorstring = Ua.Status(state.error).toString();
			return state;
		}
		
		var configNode = Ua.findNode(configAddress);
		if(!configNode.result){
			state.error = configNode.error;
			state.errorstring = Ua.Status(state.error).toString();
			return state;
		}

		var type = Ua.findNode(configNode.result.nodeId.address.replace("." + configNode.result.browseName.name, "")).result.dataType.toString();
		// todo add check if already existing conditions have same type		? NOAB

		// create basic condition depending on type
		let config = {
			nodeClass: Ua.NodeClass.OBJECT,
			typeDefinition: this.iConditionBase + options.type,
			reference: Ua.Reference.HASEVENTSOURCE,
			modellingRule: Ua.NodeId.MODELLINGRULE_MANDATORYSHARED
		};
		state = node.create(configAddress, name, config);

		if(state.error === 0){
			condition = Ua.findNode(configAddress + "." + name);
			// add category as reference
			if(param.isUndefinedOrEmpty(options, "category")){
				conditionConfig.category = this.iCategoryBase + this.basicValidFields.category;
			} else {
				if(Ua.findNode(this.iCategoryBase + options.category).result){
					conditionConfig.category = this.iCategoryBase + options.category;
				} else {
					conditionConfig.category = this.iCategoryBase + this.basicValidFields.category;
				}
			}
		
			condition.result.addReference(Ua.Reference.HASCOMPONENT, conditionConfig.category);
			delete conditionConfig.category;
			
			// set inactive_message to active_message in case is not defined
			if(options.active_message !== "" && options.inactive_message === ""){
				options.inactive_message = options.active_message;
			}		
		
			// set all other available options
			for(var i in this.validFields) {	
				if(i == options.type){
					conditionConfig = this.validFields[i];
					if(!("lower_limit" in options) || options.lower_limit == ""){
						delete conditionConfig.lower_limit;
					}
					if(!("upper_limit" in options) || options.upper_limit == ""){
						delete conditionConfig.upper_limit;
					}

					for(var j in conditionConfig){
						if(j != "category" && j != "type" ){					
							var condNode = Ua.findNode(configAddress + "." + name+ "." +j);
							var value = (j in options) ? options[j] : conditionConfig[j];
							if(j.includes("compare")) {
								value = value;
							}
							if(j === "on_delay" || j === "off_delay"){
								value = value * 1000;
							}
							if(j === "value" || j === "lower_limit" || j === "upper_limit"){
								condNode.result.value = {type: Ua.DataType[type.toUpperCase()], value: value};
							} else {
								condNode.result.value = value;
							}
						}
					}
				}
			}
		
			state.error = condition.error;
			state.errorstring = condition.errorstring;
		}
		else{
			return state;
		}
		return state;
	}
	
	
	
	/**
	 * Method to delete an alarm condition depending on address
	 * 
	 * @param {string} address 
	 */
	delete(address){
		//todo check if address is string or UaNode object
		//todo check if type is alarmcondition type
		const state = {};
		var condition = Ua.findNode(address);
		
		if(condition.result){
			if(condition.result.typeDefinition.value.indexOf("ObjectTypes.ATVISE.AlarmConditionControl")>= 0){
				return node.delete(address);
			} else {
				state.error = Ua.Status.BADTYPEMISMATCH;
				state.errorstring = Ua.Status(Ua.Status.BADTYPEMISMATCH).toString();
			}
			
		} else {
			state.error = condition.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		
		return state;
	}
	
	/**
	 * Method to return configuration of an alarm condition depending on conditionID
	 * 
	 * @param {string} conditionID 
	 * 
	 * @returns {discreteAlarm|limitAlarm}
	 */
	config(conditionID){
		var config = {};
		const state = {};
		var condition = Ua.findNode(conditionID);
		if(condition.result){
			config.type = condition.result.typeDefinition.value.replace("ns=1;s=ObjectTypes.ATVISE.AlarmConditionControl.","");
			var browseResult = condition.result.browse();

			for(var i = 0; i < browseResult.result.length; i++){
				if(browseResult.result[i].node.typeDefinition.value === "ns=1;s=ObjectTypes.ATVISE.AlarmConditionCategory"){
					config.category = browseResult.result[i].node.browseName.name;
				} else if(browseResult.result[i].node.browseName.name === "active_message" || browseResult.result[i].node.browseName.name === "inactive_message") {
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value[""];
				} else if(browseResult.result[i].node.browseName.name === "on_delay" || browseResult.result[i].node.browseName.name === "off_delay") {
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value / 1000;
				} else {
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value;
				}
			}
			
			return config;
		} else {
			state.error = condition.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state;
	}
	
	/**
	 * Method to update an alarm condition depending on conditionID and options
	 * 
	 * @param {string} conditionID 
	 * @param {discreteAlarm|limitAlarm} options 
	 */
	update(conditionID, options){
		const state = {};
		var condition = Ua.findNode(conditionID);
		if(condition.result){
			for(var i in options){
				if(i === "on_delay" || i === "off_delay"){
					options[i] = options[i] * 1000;
				} else if(i === "active_message"){
					if(!("inactive_message" in options))
						options.inactive_message = options[i];
				} else if(i === "value"){
					var configAddress = conditionID.substr(0, conditionID.lastIndexOf("."))
	
					var configNode = Ua.findNode(configAddress);
					if(!configNode.result){
						state.error = configNode.error;
						state.errorstring = Ua.Status(state.error).toString();
						return state;
					}
			
					var type = Ua.findNode(configNode.result.nodeId.address.replace("." + configNode.result.browseName.name, "")).result.dataType.toString();
					
					options[i] = {type: Ua.DataType[type.toUpperCase()], value: options[i]}
				} else if(i === "category"){
					var category = condition.result.browse({
						typeDefinition: "ObjectTypes.ATVISE.AlarmConditionCategory"
					});
					
					if(category.result.length > 0){
						condition.result.deleteReference(Ua.Reference.HASCOMPONENT, category.result[0].node.nodeId.address);
					}
					if(Ua.findNode("AGENT.ALARMING.Categories." + options.category).result){
						condition.result.addReference(Ua.Reference.HASCOMPONENT, "AGENT.ALARMING.Categories." + options.category);
					} else {
						condition.result.addReference(Ua.Reference.HASCOMPONENT, "AGENT.ALARMING.Categories.Error");
					}
	
					continue;
				}
				if(i != "type"){
					this._setAttribute(conditionID + "." + i, options[i]);
				}
			}
			state.error = condition.error;
			state.errorstring = condition.errorstring;
		} else {
			state.error = condition.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state;
	}
	
	/**
	 * Method to get default configuration for an alarm condition depending on type.
	 * 
	 * @param {*} type 
	 * 
	 * @returns {discreteAlarm|limitAlarm}
	 */
	template(type){
		
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(type))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(param.check(type, "number", {min: 0, max: 2})){
			type = Object.keys(this.validFields)[type];
		}else if(param.check(type, "string")){
			if(!this.validFields.hasOwnProperty(type)){
				return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
			}	
		}else{
			return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		}
	
		var config = {};
	
		var baseConditionType = Ua.findNode("ObjectTypes.ATVISE.AlarmConditionControl");
		var browseResultBase = baseConditionType.result.browse({
			nodeClass: Ua.NodeClass.VARIABLE
		});
		
		var conditionType = Ua.findNode(this.iConditionBase+""+type);
		
		
		var browseResultType = conditionType.result.browse({
			nodeClass: Ua.NodeClass.VARIABLE
		});
		
		//browseResultBase.result.forEach(el=>console.log(el.node.browseName.name));
		
		for(var i = 0; i < browseResultBase.result.length; i++){
			switch(browseResultBase.result[i].node.browseName.name){
				case "active_message":
				case "inactive_message":
					config[browseResultBase.result[i].node.browseName.name] = "";
					break;
				default:
					config[browseResultBase.result[i].node.browseName.name] = browseResultBase.result[i].node.value;
					break;
			}
		}
	
		for(var i = 0; i < browseResultType.result.length; i++){
			config[browseResultType.result[i].node.browseName.name] = browseResultType.result[i].node.value === null ? "" : browseResultType.result[i].node.value;
		}
	
		if(type === 0 || type === 1 || type === 2 || (!param.isUndefinedOrEmpty(type) && type in this.validFields))
			config.type = type;
			
		config.category = "Error";
		
		return config;
	}
	
	_setAttribute(address, value){
		var item = Ua.findNode(address);
		switch(item.result.dataType.toString()){
			case "BaseDataType":
				//todo implementieren von alarmconfigs die nicht unterhalb des eigentlichen elements liegen
	
				item.result.value = value;
				break;
			case "LocalizedText":
				//console.log("L ", item.browsename);
				if(typeof value === "string"){
					item.result.value = {locale: "en", text: value};
				} else {
					// todo error handling
				}
			case "String":
				//console.log("S ", item.browsename);
				if(typeof value === "string"){
					item.result.value = value;
				} else {
					// todo error handling
				}
				break;
			case "Double":
				//console.log("D ", item.browsename);
				if(typeof value === "number"){
					item.result.value = value;
				} else {
					// todo error handling
				}
				break;
			case "Boolean":
				value = JSON.parse(value);
				if(value === true || value === false){
					item.result.value = value;
				} else {
					// todo error handling
				}
				break;
			default:
				console.log("V ", item.browseName.name + " " + item.result.dataType.toString());
				break;
		}
	}

}

return new condition();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm.configuration" BrowseName="1:configuration" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">configuration</DisplayName>
		<Description Locale="en">configuration</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
const condition = call("Library.alarm.condition");

/**
* @module configuration
* @classdesc The configuration class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
* to interact with configuration.
* 
* @exports configuration
* @constructor
* @example
* const configuration = call("Library.alarm.configuration");
*/

class configuration {
	/**
	 * 
	 * @param {string} parent 
	 * @param {string} name 
	 * @param {*} options 
	 */
	static create(parent, name, options){
		let state = {};
		let parentNode = Ua.findNode(parent);
		if(parentNode.result){
			let configuration = Ua.findNode(parent + "." + name);
			if(!configuration.result){
				let config = {
					nodeClass: Ua.NodeClass.OBJECT,
					typeDefinition: "ObjectTypes.ATVISE.AlarmConfiguration"
				};
				state = node.create(parent, name, config);
				if(state.error === 0){
					configuration = Ua.findNode(parent + "." + name);
					for(let i in options){
						condition.create(configuration.result.nodeId.address, i, options[i]);
					}
				} else {
					return state;
				}
			} else {
				state.error = Ua.Status.BADNODEIDEXISTS;
				state.errorstring = Ua.Status(state.error).toString();
			}
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();		
		}
		return state;
	}
	
	/**
	 * 
	 * @param {string} configurationID 
	 * @param {*} options 
	 * @param {boolean} overwrite 
	 */
	static update(configurationID, options, overwrite){
		const state = {};
		let configuration = Ua.findNode(configurationID);
		if(configuration.result){
			let allConditions = Object.keys(options);			
			let browseConditions = configuration.result.browse();
			for(let i = 0; i < browseConditions.result.length; i++){	
				if(browseConditions.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.AlarmConditionControl") >= 0 && allConditions.includes(browseConditions.result[i].node.browseName.name)===false){
					allConditions = allConditions.concat(browseConditions.result[i].node.browseName.name);
				}
			}

			for(let i = 0; i < allConditions.length; i++){			
				let conditionNode = Ua.findNode(configurationID + "." + allConditions[i]);
				if(conditionNode.result){
					if(overwrite === true && !(allConditions[i] in options)){
						return condition.delete(conditionNode.result.nodeId.address);
					} else if(allConditions[i] in options){
						return condition.update(conditionNode.result.nodeId.address, options[allConditions[i]]);
					}
				
				} else {	
					return condition.create(configurationID, allConditions[i], options[allConditions[i]]);
				}
			}
		} else {
			state.error = Ua.Status.BADNODEIDUNKNOWN;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state
	}
	
	/**
	 * 
	 * @param {string} configurationID 
	 */
	static delete(configurationID){
		// todo check if node exists & typedefintion is configuration
		// todo implementieren, dass nodeid als auch string verwendet weden kann

		let configuration = Ua.findNode(configurationID);    
		let state = {};
	
		if(configuration.result){
				return node.delete(configurationID);
		} else {
			state.error = configuration.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state
	}

	/**
	 * 
	 * @param {number} type 
	 */
	static template(type){
		return condition.template(type);
	}

	/**
	 * 
	 * @param {string} configurationID 
	 */
	static config(configurationID){
		let config = {};
		const state = {};
		let configuration = Ua.findNode(configurationID);
		if(!configuration.result){
			state.error = configuration.error;
			state.errorstring = Ua.Status(state.error).toString();
			return state;
		}
		let browseResult = configuration.result.browse();		
		for(let i = 0; i < browseResult.result.length; i++){			
			if(browseResult.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.AlarmConditionControl") >= 0){
				config[browseResult.result[i].node.browseName.name] = condition.config(browseResult.result[i].node.nodeId.address);
			}
		}
	
		return config;
	}
}

return configuration;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper" BrowseName="1:helper">
		<DisplayName Locale="en">helper</DisplayName>
		<Description Locale="en">helper</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper.parameterCheck" BrowseName="1:parameterCheck" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">parameterCheck</DisplayName>
		<Description Locale="en">parameterCheck</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[class parameterCheck{

	check(value, type, options) {
		let isValid = false;
		if (this.isUndefinedOrEmpty(value)) {
			return isValid;
		}
	
		switch (type) {
			case 'number':
				if (!isNaN(value)) {
					let curValue = Number(value);
	
					let minValid;
					let maxValid;
	
					if ((!this.isUndefinedOrEmpty(options, 'max') && this.check(options.max, 'number') && curValue <= options.max) || this.isUndefinedOrEmpty(options, 'max')) {
						maxValid = true;
					} else if (!this.isUndefinedOrEmpty(options, 'max') && curValue > options.max) {
						maxValid = false;
					}
	
					if ((!this.isUndefinedOrEmpty(options, 'min') && this.check(options.min, 'number') && curValue >= options.min) || this.isUndefinedOrEmpty(options, 'min')) {
						minValid = true;
					} else if (!this.isUndefinedOrEmpty(options, 'min') && curValue < options.min) {
						minValid = false;
					}
	
					if (minValid !== undefined && maxValid !== undefined) {
						if (minValid && maxValid) {
							isValid = true;
						}
	
						switch (minValid && maxValid) {
							case true:
								isValid = true;
								break;
							case false:
								isValid = false;
								break;
						}
					}
	
					if (this.isUndefinedOrEmpty(options)) {
						isValid = true;
					}
				}
				break;
			case 'string':
				let curValue = String(value);
				isValid = true;
				break;
			case 'object':
				isValid = !this.isUndefinedOrEmpty(value);
				break;
			default:
				console.log('TODO doesn´t used type ' + typeof type);
		}
	
		return isValid;
	};
	
	isUndefinedOrEmpty(elem, attribute) {
		let isUndefined = false;
	
		if (elem === undefined || elem === '') {
			isUndefined = true;
		} else if (typeof elem === 'object') {
			if (Object.keys(elem).length === 0) {
				isUndefined = true;
			} else if (!(typeof attribute === 'undefined')) {
				if (!(attribute in elem)) {
					isUndefined = true;
				}
			}
		}
	
		return isUndefined;
	};
}
return new parameterCheck();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper.returnObject" BrowseName="1:returnObject" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">returnObject</DisplayName>
		<Description Locale="en">returnObject</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[class returnObject{
	constructor(){
		this.i_errormsg = {
			/* GENERAL */
			1: "Parameter [placeholder] invalid",
			2: "Mandatory parameter [placeholder] missing",
	
	
			/* HISTORY */
			1000: "Archive [placeholder] already exists",
			1001: "Couldn´t create archive [placeholder]",
			1002: "Couldn´t set option(s) [placeholder]",
			1003: "Couldn´t delete archive [placeholder]",
			1004: "Archive [placeholder] doesn´t exists",
			1005: "Typedefinition doesn´t match an archive",
	
			1100: "Template [placeholder] already exists",
			1101: "Template [placeholder] doesn´t exists",
			1003: "Couldn´t delete template [placeholder]",
	
	
			1200: "Couldn´t create Aggregate [placeholder]",
			1201: "Couldn´t update Aggregate [placeholder]"
		}
    }
    
	good(){
		return {status: 0};
	}
	
	
	bad(error, name/*, addInfo*/){
		let placeholder = "";
		if(typeof name === "object"){
			placeholder = name.map(function(e){
				return "'" + e + "'";
			}).join(", ");
		} else {
			placeholder = "'" + name + "'";
		}
		return {status: error, errorMsg: this.i_errormsg[error].replace("[placeholder]", placeholder)/* + (addInfo ? " - " + addInfo : "")*/}
	}
}


return new returnObject();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror" BrowseName="1:mirror">
		<DisplayName Locale="en">mirror</DisplayName>
		<Description Locale="en">mirror</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror.datasource" BrowseName="1:datasource" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">datasource</DisplayName>
		<Description Locale="en">datasource</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module datasource
 * @classdesc The datasource class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with datasources.
 * 
 * @exports datasource
 * @constructor
 * @example
 * let datasource = call("Library.mirror.datasource");
 */

class datasource extends node{

/**
 * Method to create datasource depending on name without path, options like url and typeDefinition and activated as boolean..
 * 
 * @param {string} name Name of datasource
 * @param {Object} options like url, typeDefinition and disable
 *
 * @example
 * let datasource = call("Library.mirror.datasource");
 * datasource.create("MyDataSource", {
 * 		url:  "opc.tcp://localhost:4841",
 * 		typeDefinition:	"OpcUa",
 * 		disable: false,
 * 		publishing_interval: 1000,
 * 		sampling_interval: 1000,
 * 		user: "root",
 * 		password: "myPassword",
 * 		queue_size: -1,
 * 		connection_timeout: 10000
 * });
 */
	static create(name, options){
		let state = {};
		let objectTypeNode = Ua.findNode("ObjectTypes.ATVISE.Datasource." + options.typeDefinition);
		let parent = "AGENT.DATASOURCES";
		let datasourceAddress = parent + "." + name;
		
		if(objectTypeNode.result){
			options.typeDefinition = objectTypeNode.result.nodeId.address;
			options.nodeClass = Ua.NodeClass.OBJECT;
			let result = node.create(parent, name, options);
			
			// Creating Redundancy to Datasource			
			let redu = node.create(datasourceAddress, "Redundancy",{
				nodeClass: Ua.NodeClass.OBJECT,
				typeDefinition: "ObjectTypes.ATVISE.DatasourceRedundancy"	
			});

			// Activate datasource
			let disableNode = Ua.findNode(datasourceAddress + ".disable");
			if(disableNode.error === 0){
				disableNode.result.value = "disable" in options ? options.disable : false;
			} else {
				return {
					error: disableNode.error,
					errorstring: Ua.Status(disableNode.error).toString()
				}
			}
			
			// Assigning the URL
			let urlNode = Ua.findNode(datasourceAddress + ".url");
			if(urlNode.error === 0){
				urlNode.result.value = options.url;
			} else {
				return {
					error: urlNode.error,
					errorstring: Ua.Status(urlNode.error).toString()
				}
			}
			
			// Sampling & Publish
			let publishNode = Ua.findNode(datasourceAddress + ".publishing_interval");
			
			if(publishNode.error === 0 && "publishing_interval" in options){
				publishNode.result.value = options.publishing_interval;
			} else if(publishNode.error !== 0) {
				return {
					error: publishNode.error,
					errorstring: Ua.Status(publishNode.error).toString()
				}
			}
			let samplingNode = Ua.findNode(datasourceAddress + ".sampling_interval");
			if(samplingNode.error === 0 && "sampling_interval" in options){
				samplingNode.result.value = options.sampling_interval;
			} else if(samplingNode.error !== 0) {
				return {
					error: samplingNode.error,
					errorstring: Ua.Status(samplingNode.error).toString()
				}
			}
			
			// Queue
			let queueNode = Ua.findNode(datasourceAddress + ".queue_size");
			if(queueNode.error === 0 && "queue_size" in options){
				queueNode.result.value = options.queue_size;
			} else if(queueNode.error !== 0) {
				return {
					error: queueNode.error,
					errorstring: Ua.Status(queueNode.error).toString()
				}
			}
			
			// ConnectionTimeout
			let timeoutNode = Ua.findNode(datasourceAddress + ".connection_timeout");
			if(timeoutNode.error === 0 && "connection_timeout" in options){
				timeoutNode.result.value = options.connection_timeout;
			} else if(timeoutNode.error !== 0) {
				return {
					error: timeoutNode.error,
					errorstring: Ua.Status(timeoutNode.error).toString()
				}
			}
			
			// Username
			let userNode = Ua.findNode(datasourceAddress + ".user");
			if(userNode.error === 0 && "user" in options){
				userNode.result.value = options.user;
			} else if(userNode.error !== 0) {
				return {
					error: userNode.error,
					errorstring: Ua.Status(userNode.error).toString()
				}
			}

			// Password
			let pwdNode = Ua.findNode(datasourceAddress + ".password");
			if(pwdNode.error === 0 && "password" in options){
				pwdNode.result.value = options.password;
			} else if(pwdNode.error !== 0) {
				return {
					error: pwdNode.error,
					errorstring: Ua.Status(pwdNode.error).toString()
				}
			}		
			
			state.error = Ua.Status.GOOD;
			state.errorstring = undefined;
		} else {
			state.error = Ua.Status.BADNODEIDUNKNOWN;
			state.errorstring = Ua.Status(state.error).toString();
		}
		
		return state;
	}

/**
 * Method to read current datasource settings
 * 
 * @param {string} name Name of datasource
 *
 * @example
 * let datasource = call("Library.mirror.datasource");
 * let config = datasource.config("MyDataSource");
 */		
	static config(name){
		let exceptProperties = [
			"namespace_array",
			"password",
			"revised_alarm_queue_size",
			"revised_lifetime_count",
			"revised_publishing_interval",
			"revised_queue_size",
			"revised_sampling_interval",
			"data_change_trigger",
			"connection_status",
			"copy_servertime_to_sourcetime",
			"dead_band",
			"history_sync_interval",
			"lifetime_count",
			"connected",
			"string_encoding",
			"variable_type_prefix",
			"variable_type_trim",
			"object_type_trim",
			"object_type_prefix",
			"user",
			"private_key",
			"pki_path",
			"client_certificate",
			"pass_through_event_history",
			"mode",
			"max_notifications_per_publish",
			"policy"
		];
	
		// check mandatory parameter
		//if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		let config = {};
		let datasource = Ua.findNode("AGENT.DATASOURCES." + name);
		if (datasource.result){
			let browseResult = datasource.result.browse({
				nodeClass: Ua.NodeClass.VARIABLE
			});

			for(let i = 0; i < browseResult.result.length; i++){
				if(exceptProperties.indexOf(browseResult.result[i].node.browseName.name) < 0)
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value;
			}
			
			return config;
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
	}
	
/**
 * Method to delete a dataSource depending on an name string.
 * 
 * @param {string} name of dataSource
 * 
 * @example
 * let datasource = call("Library.mirror.datasource");
 * datasource.delete("MyDataSource");
 */
	static delete(name){
		let address = "AGENT.DATASOURCES." + name;
		let dataSource = Ua.findNode(address);    
		const state = {};
		if(dataSource.result){
			return node.delete(dataSource.result.nodeId.address);
		} else {
			state.error = dataSource.error;
			state.errorstring = Ua.Status(dataSource.error).toString();
		}

		return state;
	}
}

return datasource;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror.mirror" BrowseName="1:mirror" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">mirror</DisplayName>
		<Description Locale="en">mirror</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module mirror
 * @classdesc The mirror class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with mirrors.
 * 
 * @exports mirror
 * @constructor
 * @example
 * const mirror = call("Library.mirror.mirror");
 */

class mirror extends node {

/**
 * Method to create mirror ..
 * 
 * @param {string} parent 
 * @param {string} name 
 * @param {object} [options] 
 *
 * @example
 * const mirror = call("Library.mirror.mirror");
 * 
 * // Create RelMirrorBase
 * mirror.create("AGENT.OBJECTS.NewCustomFolder.Engine", "RelMirrorBase", {
 * 		datasource: "PLC1",
 * 		address: "ns=2;s=Engines.Engine001"
 * 	});	
 * 
 * // Create RelMirrorBase	
 * 	mirror.create("AGENT.OBJECTS.NewCustomFolder.SinInt32", "MirrorInputOutput", {
 * 		datasource: "PLC1",
 * 		namespace: 2,
 *		address: "Simulated.SinInt32"
 * 	});	
 */
	static create(parent, name, addr){
		let parentNode = Ua.findNode(parent);
		const state = {};
		if(parentNode.result){
			let address = addr.address;
			let source = addr.datasource + "/" + address;
			let options = {
				parent:	parent,
				dataType: Ua.DataType.STRING,
				nodeClass: Ua.NodeClass.VARIABLE,
				value: source
			}
			switch(name){
				case "MirrorInput": 
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Input";
					break;
				case "MirrorOutput": 
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Output";
					break;
				case "MirrorInputOutput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.InputOutput";
					break;
				case "MirrorDisable":
					options.dataType = Ua.DataType.BOOLEAN;
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Disable";
					break;
				case "MirrorOnDemand":
					options.dataType = Ua.DataType.BOOLEAN;
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.OnDemand";
					break;
				case "RelMirrorBase":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Base";
					break;
				case "RelMirrorInput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Input";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorOutput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Output";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorInputOutput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.InputOutput";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorPathFragment":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.PathFragment";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorDisable":
					options.dataType = Ua.DataType.BOOLEAN;
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Disable";
					break;
				default:
					break;
			}
		
			return node.create(parent, name, options);
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();
		}
		return state;
	}


/**
 * Method to delete a dataSource depending on an name string.
 * 
 * @param {string} address
 * 
 * @example
 * const mirror = call("Library.mirror.mirror");
 * mirror.delete("AGENT.OBJECTS.NewCustomFolder.SinInt32.MirrorInputOutput");
 */
	static delete(address){
		let mirror = Ua.findNode(address);    
		const state = {};
		if(mirror.result){
			return node.delete(address);
		}else{
			state.error = mirror.error;
			state.errorstring = Ua.Status(mirror.error).toString();
		}
		return state;
	}
}


return mirror;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm" ExportedBrowseName="1:alarm" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm" BrowseName="1:alarm">
		<DisplayName Locale="en">alarm</DisplayName>
		<Description Locale="en">alarm</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm.condition" BrowseName="1:condition" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">condition</DisplayName>
		<Description Locale="en">condition</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
const status = call("Library.helper.returnObject");
const param = call("Library.helper.parameterCheck");

/**
* @module condition
* @classdesc The condition class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
* to interact with condition.
* 
* @exports condition
* @constructor
* @example
* const condition = call("Library.alarm.condition");
*/

/**
 * @typedef {object} discreteAlarm
 * @property {number} type
 * @property {string} [active_message]
 * @property {string} [inactive_message]
 * @property {string} [explicit_acknowledgement=false]
 * @property {string} [category=Error]
 * @property {string} [max_time_shelved=0]
 * @property {string} [modify_script=]
 * @property {string} [off_delay=0]
 * @property {string} [on_delay=0]
 * @property {string} [prevention_script]
 * @property {string} [value=false]
 * @property {string} [value_compare===]
 */

 /**
 * @typedef {object} limitAlarm
 * @property {number} type
 * @property {string} [active_message]
 * @property {string} [inactive_message]
 * @property {string} [explicit_acknowledgement=false]
 * @property {string} [category=Error]
 * @property {string} [max_time_shelved=0]
 * @property {string} [modify_script]
 * @property {string} [off_delay=0]
 * @property {string} [on_delay=0]
 * @property {string} [prevention_script]
 * @property {string} [lower_limit=0]
 * @property {string} [lower_limit_compare=>]
 * @property {string} [upper_limit=10]
 * @property {string} [upper_limit_compare=<]
 */

class condition{

	constructor(){
		//todo add all available parameters in array and go through to check -> would me more generic solution
		// constants
		this.iCategoryBase = "AGENT.ALARMING.Categories.";
		this.iConditionBase = "ObjectTypes.ATVISE.AlarmConditionControl.";
	
		this.validFields = {
			"Discrete": {},
			"Discrete.Retrigger": {},
			"Limit": {}
		}
		
		/*
		[
			"ObjectTypes.ATVISE.AlarmConditionControl.Discrete",
			"ObjectTypes.ATVISE.AlarmConditionControl.Discrete.Retrigger",
			"ObjectTypes.ATVISE.AlarmConditionControl.Limit"
		];*/
	
		this.basicValidFields = {
			type: "Discrete",
			active_message: "",
			inactive_message: "",
			explicit_acknowledgement: false,
			category: "Error",
			max_time_shelved: 0,
			modify_script: 0,
			off_delay: 0,
			on_delay: 0,
			prevention_script: 0
		}
	
		this.discreteValidFields = {
			value: false,
			value_compare: "==",
		}
	
		this.limitValidFields = {
			lower_limit: 0,
			lower_limit_compare: ">",
			upper_limit: 10,
			upper_limit_compare: "<"
		}
	
		// set validFields on init
		for(var i in this.validFields){
			switch(i){
				case "Discrete":
					this.validFields[i] = Object.assign({}, this.basicValidFields, this.discreteValidFields);
					break;
				case "Discrete.Retrigger":
					this.validFields[i] = Object.assign({}, this.basicValidFields, this.discreteValidFields);
					break;
				case "Limit":
					this.validFields[i] = Object.assign(this.basicValidFields, this.limitValidFields);
					break;
			}
		}
		
	/*
	
	
		this.iDefaults = {
			type: 0,
			category: "AGENT.ALARMING.Categories.Error",
			value: false,
			off_delay: 0,
			on_delay: 0,
			value_compare: "==",
	
			lower_limit: 0,
			lower_limit_compare: ">",
			upper_limit: 10,
			upper_limit_compare: "<"
		}
	
		this.validFields = {
			value: false,
			value_compare: "==",
	
			lower_limit: 0,
			lower_limit_compare: ">",
			upper_limit: 10,
			upper_limit_compare: "<"
		};
	*/
	}


	/**
	 * Method to create an alarm condition depending on address string, name and options
	 * 
	 * @param {string} configAddress 
	 * @param {string} name 
	 * @param {discreteAlarm|limitAlarm} [options] 
	 */

	create(configAddress, name, options){
		let state = {};
		var conditionConfig = {};
	
		// check mandatory parameters
		if(param.isUndefinedOrEmpty(configAddress))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(name))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(options))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// todo check if type is included and limit nodes
		if(param.isUndefinedOrEmpty(options, "type"))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(configAddress, "string"))  return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(!param.check(name, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(param.check(options.type, "number", {min: 0, max: 2})){
			options.type = Object.keys(this.validFields)[options.type];
		}else if(param.check(options.type, "string")){
			if(!this.validFields.hasOwnProperty(options.type)){
				return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
			}	
		}else{
			return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		}
		
		if(options.type == Object.keys(this.validFields)[2]){
			if(param.isUndefinedOrEmpty(options.lower_limit) && param.isUndefinedOrEmpty(options.upper_limit)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
			if(!param.isUndefinedOrEmpty(options.lower_limit_compare) && options.lower_limit_compare != ">" && options.lower_limit_compare != ">=") return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
			if(!param.isUndefinedOrEmpty(options.upper_limit_compare) && options.upper_limit_compare != "<" && options.upper_limit_compare != "<=") return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}else{
			if(!param.isUndefinedOrEmpty(options.value_compare) && options.value_compare != "==" && options.value_compare != "!=") return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}
		
		var condition = Ua.findNode(configAddress + "." + name);
		if(condition.result){
			state.error = Ua.Status.BADNODEIDEXISTS;
			state.errorstring = Ua.Status(state.error).toString();
			return state;
		}
		
		var configNode = Ua.findNode(configAddress);
		if(!configNode.result){
			state.error = configNode.error;
			state.errorstring = Ua.Status(state.error).toString();
			return state;
		}

		var type = Ua.findNode(configNode.result.nodeId.address.replace("." + configNode.result.browseName.name, "")).result.dataType.toString();
		// todo add check if already existing conditions have same type		? NOAB

		// create basic condition depending on type
		let config = {
			nodeClass: Ua.NodeClass.OBJECT,
			typeDefinition: this.iConditionBase + options.type,
			reference: Ua.Reference.HASEVENTSOURCE,
			modellingRule: Ua.NodeId.MODELLINGRULE_MANDATORYSHARED
		};
		state = node.create(configAddress, name, config);

		if(state.error === 0){
			condition = Ua.findNode(configAddress + "." + name);
			// add category as reference
			if(param.isUndefinedOrEmpty(options, "category")){
				conditionConfig.category = this.iCategoryBase + this.basicValidFields.category;
			} else {
				if(Ua.findNode(this.iCategoryBase + options.category).result){
					conditionConfig.category = this.iCategoryBase + options.category;
				} else {
					conditionConfig.category = this.iCategoryBase + this.basicValidFields.category;
				}
			}
		
			condition.result.addReference(Ua.Reference.HASCOMPONENT, conditionConfig.category);
			delete conditionConfig.category;
			
			// set inactive_message to active_message in case is not defined
			if(options.active_message !== "" && options.inactive_message === ""){
				options.inactive_message = options.active_message;
			}		
		
			// set all other available options
			for(var i in this.validFields) {	
				if(i == options.type){
					conditionConfig = this.validFields[i];
					if(!("lower_limit" in options) || options.lower_limit == ""){
						delete conditionConfig.lower_limit;
					}
					if(!("upper_limit" in options) || options.upper_limit == ""){
						delete conditionConfig.upper_limit;
					}

					for(var j in conditionConfig){
						if(j != "category" && j != "type" ){					
							var condNode = Ua.findNode(configAddress + "." + name+ "." +j);
							var value = (j in options) ? options[j] : conditionConfig[j];
							if(j.includes("compare")) {
								value = value;
							}
							if(j === "on_delay" || j === "off_delay"){
								value = value * 1000;
							}
							if(j === "value" || j === "lower_limit" || j === "upper_limit"){
								condNode.result.value = {type: Ua.DataType[type.toUpperCase()], value: value};
							} else {
								condNode.result.value = value;
							}
						}
					}
				}
			}
		
			state.error = condition.error;
			state.errorstring = condition.errorstring;
		}
		else{
			return state;
		}
		return state;
	}
	
	
	
	/**
	 * Method to delete an alarm condition depending on address
	 * 
	 * @param {string} address 
	 */
	delete(address){
		//todo check if address is string or UaNode object
		//todo check if type is alarmcondition type
		const state = {};
		var condition = Ua.findNode(address);
		
		if(condition.result){
			if(condition.result.typeDefinition.value.indexOf("ObjectTypes.ATVISE.AlarmConditionControl")>= 0){
				return node.delete(address);
			} else {
				state.error = Ua.Status.BADTYPEMISMATCH;
				state.errorstring = Ua.Status(Ua.Status.BADTYPEMISMATCH).toString();
			}
			
		} else {
			state.error = condition.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		
		return state;
	}
	
	/**
	 * Method to return configuration of an alarm condition depending on conditionID
	 * 
	 * @param {string} conditionID 
	 * 
	 * @returns {discreteAlarm|limitAlarm}
	 */
	config(conditionID){
		var config = {};
		const state = {};
		var condition = Ua.findNode(conditionID);
		if(condition.result){
			config.type = condition.result.typeDefinition.value.replace("ns=1;s=ObjectTypes.ATVISE.AlarmConditionControl.","");
			var browseResult = condition.result.browse();

			for(var i = 0; i < browseResult.result.length; i++){
				if(browseResult.result[i].node.typeDefinition.value === "ns=1;s=ObjectTypes.ATVISE.AlarmConditionCategory"){
					config.category = browseResult.result[i].node.browseName.name;
				} else if(browseResult.result[i].node.browseName.name === "active_message" || browseResult.result[i].node.browseName.name === "inactive_message") {
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value[""];
				} else if(browseResult.result[i].node.browseName.name === "on_delay" || browseResult.result[i].node.browseName.name === "off_delay") {
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value / 1000;
				} else {
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value;
				}
			}
			
			return config;
		} else {
			state.error = condition.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state;
	}
	
	/**
	 * Method to update an alarm condition depending on conditionID and options
	 * 
	 * @param {string} conditionID 
	 * @param {discreteAlarm|limitAlarm} options 
	 */
	update(conditionID, options){
		const state = {};
		var condition = Ua.findNode(conditionID);
		if(condition.result){
			for(var i in options){
				if(i === "on_delay" || i === "off_delay"){
					options[i] = options[i] * 1000;
				} else if(i === "active_message"){
					if(!("inactive_message" in options))
						options.inactive_message = options[i];
				} else if(i === "value"){
					var configAddress = conditionID.substr(0, conditionID.lastIndexOf("."))
	
					var configNode = Ua.findNode(configAddress);
					if(!configNode.result){
						state.error = configNode.error;
						state.errorstring = Ua.Status(state.error).toString();
						return state;
					}
			
					var type = Ua.findNode(configNode.result.nodeId.address.replace("." + configNode.result.browseName.name, "")).result.dataType.toString();
					
					options[i] = {type: Ua.DataType[type.toUpperCase()], value: options[i]}
				} else if(i === "category"){
					var category = condition.result.browse({
						typeDefinition: "ObjectTypes.ATVISE.AlarmConditionCategory"
					});
					
					if(category.result.length > 0){
						condition.result.deleteReference(Ua.Reference.HASCOMPONENT, category.result[0].node.nodeId.address);
					}
					if(Ua.findNode("AGENT.ALARMING.Categories." + options.category).result){
						condition.result.addReference(Ua.Reference.HASCOMPONENT, "AGENT.ALARMING.Categories." + options.category);
					} else {
						condition.result.addReference(Ua.Reference.HASCOMPONENT, "AGENT.ALARMING.Categories.Error");
					}
	
					continue;
				}
				if(i != "type"){
					this._setAttribute(conditionID + "." + i, options[i]);
				}
			}
			state.error = condition.error;
			state.errorstring = condition.errorstring;
		} else {
			state.error = condition.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state;
	}
	
	/**
	 * Method to get default configuration for an alarm condition depending on type.
	 * 
	 * @param {*} type 
	 * 
	 * @returns {discreteAlarm|limitAlarm}
	 */
	template(type){
		
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(type))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(param.check(type, "number", {min: 0, max: 2})){
			type = Object.keys(this.validFields)[type];
		}else if(param.check(type, "string")){
			if(!this.validFields.hasOwnProperty(type)){
				return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
			}	
		}else{
			return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		}
	
		var config = {};
	
		var baseConditionType = Ua.findNode("ObjectTypes.ATVISE.AlarmConditionControl");
		var browseResultBase = baseConditionType.result.browse({
			nodeClass: Ua.NodeClass.VARIABLE
		});
		
		var conditionType = Ua.findNode(this.iConditionBase+""+type);
		
		
		var browseResultType = conditionType.result.browse({
			nodeClass: Ua.NodeClass.VARIABLE
		});
		
		//browseResultBase.result.forEach(el=>console.log(el.node.browseName.name));
		
		for(var i = 0; i < browseResultBase.result.length; i++){
			switch(browseResultBase.result[i].node.browseName.name){
				case "active_message":
				case "inactive_message":
					config[browseResultBase.result[i].node.browseName.name] = "";
					break;
				default:
					config[browseResultBase.result[i].node.browseName.name] = browseResultBase.result[i].node.value;
					break;
			}
		}
	
		for(var i = 0; i < browseResultType.result.length; i++){
			config[browseResultType.result[i].node.browseName.name] = browseResultType.result[i].node.value === null ? "" : browseResultType.result[i].node.value;
		}
	
		if(type === 0 || type === 1 || type === 2 || (!param.isUndefinedOrEmpty(type) && type in this.validFields))
			config.type = type;
			
		config.category = "Error";
		
		return config;
	}
	
	_setAttribute(address, value){
		var item = Ua.findNode(address);
		switch(item.result.dataType.toString()){
			case "BaseDataType":
				//todo implementieren von alarmconfigs die nicht unterhalb des eigentlichen elements liegen
	
				item.result.value = value;
				break;
			case "LocalizedText":
				//console.log("L ", item.browsename);
				if(typeof value === "string"){
					item.result.value = {locale: "en", text: value};
				} else {
					// todo error handling
				}
			case "String":
				//console.log("S ", item.browsename);
				if(typeof value === "string"){
					item.result.value = value;
				} else {
					// todo error handling
				}
				break;
			case "Double":
				//console.log("D ", item.browsename);
				if(typeof value === "number"){
					item.result.value = value;
				} else {
					// todo error handling
				}
				break;
			case "Boolean":
				value = JSON.parse(value);
				if(value === true || value === false){
					item.result.value = value;
				} else {
					// todo error handling
				}
				break;
			default:
				console.log("V ", item.browseName.name + " " + item.result.dataType.toString());
				break;
		}
	}

}

return new condition();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm.configuration" BrowseName="1:configuration" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">configuration</DisplayName>
		<Description Locale="en">configuration</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.alarm</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
const condition = call("Library.alarm.condition");

/**
* @module configuration
* @classdesc The configuration class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
* to interact with configuration.
* 
* @exports configuration
* @constructor
* @example
* const configuration = call("Library.alarm.configuration");
*/

class configuration {
	/**
	 * 
	 * @param {string} parent 
	 * @param {string} name 
	 * @param {*} options 
	 */
	static create(parent, name, options){
		let state = {};
		let parentNode = Ua.findNode(parent);
		if(parentNode.result){
			let configuration = Ua.findNode(parent + "." + name);
			if(!configuration.result){
				let config = {
					nodeClass: Ua.NodeClass.OBJECT,
					typeDefinition: "ObjectTypes.ATVISE.AlarmConfiguration"
				};
				state = node.create(parent, name, config);
				if(state.error === 0){
					configuration = Ua.findNode(parent + "." + name);
					for(let i in options){
						condition.create(configuration.result.nodeId.address, i, options[i]);
					}
				} else {
					return state;
				}
			} else {
				state.error = Ua.Status.BADNODEIDEXISTS;
				state.errorstring = Ua.Status(state.error).toString();
			}
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();		
		}
		return state;
	}
	
	/**
	 * 
	 * @param {string} configurationID 
	 * @param {*} options 
	 * @param {boolean} overwrite 
	 */
	static update(configurationID, options, overwrite){
		const state = {};
		let configuration = Ua.findNode(configurationID);
		if(configuration.result){
			let allConditions = Object.keys(options);			
			let browseConditions = configuration.result.browse();
			for(let i = 0; i < browseConditions.result.length; i++){	
				if(browseConditions.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.AlarmConditionControl") >= 0 && allConditions.includes(browseConditions.result[i].node.browseName.name)===false){
					allConditions = allConditions.concat(browseConditions.result[i].node.browseName.name);
				}
			}

			for(let i = 0; i < allConditions.length; i++){			
				let conditionNode = Ua.findNode(configurationID + "." + allConditions[i]);
				if(conditionNode.result){
					if(overwrite === true && !(allConditions[i] in options)){
						return condition.delete(conditionNode.result.nodeId.address);
					} else if(allConditions[i] in options){
						return condition.update(conditionNode.result.nodeId.address, options[allConditions[i]]);
					}
				
				} else {	
					return condition.create(configurationID, allConditions[i], options[allConditions[i]]);
				}
			}
		} else {
			state.error = Ua.Status.BADNODEIDUNKNOWN;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state
	}
	
	/**
	 * 
	 * @param {string} configurationID 
	 */
	static delete(configurationID){
		// todo check if node exists & typedefintion is configuration
		// todo implementieren, dass nodeid als auch string verwendet weden kann

		let configuration = Ua.findNode(configurationID);    
		let state = {};
	
		if(configuration.result){
				return node.delete(configurationID);
		} else {
			state.error = configuration.error;
			state.errorstring = Ua.Status(state.error).toString();
		}
		return state
	}

	/**
	 * 
	 * @param {number} type 
	 */
	static template(type){
		return condition.template(type);
	}

	/**
	 * 
	 * @param {string} configurationID 
	 */
	static config(configurationID){
		let config = {};
		const state = {};
		let configuration = Ua.findNode(configurationID);
		if(!configuration.result){
			state.error = configuration.error;
			state.errorstring = Ua.Status(state.error).toString();
			return state;
		}
		let browseResult = configuration.result.browse();		
		for(let i = 0; i < browseResult.result.length; i++){			
			if(browseResult.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.AlarmConditionControl") >= 0){
				config[browseResult.result[i].node.browseName.name] = condition.config(browseResult.result[i].node.nodeId.address);
			}
		}
	
		return config;
	}
}

return configuration;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper" ExportedBrowseName="1:helper" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper" BrowseName="1:helper">
		<DisplayName Locale="en">helper</DisplayName>
		<Description Locale="en">helper</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper.parameterCheck" BrowseName="1:parameterCheck" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">parameterCheck</DisplayName>
		<Description Locale="en">parameterCheck</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[class parameterCheck{

	check(value, type, options) {
		let isValid = false;
		if (this.isUndefinedOrEmpty(value)) {
			return isValid;
		}
	
		switch (type) {
			case 'number':
				if (!isNaN(value)) {
					let curValue = Number(value);
	
					let minValid;
					let maxValid;
	
					if ((!this.isUndefinedOrEmpty(options, 'max') && this.check(options.max, 'number') && curValue <= options.max) || this.isUndefinedOrEmpty(options, 'max')) {
						maxValid = true;
					} else if (!this.isUndefinedOrEmpty(options, 'max') && curValue > options.max) {
						maxValid = false;
					}
	
					if ((!this.isUndefinedOrEmpty(options, 'min') && this.check(options.min, 'number') && curValue >= options.min) || this.isUndefinedOrEmpty(options, 'min')) {
						minValid = true;
					} else if (!this.isUndefinedOrEmpty(options, 'min') && curValue < options.min) {
						minValid = false;
					}
	
					if (minValid !== undefined && maxValid !== undefined) {
						if (minValid && maxValid) {
							isValid = true;
						}
	
						switch (minValid && maxValid) {
							case true:
								isValid = true;
								break;
							case false:
								isValid = false;
								break;
						}
					}
	
					if (this.isUndefinedOrEmpty(options)) {
						isValid = true;
					}
				}
				break;
			case 'string':
				let curValue = String(value);
				isValid = true;
				break;
			case 'object':
				isValid = !this.isUndefinedOrEmpty(value);
				break;
			default:
				console.log('TODO doesn´t used type ' + typeof type);
		}
	
		return isValid;
	};
	
	isUndefinedOrEmpty(elem, attribute) {
		let isUndefined = false;
	
		if (elem === undefined || elem === '') {
			isUndefined = true;
		} else if (typeof elem === 'object') {
			if (Object.keys(elem).length === 0) {
				isUndefined = true;
			} else if (!(typeof attribute === 'undefined')) {
				if (!(attribute in elem)) {
					isUndefined = true;
				}
			}
		}
	
		return isUndefined;
	};
}
return new parameterCheck();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper.returnObject" BrowseName="1:returnObject" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">returnObject</DisplayName>
		<Description Locale="en">returnObject</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.helper</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[class returnObject{
	constructor(){
		this.i_errormsg = {
			/* GENERAL */
			1: "Parameter [placeholder] invalid",
			2: "Mandatory parameter [placeholder] missing",
	
	
			/* HISTORY */
			1000: "Archive [placeholder] already exists",
			1001: "Couldn´t create archive [placeholder]",
			1002: "Couldn´t set option(s) [placeholder]",
			1003: "Couldn´t delete archive [placeholder]",
			1004: "Archive [placeholder] doesn´t exists",
			1005: "Typedefinition doesn´t match an archive",
	
			1100: "Template [placeholder] already exists",
			1101: "Template [placeholder] doesn´t exists",
			1003: "Couldn´t delete template [placeholder]",
	
	
			1200: "Couldn´t create Aggregate [placeholder]",
			1201: "Couldn´t update Aggregate [placeholder]"
		}
    }
    
	good(){
		return {status: 0};
	}
	
	
	bad(error, name/*, addInfo*/){
		let placeholder = "";
		if(typeof name === "object"){
			placeholder = name.map(function(e){
				return "'" + e + "'";
			}).join(", ");
		} else {
			placeholder = "'" + name + "'";
		}
		return {status: error, errorMsg: this.i_errormsg[error].replace("[placeholder]", placeholder)/* + (addInfo ? " - " + addInfo : "")*/}
	}
}


return new returnObject();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history" ExportedBrowseName="1:history" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history" BrowseName="1:history">
		<DisplayName Locale="en">history</DisplayName>
		<Description Locale="en">history</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.archive" BrowseName="1:archive" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">archive</DisplayName>
		<Description Locale="en">archive</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const param = call("Library.helper.parameterCheck");
const node = call("Library.objects.node");

/**
 * @module archive
 * @classdesc The archive class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with archives.
 * 
 * @exports archive
 * @constructor
 * @example
 * const archive = call("Library.history.archive");
 */

class archive{

	constructor(){
		this.i_debug = false;	
		this.i_parent = "AGENT.HISTORY";
		this.i_types = ["Aggregate", "Data", "Event"];

		this.i_partition_interval = ["n", "y", "q", "m", "w", "d", "h"];

		this.i_configObject = {
			Stepped: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			},
			disable: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			},
			file_limit: {
				type: Ua.DataType.UINT32,
				defaultValue: 0
			},
			partition_interval: {
				type: Ua.DataType.STRING,
				defaultValue: "n"
			}
		};
	}	
	
	
	_setOption(address, key, value){
		let isValid = false;
		let option = Ua.findNode(address + "." + key);
	
		if(option.result){
			switch(key){
				case "partition_interval":
					if(this.i_partition_interval.indexOf(value) >= 0) isValid = true;
					break;
				case "file_limit":
					// TODO check with development
					isValid = true;
					break;
				case "disable": isValid=true; break;
				case "Stepped":
					if(value === true || value === false || value === 1 || value === 0) isValid = true;
					break;
			}
	
			if(isValid){
				option.result.value = value;
				return {error: 0, errorstring: undefined};
			}
		} else {
			return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}
	}
	
	/**
	 * Method to create an archive of all possible types with optional possibility to set
	 * some custom configuration for the archive.
	 * 
	 * @param {string} name 
	 * @param {string} type 0 | 1 | 2
	 * @param {object} [options]
	 * @param {string} [options.disable]
	 * @param {string} [options.partition_interval]
	 * @param {number} [options.file_limit]
	 * @param {boolean} [options.Stepped]
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * 
	 * // Create "Aggregate" archive with default settings
	 * archive.create("aggArchive", "Aggregate");
	 * 
	 * // Create "Data" archive with default config
	 * archive.create("dataArchive", "Data");
	 * 
	 * // Create "Event" archive with default settings
	 * archive.create("eventArchive", "Event");
	 * 
	 * // Create "Data" archive with yearly partition interval and a file limit of 5
	 * let config = {
	 * 	partition_interval: "y",
	 * 	file_limit: 5
	 * }
	 * archive.create("dataArchive2", 1, config);
	 * 
	 */
	create(name, type, options){
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		if(param.isUndefinedOrEmpty(type)) return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
	
		// check parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.check(type, "number", {min: 0, max: 2})) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(!param.isUndefinedOrEmpty(options.partition_interval) && !this.i_partition_interval.includes(options.partition_interval)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.isUndefinedOrEmpty(options.file_limit) && !param.check(options.file_limit, "number")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		
		let state = {};
		state = node.create(this.i_parent, name, {
			nodeClass: Ua.NodeClass.OBJECT,
			typeDefinition: "ObjectTypes.ATVISE.ArchiveGroup." + this.i_types[type]
		});	
		
		if(state.error === 0){
			let node = Ua.findNode(this.i_parent + "." + name);		
			for(let i in options){
				let optionState = this._setOption(node.result.nodeId.address, i, options[i]);
				if(optionState.error !== 0){
					return optionState;
				}
			}
		}

		return state;
	}
	
	/**
	 * Method to update an archive configuration.
	 * 
	 * @param {string} name 
	 * @param {object} options
	 * @param {string} [options.disable]
	 * @param {string} [options.partition_interval]
	 * @param {number} [options.file_limit]
	 * @param {boolean} [options.Stepped]
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * 
	 * // Disable historization for archive
	 * let config = {
	 * 	disable: true
	 * }
	 * archive.update("aggArchive", config);
	 * 
	 * // Change partition interval and file_limit
	 * let config = {
	 * 	partition_interval: "m",
	 * 	file_limit: 15
	 * }
	 * archive.update("aggArchive", config);
	 * 
	 */
	update(name, options){
		// check parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.check(options, "object")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.isUndefinedOrEmpty(options.partition_interval) && !this.i_partition_interval.includes(options.partition_interval)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(!param.isUndefinedOrEmpty(options.file_limit) && !param.check(options.file_limit, "number")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};

		let state = {};
		let archive = Ua.findNode(this.i_parent + "." + name);
		if (archive.result){
			for(let i in options){
				let optionState = this._setOption(archive.result.nodeId.address, i, options[i]);
				if(optionState.error !== 0){
					return optionState;
				}
			}

			state = {error: Ua.Status.GOOD, errorstring: undefined};
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
		
		return state;
	}
	
	/**
	 * Method to delete an archive from atvise project.
	 * 
	 * @param {string} name 
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * archive.delete("aggArchive");
	 */
	delete(name){
		// check parameter
		if(!param.check(name, "string"))  return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		
		let archive = Ua.findNode(name.indexOf("AGENT.HISTORY.") >= 0 ? name : this.i_parent + "." + name);

		if (archive.result){		
			if(this.i_types.indexOf(archive.result.typeDefinition.toString()) < 0){
				return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
			} else {
				return node.delete(archive.result.nodeId.address);
			}
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
	}
	
	/**
	 * Method to read current configuration for an archive.
	 * 
	 * @param {string} name 
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * let config = archive.config("aggArchive");
	 * 
	 * for(let i in config){
	 * 	console.log("setting: " + i + " - value: " + config[i]);
	 * }
	 */
	config(name){
		let exceptProperties = ["filter"];
	
		// check mandatory parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		let config = {};
		let archive = Ua.findNode(this.i_parent + "." + name);
		if (archive.result){
			let browseResult = archive.result.browse({
				nodeClass: Ua.NodeClass.VARIABLE
			});

			for(let i = 0; i < browseResult.result.length; i++){
				if(exceptProperties.indexOf(browseResult.result[i].node.browseName.name) < 0)
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value;
			}
			
			return config;
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
	}
	
	/**
	 * Method to get whole configuration default configuration for an archive.
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * // Get default configuration
	 * let defaultConfig = archive.template();
	 * 
	 * // Disable archive
	 * defaultConfig.disable = true;
	 * 
	 * // Create archive with modified default config
	 * archive.create("dataArchive", 1, defaultConfig);
	 */
	template(){
		let config = {};
	
		for(let i in this.i_configObject) {
			if(i === "function") {
				config[i] = this.i_agg_browsename[0];
			} else {
				config[i] = this.i_configObject[i].defaultValue;
			}
		}
	
		return config;
	}	
	
	/**
	 * Method to assign archive to one or multiple nodes. Already existing configurations will be removed
	 * 
	 * @example
	 * let archive = call("Library.history.archive");
	 * archive.assign("datavalues", "AGENT.OBJECTS.myBoolean");
	 * 
	 * archive.assign("datavalues", ["AGENT.OBJECTS.myBoolean", "AGENT.OBJECTS.myString"]);
	 */
	assign(archive, nodes){
		let archiveNode = Ua.findNode("AGENT.HISTORY." + archive);
		if(archiveNode.result && archiveNode.result.typeDefinition.value === "ns=1;s=ObjectTypes.ATVISE.ArchiveGroup.Data"){
			if(typeof nodes === "string"){
				var node = Ua.findNode(nodes);
				if(node.result){
					//remove existing configuration
					var historicalReferences = node.result.browse({
						reference: Ua.Reference.HASHISTORICALCONFIGURATION
					});

					for(var i = 0; i < historicalReferences.result.length; i++){
						if(historicalReferences.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.ArchiveGroup.Data") >= 0){
							node.result.deleteReference(Ua.Reference.HASHISTORICALCONFIGURATION, historicalReferences.result[i].node.nodeId.address);
						}
					}

					//assign archive
					node.result.addReference(Ua.Reference.HASHISTORICALCONFIGURATION, archiveNode.result.nodeId.address);
				} else {
					return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
				}
			} else if(typeof nodes === "object"){
				for(var j = 0; j < nodes.length; j++){
					var node = Ua.findNode(nodes[j]);
					if(node.result){
						//remove existing configuration
						var historicalReferences = node.result.browse({
							reference: Ua.Reference.HASHISTORICALCONFIGURATION
						});

						for(var i = 0; i < historicalReferences.result.length; i++){
							if(historicalReferences.result[i].node.typeDefinition.value.indexOf("ObjectTypes.ATVISE.ArchiveGroup.Data") >= 0){
								node.result.deleteReference(Ua.Reference.HASHISTORICALCONFIGURATION, historicalReferences.result[i].node.nodeId.address);
							}
						}

						//assign archive
						node.result.addReference(Ua.Reference.HASHISTORICALCONFIGURATION, archiveNode.result.nodeId.address);
					} else {
						return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
					}
				}
			}
		} else {
			return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		}
	}
}

return new archive();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.aggregate" BrowseName="1:aggregate" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">aggregate</DisplayName>
		<Description Locale="en">aggregate</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const param = call("Library.helper.parameterCheck");

/**
 * @module aggregate
 * @classdesc The aggregate class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with aggregate.
 * 
 * @exports aggregate
 * @constructor
 * @example
 * const aggregate = call("Library.history.aggregate");
 */

/**
 * @typedef {object} aggregateConfig
 * @property {string} [interval_unit]
 * @property {number} [interval_value]
 * @property {string} [offset_unit]
 * @property {number} [offset_value]
 * @property {boolean} [stepped]
 * @property {object} [function] //todo
 * @property {object} [archive] //todo
 * @property {number} [PercentDataGood]
 * @property {number} [PercentDataBad]
 * @property {boolean} [TreatUncertainAsBad]
 * @property {boolean} [UseSlopedExtrapolation]
 */

class aggregate {

	constructor(){
		this.i_parent = "AGENT.HISTORY";
	
		this.i_partition_interval = ["n", "y", "q", "m", "w", "d", "h"];
	
		this.i_agg_nodeid = ["i=0"];
		this.i_agg_browsename = ["(None)"];
	
		let node = Ua.findNode("2997");
		let res = node.result.browse();
		for(let i = 0; i < res.result.length; i++){
			this.i_agg_nodeid.push(res.result[i].node.nodeId.value);
			this.i_agg_browsename.push(res.result[i].node.browseName.name);
		}

		this.i_configObject = {
			interval_unit: {
				type: Ua.DataType.STRING,
				defaultValue: "m"
			},
			interval_value: {
				type: Ua.DataType.UINT32,
				defaultValue: 5
			},
			offset_unit: {
				type: Ua.DataType.STRING,
				defaultValue: "m"
			},
			offset_value: {
				type: Ua.DataType.UINT32,
				defaultValue: 0
			},
			stepped: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			},
			function: {
				type: Ua.DataType.NODEID,
				defaultValue: "i=0"
			},
			archive: {
				type: Ua.DataType.NODEID,
				defaultValue: ""
			},
			PercentDataGood: {
				type: Ua.DataType.BYTE,
				defaultValue: 100
			},
			PercentDataBad: {
				type: Ua.DataType.BYTE,
				defaultValue: 100
			},
			TreatUncertainAsBad: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: true
			},
			UseSlopedExtrapolation: {
				type: Ua.DataType.BOOLEAN,
				defaultValue: false
			}
		}
	  } 
		
		
	/**
	 * Method to create an aggregate depending on template, name and optional options.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * @param {aggregateConfig} config
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 *  
	 * //create Aggregate for 1 minute average function
	 * let config = {
	 * 	 function: "Average",
	 * 	 interval_unit: "m",
	 * 	 interval_value: 1,
	 * 	 archive: "aggArchive",
	 * 	 stepped: false
	 * }
	 * aggregate.create("AvgTemplate","avg_1min",config);
	 * 
	 */
	
	create(template, name, options){
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(template)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
		if(param.isUndefinedOrEmpty(options)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(template, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
		if(!param.check(name, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
	
		let keys = [];
		let values = [];
		const state = {};
		for(let i in this.i_configObject) {
			switch(i){
				case "archive":
					try{
						let position = options === null ? -1 : options[i].indexOf("AGENT.HISTORY");
						if(position >= 0 && options[i].length > 0){
							options[i] = position === 0 ? "ns=1;s=" + options[i] : options[i];
						} else if(options[i].length === 0){
							delete options[i];
						} else if(options[i] !== "i=0") {
							options[i] = "ns=1;s=AGENT.HISTORY." + options[i];
						}
					} catch(ex){
						console.log(ex);
					}
					break;
				case "function":
					if(options !== undefined && i in options){
						console.log(options[i]);
						options[i] = this.i_agg_nodeid[this.i_agg_browsename.indexOf(options[i])];
					}
					break;
			}
	
			keys.push(i);
			values.push({
				type: this.i_configObject[i].type,
				value: options !== undefined && i in options ? options[i] : this.i_configObject[i].defaultValue
			});
		}
		
		console.log(keys);
		console.log(values);
	

		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name},
				{type: Ua.DataType.STRING, value: keys},
				{type: Ua.DataType.BASEDATATYPE, value: values}
			]
		});
		
		
		state.error = callResult.error;
		state.errorstring = callResult.errorstring;
		return state;
	}


	
	/**
	 * Method to update a aggregate depending on template, name and options.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * @param {aggregateConfig} config
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * 
	 * let config = {
	 * 	 interval_unit: "d",
	 * 	 interval_value: 2,
	 * }
	 * aggregate.update("AvgTemplate","avg_1min",config);
	 */
	update(template, name, options){
		let keys = [];
		let values = [];
		const state = {};
		for(let i in options) {
			switch(i){
				case "archive":
					let position = options[i].indexOf("AGENT.HISTORY");
					if(position >= 0 && options[i].length > 0){
						options[i] = position === 0 ? "ns=1;s=" + options[i] : options[i];
					} else if(options[i].length === 0){
						delete options[i];
					} else {
						options[i] = "ns=1;s=AGENT.HISTORY." + options[i];
					}
					break;
				case "function":
					options[i] = this.i_agg_nodeid[this.i_agg_browsename.indexOf(options[i])];
					break;
			}
	
			keys.push(i);
			values.push({
				type: this.i_configObject[i].type,
				value: options[i]
			});
		}
		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 2},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name},
				{type: Ua.DataType.STRING, value: keys},
				{type: Ua.DataType.BASEDATATYPE, value: values}
			]
		});
		if(callResult.result==""){
			state.error = callResult.error;
			state.errorstring = callResult.errorstring;
			return state;
		}else{	
			return callResult.result;	
		}
		//todo fehlerhandling
	}
	
	/**
	 * Method to delete a aggregate depending on template and name. Not supported until [AT-D-12115] is fixed.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * aggregate.delete("AvgTemplate","avg_1min");
	 */
	delete(template, name){
		// Both solutions work without problem and the first one was chosen because it uses the same call method as create and update
		var state;

		// Solution 1
		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 4},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name}
			]
		});
		/*
		// Solution 2
		let aggregate = Ua.findNode("AGENT.HISTORY.AGGREGATETEMPLATES." + template + "." + name);
		if(aggregate.result){{
			state=aggregate.result.remove().result;
		} else {
			// todo
		}
	
		return state === 0 ? true : false;
		*/
		if(callResult.result==""){
			state.error = callResult.error;
			state.errorstring = callResult.errorstring;
			return state;
		}else{	
			return callResult.result;	
		}
	}
	
	/**
	 * Method to get config of aggregate depending template and name.
	 * 
	 * @param {string} template 
	 * @param {string} name 
	 * 
	 * @returns {aggregateConfig}
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * aggregate.config("AvgTemplate","avg_1min");
	 */
	config(template, name){
		let config = {};
		let methodCall = Ua.findNode("AGENT.HISTORY.METHODS.aggregateConfiguration");
		let callResult = methodCall.result.call({      
			object: "AGENT.HISTORY.METHODS",
			input: [
				{type: Ua.DataType.UINT32, value: 1},
				{type: Ua.DataType.UINT32, value: 3},
				{type: Ua.DataType.NODEID, value: "AGENT.HISTORY.AGGREGATETEMPLATES." + template},
				{type: Ua.DataType.STRING, value: name}
			]
		});

		for(let j = 0; j < callResult.result[0].length; j++){
			if(callResult.result[0][j] === "archive"){
				callResult.result[1][j].value = callResult.result[1][j].value.replace("ns=1;s=AGENT.HISTORY.", "");
			} else if (callResult.result[0][j] === "function") {
				if(callResult.result[1][j].value === "i=0"){
					callResult.result[1][j].value = "(None)";
				} else {
					var aggregate = new UaNode(callResult.result[1][j].value);
					callResult.result[1][j].value = aggregate.browsename;
				}
			}
	
			config[callResult.result[0][j]] = callResult.result[1][j].value;
		}

		return config;
	}
	
	/**
	 * Method to get default configuration for an aggregate.
	 * 
	 * @returns {aggregateConfig}
	 * 
	 * @example
	 * const aggregate = call("Library.history.aggregate");
	 * aggregate.template(); 
	 */
	template(){
		let config = {};
		for(let i in this.i_configObject) {
			if(i === "function") {
				config[i] = this.i_agg_browsename[0];
			} else {
				config[i] = this.i_configObject[i].defaultValue;
			}
		}
		return config;
	}
}
return new aggregate();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history.template" BrowseName="1:template" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">template</DisplayName>
		<Description Locale="en">template</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.history</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const param = call("Library.helper.parameterCheck");
const aggregate = call("Library.history.aggregate");

/**
 * @module template
 * @classdesc The template class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with templates.
 * 
 * @exports template
 * @constructor
 * @example
 * const template = call("Library.history.template");
 */

class template {
	constructor(){
	    this.i_parent = "AGENT.HISTORY.AGGREGATETEMPLATES";
	}
	
	/**
	 * Method to create a template depending name and object of aggregate configurations.
	 * 
	 * @param {string} name 
	 * @param {object} [config] 
	 * @param {aggregateConfig} [config.name]
	 * 
	 * @example
	 * let template = call("Library.history.template");
	 * 
	 * // Create template with no aggregate functions
	 * template.create("T1");
	 * 
	 * // Create template with simple aggregate function
	 * let config = {
	 *  "avg_1min": {
	 *      ...
	 *  }
	 * }
	 * template.create("T2", config);
	 * 
	 * // Create template with cascaded aggregate functions
	 * let config = {
	 *  "avg_1min": {
	 *      ...
	 *  },
	 * "avg_1min.avg_5min": {
	*       ...
	 *  },
	 * }
	 * template.create("T3", config);
	 */
	create(name, config){
		const state = {};
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(name, "string")) return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
	
		let template = Ua.findNode(this.i_parent + "." + name);
		
		if(template.result){
			state.error = Ua.Status.BADNODEIDEXISTS;
			state.errorstring = Ua.Status(state.error).toString();
		}
		else{
			let tempNode = Ua.createNode(this.i_parent + "." + name,{
				nodeClass: Ua.NodeClass.OBJECT,
				parent: this.i_parent,
				typeDefinition: "ObjectTypes.ATVISE.AggregateTemplate",
				reference: Ua.Reference.HASCOMPONENT
			});
			
			// Workaround waiting some time before continue due to problem with OPC UA Method call
			let time = new Date().getTime();
			while(new Date().getTime() - 50 <= time){}
			
			//return state === 0 ? true : false;
			state.error = tempNode.error;
			state.errorstring = tempNode.errorstring;
			
			if(config !== undefined && typeof config === "object"){
				for(let i in config){
					aggregate.create(name, i, config[i]);
				}
			}
		}
		return state;
	}
	
	/**
	 * Method to delete a template depending name.
	 * 
	 * @param {string} name 
	 */
	delete(name){
		const state = {};
		// check mandatory parameter
		if(param.isUndefinedOrEmpty(name)) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		// check parameter
		if(!param.check(name, "string"))  return {error: Ua.Status.BADTYPEMISMATCH, errorstring: Ua.Status(Ua.Status.BADTYPEMISMATCH).toString()};
	
		let template = Ua.findNode(this.i_parent + "." + name);
		if(template.result){
			return template.result.remove();
		} else {
			state.error = template.error;
			state.errorstring = Ua.Status(template.error).toString();
		}
		return state;
	}
	
	/**
	 * Method to update a template depending on name and object of aggregate configurations.
	 * 
	 * @param {string} name 
	 * @param {object} config 
	 * @param {aggregateConfig} config.name
	 * @param {boolean} remove not supported at the moment
	 */
	update(name, config, remove){
		let configNames = [];
		const state = {};
		let template = Ua.findNode(this.i_parent + "." + name);
		if(!template.result){
			state.error = template.error;
			state.errorstring = Ua.Status(template.error).toString();
			return state;
		}

		configNames = this._browseItem(template.result.nodeId.address, template.result.browseName.name);

		//remove = false; 
		if(remove === true){
			let configurations = this.config(name);
			let allConfigs = configNames.concat(Object.keys(config).filter(seccondArrayItem => !configNames.includes(seccondArrayItem)));

			for(let i = 0; i < allConfigs.length; i++){
				if(allConfigs[i] in config && configNames.indexOf(allConfigs[i]) < 0){
					return aggregate.create(name, configNames[i], config[configNames[i]]);
				} else if(allConfigs[i] in config && configNames.indexOf(allConfigs[i]) >= 0){
					return aggregate.update(name, allConfigs[i], config[allConfigs[i]]);
				} else {
					return aggregate.delete(name, allConfigs[i]);
				}
			}
	
		} else {
			for(let item in config){
				if(configNames.indexOf(item) >= 0){
					return aggregate.update(name, item, config[item]);
				} else {
					let time = new Date().getTime();
					while(new Date().getTime() - 50 <= time){}
					return aggregate.create(name, item, config[item]);
				}
			}
		}
	}
		
	/**
	 * Method to get current configuration of a template depending name.
	 * 
	 * @param {string} name 
	 */
	config(name){
		// todo fehlerhandling bei nicht zurückgeliefertem result der opc ua methode
		let configNames = [];
		let config = {};
		const state = {};
		let template = Ua.findNode(this.i_parent + "." + name);
		// überprüfen ob template existiert
		if(template.result){
			configNames = this._browseItem(template.result.nodeId.address, template.result.browseName.name);
			
			for(let i = 0; i < configNames.length; i++){
				config[configNames[i]] = aggregate.config(name, configNames[i]);
			}
		
			return config;
		} else {
			state.error = template.error;
			state.errorstring = Ua.Status(template.error).toString();
			return state;
		}

	}
		
	/**
	 * Method to get default configuration for an aggregate function.
	 * 
	 * @returns {aggregateConfig}
	 */
	template(){
		return aggregate.template();
	}
	
	_browseItem(nodeid, templateName){
		let configNames = [];
	
		let parentNode = Ua.findNode(nodeid);
		let browseResult = parentNode.result.browse({
			typeDefinition : "ObjectTypes.ATVISE.AggregateFunction"
		});
		
		for(let i = 0; i < browseResult.result.length; i++){
			configNames.push(browseResult.result[i].node.nodeId.address.replace(nodeid + ".", ""));
			let result = this._browseItem(browseResult.result[i].node.nodeId.address, nodeid);
	
			configNames = configNames.concat(result);
		}
	
		return configNames;
	}
}
return new template();]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror" ExportedBrowseName="1:mirror" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror" BrowseName="1:mirror">
		<DisplayName Locale="en">mirror</DisplayName>
		<Description Locale="en">mirror</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror.datasource" BrowseName="1:datasource" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">datasource</DisplayName>
		<Description Locale="en">datasource</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module datasource
 * @classdesc The datasource class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with datasources.
 * 
 * @exports datasource
 * @constructor
 * @example
 * let datasource = call("Library.mirror.datasource");
 */

class datasource extends node{

/**
 * Method to create datasource depending on name without path, options like url and typeDefinition and activated as boolean..
 * 
 * @param {string} name Name of datasource
 * @param {Object} options like url, typeDefinition and disable
 *
 * @example
 * let datasource = call("Library.mirror.datasource");
 * datasource.create("MyDataSource", {
 * 		url:  "opc.tcp://localhost:4841",
 * 		typeDefinition:	"OpcUa",
 * 		disable: false,
 * 		publishing_interval: 1000,
 * 		sampling_interval: 1000,
 * 		user: "root",
 * 		password: "myPassword",
 * 		queue_size: -1,
 * 		connection_timeout: 10000
 * });
 */
	static create(name, options){
		let state = {};
		let objectTypeNode = Ua.findNode("ObjectTypes.ATVISE.Datasource." + options.typeDefinition);
		let parent = "AGENT.DATASOURCES";
		let datasourceAddress = parent + "." + name;
		
		if(objectTypeNode.result){
			options.typeDefinition = objectTypeNode.result.nodeId.address;
			options.nodeClass = Ua.NodeClass.OBJECT;
			let result = node.create(parent, name, options);
			
			// Creating Redundancy to Datasource			
			let redu = node.create(datasourceAddress, "Redundancy",{
				nodeClass: Ua.NodeClass.OBJECT,
				typeDefinition: "ObjectTypes.ATVISE.DatasourceRedundancy"	
			});

			// Activate datasource
			let disableNode = Ua.findNode(datasourceAddress + ".disable");
			if(disableNode.error === 0){
				disableNode.result.value = "disable" in options ? options.disable : false;
			} else {
				return {
					error: disableNode.error,
					errorstring: Ua.Status(disableNode.error).toString()
				}
			}
			
			// Assigning the URL
			let urlNode = Ua.findNode(datasourceAddress + ".url");
			if(urlNode.error === 0){
				urlNode.result.value = options.url;
			} else {
				return {
					error: urlNode.error,
					errorstring: Ua.Status(urlNode.error).toString()
				}
			}
			
			// Sampling & Publish
			let publishNode = Ua.findNode(datasourceAddress + ".publishing_interval");
			
			if(publishNode.error === 0 && "publishing_interval" in options){
				publishNode.result.value = options.publishing_interval;
			} else if(publishNode.error !== 0) {
				return {
					error: publishNode.error,
					errorstring: Ua.Status(publishNode.error).toString()
				}
			}
			let samplingNode = Ua.findNode(datasourceAddress + ".sampling_interval");
			if(samplingNode.error === 0 && "sampling_interval" in options){
				samplingNode.result.value = options.sampling_interval;
			} else if(samplingNode.error !== 0) {
				return {
					error: samplingNode.error,
					errorstring: Ua.Status(samplingNode.error).toString()
				}
			}
			
			// Queue
			let queueNode = Ua.findNode(datasourceAddress + ".queue_size");
			if(queueNode.error === 0 && "queue_size" in options){
				queueNode.result.value = options.queue_size;
			} else if(queueNode.error !== 0) {
				return {
					error: queueNode.error,
					errorstring: Ua.Status(queueNode.error).toString()
				}
			}
			
			// ConnectionTimeout
			let timeoutNode = Ua.findNode(datasourceAddress + ".connection_timeout");
			if(timeoutNode.error === 0 && "connection_timeout" in options){
				timeoutNode.result.value = options.connection_timeout;
			} else if(timeoutNode.error !== 0) {
				return {
					error: timeoutNode.error,
					errorstring: Ua.Status(timeoutNode.error).toString()
				}
			}
			
			// Username
			let userNode = Ua.findNode(datasourceAddress + ".user");
			if(userNode.error === 0 && "user" in options){
				userNode.result.value = options.user;
			} else if(userNode.error !== 0) {
				return {
					error: userNode.error,
					errorstring: Ua.Status(userNode.error).toString()
				}
			}

			// Password
			let pwdNode = Ua.findNode(datasourceAddress + ".password");
			if(pwdNode.error === 0 && "password" in options){
				pwdNode.result.value = options.password;
			} else if(pwdNode.error !== 0) {
				return {
					error: pwdNode.error,
					errorstring: Ua.Status(pwdNode.error).toString()
				}
			}		
			
			state.error = Ua.Status.GOOD;
			state.errorstring = undefined;
		} else {
			state.error = Ua.Status.BADNODEIDUNKNOWN;
			state.errorstring = Ua.Status(state.error).toString();
		}
		
		return state;
	}

/**
 * Method to read current datasource settings
 * 
 * @param {string} name Name of datasource
 *
 * @example
 * let datasource = call("Library.mirror.datasource");
 * let config = datasource.config("MyDataSource");
 */		
	static config(name){
		let exceptProperties = [
			"namespace_array",
			"password",
			"revised_alarm_queue_size",
			"revised_lifetime_count",
			"revised_publishing_interval",
			"revised_queue_size",
			"revised_sampling_interval",
			"data_change_trigger",
			"connection_status",
			"copy_servertime_to_sourcetime",
			"dead_band",
			"history_sync_interval",
			"lifetime_count",
			"connected",
			"string_encoding",
			"variable_type_prefix",
			"variable_type_trim",
			"object_type_trim",
			"object_type_prefix",
			"user",
			"private_key",
			"pki_path",
			"client_certificate",
			"pass_through_event_history",
			"mode",
			"max_notifications_per_publish",
			"policy"
		];
	
		// check mandatory parameter
		//if(!param.check(name, "string")) return {error: Ua.Status.BADINVALIDARGUMENT, errorstring: Ua.Status(Ua.Status.BADINVALIDARGUMENT).toString()};
	
		let config = {};
		let datasource = Ua.findNode("AGENT.DATASOURCES." + name);
		if (datasource.result){
			let browseResult = datasource.result.browse({
				nodeClass: Ua.NodeClass.VARIABLE
			});

			for(let i = 0; i < browseResult.result.length; i++){
				if(exceptProperties.indexOf(browseResult.result[i].node.browseName.name) < 0)
					config[browseResult.result[i].node.browseName.name] = browseResult.result[i].node.value;
			}
			
			return config;
		} else {
			return {error: Ua.Status.BADNODEIDUNKNOWN, errorstring: Ua.Status(Ua.Status.BADNODEIDUNKNOWN).toString()};
		}
	}
	
/**
 * Method to delete a dataSource depending on an name string.
 * 
 * @param {string} name of dataSource
 * 
 * @example
 * let datasource = call("Library.mirror.datasource");
 * datasource.delete("MyDataSource");
 */
	static delete(name){
		let address = "AGENT.DATASOURCES." + name;
		let dataSource = Ua.findNode(address);    
		const state = {};
		if(dataSource.result){
			return node.delete(dataSource.result.nodeId.address);
		} else {
			state.error = dataSource.error;
			state.errorstring = Ua.Status(dataSource.error).toString();
		}

		return state;
	}
}

return datasource;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror.mirror" BrowseName="1:mirror" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">mirror</DisplayName>
		<Description Locale="en">mirror</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.mirror</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module mirror
 * @classdesc The mirror class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with mirrors.
 * 
 * @exports mirror
 * @constructor
 * @example
 * const mirror = call("Library.mirror.mirror");
 */

class mirror extends node {

/**
 * Method to create mirror ..
 * 
 * @param {string} parent 
 * @param {string} name 
 * @param {object} [options] 
 *
 * @example
 * const mirror = call("Library.mirror.mirror");
 * 
 * // Create RelMirrorBase
 * mirror.create("AGENT.OBJECTS.NewCustomFolder.Engine", "RelMirrorBase", {
 * 		datasource: "PLC1",
 * 		address: "ns=2;s=Engines.Engine001"
 * 	});	
 * 
 * // Create RelMirrorBase	
 * 	mirror.create("AGENT.OBJECTS.NewCustomFolder.SinInt32", "MirrorInputOutput", {
 * 		datasource: "PLC1",
 * 		namespace: 2,
 *		address: "Simulated.SinInt32"
 * 	});	
 */
	static create(parent, name, addr){
		let parentNode = Ua.findNode(parent);
		const state = {};
		if(parentNode.result){
			let address = addr.address;
			let source = addr.datasource + "/" + address;
			let options = {
				parent:	parent,
				dataType: Ua.DataType.STRING,
				nodeClass: Ua.NodeClass.VARIABLE,
				value: source
			}
			switch(name){
				case "MirrorInput": 
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Input";
					break;
				case "MirrorOutput": 
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Output";
					break;
				case "MirrorInputOutput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.InputOutput";
					break;
				case "MirrorDisable":
					options.dataType = Ua.DataType.BOOLEAN;
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Disable";
					break;
				case "MirrorOnDemand":
					options.dataType = Ua.DataType.BOOLEAN;
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.OnDemand";
					break;
				case "RelMirrorBase":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Base";
					break;
				case "RelMirrorInput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Input";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorOutput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Output";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorInputOutput":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.InputOutput";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorPathFragment":
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.PathFragment";
					options.valueRank = Ua.ValueRank.ONEDIMENSION;
					options.value = address;
					break;
				case "RelMirrorDisable":
					options.dataType = Ua.DataType.BOOLEAN;
					options.typeDefinition = "VariableTypes.ATVISE.Mirror.Relative.Disable";
					break;
				default:
					break;
			}
		
			return node.create(parent, name, options);
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();
		}
		return state;
	}


/**
 * Method to delete a dataSource depending on an name string.
 * 
 * @param {string} address
 * 
 * @example
 * const mirror = call("Library.mirror.mirror");
 * mirror.delete("AGENT.OBJECTS.NewCustomFolder.SinInt32.MirrorInputOutput");
 */
	static delete(address){
		let mirror = Ua.findNode(address);    
		const state = {};
		if(mirror.result){
			return node.delete(address);
		}else{
			state.error = mirror.error;
			state.errorstring = Ua.Status(mirror.error).toString();
		}
		return state;
	}
}


return mirror;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAObject NodeId="ns=1;s=SYSTEM" BrowseName="1:SYSTEM">
		<DisplayName Locale="en">SYSTEM</DisplayName>
		<Description Locale="en">SYSTEM</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">Objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise ExportedNodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects" ExportedBrowseName="1:objects" Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY" BrowseName="1:LIBRARY">
		<DisplayName Locale="en">LIBRARY</DisplayName>
		<Description Locale="en">LIBRARY</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT" BrowseName="1:PROJECT">
		<DisplayName Locale="en">PROJECT</DisplayName>
		<Description Locale="en">PROJECT</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS" BrowseName="1:SERVERSCRIPTS">
		<DisplayName Locale="en">SERVERSCRIPTS</DisplayName>
		<Description Locale="en">SERVERSCRIPTS</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library" BrowseName="1:Library">
		<DisplayName Locale="en">Library</DisplayName>
		<Description Locale="en">Library</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
		<Extensions>
			<atvise Upstream="true"/>
		</Extensions>
	</UAObject>
	<UAObject NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects" BrowseName="1:objects">
		<DisplayName Locale="en">objects</DisplayName>
		<Description Locale="en">objects</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library</Reference>
			<Reference ReferenceType="HasTypeDefinition">FolderType</Reference>
		</References>
	</UAObject>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.node" BrowseName="1:node" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">node</DisplayName>
		<Description Locale="en">node</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[/**
 * @module node
 * @classdesc The node class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with nodes.
 * 
 * @exports node
 * @constructor
 * @example
 * let node = call("Library.objects.node");
 */

class node {
//todo globaler valid path überprüfung für alle datenpunkt aktionen

/**
 * 
 * @param {string} parent 
 * @param {string} name 
 * @param {object} [options] 
 * @param {string} [options.description=[name]] 
 * @param {string} [options.displayName=[name]] 
 * @param {string} [options.browseName=[name]] 
 * @param {string} [options.dataType=Ua.DataType.INT32]
 * @param {string} [options.nodeClass=Ua.NodeClass.VARIABLE]
 * @param {string} [options.valueRank=Ua.ValueRank.SCALAR] 
 * @param {string} [options.modellingRule=null]
 * @param {string} [options.typeDefinition=Ua.VariableType.BASEVARIABLETYPE]
 * @param {string} [options.reference=Ua.Reference.HASCOMPONENT]
 * @param {*} options.value 
 * 
 * @example
 * let node = call("Library.objects.node");
 * 
 * // Create node with default values
 * node.create("AGENT.OBJECTS", "int32");
 * 
 * // Create array bool node and set initial value
 * let config = {
 *  dataType: Ua.DataType.BOOLEAN,
 *  valueRank: Ua.ValueRank.ONEDIMENSION,
 *  value: [true, false, true, false]
 * }
 * node.create("AGENT.OBJECTS", "boolArray", config);
 */
static create(parent, name, options){
    let node = Ua.findNode(parent + "." + name);
    let parentNode = Ua.findNode(parent);
    const state = {};
    
    if(!parentNode.result){
		state.error = parentNode.error;
		state.errorstring = Ua.Status(parentNode.error).toString();
    } else {
		if(!node.result){
			let config = {
				nodeClass:		Ua.NodeClass.VARIABLE,
				parent:			parent,
				typeDefinition: Ua.VariableType.BASEVARIABLETYPE,
				displayName:	name,
				browseName:		name,
				description:	name,
				reference:		Ua.Reference.HASCOMPONENT,
				dataType:		Ua.DataType.INT32,
				valueRank:		Ua.ValueRank.SCALAR,
				value:			0
			};
	 
			for(let i in options){
				switch(i){
					case "displayName":
						config[i] = options[i];
						break;
					case "browseName":
						config[i] = options[i];
						break;
					case "description":
						config[i] = options[i];
						break;
					case "nodeClass": 
						config[i] = options[i];
						break;
					case "typeDefinition": 
						config[i] = options[i];
						break;
					case "value":
						config[i] = options[i];
						break;
					case "dataType":
						config[i] = options[i];
						break;
					case "valueRank":
						config[i] = options[i];
						break;
					case "modellingRule":
						config[i] = options[i];
						break;
					case "reference":
						config[i] = options[i];
						break;
					default:
						break;
				}
			}
		  
			node = Ua.createNode(parent + "." + name, config);
			state.error = node.error;
			state.errorstring = node.errorstring;
		} else {
			state.error = Ua.Status.BADNODEIDEXISTS;
			state.errorstring = Ua.Status(state.error).toString();
		}
	}
    return state;
}

/**
 * Method to delete a node depending on an address string.
 * 
 * @param {string} address 
 * 
 * @example
 * let node = call("Library.objects.node");
 * node.delete("AGENT.OBJECTS.bool");
 */
static delete(address){
	let node = Ua.findNode(address);    
	const state = {};
	if(node.result){
		return node.result.remove();
	} else {
		state.error = node.error;
		state.errorstring = Ua.Status(node.error).toString();
	}

	return state;
}
}

return node;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.property" BrowseName="1:property" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">property</DisplayName>
		<Description Locale="en">property</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");

/**
 * @module property
 * @classdesc The property class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with property.
 * 
 * @exports property
 * @constructor
 * @example
 * let property = call("Library.objects.property");
 */

class property extends node {

/**
 * Method to create a property depending on parent, name and optional options in defined paths. The name of the property can contain "." in it´s name.
 * 
 * @param {string} parent 
 * @param {string} name 
 * @param {object} [options] 
 * @param {string} [options.description=[name]] 
 * @param {string} [options.displayName=[name]] 
 * @param {string} [options.browseName=[name]] 
 * @param {string} [options.dataType=Ua.DataType.INT32] 
 * @param {string} [options.valueRank=Ua.ValueRank.SCALAR]
 * @param {string} [options.modellingRule=null]
 * @param {*} [options.value] 
 * 
 * @example
 * let property = call("Library.objects.property");
 * 
 * // Create property with default values
 * property.create("AGENT.OBJECTS", "int32Prop");
 * 
 * // Create bool property and value true
 * let config = {
 *  dataType: Ua.DataType.BOOLEAN
 *  value: true
 * }
 * property.create("AGENT.OBJECTS", "boolProp", config);
 * 
 * // Create array bool property
 * let config = {
 *  dataType: Ua.DataType.BOOLEAN,
 *  valueRank: Ua.ValueRank.ONEDIMENSION
 * }
 * property.create("AGENT.OBJECTS", "boolArrayProp", config);
 */
 
 
	static create(parent, name, options){
		let parentNode = Ua.findNode(parent);
		const state = {};
		if(parentNode.result){
			typeof options === "undefined" ? options = {} : "";
			options.typeDefinition = Ua.VariableType.PROPERTYTYPE;
			return node.create(parent, name, options);
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();
		}
		return state;
	}

/**
 * Method to delete a property depending on an address string.
 * 
 * @param {string} address 
 * 
 * @example
 * let property = call("Library.objects.property");
 * property.delete("AGENT.OBJECTS.boolProp");
 */
	static delete(address){
		let property = Ua.findNode(address);    
		const state = {};
		if(property.result){
			if(property.result.typeDefinition.value === "i=68"){
				return node.delete(address);
			} else {
				state.error = Ua.Status.BADTYPEMISMATCH;
				state.errorstring = Ua.Status(state.error).toString();
			}
		} else {
			state.error = property.error;
			state.errorstring = Ua.Status(property.error).toString();
		}
		return state;
	}
}

return property;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.folder" BrowseName="1:folder" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">folder</DisplayName>
		<Description Locale="en">folder</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module folder
 * @classdesc The folder class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with folders.
 * 
 * @exports folder
 * @constructor
 * @example
 * let folder = call("Library.objects.folder");
 */

class folder extends node{
/**
 * Method to create a folder depending on parent and name in defined paths. The name of the folder can contain "." in it´s name.
 * 
 * @param {string} parent Address of root element
 * @param {string} name Name of folder
 * 
 * @example
 * let folder = call("Library.objects.folder");
 * folder.create("AGENT.OBJECTS", "folder1");
 */
	static create(parent, name){
		let parentNode = Ua.findNode(parent);
		const state = {};

		if(parentNode.result){
			return node.create(parent, name, {
				nodeClass: Ua.NodeClass.OBJECT,
				typeDefinition: Ua.ObjectType.FOLDERTYPE		
			});
		} else {
			state.error = parentNode.error;
			state.errorstring = Ua.Status(parentNode.error).toString();
		}
		return state;
	}
	
/**
 * Method to delete a folder depending on an address string.
 * 
 * @param {string} address Address of folder
 * 
 * @example
 * let folder = call("Library.objects.folder");
 * folder.delete("AGENT.OBJECTS.folder1");
 */
	static delete(address){
		var folder = Ua.findNode(address);
		const state = {};
		
		if(folder.result){
			if(folder.result.typeDefinition.value === "i=61"){
				return node.delete(address);
			} else {
				state.error = Ua.Status.BADTYPEMISMATCH;
				state.errorstring = Ua.Status(state.error).toString();
			}
		} else {
			state.error = folder.error;
			state.errorstring = Ua.Status(folder.error).toString();
		}
		return state;
	}
}

return folder;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.variabletype" BrowseName="1:variabletype" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">variabletype</DisplayName>
		<Description Locale="en">variabletype</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module VariableType Instance
 * @classdesc The variabletype class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with VariableType Instances.
 * 
 * @exports variabletype
 * @constructor
 * @example
 * let VT_instance = call("Library.objects.variabletype");
 */

class variabletype extends node{

/**
 * Method to create a VariableType instance depending on parent, name in defined paths and options.
 * 
 * @param {string} parent
 * @param {string} name
 * @param {Object} options
 *
 * @example
 * let instance = call("Library.objects.variabletype");
 * instance.create("AGENT.OBJECTS", "Instance2", {dataType:	Ua.DataType.UINT16, typeDefinition:	"VariableTypes.PROJECT.word_to_bits"});
 */
	static create(parent, name, options){
		let VariableTypeNode = Ua.findNode(options.typeDefinition);
		const state = {};
		if(VariableTypeNode.result){
			let parentNode = Ua.findNode(parent);
			if(parentNode.result){
				return node.create(parent, name, options);
			} else {
				state.error = parentNode.error;
				state.errorstring = Ua.Status(parentNode.error).toString();
			}
		} else {
			state.error = VariableTypeNode.error;
			state.errorstring = Ua.Status(VariableTypeNode.error).toString();
		}
		return state;
	}
	
	
/**
 * Method to delete a instance depending on an address string.
 * 
 * @param {string} address
 * 
 * @example
 * let instance = call("Library.objects.variabletype");
 * instance.delete("AGENT.OBJECTS.Instance_2");
 */
	static delete(address){
		let instance = Ua.findNode(address);    
		const state = {};
		if(instance.result){
			return node.delete(address);
		} else {
			state.error = instance.error;
			state.errorstring = Ua.Status(instance.error).toString();
		}
		
		return state;
	}
}


return variabletype;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
	<UAVariable NodeId="ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects.objecttype" BrowseName="1:objecttype" DataType="XmlElement" AccessLevel="103" UserAccessLevel="103" Historizing="true">
		<DisplayName Locale="en">objecttype</DisplayName>
		<Description Locale="en">objecttype</Description>
		<References>
			<Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SYSTEM.LIBRARY.PROJECT.SERVERSCRIPTS.Library.objects</Reference>
			<Reference ReferenceType="HasTypeDefinition">ns=1;s=VariableTypes.ATVISE.ScriptCode</Reference>
		</References>
		<Value>
			<uax:XmlElement><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<script>
  <metadata>
    <priority>0</priority>
    <owner>root</owner>
    <runcontext>caller</runcontext>
  </metadata>
  <code><![CDATA[const node = call("Library.objects.node");
/**
 * @module ObjectType Instance
 * @classdesc The objecttype class can be imported in a serverside script using the serverside "call" method and provide the below documented methods
 * to interact with ObjectType Instances.
 * 
 * @exports instance
 * @constructor
 * @example
 * let instance = call("Library.objects.objecttype");
 */

class objecttype extends node{

/**
 * Method to create an objectType instance depending on parent, name in defined paths and objectType Address. The name of the folder can contain "." in it´s name.
 * 
 * @param {string} parent Address of root element
 * @param {string} name Name of folder
 * @param {string} Address of objectType
 *
 * @example
 * let instance = call("Library.objects.objecttype");
 * instance.create("AGENT.OBJECTS", "Instance1", "ObjectTypes.PROJECT.TestObject");
 */
	static create(parent, name, objectType){
		const state = {};
		let objectTypeNode = Ua.findNode(objectType);
		if(objectTypeNode.result){
			let parentNode = Ua.findNode(parent);
			if(parentNode.result){
				return node.create(parent, name, {
					nodeClass: Ua.NodeClass.OBJECT,
					typeDefinition: objectType	
				});
			} else {
				state.error = parentNode.error;
				state.errorstring = Ua.Status(parentNode.error).toString();
			}		
		} else {	
			state.error = objectTypeNode.error;
			state.errorstring = Ua.Status(objectTypeNode.error).toString();
		}
		return state;
	}
	
	
/**
 * Method to delete a instance depending on an address string.
 * 
 * @param {string} address Address of instance
 * 
 * @example
 * let instance = call("Library.objects.objecttype");
 * instance.delete("AGENT.OBJECTS.Instance1");
 */
	static delete(address){
		let instance = Ua.findNode(address);    
		const state = {};
		if(instance.result){
			return node.delete(address);
		} else {
			state.error = instance.error;
			state.errorstring = Ua.Status(instance.error).toString();
		}
		
		return state;
	}
}


return objecttype;]]]]><![CDATA[></code>
</script>
]]></uax:XmlElement>
		</Value>
	</UAVariable>
</UANodeSet>
