Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Grunderna i skriptspråket

I det här avsnittet kan den som inte använt språk som JavaScript, Java, C eller C++ få en introduktion till grundläggande syntaktiska konstruktioner som är gemensamma för dessa programspråk. Dessutom kan du här få en introduktion till det som särskiljer ECMAScript från Java, C och C++.

Det här avsnittet kan inte ersätta en lärobok i JavaScript eller JScript. Däremot har exemplen i det här avsnittet anknytning till kameraövervaknings tillämpningar där så är möjligt, vilket gör att det kan vara intressant läsning även för dig som är bekant med programmeringsspråket.

För att komma i gång behöver du veta hur du redigerar och kör skript i Ethiris. Detta beskrivs i första avsnittet. Sedan kommer några avsnitt som går igenom de mest grundläggande elementen i skriptspråket och några viktiga felsökningstekniker.

Redigera och köra skript i Ethiris

För att testa skripten i detta kapitel, mata in dem i skripteditorn. Språkets reserverade ord visas med blå text, strängliteraler visas röda och kommentarer visas gröna.

Om en rad innehåller syntaxfel markeras det med röd understrykning. Observera att raden där syntaxfelet detekteras inte nödvändigtvis är den som ger upphov till felet.

När du redigerar skript, kan du använda Variabelbläddraren för att se vilka variabler som finns i datalagret. En insignal kan t.ex. vara ett schemas variabel för aktuellt tillstånd, en utsignal kan vara en kameras variabel för att starta en inspelning. Du kan dra och släppa variabler från listorna till dina skript för att infoga namnet på den variabeln i skriptet. Du kan även dubbelklicka på en variabel för att åstadkomma detta

Skripten exekveras kontinuerligt med det intervall som är satt i textrutan för Exekveringsvall. Ett exekveringsintervall valt till 100 ms innebär att skriptet kommer exekveras 10 gånger per sekund. Tänk på att om ett skript utför många krävande beräkningar är det inte garanterat att ett skript utförs så ofta som det var tänkt, systemvariabeln System.Script.ExecutionTime talar om aktuell exekveringstid.

Grundläggande syntax

Variabler deklareras med ”var”. Eftersom värden med olika datatyp kan lagras i en viss variabel, anger man inte någon datatyp i variabeldeklarationen.

Satser i skriptspråket avslutas med semikolon, ”;”. Block i skriptspråket definieras med särskilda parenteser, ”{” och ”}”. Ett block är en sats som består av flera andra satser.

Tilldelning görs med likhetstecken, ”=”. Jämförelse görs med dubbla likhetstecken, ”==”. Blanda inte ihop dessa!

Exempel:

var b = true;
var c = 13;
if(b == true)
{
   b = false;
   c = c + 1;
}

Om du vill göra en jämförelse som är sann om två värden är olika, skriver du ”!=”. Om du vill göra en jämförelse som är sann om två värden är lika och har samma typ, skriver du ”===”. Den omvända operationen är ”!==”.

Booleska uttryck är AND ”&&”, OR ”||” samt NOT ”!”. Exempel:

sumMotion = motion1 || motion2 || motion3;

De enkla matematiska operationerna är addition: ”+”, subtraktion: ”-”, multiplikation: ”*” division: ”/” samt modulo: ”%”. Exempel:

angle = (angle + 1) % 360;

Precis som i C/C++/Java kan man öka och minska en variabels värde med ett genom att använda operatorerna ”++” och ”–”. Exempel:

angle = (angle++) %360;

Semikolon kan i många fall utelämnas, och man kan i stället låta en radbrytning indikera var en sats slutar. Många JavaScript-programmerare rekommenderar dock att använda semikolon för att undvika att skriptmotorn tolkar ens skript på ett annat sätt än vad som avsågs.

Kommentarer

Du kan mata in kommentarer som förklarar och förtydligar detaljer i ditt skript. Det finns enrads- och flerraderskommentarer. Detta fungerar precis som i C/C++/Java.

För att skapa en kommentar som fortsätter till slutet av raden skriver du ”//”.

// Initiera räknaren r
var r = 0;
var g = (1 + Math.sqrt(5))/2; // Det gyllene snittet

För att skapa en kommentar som eventuellt spänner över flera rader påbörjar du kommentaren med ”/” och avslutar den med ”/”. Detta används ibland också för att kommentera bort delar av koden.

if(motion1 /*&& motion2 && motion3*/)
{
   ...
}

Flödeskontroll

I detta avsnitt beskrivs vilka satser i skriptspråket som kan användas för flödeskontroll.

Flödeskontroll kan vara hopp framåt, dvs. att man hoppar över vissa satser. Denna kategori innefattar satsen if-else. Dessa är ofarliga att använda.

Det kan även vara hopp bakåt, dvs. att man utför vissa satser en gång till. Detta kan användas för att skapa så kallade loopar i programkoden, som t.ex. satserna while och for. Problemet är att man kan skapa oändliga loopar, vilket resulterar i att programmet ”hänger sig”. Detta är inte önskvärt beteende, allra minst i ett övervakningssystem, varför läsaren uppmanas att använda loopar med största försiktighet.

Exempel på oändlig loop:

while(true);

If-else

En if-sats innehåller en annan sats som endast utförs om det angivna uttrycket är sant. Om flera satser skall utföras när uttrycket är sant, kan de samlas i ett block.

Man kan även ange en sats som skall utföras om uttrycket är falskt.

// Om rörelse på kameran Utb2
if(Utb2.motion2.Motion)
{
   // Starta inspelning för kameran Utb1
   Utb1.RecordEvent = true;
}
else
{
   // Stoppa inspelning
   Utb1.RecordEvent = false;
}

do-while

Detta är en iterativ sats som utför en annan sats så länge ett uttryck är sant. Satsen utförs dock minst en gång.

Exempel:

var result = 0;
do
{
   result++;
}
while(result < 12)

while

Detta är en iterativ sats som utför en annan sats så länge ett uttryck är sant.

var result = 0;
while(result < 12)
   result++;

for

For-loopar finns i två varianter i skriptspråket. En av dem fungerar likadant som motsvarigheten i C/C++/Java.

Exempel:

for(var i = 0; i < 12; i++)
{
   ...
}

for-in

For-in loopar används för att i tur och ordning tilldela en variabel namnet på egenskaperna i ett objekt.

Följande loop skapar en sträng som innehåller namn och värde på variabler som ligger i datalagret.

var s = "";
for(var variableName in DataStore)
{
s += "The variable " + variableName;
s += " has the value " + DataStore[variableName];
s += "\n";
}

Tillsammans med Sequence-objekt erbjuder detta ett enklare sätt att skapa for-loopar som använder heltal. Följande skript är ekvivalent med exemplet i avsnittet om for-loopar.

for(var i in Sequence(0, 11))
{
   ...
}

Egenskaper som har attributet DontEnum hoppas automatiskt över av for-in loopar.

En for-in loop kan inte resultera i en oändlig loop, och kan därför med fördel användas i stället för for-loopar.

continue

Continue-satsen används för att gå vidare till nästa varv i loopen.

Följande skript skapar en sträng som innehåller namnet på alla variabler som hör till kameran ”Utb1” i datalagret.

var s = "";
for(var variableName in DataStore)
{
   if(variableName.indexOf("Utb1") != 0)
      continue;
   s += "The variable " + variableName;
   s += " has the value " + DataStore[variableName];
   s += "\n";
}

break

Break-satsen används för att avbryta en loop.

return

Return-satsen används för att avbryta skriptets exekvering av en funktion och eventuellt returnera ett värde.

switch

Switch-satser fungerar på samma sätt som i C/C++/Java. Beroende på värdet av ett uttryck utför den olika satser. Om en sats inte avbryter flödet med break eller return kommer den att gå vidare till nästa.

Exempel:
switch(currentIndex)
{
case 0: Utb2.PTZ.Pos1.Preset = true; break;
case 1: Utb2.PTZ.Pos2.Preset = true; break;
case 2: Utb2.PTZ.Pos3.Preset = true; break;
}

Alert och Debug.Print

Try-catch

Ett viktigt hjälpmedel för att hitta fel i dina skript är konstruktionen try-catch.

När någonting går snett i ECMAScript kastas ofta ett undantag (eng. exception). Om du inte fångar detta undantag, så avbryts skriptets exekvering och ett felmeddelande skrivs till log-filen för Ethiris Server.

Säg t.ex. att du har problem med att skriptet aldrig startar inspelning för en kamera när ett visst villkor är uppfyllt. Ditt skript ser ut ungefär som följer (ersätt de tre punkterna med något villkor):

Utb1.RecordEvent = ...

För att se om din beräkning kastar ett undantag, omgärdar du den med en try-catch sats.

try
{
   Utb1.RecordEvent = ...
}
catch(e)
{
   sResult = e.message;
}

Ett undantag är oftast ett Error-objekt. Sådana objekt har en enda viktig egenskap, nämligen message. Den innehåller ett meddelande som berättar vad som gick snett.

Med koden ovan kommer sResult att innehålla felmeddelandet om något gick snett

throw

Om du av någon anledning vill kasta ett undantag själv, gör du det med hjälp av nyckelordet throw. Det är lämpligt att kasta ett Error-objekt:

var error = new Error("Something went wrong")
throw error;

Värden

I skriptspråket kan du använda tre olika typer av primitiva värden. Dessa är tal, strängar och booleska värden.

12, 14.1, 1.5e-19 och 12.3E14 är alla exempel på tal.

“Hello, world!”, “Русский” och “中文” är alla exempel på strängar. Tack vare UNICODE-standarden kan tecken från många olika språk användas. Strängar skapar du genom att omge en text med citattecken.

True och false är exempel på booleska värden.

Det finns även tre andra sorters värden. De är objekt, null och undefined. De får sin förklaring i nästa avsnitt.

Tal

För att skapa tal i skriptspråket skriver du in ett tal på decimal notation, vetenskaplig notation eller hexadecimal notation:

12 och 12.4 är exempel på decimal notation.

1e12, 1.3e5, 33E-2 och 412.4E7 är exempel på vetenskaplig notation.

0xF01B, 0X120C och 0xffb0c är exempel på hexadecimal notation.

Ett tal kan också ha värdet ∞ (oändligheten) eller -∞ (minus oändligheten). T ex blir resultatet när man dividerar ett positivt tal med noll lika med oändligheten. För att skapa ett tal med värdet oändligheten skriver du Infinity. För att skapa ett tal med värdet minus oändligheten skriver du -Infinity.

Ett tal kan också ha värdet NaN, Not a Number. Detta värde indikerar att en beräkning misslyckas. T.ex. så blir resultatet när man dividerar noll med noll lika med NaN. För att skapa ett tal med värdet NaN skriver du NaN.

Skriptmotorn kan hålla reda på både +0 och -0. Dessa har för de flesta praktiska användningsområde samma värde, men i vissa beräkningar kan det göra skillnad. T ex är 1/(+0) =Infinity medan 1/(-0) = -Infinity.

Strängar

För att skapa strängar i skriptspråket skriver du en text inom citattecken. Vissa tecken är svåra att mata in via tangentbordet. Genom att lägga in ett backslash-tecken ”\” följt av en så kallad escape sequence kan du mata in särskilda tecken.

Följande exempel blir två rader text i stängen, “Hello,” och “How are you?” eftersom man har lagt in en radbrytning i strängen. Men det syns först t.ex. när man skriver strängen till en fil.

var s = "Hello,\nHow are you?";

Du kan även använda enkla citattecken. Detta underlättar om du vill mata in en sträng som skall innehålla dubbla citattecken, och vice versa.

'Huvudstaden i Danmark heter "København" på danska'

Booleska värden

Booleska värden skapar du genom att helt enkelt skriva true eller false i ditt skript.

Omvandling av värden

Värden i skriptspråket kan omvandlas till andra typer automatiskt enligt följande tabell.

Omvandling till boolskt värde:

Omvandling till tal:

Omvandling till sträng:

När man skall omvandla ett objekt till ett tal anropas alltså bakom kulisserna funktionen valueOf(), precis som om man hade skrivit obj.valueOf(). Motsvarande gäller när man skall omvandla ett objekt till en sträng:

obj.toString();

För att tvinga fram en omvandling av ett värde value från en typ till en annan skriver du Boolean(value), Number(value) eller String(value) beroende på vilken typ du vill omvandla till.

Du kan även omvandla till ett objekt genom att skriva Object(value). Om value redan är ett objekt returneras value, annars försöker skriptmotorn konvertera value till ett objekt.

Exempel:

När man adderar en sträng med ett värde returneras en ny sträng.

"12" + 3
blir alltså "123".
Number("12")+3

blir däremot 15.

Eftersom objekt alltid omvandlas till true medan null och undefined omvandlas till false när de skall konverteras till boolskt värde, kan du använda en if-sats för att undersöka om ett visst objekt finns.

Objekt

För att man skall ha någonstans att lagra sina värden finns det objekt. Ett objekt fungerar som en uppslagsbok, du kan slå upp ett sökord och hitta ett värde. Du kan även lägga in nya sökord i objekt och lagra värden där.

Egenskaper

Man säger att objekten har egenskaper (eng. property). En egenskap är helt enkelt en post i uppslagsboken, d v s ett sökord och värdet som finns lagrat där. Egenskapen har alltså ett namn och ett värde. Värdet av en egenskap kan vara ett annat objekt.

Exempel:

För att hämta värdet av egenskapen “PI” i objektet Math skriver du

Math.PI

eller

Math["PI"]

Denna egenskap är ett tal, nämligen värdet av π: kvoten mellan omkretsen hos en cirkel och dess diameter.

För att skapa en ny egenskap i objektet Math och lagra värdet av det gyllene snittet i den, kan du skriva

Math.goldenRatio = 1.6180339887499

eller

Math["goldenRatio"] = 1.6180339887499

Men var lagras objektet Math? Förklaringen är att det finns ett så kallat globalt objekt som har en egenskap med namn “Math” vars värde är ett objekt som innehåller användbara matematiska värden och funktioner. När du skriver ”Math” så söker skriptmotorn igenom det globala objektet efter en egenskap med namnet “Math” och returnerar dess värde. Texten ”Math” i ditt skript kallas för en identifierare.

När du deklarerar en variabel genom att skriva t.ex. var myVariable skapas en ny egenskap med namnet myVariable i ett särskilt objekt som kallas variabel-objektet. Variabel-objektet söks också igenom när du skriver en identifierare. Tack vare detta kan du komma åt värdet på den variabeln i fortsättningen.

Låt oss se på skriptet:

var myVariable; 
myVariable = 5;

Vad händer egentligen när skriptmotorn kör detta skript? På första raden skapas en ny egenskap i variabel-objektet med namnet “myVariable” och värdet undefined. På andra raden söker skriptmotorn igenom variabel-objektet och det globala objektet efter en egenskap med namn “myVariable” och lagrar värdet 5 i den egenskapen.

Förutom namn och värde har egenskaper även attribut. Dessa attribut kan vara en eller flera av DontEnum, DontDelete och ReadOnly. Den enklaste att förklara av dessa är ReadOnly, som innebär att du inte kan ändra egenskapens värde.

Egenskapen PI i objektet Math har attributet ReadOnly. Följande sats har alltså ingen effekt:

Math.PI = 4;

För att ta bort en egenskap ur ett objekt skriver man delete. Vi kan t ex ta bort det gyllene snittet från objektet Math:

delete Math.goldenRatio;

Egenskaper med attributet DontDelete går inte att ta bort. Egenskapen PI i objektet Math har attributet DontDelete. Följande sats har alltså ingen effekt:

delete Math.PI;

Satsen delete returnerar true om egenskapen gick att ta bort, false annars.

if(delete Math.PI)
{
   // Hit kommer vi aldrig, delete returnerar false 
}

Egenskaper med attributet DontEnum traverseras inte av for-in loopar. Mer om detta i avsnittet om flödeskontroll.

undefined

Om du försöker hämta en egenskap som inte finns i objektet, får du ut värdet undefined. Om du vill skapa ett värde av typen undefined i ditt skript, skriver du helt enkelt undefined.

if(Math.goldenRatio == undefined)
{
Math.goldenRatio = (1+Math.sqrt(5))/2
}

Skapa objekt

För att skapa nya objekt använder du nyckelordet new. När du har skapat ett nytt objekt kan du lägga in egenskaper i det.

var myObj = new Object();
myObj.name = "Kalle Kula";
myObj.address = "Kulgatan 9";

Du kan även skapa nya objekt genom att använda en så kallad objektsliteral.

var myObj = { name: "Kalle Kula", address: "Kulgatan 9" };

Det finns andra typer av objekt, som File och Array. För att skapa objekt av en viss typ skriver du till exempel

var myFile = new File("Test.txt", 4);
var myArray = new Array();

File-objekt används för att läsa och skriva till text-filer.

Array-objekt är som vanliga objekt fast namnen på egenskaperna är tal, samt att de alltid har en egenskap som heter ”length”. Värdet av length är alltid ett större än det största namnet på någon egenskap i Array-objektet. Följande exempel lagrar värdet 12 i egenskapen med namnet ”0” och värdet 9 i egenskapen med namnet ”5”. Längden blir då 6.

myArray[0] = 12;
myArray[3] = 2;
myArray[5] = 9;
if(myArray.length == 6)
{
   // Vi kommer hit eftersom length egenskapen är 6
}

Ett annat sätt att skapa nya arrayer är att använda en så kallad array-literal. Följande exempel skapar samma array som förra exemplet.

var myArray = [12, , ,2, , 9];

null

Om du vill indikera att en egenskap kan vara ett objekt, men just nu inte har något objekt att använda, lagrar du värdet null i egenskapen. Du skapar värdet null genom att skriva null i ditt skript.

Funktion

Funktioner är objekt som kan göra någonting. För att få en funktion att göra sitt jobb, anropar man funktionen genom att använda parenteser, ”(” och ”)”.

Egenskapen ”sin” i objektet ”Math” är en funktion som beräknar sinus av ett värde. Du anropar den genom att skriva t.ex.

var v = Math.sin();

Den här funktionen förväntar sig ett argument. Argument matas in mellan parenteserna, separerade av komma i de fall du vill ange flera argument. I det här fallet skall argumentet vara det antal radianer du vill beräkna sinus för.

var v = Math.sin(Math.PI/2);

En funktion som kan ta flera argument är egenskapen ”max” i objekt Math. Följande funktionsanrop returnerar det största av två tal, i detta fall 2 och 5.

var v = Math.max(2, 5);

Man brukar säga att man anropar funktionen max på objektet Math. Det beror på att objektet som funktionen ligger i har en särskild roll att spela. Funktionens skriptkod kan nämligen komma åt objektet som den anropar på genom att skriva this.

Om du vill skapa en egen funktion, använder du nyckelordet function.

Exempel:

var myObj = new Object();
myObj.addProperty = function(name, value) 
{ 
   this[name] = value; 
}

myObj.addProperty("name", "Kalle Kula");
myObj.addProperty("address", "Kulgatan 9");
var s = "";
s += "name = " + myObj.name;
s += ", ";
s += "address = " + myObj.address;
sResult = s;

När du kör skriptet ovan och har lagt variabeln sResult i Watch-panelen kommer värdet av egenskaperna ”name” och ”address” i myObj att visas. Texten i sResult kommer att vara

name = Kalle Kula, address = Kulgatan 9

Det beror på att vi först lade in en funktion i egenskapen ”addProperty”, varefter vi använde den funktionen för att lägga till ytterligare två egenskaper, nämligen ”name” och ”address”.

Formella parametrar

En formell parameter är en parameter som namngavs när funktionen skapades. I följande exempel är a, b och c formella parametrar.

function sum(a, b, c) 
{ 
   return a+b+c; 
}

Argument

När funktionen väl anropas har den faktiska parametrar, eller så kallade argument.

I exemplet nedan är 1 och 2 argument. Observera att antalet argument kan vara fler eller färre än antalet formella parametrar.

function sum(a, b, c) { return a+b+c; };
var mySum = sum(1, 2);

I skriptet ovan kommer mySum att innehålla värdet NaN. Varje argument som utelämnas antas ha värdet undefined, och 1+2+undefined blir NaN.

Prototyp

Vad händer när ett objekt saknar egenskapen du söker?

Varje objekt har en prototyp. Denna kan vara ett annat objekt, eller null. Om den är ett objekt, fortsätter skriptmotorn att leta efter egenskapen i prototypen. Om prototypen i sin tur har en prototyp, kan skriptmotorn fortsätta leta där, och så vidare. Man säger att objektet har en prototyp-kedja.

Objekt av typen Object har prototypen Object.prototype. Den har en funktionsegenskap med namnet ”hasOwnProperty”. Den funktionen kan lista ut om ett objekt har en egenskap med ett visst namn.

var myObj = new Object();
myObj.someProperty = 7;
if(myObj.hasOwnProperty("someProperty"))
{
// Vi kommer hit eftersom egenskapen finns
sResult = "myObj has a property someProperty";
}
if(myObj.hasOwnProperty("hasOwnProperty"))
{
// Vi kommer inte hit, då egenskapen hasOwnProperty
// egentligen finns i prototypen
}
else
{
sResult = "myObj has no property hasOwnProperty";
}

Hur kommer det sig att vi kan använda egenskapen ”hasOwnProperty” i myObj, om myObj inte har en egenskap som heter så?

När du skriver myObj.hasOwnProperty

börjar skriptmotorn leta i myObj, men där hittar den ingen sådan egenskap. Den går då vidare till prototypen, och finner där en egenskap med det namnet.

Man brukar lägga funktioner i prototypen, eftersom de inte behöver ligga som egenskaper i varenda objektinstans.

Man brukar även lägga standardvärden på egenskaper i prototypen, så att om objektet självt inte har definierat något värde, så används det standardvärde som finns i prototypen.

När du drar in egenskapen hasOwnProperty från listvyn till texteditorn, får du texten

Konstruktor

En konstruktor är helt enkelt en funktion som används för att skapa ett nytt objekt. Normalt måste man skriva new framför funktionsanropet för att konstruktorn skall göra sitt jobb korrekt. Konstruktorer har enligt konvention alltid stor begynnelsebokstav i sitt namn.

Exempel:

var myObject = new Object();

När en konstruktor anropas i ett new-uttryck hämtar den värdet av sin prototype-egenskap och sätter det som prototyp för sitt nyskapade objekt.

Du kan alltså skapa egna konstruktorer.

function Person(name, address)
{
   this.name = name;
   this.address = address;
}

Person.prototype.toString = function()
{
   return this.name + ", " + this.address;
}

var myPerson = new Person("Kalle Kula", "Kulgatan 9");
sResult = myPerson;

Exemplet ovan definierar en Person-konstruktor som kan användas för att skapa nya objekt. Dessa objekt kan omvandlas till strängar tack vare att vi lägger in funktionen toString i Person.prototype. Avslutningsvis skapar vi en ny person och tilldelar personen till strängen sResult. Tilldelningen till sResult omvandlar objektet till en sträng för att beräkna den text som skall tilldelas sResult. Resultatet blir

Kalle Kula, Kulgatan 9

Om man inte skapar en toString-funktion i Person.prototype, kommer den från Object.prototype att användas i stället. Resultatet hade då blivit

[object Object].

Det globala objektet

När du kör skript i Ethiris finns ett antal objekt tillgängliga att använda. Det viktigaste av dessa är det så kallade globala objektet. Ett annat centralt objekt är datalagrets skriptrepresentation, DataStore-objektet.

Det finns alltid ett så kallat globalt objekt tillgängligt. Det globala objektet har de egenskaper som föreskrivs av specifikationen för programspråket ECMAScript.

Det globala objektet har dessutom ytterligare egenskaper:

DataStore är skriptrepresentationen av datalagret. Detta objekt låter dig komma åt variabler i datalagret inifrån skript.

COMObject är en konstruktor som skapar nya COM-objekt. Konstruktorn kan ta ett GUID eller ett ProgID. När du skapat objektet, kan du anropa metoder, hämta egenskaper och ta emot events från COM-objektet.

Timer är en konstruktor som skapar nya objekt för att mäta och kontrollera om en viss tid har gått.

File är en konstruktor som skapar nya objekt för att läsa från och skriva till textfiler.

Process är en konstruktor som kan användas för att starta och terminera externa program från skript.

Clients är ett hjälpobjekt med ett antal metoder som används för att få tag i referenser till olika klientobjekt. Clients är endast tillgänglig från nivå Advanced och uppåt.

Transaction är ett objekt som returneras när man skickar ett kommando till en eller flera klienter.

Cameras är ett objekt som innehåller en lista över alla kameror som är definierade i servern.

RemoteClients är ett objekt som innehåller en lista över de klienter som är definierade under Anslutna klienter i Ethiris Admin.

AccessControllers är ett objekt som innehåller en lista över de Dörrenheter som definierats i serverns konfiguration.

Date är ett objekt som kan användas för beräkningar på datum och tid.

Scope kedjan

Scope-kedjan ser lite annorlunda ut i Ethiris än vad den kanske gör i andra program som använder ECMAScript.

Låt oss börja med att titta på variabel- och funktionsdeklarationer. Exempel på sådana deklarationer är:

var temp;
var myString = "hej";
function add(a, b) { return a+b; }

För att förklara begreppet scope-kedja, beskrivs nedan hur skriptmotorn bär sig åt för att nå egenskaper i olika objekt.

De namn som du skriver in i skript kallas för identifierare. Tag följande uttryck som exempel:

offset + factor*value;

I exemplet ovan är offset, factor och value identifierare.

När du använder en identifierare, så försöker skriptmotorn hitta en egenskap med det namnet bland objekten i den så kallade scope-kedjan. Scope-kedjan är helt enkelt en lista med objekt. Normalt innehåller scope-kedjan bara det så kallade globala objektet. För att du enkelt skall kunna komma åt variabler i datalagret, innehåller scope-kedjan även DataStore-objektet.

Tack vare detta kan du skriva t ex

variable1 + variable2;

i stället för

DataStore.variable1 + DataStore.variable2;

Skriptmotorn börjar leta efter en egenskap med namnet “variable1” i det globala objektet. När den inte hittar någon sådan där, fortsätter den till nästa objekt i scope-kedjan, nämligen DataStore-objektet. Där finns en sådan egenskap, och den används i uttrycket. Man säger att identifieraren kunde resolveras.

Om en identifierare inte kan resolveras, kastas ett undantag (eng. exception). Följande skript kommer att returnera “Reference error” eftersom det inte finns något objekt med egenskapen “vriable1” i scope-kedjan.

try
{
   return vriable1 + variable2; // OBS Stavfel!
}
catch(e)
{
   return e.message;
}

Variabler i datalagret

Datalagrets variabler är av typen Integer, Double, String och Boolean och konverteras till ECMAScript-värden när du hämtar de egenskaperna i skriptobjektet DataStore.

DataStore-typerna Integer och Double konverteras till värden av typen Number utan att förlora precision.

DataStore-typerna String och Boolean konverteras förlustfritt till ECMAScript-typerna String respektive Boolean.

ECMAScript-värden av typen Number avrundas till närmsta heltal vid tilldelning till en DataStore-variabel av typen Integer. Du förlorar ingen precision vid tilldelning till en Double.

Vid tilldelning av olika datatyper används följande konverteringsregler.

Bitvisa uttryck

I ECMAScript finns de bitvisa operatorerna AND, OR, XOR och NOT tillgängliga. Dessutom finns så kallade bitvisa shift-operatorer: Left Shift, Signed Right Shift och Unsigned Right Shift.

Ofta används en heltals-variabel för att föra över upp till 32 booleska värden mellan system. Därför kan det vara bra att veta hur man utför bitvisa operationer på sådana värden.

Det kan t.ex. vara 32 olika status-värden som anger ifall olika kameror är aktiva eller ej, och som man har packat i ett heltal i stället för att ha 32 boolska variabler i OPC-servern.

Vissa av operatorerna i detta avsnitt kan användas som tilldelningsoperatorer. Dessa är

<<= >>= >>>= &= ^= |=

Uttrycket

a @= b (där @ representerar någon av operatorerna <<, >>, >>>, &, ^ eller |)

är ekvivalent med

a = a @ b.

Bitvis Left Shift ( << )

Bitvis Left Shift flyttar bitmönstret i den binära representationen av heltalet ett steg åt vänster och fyller på med nollor. Exempel:

00000000000000000000000000000101 = 5 00000000000000000000000000001010 = 10

Talets värde kommer härvid att multipliceras med två.

Att skapa en mask är ett viktigt användningsområde för denna operator när det gäller att arbeta med enskilda bitar i ett heltal. Skriptet

var mask = 1 << k;

skapar ett heltalsvärde som har en etta på position k och nollor för övrigt. Värdet av talet blir alltså 2k.

Bitvis Signed Right Shift ( >> )

Bitvis Signed Right Shift flyttar bitmönstret i den binära representationen av heltalet ett steg åt höger och fyller på med värdet av den bit som representerar tecknet på heltalet. Exempel:

00000000000000000000000000001010 = 10 00000000000000000000000000000101 = 5

11111111111111111111111111110110 = -10 11111111111111111111111111111011 = -5

Bitvis Unsigned Right Shift ( >>> )

Bitvis Unsigned Right Shift flyttar bitmönstret i den binära representationen av heltalet ett steg åt höger och fyller på med nollor. Exempel:

11111111111111111111111111110110 = -10 01111111111111111111111111111011 = 2147483643

Bitvis AND ( & )

Bitvis AND returnerar ett tal vars bitar är satta där motsvarande bitar är satta i båda operanderna.

Bitvis AND kan användas för att filtrera ut en viss bit ur ett heltalsvärde.

var mask = 1 << k;
var temp = value & mask;
var bit = temp? 0 : 1;

Operationen 1 << k ger ett binärt tal som har en etta på position k, och nollor för övrigt. Detta tal har värdet 2k.

Operationen value & mask filtrerar ut biten på position k från value.

Värdet av temp är 0 om biten på position i är satt, 2k annars. I sista steget omvandlar vi detta till heltalet 0 eller 1 beroende på om biten är satt eller inte.

Man kan använda uttrycket value & mask direkt i if-satser och logiska uttryck, eftersom det numeriska värdet 0 omvandlas till false och alla andra till true.

// Om biten på position k är satt i value
if(value & (1 << k))
{
   ...
}

// Om bitarna på position 3 och 5 är satta i value
if( (value & (1 << 3)) && (value & (i << 5)) )
{
   ...
}

Bitvis OR ( | )

Bitvis OR returnerar ett tal vars bitar är satta där motsvarande bit är satt i någon av operanderna.

Bitvis OR kan användas för att sätta en viss bit i ett heltalsvärde.

// Sätter bit nummer k i variabeln value
var mask = 1 << k;
value = value | mask;

Bitvis XOR ( ^ )

Bitvis XOR returnerar ett tal vars bitar är satta där motsvarande bit är satt i precis en (men inte båda) av operanderna.

Bitvis XOR kan användas för att växla en viss bit i ett heltalsvärde.

// Ändrar bit nummer k i variabeln value till 
// 0 om den är 1
// 1 om den är 0
var mask = 1 << k;
value = value ^ mask;

Bitvis NOT ( ~ )

Bitvis NOT byter alla nollor mot ettor i den binära representationen av ett värde. Exempel:

00000000000000000000000000001010 11111111111111111111111111110101

Bitvis NOT kan användas tillsammans med bitvis AND för att nollställa en viss bit i ett heltalsvärde.

// Sätter bit nummer k i variabeln value
var mask = 1 << k;
value = value & ~mask;