IBM Doors DXL: Line Feed w/o carriage return on stream write.

Problem

Die normale write methode eines Stream-Objektes erzeugt beim schreiben bei den folgenden Operationen stets ein Carriage Return am Zeilenende:

  • \n – erzeugt line feed mit carriage return
  • \012 – Explizites ausgeben oktal 12 (erzeugt line feed inkl. carriage return)
  • \r – erzeugt ledigtlich ein Carriage Return

Ansatz – Approach

Änderung der Stream write Methode

Lösung – Solution

Nutzt man die Methode mit dem Parameter binary, gibt es künftig mit \012 ausschließlich line feeds ohne carriage returns.

Stream theStream = write binary (dirPath "afile.txt" "");

VBA (Import Excel in Doors): In Unicode Datei schreiben (TSV Datei erstellen) / Write to unicode file (create TSV file)

Aufgabe – Problem

Der Inhalt eines Excel-Sheets mit Unicode soll in eine Datei geschrieben werden (damit z.B. IBM Doors über File -> Importieren -> Spreadsheet den Inhalt importieren kann).

A unicode file should be written with the contents of an Excel Sheet using VBA

Vorraussetzung – Prerequirement

Im VBA Editor muss im Menü unter „Verweise“ der Punkt „Microsoft Scripting Runtime“ aktiviert werden.
In the VBA Editor a reference should be added „Microsoft Scripting Runtime“

Ansatz – Approach

(Es wird VBA Code im Excel sheet hinterlegt was den Inhalt in eine TSV-Datei, die von Doors importiert werden kann, auslagert).
Das folgende Skript zeigt ein Iterieren über ein komplettes Excel sheet und schreibt die ersten 3 Spalten in Tabulator-Separiert in eine Datei. Somit könnte man eine TSV-Datei generieren die z.B. von IBM Doors eingelesen werden kann.

Lösung – Solution

    Dim letzteZeile As Integer
    Dim i As Integer
    
    Dim ID,Col1,Col2 As String
    
    Dim fso As New FileSystemObject

    ' Declare a TextStream.
    Dim stream As TextStream

    ' Create a TextStream.
    Set stream = fso.CreateTextFile("c:\dev\wingstext.csv", True, True)
      
    ' Finde die letzte Zeile
    letzteZeile = Cells.Find("*", [A1], , , xlByRows, xlPrevious).Row + 1
               
    For i = 1 To letzteZeile
        ID = Tabelle1.Cells(i, 1)
        Col1 = Tabelle1.Cells(i, 2)
        Col2 = Tabelle1.Cells(i, 3)
        
        stream.WriteLine ID & vbTab & Col1 & vbTab & Col2

    Next i
    
    ' Close the file.
    stream.Close

IBM Doors DXL: Ist ein Filter gesetzt? Test if a filter is active / has been set

Aufgabenstellung – Problem

In einem DXL Skript wird geprüft, ob ein Filter aktiviert ist. Diese Funktion ist nicht in der Referenz dokumentiert!

In a DXL Skript the activation of a filter should be tested. This is not documented in the reference!

Ansatz – Approach

Use the filtering method.

Lösung – Solution

Module m = current; 
bool filterIsActive = filtering(m);

Danke an Johannes R. 🙂

Welcher Prozess / Benutzer sperrt eine Datei?

Problem

Man erhält die Meldung, dass ein Prozess oder ein Benutzer eine Datei sperrt

Ansatz

* Herausfinden welcher Prozess die Datei sperrt bzw…
* Herausfinden welcher user die Datei sperrt (Windows Server)
* Prozess beenden

Lösung

Prozess sperrt Datei

Um herauszufinden welcher Prozess eine Datei sperrt, gibt es von Dr. Hoiby das Freeware Tool WhoLockMe. Es bietet an, den Prozess zu beenden

User sperrt Datei

Wenn ein User eine Datei sperrt, kann man auf dem Server unter Benutzung von

net file

oder

net share

entsprechende Benutzerlocks auffinden.

IBM Doors DXL: How-to access Microsoft SQL Server & Microsoft Access from DXL (eine Bibliothek für SQL Statements / a library to perform sql statements)

Aufgabenstellung — Problem

Es wird eine einfache Bibliothek benötigt um SELECT Statements auf eine Microsoft SQL Server Datenbank abzusetzen und diese in einem DXL Programm zu verwenden

A library is needed for easily creating SQL Statements with result sets. This library should be embeddable in a DXL Skript.

Ansatz — Approach

Die Bibliothek wird von Capri-Soft (Björn Karpenstein) ständig weiterentwickelt und an dieser Stelle mit regelmäßigen Updates zur Verfügung gestellt.

The library will be provided on this site with frequently updates and can be downloaded at this site.

Lösung — Solution

Implemented Functions

Skip selectStatement(string sql, string dbServer, string dbName, string dbUsername, string dbPassword)
Skip selectStatementOnMDB(string sql, string mdbFile) [since Version 0.3]
Array selectStatement(string sql, string dbServer, string dbName, string dbUsername, string dbPassword)
Array selectStatementOnMDB(string sql, string mdbFile)  [since Version 0.3]
int executeQuery(string sql, string dbServer, string dbName, string dbUsername, string dbPassword)
int executeQueryOnMDB(string sql, string mdbFile)  [since Version 0.3]
OleAutoObj openDatabase(string dbServer, string dbName, string dbUsername, string dbPassword)
OleAutoObj openDatabaseOnMDB(string mdbFile)  [since Version 0.3]

Download

First download the DXL-Library here…

sqllibrary.dxl — Version 0.3 [match filename the includes of the examples!!!]
sqlserverlibrary.dxl — Version 0.2
sqlserverlibrary.dxl — Version 0.1

Create your DXL Skript in Doors

Recommendation: When you are writing your SQL Statements, use the CAST-Method every time you define a Field in the SELECT-Clause.

Eample for Version 0.3 with MS Access Support

In this example (only works since version 0.3) you can access .mdb Files like in the following example:

#include "C:\\dev\\dxl\\client\\sqllibrary.dxl";


Skip result = selectStatementOnMDB("SELECT [Connector_ID], [Connector_Type], [Btm_Mid_Label], [Stereotype]  FROM q_connector", "\\\\bbmag295\\ProcessModellingPrototypeAdvanced\\mdb\\modell.eap");
				
// Iteration through the SkipList
for myIterator in result do 
{
   string keyValue = (string key(result));
   DxlObject currentObject = null;

   if(find(result, keyValue, currentObject))
   {
       // Just put the column names here.. it will work
       print (string currentObject->"Connector_ID") "\t";
       print (string currentObject->"Connector_Type") "\t";
       print (string currentObject->"Btm_Mid_Label") "\t";
       print (string currentObject->"Stereotype") "\n";
   }
}

delete result;

Example:

string customFieldSQL= "" //-
" SELECT  CAST(pk.PROJECT_KEY+'-'+CAST(ji.issuenum as VARCHAR(7)) as varchar(10)) as pkey,  " //-
"	     CAST(convert(char, cg.created, 120) as varchar(30)) as on_date, " //-
"		 CAST(ci.field as varchar(50)), " //-
"		 CAST(cg.author as varchar(10)) as changed_by, " //-
"		 CAST(ISNULL(ci.oldstring,'') as varchar(4096)) as changed_from,  " //-
"		 CAST(ISNULL(ci.newstring,'') as varchar(4096)) as changed_to " //-
" FROM jiraissue ji, changegroup cg, changeitem ci, project_key pk " //-
" WHERE ci.groupid = cg.id " //-
" AND cg.issueid = ji.id  " //-
" AND pk.PROJECT_ID=ji.PROJECT " //-
" AND ci.field = '" customField "' " 

Examples for Version 0.2

#include "sqlserverlibrary.dxl"; // renamed to sqllibrary since version 0.3

// Here you can define your connection setting
string dbServer="IPorSERVERNAME";
string dbName="Databasename";
string dbUsername="databaseuser";
string dbPassword="databasepasswort";

// Reduce methods only to get SQL 
Skip selectStatement(string sql) 
{	return selectStatement(sql,dbServer,dbName,dbUsername,dbPassword); }
Array selectStatement(string sql) 
{	return selectStatement(sql,dbServer,dbName,dbUsername,dbPassword); }
int executeQuery(string sql) 
{ return executeQuery(sql,dbServer,dbName,dbUsername,dbPassword); }

// A manipulation like INSERT/UPDATE/DELETE
executeQuery("INSERT INTO table(col1) VALUES('TEST')");

// SKIPLIST: SELECT statement will return a SKIP-List with 
//           dynamic attributes (KEY is first column) 
Skip result = //-
selectStatement("SELECT [id], [col1], [col2], [col3], " //-
			    "[col4], [col5], [col6] " //-
				"FROM table " //-
				"WHERE col1<>'TEST' ORDER BY id ASC");
				
// Iteration through the SkipList
for myIterator in result do 
{
   string keyValue = (string key(result));
   DxlObject currentObject = null;

   if(find(result, keyValue, currentObject))
   {
       // Just put the column names here.. it will work
       print (string currentObject->"id") "\t";
       print (string currentObject->"col1") "\t";
       print (string currentObject->"col2") "\t";
       print (string currentObject->"col3") "\t";
       print (string currentObject->"col4") "\t";
       print (string currentObject->"col5") "\t";
       print (string currentObject->"col6") "\n";
   }
}

delete result;

// Array: SELECT statement will return an  Array 
Array result2 = selectStatement("SELECT [id], [col1], [col2], " //-
				"[col3], [col4], [col5], [col6] " //-
				"FROM table " //-
				"WHERE col1<>'TEST' ORDER BY id ASC");

int i;
for (i=0; i<resultCount; i++)
{
   DxlObject currentObject2 = (DxlObject get(result2,i,1));
   
   if(!null(currentObject2))
   {
       // Just put the column names here.. it will work
       print (string currentObject2->"id") "\t";
       print (string currentObject2->"col1") "\t";
       print (string currentObject2->"col2") "\t";
       print (string currentObject2->"col3") "\t";
       print (string currentObject2->"col4") "\t";
       print (string currentObject2->"col5") "\t";
       print (string currentObject2->"col6") "\n";
   }
}

delete result2;

Example for Version 0.1

#include "sqlserverlibrary.dxl"; // renamed to sqllibrary since version 0.3

// Here you can define your connection setting
Skip selectStatement(string sql)
{
	// Connection details for test
	string dbServer="DATABASE_SERVER_OR_IP";
	string dbName="DATABASE_NAME";
	string dbUsername="DATABASE_USER";
	string dbPassword="DATABASE_PASSWORD";

	return selectStatement(sql,dbServer,dbName,dbUsername,dbPassword);
}

// selectStatement will return a SKIP-List with dynamic attributes
Skip result = //-
selectStatement( //-
"SELECT col1,col2,col3,col4,col5,col6 " //-
			"FROM table " //-
			"WHERE col1 " //-
			" NOT IN ( " //-
			"	SELECT col1 " //-
			"	FROM table " //-
			" 	WHERE LTRIM(RTRIM(col1))<>'---' " //-
			"   GROUP BY alarm_id " //-
			"	HAVING COUNT(*)>1 " //-
			"	) ")
				
// Iteration durch SkipList
for myIterator in result do 
{
   string keyValue = (string key(result));
   DxlObject currentObject = null;

   if(find(result, keyValue, currentObject))
   {
       // Just put the column names here.. it will work
       print (string currentObject->"col1") " ";
       print (string currentObject->"col2") " ";
       print (string currentObject->"col3") " ";
       print (string currentObject->"col4") " ";
       print (string currentObject->"col5") " ";
       print (string currentObject->"col6") "\n";
   }
}

delete result;

Comment

Don’t forget to leave some improvement comments here!!!

IBM Doors DXL: Zugriff auf MS SQL Server

Aufgabenstellung

In der Programmiersprache DXL von IBM Doors soll auf Daten eines Microsoft SQL Servers zugegriffen werden.

Ansatz

Die Verwendung der COM-Schnittstelle erlaubt Zugriff auf die ADODB.Connection. Hierbei handelt es sich um eine Microsoft Bibliothek für den Zugriff auf SQL Server.

Lösung 1

Das folgende Skript erlaubt Zugriff auf den Microsoft SQL Server und führt eine SELECT-Anweisung durch:

// Connection details
string dbServer=“SERVER“;
string dbName=“DATABASE“;
string dbUsername=“USER“;
string dbPassword=“PASS“;

string connectionString = „Data Source='“ dbServer „‚;
Initial Catalog='“ dbName „‚;User Id='“ dbUsername „‚;Password='“ dbPassword „‚;“;

OleAutoArgs oleAutoArgs=create;
OleAutoObj adodbConnection, adodbRecordset, objFields, objField;

string fieldName, result, err;
int numFields, index;

// Instantiate a new ADODB Connection object
adodbConnection = oleCreateAutoObject „ADODB.Connection“;

if (null adodbConnection)
{
print „Unable to instantiate database connection\n“;
halt;
}

// Instantiate a new ADODB Recordset object
adodbRecordset = oleCreateAutoObject „ADODB.Recordset“;

if(null adodbRecordset)
{
print „Unable to create Recordset object\n“;
halt;
}

// Connection details

// Set the provider and data source of the connection
// based on information from connectionstrings.com
olePut(adodbConnection, „Provider“, „sqloledb“);
clear oleAutoArgs;
put(oleAutoArgs, connectionString );
// „Password=“ dataPass „;“)
// Open the connection to the database
err=oleMethod(adodbConnection, „Open“, oleAutoArgs);

if(!null err „“)
{
print „Error opening database: “ err „\n“;
halt;
}

// SQL Command: Open a cursor to return all columns and rows of ‚tableName‘
clear oleAutoArgs

put(oleAutoArgs, „select * from [alarms] order by 1“) // SQL Command
put(oleAutoArgs, adodbConnection) // ACTIVE CONNECTION
put(oleAutoArgs, 1) // CURSOR TYPE – ‚adOpenKeyset‘
put(oleAutoArgs, 1) // LOCK TYPE – ‚adLockReadOnly‘
put(oleAutoArgs, 1) // OPTIONS – ‚adCmdText‘

err=oleMethod(adodbRecordset, „Open“, oleAutoArgs);

if(!null err „“)
{
print „Error opening table: “ err „\n“;
halt;
}

// From the Recordset object, list each field name (defined in database)
oleGet(adodbRecordset, „Fields“, objFields);
oleGet(objFields, „Count“, numFields);

for(index=0; index0?“\“,“:““) „\““ fieldName;
}

print „\“\n“;

// From the Recordset object cursor, loop through and print each row
while(true)
{
result=““;
clear oleAutoArgs;
put(oleAutoArgs, 2); // StringFormat – ‚adClipString‘
put(oleAutoArgs, 1); // NumRows
put(oleAutoArgs, „\“,\““); // ColumnDelimiter
put(oleAutoArgs, „\““); // RowDelimiter
put(oleAutoArgs, „“); // NullExpr
oleMethod(adodbRecordset, „GetString“, oleAutoArgs, result);
if(length(result)<=0) break else print "\"" result "\n"; } [/javascript]

Lösung 2

Das untere Beispiel zeigt die Kapselung in eine Funktion und den Zugriff auf Einzelwerte, bzw. auf einzelne Zellen eines Recordsets. Dieses Beispiel lässt sich ausgezeichnet in andere Bibliotheken auslagern:

OleAutoArgs args = null;

void cleanup (OleAutoObj &obj)
{
if (!null obj)
{
oleCloseAutoObject obj;
obj = null;
}
}

// some syntax helpers for arguments (from DXL standard library) …
void checkNull (string s)
{
if (!null s)
{
print „Error: “ s „\n“ dxlHere();
halt;
}
}

OleAutoArgs createArgs ()
{
if (!null args) delete args;
args = create();
return args
}

OleAutoArgs ::<-(OleAutoArgs x, int a) { put(x, a); return x } OleAutoArgs ::<-(OleAutoArgs x, string a) { put(x, a); return x } OleAutoArgs ::<-(OleAutoArgs x, bool a) { put(x, a); return x } // Hier bekommt man eine Spalte des aktuellen Records als String string stringProperty (OleAutoObj obj, string s) { string result = null; checkNull oleGet (obj, s, result); return result } void selectStatement(string sql, string dbServer, string dbName, string dbUsername, string dbPassword) { string connectionString = "Data Source='" dbServer "';Initial Catalog='" dbName "';User Id='" dbUsername "';Password='" dbPassword "';"; OleAutoArgs oleAutoArgs=create; OleAutoObj adodbConnection, adodbRecordset, objFields, objField; string fieldName, result, err; int numFields, index; // Instantiate a new ADODB Connection object adodbConnection = oleCreateAutoObject "ADODB.Connection"; if (null adodbConnection) { print "Unable to instantiate database connection\n"; halt; } // Instantiate a new ADODB Recordset object adodbRecordset = oleCreateAutoObject "ADODB.Recordset"; if(null adodbRecordset) { print "Unable to create Recordset object\n"; halt; } // Connection details // Set the provider and data source of the connection // based on information from connectionstrings.com olePut(adodbConnection, "Provider", "sqloledb"); clear oleAutoArgs; put(oleAutoArgs, connectionString ); // "Password=" dataPass ";") // Open the connection to the database err=oleMethod(adodbConnection, "Open", oleAutoArgs); if(!null err "") { print "Error opening database: " err "\n"; halt; } // SQL Command: Open a cursor to return all columns and rows of 'tableName' clear oleAutoArgs put(oleAutoArgs, sql) // SQL Command put(oleAutoArgs, adodbConnection) // ACTIVE CONNECTION put(oleAutoArgs, 1) // CURSOR TYPE - 'adOpenKeyset' put(oleAutoArgs, 1) // LOCK TYPE - 'adLockReadOnly' put(oleAutoArgs, 1) // OPTIONS - 'adCmdText' err=oleMethod(adodbRecordset, "Open", oleAutoArgs); if(!null err "") { print "Error opening table: " err "\n"; halt; } // From the Recordset object, list each field name (defined in database) oleGet(adodbRecordset, "Fields", objFields); oleGet(objFields, "Count", numFields); for(index=0; index0?“\“,“:““) „\““ fieldName;
}

print „\“\n“;

// From the Recordset object cursor, loop through and print each row
while(true)
{
// Break when we are still at the end
bool bEOF = false;
checkNull oleGet(adodbRecordset, „EOF“, bEOF);
if (bEOF) break;

oleGet(adodbRecordset, „Fields“, objFields);

// Hier werden die Einzelfelder ausgelesen
oleGet(objFields, „Item“, createArgs <- 0 , objField); string feld1 = stringProperty(objField, "Value"); oleGet(objFields, "Item", createArgs <- 1 , objField); string feld2 = stringProperty(objField, "Value"); oleGet(objFields, "Item", createArgs <- 2 , objField); string feld3 = stringProperty(objField, "Value"); print feld3 " " feld1 " " feld2 "\n"; checkNull oleMethod (adodbRecordset, "MoveNext"); } cleanup adodbConnection; cleanup adodbRecordset; cleanup objFields; cleanup objField; } // Connection details string sql="select [alarm_id], [subsystem], [bitnumber] from [alarms] order by 1"; string dbServer="SERVER"; string dbName="DATENBANK"; string dbUsername="USER"; string dbPassword="PASS"; selectStatement(sql,dbServer,dbName,dbUsername,dbPassword); [/javascript]

IBM Doors DXL Skript: Eingeloggte User und blockierte Module / current logged in users and modules blocked or in edit

Problembeschreibung – Problem description

Aufgrund von Lizenzeinschränkungen (z.B. durch die floating Licence) ist es manchmal nötig die Liste der momentan eingeloggten DOORS-User herauszufinden. Darüberhinaus möchte man oft wissen, wer welches Modul gerade editiert.

Because of licence restrictions in IBM DOORS it is sometimes necessary to find out which users are currently logged in. Further it is helpful to know which users are currently editing or blocking one module.

Ansatz – Approach

Das folgende DXL Skript gibt eine Liste der eingeloggten User und der Module aus, die gerade editiert werden.

The following DXL Skript creates a list of logged in users an blocked modules.

Lösung – Solution

Das DXL Skript kann als Menüpunkt in Doors eingebunden werden (hierzu gibt es genug IBM Beispiele).

Please follow IBM examples to embed the DXL script as menu in your DOORS client.

Download DXL Skript

Achtung! Dieses Skript funktioniert nur mit Administratorrechten.
Please note: You will need admin rights to show the Dialogs.

IBM Doors DXL: Tipps und Tricks

Zahlen an String konkatenieren

Zahlen oder andere Datentypen die in einen String gecastet werden sollen müssen mit leerem String abgeschlossen werden:

real zahl = 4.555;
print "Die Zahl lautet: " zahl " "; 

Jedes Attribut sollte bei einem String-Vergleich immer mit Doppelanführungszeichen abgeschlossen werden.

Substrings in DXL

Auf Abschnitte aus einem String zugreifen:

string ganzerString = "Hello world";
string meinSubstring = ganzerString[0:6];
string vonVierBisEnde = ganzerString[4:];
print meinSubstring " " vonVierBisEnde; 

Es können auch Funktionen und Variablen in die eckigen Stringklammern geschrieben werden.

int i=2;
string einString = "Hello world";
print einString[i: (length(einString)-4)];

Strings unterliegen der lexikographischen Ordnung

if("Hallo" > "hallo") print "Hallo ist groesser uppercase first";

Ist ein Teilstring enthalten indexOf

string einString = „Hello world“;
if (matches(„Hello“,einString))
{
print „Is drinn!“;
}

Type conversions von String

string eineZahl= „12.3“
int iType = intOf(eineZahl);
real rType = realOf(eineZahl);
char cType = charOf(iType); // Konvertiert in den Ascii-Wert

Datumsvergleich

Date heute = today();
Date einDatum = „1/1/2000 0:0:0“;
if(heute>einDatum)
{
print „Das Jahr 2000 ist vorbei!“;
}

FOR-Schleifen

for i in 0 : 100 do
{
   print "Hello world! " i "";
}

for (x=0; x<100; x++)
{
   print "Hello world";
}

Ausschalten von Skript Timeout

Folgendes Statement im Kopf der Schleife setzt den Skript-Timeout auf unendlich:

pragma runLim,0 // 0 bedeutet unendlich

Iterations- und Ablaufssteuerungsbefehle

halt: Das gesamte DXL Programm wird abgebrochen
continue: Vergiss den Rest der Schleife und beginne mit nächster Iteration
break: Brich die Schleife komplett ab

Left Trim in DXL

string leftTrim(string einString)
{
   int dieLetztePosition=-1;
   for (x=0; x<length(einString); x++)
   {
      if( einString[x:x] != " " )
      {
        dieLetztePosition=x;
         break;
      }
   }

   return einString[dieLetztePosition:];
}


print leftTrim("    Hello world!");

Bibliothek gängiger String-Funktionen wie in JAVA

DXL ist sehr rudimentär was String Manipulationen angeht.

Ich empfehle meine String Bibliothek mit den gängigsten String Functions zu verwenden:

Zum Beitrag über String Functions

Call by reference und Call by Value

a: Call by Value
b: Call by Reference

Bei Call by Reference wird die Zahl nach der Übergabe auch im Context außerhalb des FUnktionsaufrufs geändert.

void myfunction (int a, int<b)
{
   b=a+100;
   a=b+1;
   return(a);
}

Durch statisches Array iterieren

string biers[]={"Becks", "Licher", "Jever", "Heineken"};

// sortiere die Biers lexikographisch
sort(biers);

int i=0;
for (i=0; i<sizeof(biers); i++)
{
   print biers[i] "\n";
}

Dynamische Arrays

Dynamische Arrays sind in DXL immer 2-dimensional.

// Eine Spalte, eine Zeile array
Array einArray = create(1,1);

put(einArray, "Huhuhuhu", 0,0);
// Das Array passt sich automatisch an
put(a,23,10,10);

string huhuString = (string get(einString,0,0));
real eineZahl = (real get(10,10));

// Speicher wieder freigeben
delete(einArray);

Anmerkung zu dyn. Arrays:

Es gibt die Möglichkeit ein statisches Array dynamisch zu alloziieren, allerdings nicht zur Laufzeit anzupassen.

int hallo = 10;
int i;

string test[hallo];

for (i=0; i<10; i++)
{
	test[i]=i "";
}

for (i=0; i<10; i++)
{
	print test[i] "\n";
}

Da ein dynamisches Array allemöglichen Datentypen enthalten kann, gibt es hier keine Standardfunktion um dieses zur sortieren. Die eingetragenen Daten können von beliebigem Typ sein ; z.B. kann auf (1,1) ein String stehen, auf (2,1) ein Object, auf (3,1) eine Attributdefinition etc. Man könnte die Spalte in ein Standard-Array (z.B. string myArray) wie im obigen Beispiel übertragen, darin sortieren und danach zurückkopieren.

Es gibt keine Möglichkeit die Größe eines dynamischen Arrays (Anzahl Zeilen) zu ermitteln.
Da man aber das Array im Verlauf des Programms mit „put“ gefüllt hat, wurden dort zwangsläufig die maximalen Koordinaten verwendet; wenn man die größte Koordinate also nach der Befüllung speichert (typischerweise in globalen Variablen myMaxX, myMaxY), kann man sie apäter wieder nutzen.

Skip Listen

Skip Listen sind assoziative Arrays (PHP), HashMaps (Java) oder Dictionaries (C#) . Laut IBM sind dies sehr effiziente Datenstrukturen.

Skip idStringList = createString; // Für String Datentypen
Skip idList = create; // Für nicht-String Datentypen

// put Paramater: skip-list, key, value
put (idList, "234-22-2345", "Hallo");

// PUT liefert falls zurück falls der Wert schon vorhanden ist
put (idList, "123-45-6789", "Welt");

string gleichZuweisen;
// find nutzen um zu testen ob in Skip List vorhanden
if( find(idList, "234-22.2345", gleichZuweisen) )
{
   print gleichZuweisen;
   delete (idList, "234-22.2345");
}

put(idList "234-222-2222", "world");

Beispiel um Dubletten mit Skip-Listen rauzufiltern:

Skip myList = create;
int numList[] = {1,6,2,3,1,2,1,4,2,5,6,7,2,1,3,4,8};

int i;
for (i=0; i<sizeof(numList); i++)
{
   put (myList, numList[i], numList[i]);
}

// Iteration durch SkipList
for i in myList do 
{
   print i;
}


Iteration durch SkipList mit DxlObject.

Anmerkung: DxlObject eignet sich prima als Ersatz für ein Model/Value-Object. Hierzu folgendermaßen d


Skip result = create;
DxlObject obj = new();
obj->“alarm_id“ = „Bla!“; ….
put(result, alarm_id, obj);
….

// Iteration durch SkipList
for myIterator in result do 
{
   string keyValue = (string key(result));
   DxlObject currentObject = null;

   if(find(result, keyValue, currentObject))
   {
       // Just put the column names here.. it will work
       print (string currentObject->"alarm_id") " ";
       print (string currentObject->"subsystem") " ";
       print (string currentObject->"bitnumber") " ";
       print (string currentObject->"eqwinid") " ";
       print (string currentObject->"eqwintextid") " ";
       print (string currentObject->"eqwinhandling ") "\n";
   }
}

Current

Current ist eine Referenz auf das momentan selektierte / markierte Element und kann ein Project, Folder, Module oder Object sein.

Wenn man über mehrere Objekte eines Moduls iterierts muss es oft explizit das current gesetzt werden.

Project p = current;
Folder f = current;
Module m = current;
Object o = current;

current = f;

Über das Projekt iterieren

Project p = create("/Project1","");

for name in p do 
{
   print name;
}

current = folder "/" 

for itemRef in current  do 
{
   print name(itemRef) " " type(itemRef) " \n";
}

Unterschied zwischen Baseline und Baseline Set

Eine Baseline ist ein eingefrorener Stand für ein Modul. Ein Baseline Set ist ein eingefrorener Stand für die gesamte Maschine / das Projekt / alle Module des Projekts.

Zugriff auf Attribute eines Moduls

Module m = null;
m=read("/Projekt/Modul", true);

string modulName = m."Name";
string attributName = "Created On";
Date createdOn = m.attributeName;

Editieren von Modulattributen

current = folder("/New Family Car Project/Requirements");
Module m = edit("System Requirements", true);
m."Description" = "Capri Soft- System requirements";
save(m);
close(m);
refreshDBExplorer();

Link Beziehungen

ManyToMany beliebig viele Inlinks beliebig viele Outlinks
OneToMany beliebig viele Inlinks maximal ein Outlink
manyToOne beliebig viele Outlinks maximal ein Inlink
OneToOne maximal ein Outlink maximal ein Inlink

Zugriff auf Attribute

identifier(o): Die Doors-Containernummer (z.B. UR12)
number(o): Sagt was aus über die Stellung in der Hierarchie
level(o): Gibt die Tiefe der Schachtelung zurück
leaf(o): Gibt true zurück, wenn das Objekt keine Unterobjekte hat.

// Modul manuell öffnen und im Modul-Menü Tools -> "Edit DXL" 
Module m = current;

Object o;

for o in m do 
{
   print number(o) " \t\t"identifier(o) "    ";
   print o."Object Heading" " ";
   print o."Object Text" "\n"
}

Navigation durch Kindkapitel eines Objektes

Object erstesKind = first(o);
Object letztesKind = last(o);
Object vorherige = previous(o);
… next(o)
… first sibling(o)
… last sibling(o)
… next sibling(o)

Bsp.: Zähle alle Kinder eines Kapitels:

Module m = current;
Object oChild, o = current;

int count = 0;
for oChild in o do 
{
   count++;
}

print count " ";

Loop über alle Links jedes Linkmoduls

Folgendes Beispiel iteriert über die Outgoing links (->) jedes Linkmoduls („*“)

// Modul öffnen und auf Tools -> Edit DXL 
Module m = current;
Link derLink;
Object objekt ;
int linkAnzahl = 0;
Skip eindeutigeZielModule;

for objekt in m do
{
   for derLink in objekt->"*" do
   {
      linkAnzahl++;
      

      // Der Name des Ziel Moduls;
      print fullName target(derLink) " \n";

   }
}

print "Es gibt " linkAnzahl " Outgoing Links!";

Mit diesem Beispiel kann man sich die Outgoing Links in einem Layout DXL ausgeben lassen

Link lnk;
string textToShow = "";
bool isFirst = true;

for lnk in obj->"*" do
{
	string tmn=fullName target(lnk);

	if(!open module tmn)
	{
		read(tmn,false);
	}

	Object tgt = target(lnk);

	if(isFirst)
	{
		textToShow = identifier(tgt) "" textToShow ;
	}
	else
	{
		textToShow = identifier(tgt) "\n"  textToShow;
	}

	isFirst = false;

}

displayRich (textToShow "");

Bei Incoming Links muss das SourceModul immer geöffnet werden:

// Modul öffnen und auf Tools -> Edit DXL 

void handleInLinks(Object einObjekt)
{
   LinkRef eineLinkReferenz;
   string modulWoDerIncomingLinkHerkommt;

   print identifier(einObjekt) " \n";

   for eineLinkReferenz in einObjekt<-"*" do
   {
     modulWoDerIncomingLinkHerkommt= fullName source (eineLinkReferenz);
     print modulWoDerIncomingLinkHerkommt;

   }
}

Module m = current;

Link lnk;
LinkRef lref;
Object o;
int numLinks = 0;
string srcModuleName;

for o in current Module do 
{
   for lref in o<-"*" do
   {
     srcModuleName = fullName source (lref);

     if (!open module srcModuleName)
     {
        read(srcModuleName, false);
     }
   }
}

for o in m do
{
  handleInLinks(o);
}

Eine weitere Möglichkeit bietet das Iterieren über die Module der Inlinks. Man kann sie z.B. in einer Schleife öffnen.

string srcModName;
for srcModName in o<-"*" do
{
   print srcModName "\n";
}

// Voller Pfad der Module zu den IncomingLinks
ModName_ srcModRef
for srcModRef in o<-"*" do
{
    print fullName(srcModRef) "\n";
}

Hier ein Beispiel für ein Layout DXL, das alle Incoming Link Objekte ausgibt

LinkRef lRef;
Link aLink;
ModName_ srcModRef;
int inCount = 0;

Object anObj = obj;

for lRef in anObj <- "*" do { 

 string smn = fullName(source(lRef)) 

  if (! open(module(smn))) { 
      oMod = read(smn, false) 
   } 
} 

for aLink in anObj <- "*" do 
{ 
         Object src = source aLink;
          displayRich( identifier(src) "");
} 

Alle Attribute eines Moduls ausgeben

Module m=read("System Requirements", false);

AttrDef adRec;

for adRec in m do 
{
   if(!adRec.system)
   {
      print adRec.typeName " attribute: " adRec.name " \n";
   }
}

Rechnen in Layout DXL

Wenn man auf einer Spaltenüberschrift in einem Doors Modul rechte Maustaste -> Properties -> Layout DXL -> Browse öffnet, kann man in eine Spalte hinzufügen um z.B. zu rechnen.

Die Variable obj ist die aktuelle Zeile.

real cost, result;
int noPerDay;

cost = obj."Cost";
noPerDay = obj."No per day";
result = cost * realOf(noPerDay);
display result "";

Filtern und Sortieren

Filter und Sortierungen können miteinander verknüpft werden

Module m=read("Stakeholder Requirements", false);

Filter f, f1, f2;
Sort s1,s2,s;

s1 = descending("Cost");
s2 = ascending("Priority");

f1 = attribute "Cost" > "1000.0"
f2 = attribute "Cost" < "2000.0"
f3 = contains (attribute "Object Text", "shall", false);

// Filter verknüpfen
f = f1 << f2 << f3;

// Sortierungen verknüpfen
s = s1 << s2;

set(m,f);
set(m,s);
filtering on;

In Datei schreiben

Stream output = write("c:\\datafile.txt");
Object o;
real cost;

for o in current Module do
{
   cost = o."Cost";
   output << cost "\n";
}

Lesen…

print fileExists("C:\\meinedate.txt") " ";
Stream eingabe = read "C:\\meinedate.txt";

string costLine;

while(!end(eingabe))
{
   input >> costLine;
   print costLine "\n";
}
close(eingabe);

User Interfaces

DB: Dialog Box
DBE: Dialog Box Element

OnChange-Ereignisse über callback()-Funktion, die mit Set auf das DBE gesetzt wird. set(DBE dbe, callbackfkt)

DB bierDialogBox = create ("Bier Type", styleStandard);

string meineRadioButtonListe[] = {"Heineken", "Becks", "Krombacher", "Licher"};
DBE bierName=field(bierDialogBox, "Biername:", "Schnaps", 20,false);
DBE radioButton = radioBox (bierDialogBox, "Biersche:", meineRadioButtonListe, 0);
DBE datumFeld = date (bierDialogBox, 30, today, true);
DBE slider = slider (bierDialogBox, "", 5,0,10);

void printOrder(DB win)
{
   string customer = get bierName;
   int radioButtonNummer = get radioButton;
   string radioButtonStyle=meineRadioButtonListe[radioButtonNummer];
   print "Ein " radioButtonStyle " bier vom " customer "";
}

apply (bierDialogBox, "Print", printOrder);

show bierDialogBox;

History

Die History wird mit einer Baseline abgespeichert und beginnt von vorne. D.h. das History-Objekt ist auf einem gerade gebaselintem Modul leer.

Bsp.-Anwendungsfälle

  • Änderungsreports
  • Recherche über Änderungen

Discussions

Eine Chatähnliche Option sind Discussions. Sie werden in der Baseline abgelegt aber nicht ins Archiv eingepackt. Discussions setzen nur Leserechte voraus. Derjenige der die Discussion ausgelöst hat ist der einzige, der sie schließen kann.

Probleme mit Rechten: Kein Zugriff auf ein Objekt

Wenn man keine Zugriffsrechte auf ein Objekt hat kann mit

inherited(o);

Das Objekt auf die Rechte des parents gesetzt werden.

Einfaches isNumeric

bool isNumeric(string einString)
{
   int x;
   for (x=0; x<length(einString); x++)
   {
      if( (!isdigit(einString[x]) ) << (einString[x]!='.' ) << (einString[x]!='-' ) )
      {
		return false;
      }
   }
   return true;
}


string eineZahl = "12.345";
print isNumeric(eineZahl) "";

Installierte und unterstützte Codepages anzeigen

int d;

int installed=0;
int supported=0;

print "INSTALLED:\n";
for d in installedCodepages do
{
	print d "\n";
	installed++;
}
print "\n\n\nSUPPORTED:\n";
for d in supportedCodepages do
{
	print d "\n";
	supported++;
}

print "Installed: " installed "\n";
print "Supported: " supported "\n";

Android&PHP Backend: Netzwerkkommunikation, Authentifizierung und Login

Aufgabenstellung

Es wird eine Möglichkeit gesucht eine Android App mit einen PHP Backend kommunizieren zu lassen

Intention

PHP mit MySQL Server erhält man oft schon mit billigem Webspace. Ein weiterer Vorteil eines fertigen Webpacks/Webspace ist, dass es nicht notwendig wird sich um eine Backupstrategie zu kümmern. In den meisten Fällen (SLAs und AGBs des Providers studieren) liegt dieses Problem in den Händen des Providers. Er kümmert sich auch um die Sicherheit des Servers. Bei Hackerattacken ist man nicht in der Verantwortung den Server zu härten und zu schützen. Daher kann der Provider auch keine Schadensersatzansprüche gegen den Kunden geltend machen, wenn ein Server gekapert wird und z.B. mit der Installation eines Botnetzes die Leistungsfähigkeit des Rechenzentrums beeinträchtigt ist.

Ansatz

  • Implementierung eines möglichst einfachen Authentifierungsmechanismus in PHP mit MySQL-Anbindung
  • Wegkapseln der Serviceaufrufe
  • Asynchrone Verarbeitung der JSON Aufrufe per Java Thread
    • Reaktion in der MainActivity beim Erhalten des Service-Ergebnisses für UI Manipulationen

Der häufigste Ansatz, den man in Tutorials findet, ist die Kommunikation über JSON, einem kompakten Datenformat in für Mensch und Maschine einfach lesbarer Textform zum Zweck des Datenaustauschs zwischen Anwendungen.

Lösung

BjoernServiceCaller.java – Klasse für weggekapselte Service aufrufe mit CallBack-Funktionalität über onLoginResult

package com.caprisoft.contactupload.services;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;

import com.caprisoft.contactupload.MainActivity;

import android.content.Context;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;


public class BjoernsServiceCaller 
{
	
	final String authenticationURL = "authenticationservice.php";
	private static BjoernsServiceCaller instance = null;
	private BjoernsServiceCaller() 	{}
	

    /**
     * Statische Methode, liefert die einzige Instanz dieser
     * Klasse zurück
     */
    public static BjoernsServiceCaller getInstance() 
    {
        if (instance == null) 
        {
            instance = new BjoernsServiceCaller();
        }
        return instance;
    }
	
	
	public void loginUser(final MainActivity ctx, 
                                    final String email, final String password) 
	{
        Thread t = new Thread()
        {
	        public void run() 
	        {
                        //For Preparing Message Pool for the child Thread
		        Looper.prepare(); 
				
		        HttpClient client = new DefaultHttpClient();
                         //Timeout Limit
		        HttpConnectionParams
                               .setConnectionTimeout(client.getParams(), 10000);
		        HttpResponse response;
		        JSONObject json = new JSONObject();
		        try
		        {
		            HttpPost post = new HttpPost(authenticationURL);
		            json.put("email", email);
		            json.put("password", password);
		            StringEntity se = new StringEntity( json.toString());  
		            se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE,
                                          application/json"));
		            post.setEntity(se);
		            response = client.execute(post);
		            /*Checking response */
		            if(response!=null)
		            {
		                BufferedReader reader = new BufferedReader
                                (
                                    new InputStreamReader
                                    (
                                       response.getEntity().getContent(), "UTF-8"
                                    )
                                );
		                String derText = reader.readLine();
                                // CallBack aufruf in MainActivity
		                ctx.onLoginResult(derText);
		            }
		
		        }
		        catch(Exception e)
		        {
		            e.printStackTrace();
		        }
		        Looper.loop(); //Loop in the message queue
	        }
        };
        t.start();          
    }
}

authenticationservice.php – Das PHP Backend, was die Datenbankabfragen macht und auf jedem Billigwebspace läuft

<?php  
    include("configuration/database.php");
	$json = file_get_contents('php://input');
	$obj = json_decode($json);
	$result2=mysql_query("SELECT id, activationcode FROM users ".
                                      "WHERE email='".$obj->{'email'}."' AND ".
                                      "password='".$obj->{'password'}."'") 
                                       or die(mysql_error());
	
	$user="nouser";
	while ($row = mysql_fetch_array($result2, MYSQL_NUM)) {
	   $user=$row[0];
	   $activationcode=$row[1];
	}	
    
    $posts = array(1);
    
    if($user!="nouser")
    {
    	if($activationcode=="activated")
    	{
    		session_start();
    		session_register("userid");
    		$_SESSION["userid"] =$user;
    		header('Content-type: application/json');
    		echo "success";
    	}
    	else 
    	{
    	   $registrationcode=substr(md5($obj->{'email'}." ".$obj->{'password'}),6);
    	   mysql_query("UPDATE users SET activationcode='".$registrationcode.
           "' WHERE email='".$obj->{'email'}."'") or die(mysql_error());
    	   mail($obj->{'email'},"Registrationcode for contactupload.com",
            "Hello!<br><br>\n You or someone else has registered this email ".
            "address via mobile phone.<br><br>\nThe generated registrationcode is ".
            $registrationcode.".<br><br>\nPlease type this registrationcode in ".
            "the register section of the contact sync app.<br><br> .
            "\nWith best regards,<br>\ncontactupload.com");
    		echo "not activated";
    	}
    }
    else 
    {
        echo "not registered";
    } 
    mysql_close($con);
?>

MainActivity.java – Die Klasse präsentiert den Code der Benutzeroberfläche der Activity (VIEW)

package com.caprisoft.contactupload;

import com.caprisoft.contactupload.services.BjoernsServiceCaller;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
	
	private EditText email = null;
	private EditText passwort = null;
	
	/** Called when the user selects the Send button */
	public void onClickLoginButton(View view) {
	    // Do something in response to button
		Log.d(this.getClass().toString(), "Login Button gedrückt!");
		BjoernsServiceCaller.
                getInstance().
                loginUser
                (
                    this, 
                    email.getText().toString(), 
                    passwort.getText().toString()
                );
	}
	
	/** Called when the user selects the Send button */
	public void onClickRegisterButton(View view) {
	    // Do something in response to button
		Log.d(this.getClass().toString(), "Register Button gedrückt!");
	}	
	
	public void onLoginResult(String text)
	{
		Toast.makeText(this, "HALLO: "+text, Toast.LENGTH_LONG).show();
	}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        email = (EditText) findViewById(R.id.textUsername);
        passwort = (EditText) findViewById(R.id.textPassword);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    
}

Activity_main.xml – Das eigentliche Frontend

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/labelUsername"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/labelUsername"
        android:textAppearance="?android:attr/textAppearanceLarge" />
    
  <EditText android:id="@+id/textUsername"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:inputType="textNoSuggestions"
        android:hint="@string/hintUsername" />
  
   <TextView
        android:id="@+id/labelPassword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/labelPassword"
        android:textAppearance="?android:attr/textAppearanceLarge" />
      
   <EditText android:id="@+id/textPassword"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:inputType="textPassword"
        android:hint="@string/hintPassword" />

   <Button
       android:id="@+id/buttonLogin"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:onClick="onClickLoginButton"
       android:text="@string/buttonLogin" />

   <Button
       android:id="@+id/buttonRegister"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:onClick="onClickRegisterButton"
       android:text="@string/buttonRegister" />
  
</LinearLayout>

AndroidManifest.xml – Permission für den Zugriff auf das Internet einrichten

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.caprisoft.contactupload"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
     <uses-permission android:name="android.permission.INTERNET" /> 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Die Besten natürliche VST-Instrumente / The best natural sounding VST Instruments

Natural sounding VST instruments

The following list contains a bunch of VST Instruments with natural sounds of naturall instruments, which are in my opinion the best implemented products.
Die nachfolgende Liste enthält einige VST Instrumente, die meiner Meinung nach einige der besten Umsetzungen für echte Instrumente sind.

Orchester / Orchestra

  • EastWest Symphonic Orchestra
  • Edirol Superquartett

Trompete / Trumpet

  • Miles’Tone VST Trumpet

Klavier / Piano

  • True Pianos
  • Grand Piano Pianos

Schlagzeug / Drums

  • EZDrummer
  • Drumkit from hell

by Björn Karpenstein