/
MDE

MDE

Inhaltsverzeichnis

MDE-Toolkit

Inhaltsverzeichnis

Inhaltsverzeichnis3

MDE-Verwaltung5

Aufruf der Verwaltung5

Definition der Abfragen6

Funktionen erfassen7

Programme importieren / exportieren9

Neues Programm definieren10

Grundlegende Einsprungspunkte definieren12

Sprachreferenz13

MDE oder PC13

Sequenzieller Ablauf und Steuerung13

Variablen16

Systemvariablen17

Konfigurationsvariablen lesen und setzten18

Protkollierung26

Arrays27

Properties28

Kommentare28

Die Funktion ASK()28

Die Funktion AddField()29

Maske, Reiter und Container30

Funktionen des Maskenobjektes (PGF)31

Scrollbalken für einen Container (SCR)31

Felder auf die Maske bringen32

Eigenschaften der Textfelder (TXT und EDT)34

Eigenschaften der Bezeichnungsfelder (LBL)35

Eigenschaften von Schaltflächen (CMD, CHK)36

Eigenschaften von Comboboxen (CBO, CBK, CBT)36

Eigenschaften von Comboboxen (OPT)37

Eigenschaften der Grids (GRD, GRH, GRS,GRT und GRM)37

Eigenschaften des Kalenders (CAL)40

Eigenschaften des Grafikobjektes (CHA)40

Felder gruppieren41

Masken und Container leeren42

Hotkeys42

Veränderung von Werten43

Funktions-Menüs auf Maske44

Ribbon-Bar auf Maske (Dynamic-Mode)45

Panels auf der Maske47

Maskensteuerung48

Tastatursteuerung50

Kommunikation / Rückfragen51

Operatoren52

Stringoperationen53

Runden56

Arbeiten mit Datumswerten57

Arbeiten mit Binärfeldern58

Schleifen und Verzweigungen58

Prüfung auf Funktionen59

Zugriff auf die lokale Datenbank59

Zugriff auf die lokale Datenbank mit oFields63

MS Sql-Server als lokale Datenbank64

Zugriff auf Serverprozess64

Kapselung von Servercode66

Zugriff auf das Dateisystem - Dateiupload67

Dateisynchronisation auf alle MDE69

Fotofunktionen – nur für das MDE71

Reports am MDE-PC73

Start externer Programme:74

Fremdsprachigkeit:74

MDE-Jobverarbeitung76

Unterschiede in den Sprachversionen:76

Umstellung von Version 1.xxx auf Version 2.xxx77

Umstellung auf Dynamic-Mode78

_INIT78

MASK78

Meldungen:80

Sonstige Änderungen:81

Der Serverprozess82

Der MDE-Systemstatus83

Die SWSWeb.ini83

Installationsprobleme85

Fallen in der Programmierung SQL85

Programmhistorie85

Empfehlungen90

Parameter in MDE-Jobs90

Hilfsfunktionen zur Analyse90

Granularität von MDE-Jobs91

Fehlerbehandlung und Stabilität91

MDE-Verwaltung

Aufruf der Verwaltung

Die MDE-Verwaltung finden Sie in den Stammdaten unter den Firmen Firmendaten.

Unable to render {include} The included page could not be found.

Die eigentliche Maske teilt sich über die Register in zwei Teile auf:

Abfragen

Hier werden die Tabellen und Inhalte definiert, die auf die einzelnen mobilen Datenstationen synchronisiert werden sollen.

Funktionen

In diesem Bereich werden die Masken, der Programmablauf, die Prüfungen und Buchungen definiert.

Definition der Abfragen

Die Basis der Programme auf den mobilen Geräten sind die Daten aus Professional. Da jedoch nicht alle Daten und vor allem nicht alle Felder der Tabellen in diesen Geschäftsprozessen benötigt werden, definiert man zu beginn, welche Daten mit dem Server in definierten Rhythmen synchronisiert werden sollen.

Unable to render {include} The included page could not be found.

Name

Dieser Name wird auf dem MDE angezeigt, wenn Daten aus diesem Bereich aktualisiert werden.

Ab Version 2.03.004 wird vor dem Namen eine # angezeigt, wenn die Synchronisation bei einem Erstsync unterbrochen wurde. In der Tabelle wird bei jedem Erzeugen der Tabelle eine # vorangestellt. Dieser wird nach vollständiger Synchronisation wieder entfernt. Am MDE-Client ist diese # allerding erst nach einem erneuten Aufruf nach einer Unterbrechung sichtbar.

Geräte

Über diesen Eintrag kann gesteuert werden, auf welchen Geräten diese Daten zur Verfügung stehen. Bleibt das Feld leer, so werden die Daten allen Geräten zur Verfügung stehen. Mehrere Geräte werden Kommagetrennt eingegeben.

Tabelle

Tabellenname in Professional-ERP

Felder

Hier werden Komma getrennt alle gewünschten Felder aufgeführt. Generell werden die Felder ID und CURTIMEST mit übertragen. Diese werden zur Synchronisation benötigt.

Index-Felder

Hier werden die Felder eingetragen, nach denen auf den mobilen Geräten am ehesten gesucht wird. Hier sind aktuell keine zusammen gesetzten Indexe möglich.

Filter

Es werden nur die Datensätze übertragen, für den der Filter gültig ist.

Um auch Gerätespezifische Daten filtern zu können, kann hier auch auf System- bzw. die Konfigurationsvariablen des MDE zugegriffen werden. Diese werden dann in spitzen Klammern angegeben.

and USER=<<MDEUSER>>

Es ist so, dass immer die Tabelle angesprochen wird, die als erste im Feld TABELLE eingetragen wird. Möchte man aus Index-Gründen den Join umgedreht eintragen, so muss die gewünschte Tabelle in Pipes angegeben werden.

Im folgenden Besipiel wird die Tabelle ARTIKEL synchronisiert:

POSITION join |ARTIKEL| on ART_ARTNR=POS_ARTNR

Tipp:

Immer wenn mehreren Tabellen und mit einem Filter über

curimest 
gearbeitet wird, sollte dieser in die in die „
Join
“-Bedingung aufgenommen werden. Dieses vermeidet eine
or
-Verknüpfung im Filter und macht auch keine Probleme (
nvl
) bei
left-join
Verknüpfungen.

WFKOPF join WFPOS on WFP_WFKID=WFKOPF.ID and year(WFKOPF.curtimest)>=2019

Tipp:

Bei einer Foxpro-Datenbank kann mit folgendem Trick auch ein Group by erreicht werden. Dabei ist der Group in der Spalte „Tabelle“ einzutragen. Es sind alle Felder plus die qualifizierte ID und der qualifizierte Zeitstempel aufzuführen.

WEKOPF join WEPOS on WEP_WEKID=WEKOPF.ID group by WEK_NUMMER, WEK_LSDAT,WEKOPF.ID,WEKOPF.CURTIMEST

Funktionen erfassen

Die Steuerung des Menüs, der Aufbau der Masken und die Logik innerhalb der Masken werden über das Register „Funktionen“ definiert.

Unable to render {include} The included page could not be found.

Dabei werden 3 unterschiedliche Ebenen unterschieden:

Funktion

Diese entsprechen einem kompletten Programmteil, der über das Menü am mobilen Gerät aufgerufen werden kann, oder als Folgedialog z.B. hinter einer Auswahl verbergen kann.

Spezielle Funktionen:

_LOGINWird beim Start der Loginmaske ausgeführt. _INFO zur Anzeige Versionsnummer.

_MENUWird nach dem Login ausgeführt. _LOGIN bei der Anmeldung

_SHUTDOWN Wird beim Abmelden / Beenden ausgeführt

Einsprungspunkt (lokal)

Einsprungspunkte sind wie Programmteile, die auf dem lokalen mobilen Endgerät zu bestimmten Zeiten aufgerufen werden.

Dabei gibt es vordefinierte Namen, die bei allen Funktionen definiert sind. Diese beginnen mit einem Unterstrich z.B. _INIT, _EXIT, _SHUTDOWN, _WHEN, _VISIBLE

Weitere fest definierte Präfixe definieren spezielle Zeitpunkte, wann diese Programmteile ausgeführt werden. Dazu gehören:

- VA_Validierung beim Verlassen des Feldes (Bei CBO und CHK beim Ändern)

- WH_Wird beim Betreten des Feldes gestartet

- VI_Wird beim Anzeigen ausgeführt (Im Einsprungspunkt können über RefThis die Eigenschaften beeinflusst werden. Z.B.:

RefThis.Backcolor= RGB(255,0,0)
)

- _WHENWird generell beim Betreten eines Feldes

gestartet.

- _VALIDWird generell beim Anzeigen eines Feldes gestartet

- _SHUTOWN Wird generell ausgeführt, wenn das Programm beendet wird.

Bei den beiden letzt genannten Einsprungspunkten steht neben der Variablen vParam1, die den Feldnamen enthält, auch vParam2 zur Verfügung. Hierin steht der Objektname des übergeordneten Objekts. So kann z.B. ein ganzer Container gesperrt werde.

Die jeweiligen Endungen entsprechen den angegebenen Objektnamen bzw. aus Gründen der Updatefähigkeit den Feldnamen.

Alle weiteren Funktionen sind frei und werden aus diesen Funktionen aufgerufen.

Einsprungspunkt (Server)

Daneben gibt es Einsprungspunkte, die nicht auf dem mobilen Gerät ausgeführt werden, sondern auf dem Server gestartet werden.

Diese werden zur Verbuchung der erfassten Daten auf dem MDE benötigt. Details hierzu werden im Kapitel „Serverprozess“ gegeben.

Programme importieren / exportieren

Diverse Programme stehen bereits als Vorlagen zum Import zur Verfügung. Diese können genauso importiert werden, wie auch eigene Sicherungen.

Diese Funktionen finden Sie unter dem Menüpunkt „Extras“.

Unable to render {include} The included page could not be found.

Alle Funktionen aktualisieren

Die Darstellung wird aktualisiert

Als Vorlage speichern

Speichert die aktuell markierte Funktion in das Vorlagen-Verzeichnis

Nur sichtbar, wenn Konfigurationsvariable „MDEVORL=YES“ gesetzt ist

Funktion als XML-Datei exportieren

Speichert die aktuell markierte Funktion im XML-Format auf die Festplatte

Funktion als XML-Datei exportieren

Importiert eine zuvor im XML-Format auf die Festplatte gespeicherte Funktion

Datenregel bearbeiten

Mit dieser Funktion wird der aus den Datenregeln bekannte Editor gestartet und die markierte Funktion geladen.

Die Konfigurationsvariable MDEXMLEXPTIMER steuert den Export der Timer.

0 = keine Rückfrage und exportieren

1 = keine Rückfrage und nicht exportieren

2 = Rückfrage ob exportiert werden soll

Neues Programm definieren

Über Datei – Neu – Funktion oder das Tastenkürzel [Strg]+[F11] kann eine neue Funktion definiert werden.

Unable to render {include} The included page could not be found.

Unable to render {include} The included page could not be found.

Hierzu sind folgende Informationen anzugeben:

Funktionsname

Eindeutiger Name für das Programm. Hierüber werden die internen Verknüpfungen und Aufrufe im Programm abgebildet.

Funktionsbezeichnung

So wird das Programm im Menü bezeichnet

Hauptmenüpunkt

Diese Auswahl, die sich nach den Menüpunkten im Professional-ERP richtet, steuert die Zuordnung des Programms zu dem Menüpunkt auf der ersten MDE-Maske.

Sortierung

Gibt die Position des Programms innerhalb des Menüpunktes an.

Geräte

Hier können wieder spezielle Funktionen auf nur bestimmten Geräten sichtbar gemacht werden.

Bleibt das Feld leer, so sehen alle mobilen Geräte diese Funktion. Mehrere Geräte werden Kommagetrennt eingegeben.

Bemerkung

Hier werden die Voraussetzungen für das Programm dokumentiert um auch später noch einen Überblick über das Programm zu haben.

Grundlegende Einsprungspunkte definieren

Nachdem die Funktion nun definiert ist kann mit dem Maskenaufbau gestartet werden.

Über Datei – Neu – Einsprungspunkt oder das Tastenkürzel [F11] kann ein neuer Einsprungspunkt definiert werden.

Unable to render {include} The included page could not be found.

ToDo

NameEindeutiger Name des Einsprungpunktes

Lokal / Server

Hier wird festgelegt, ob der Einsprungspunkt auf dem MDE benötigt wird (Lokal) – Erkenntlich am Icon,

oder ob die Datenregel auf dem Server zur Verarbeitung der MDE-Anforderung ausgeführt wird (Server) – Icon

Datenregel

Code der in diesem Einsprungspunkt ausgeführt wird.

Der erste Einsprungspunkt der in jedem Programm zuerst aufgerufen wird ist _INIT. Dieser wird zur Definition der Variablen, der Steuerung der Maske und dem Maskenaufbau eingesetzt.

Im obigen Beispiel wird bei Erhalt eines Wertes in der Variable cSerachArtNr die noch leere Maske mit dem internen Aufruf Do(„MASK“) aufgebaut. Direkt danach wird die Suche der Daten mit Do(„SEARCH“) gestartet und somit die Maske gefüllt.

Wurde kein Wert übergeben, so wird die Maske lediglich mit Do(„MASK“) leer dargestellt.

Weiteres zu der Programmierung und den Befehlen im Kapitel „Sprachreferenz“.

Sprachreferenz

MDE oder PC

Prinzipiell ist der Aufbau der Funktionen und Einsprungspunkte für MDE und PC identisch. Einige Parameter oder auch ganze Funktionen stehen nur in der einen oder anderen Version zur Verfügung.

Daher haben wir die Teile, die nur für die PC-Version zur Verfügung stehen blau geschrieben.

Die, die nur für die MDE-Versionen verfügbar sind, sind dagegen magenta markiert.

Mit der Einführung der Dynamic Oberfläche sind zudem einige Elemente so nicht mehr verwendbar. Siehe hierzu den Menüpunkt „Umstellung auf Dynamic-Mode“.

Sequenzieller Ablauf und Steuerung

Der Ablauf innerhalb der Einsprungspunkte ist genauso wie zwischen Funktionen streng sequentiell. Das heißt es werden alle Zeilen nacheinander abgearbeitet. Daher ist es wichtig, dass es sogenannte Sprungbefehle gibt.

Sprünge zwischen Einsprungspunkten:

Der Absprung

cRet=
do(„MASK“)

Der Code wird an der aktuellen Stelle verlassen und setzt mit der ersten Zeile im Einsprungspunkt fort. Ist dieser dann abgearbeitet wird mit der nächsten Zeile nach

do(„…“)
weiter gearbeitet.

Es ist möglich impliziert einen Rückgabewert zurück zu bekommen.

Der Funktion können mehrere (max. 10) Parameter mit übergeben werden, die dann als private Variablen vParam1 bis vParam<n> angesprochen werden können.

Werden in der aufgerufenen Funktion die Werte über parameter oder lparameter entgegengenommen, so können diese mit sprechenden Namen versehen werden.

do(„MASK“,”P1”,”P2”,…)

Ein @ vor dem Parameter übergibt den Parameter als Referenz.

Der entkoppelnde Absprung

SetDo(“SEARCH”)

Im Gegensatz zu do(..) wird in SetDo ein Timer-Event angestartet, das die beiden Prozesse entkoppelt. So kehrt der Programmablauf nach Start des Timer-Events in das Programm zurück und arbeitet dies ab. Nach einer gewissen Wartezeit wird dann der Code im Timer ausgeführt.

Diese Methode benötigt man, wenn man z.B. eine Maske schließen will, aber danach noch Code auf der alten Maske ausgeführt werden soll. (VA_...).

Zu beachten ist, dass SetDo keinen Rückgabewert haben kann, da ja de Prozesse entkoppelt wurden.

Der Rücksprung

return
 cRet

Der Code eines Einsprungspunktes endet mit der letzten Zeile oder dem Befehl

return
. Das Programm geht dann an den Aufrufpunkt zurück.

Die Rückgabewerte werden hier im Gegensatz zu DoFunc nicht in Klammern angegeben.

Sprünge zwischen Funktionen

Der Aufruf

cArtNr=D
o
Func("ARTINFO
"
,.t.
,P1,@P2,…
)

Die Funktion wird an der aktuellen Stelle verlassen und die aufgerufene Funktion wird mit dem Einsprungspunkt _INIT gestartet.

Während der Abarbeitung in der aufgerufenen Funktion ist die Variable

func()
gesetzt.

Der zweite Parameter gibt an, ob das aufgerufene Programm in einem eigenen Fenster geöffnet werden soll (nur PC)

Es ist darauf zu achten, dass nach der Rückkehr nur die aktuelle Routine abgearbeitet wird, da die Tiefe der aufrufenden Struktur nicht gespeichert wird. Im Laptopmode wird nichts mehr abgearbeitet, da hier die Masken über den 2. Parameter entkoppelt sind (siehe

ParentDo()
).

Ab der Version 2.5 werden auch weitere Übergabeparameter angenommen. Es besteht auch die Möglichkeit die Parameter per Ferenz zu übergeben (@P2)

Zu beachten ist, dass DoFunc die Private-Variablen des aufrufenden Einsprungs-Punktzerstört. (nicht DoDialog)

Der Aufruf modal

cArtNr=Do
Dialog
("ARTINFO
",P1,@P2,…)

Analog zu DoFunc. Das neue Fenster öffnet sich modal, das heißt das darunterliegende Fenster ist bis zum Abschluss der Arbeiten an dem neuen Fenster nicht bedienbar.

Siehe Beschreibung zu DoFunc. Lediglich der zweite Parameter entfällt hier.

Achtung: In diesem Fall wird die Maske erst mit dem Ende des _INIT gezeichnet. Es können somit keine FOCUS-Anweisungen in diesem Bereich direkt angegeben werden. Für diesen Falls wird über SetDo(„_INIT_SETFOCUS“) dieses in einen Einsprungspunkt ausgelagert und entkoppelt aufgerufen!

Der Rücksprung

R
eturn(cArtNr)

Hier erfolgt der Rücksprung zu der Zeile

DoFunc()
/DoDialog()
 
und das Programm setzt mit der Wertezuweisung fort. Somit steht der Übergabewert in der aufgerufenen Funktion zur Verfügung.

Maske schließen

close()
return()

Mit Close() wird die aktuelle Maske geschlossen (analog return(cArtNr)) – z.B.: im Init, wenn Maske noch nicht aufgebaut war.

Logout

Logout()

Schliesst alle offenen Programmfenster und meldet den aktuellen User ab.

Diese Funktion ist bereits im Einsprungspunkt _MENU._LOGIN verfügbar.

Folgeaufruf in aufrufender Maske

ParentDo („Search“)

Mit dem Verlassen der Maske im Laptopmode, weiß die aufrufende Maske nicht mehr, welche Maske nun geschlossen wird. Es findet somit im Standard keine weitere Funktion statt (auch nicht _INIT). Möchte man aber zum Beispiel eine Refresh der Maske starten, so kann man mit diesem Aufruf eine Funktion der Maske aufrufen, die die aktuelle gestartet hat.

Ab der MDE-Client-Version 1.3.002 ist es auch möglich Einsprungspunkte in anderen Funktionen direkt zu nutzen

Do(„_SHUTDOWN._INIT“)

AsyncJob(„_SHUTDOWN.LOGOUT“)

Variablen

Alle Variablen auf den MDE ohne spezielle Definition sind für das gesamte Programm gültig, daher ist es wichtig alle verwendeten Variablen im Einsprungspunkt _INIT zu definieren und mit einem gültigen / neutralen Wert zu belegen.

Zwischen den Programmen kann man auf die Werte im „anderen“ Fenster zugreifen, indem man im _INIT Bereich über ParentVar() die Zuordnung auf eine lokale Variable macht. (

nWertHier=ParentVar(„nWertDort“
)

Anmerkung DG: Im Funktionscode würde

Parent()
ausreichen. Aber um eine Verwechslung mit
ParentDo()
zu vermeiden sollte der Funktionsaufruf erweitert um
Var
angegeben werden.

Ab der Version 2.xxx können Parameter auch über die Aufrufe Do(), DoFunc() und DoDialog() übergeben werden. Die weitergegebenen Variablen können im _INIT über „parameters“ abgefragt werden. Diese sind allerdings nur Private und müssen daher für das Programm entsprechend umbelegt werden.

Des weitern sind hier die Variablen „case sensitiv“. Das heißt es wird zwischen Groß- und Kleinschreibung unterschieden. cSNR hat einen anderen Inhalt wie cSNr.

Für das Verhalten der Felder auf den Masken ist es wichtig, dass die Variablen mit einem Präfix beginnen, der über den Typ der Variable Auskunft gibt:

c

Zeichenkette

cSNr=“S12345“

n

Numerischer Wert

nMenge=1.3

l

logische Variable

lOk=.t.

d

Datumswert

dDat={} – dDat=date() 

Variablen können mit dem Befehl „Release“ zerstört werden, so dass sie in weiteren Programmteilen nicht mehr zur Verfügung stehen.

Release(“nWert1”,”nWert2”)

Die Variablen „nWert1“ und „nWert2“ werden zerstört.

Die Anzahl der Variablen die hier angegeben werden können ist nicht auf 2 begrenzt. Alle weiteren müssen nur mit Komma-getrennt angegeben werden.

Ab der Version 2.5. können Variablen nun auch explizit deklariert werden

Public

Entspricht der bisherigen einfachen Definition ohne Benennung. Die Variablen leben in der aktuellen Funktion.

Local

Diese Variablen leben nur in dem aktuellen Funktionscode

Private

Diese Variablen leben nur in dem aktuellen Einsprungspunkt inkl. aller Aufrufe aus diesem heraus via DO()

Systemvariablen

Folgende feste Variablen sind definiert:

_USERder angemeldete User

_USERIDDie User-ID des angemeldeten Users

_MDEIDID des MDE-Gerätes aus der INI-Datei

_GRUPPEDie Gruppe der das Gerät zugeordnet wurde

_CRZeilenumbruch

_CRLFZeilenumbruch (identisch zu _CR)

_PCDiese Variable ist wahr, wenn PC-Modus aktiv ist

_MDEDiese Variable ist wahr, wenn MDE-Modus aktiv ist

_SQLDiese Variable ist wahr, wenn die lokale Datenbank eine SQL-Datenbank ist

_INFODie Variable nimmt die Versionsnummer der Datenregeln des MDE auf. Gesetzt und angezeigt in _LOGIN._INFO.

_INFOINTERVALNimmt die Anzahl in Sekunden auf, wie die Versionsinfo aktualisiert wird. Default = 5

_VERSIONVersionsnummer der Clientsoftware in Form 2.02.002

_VERSVersionsnummer als Zahl

(Major * 1000000 + Minor * 1000 + Build
)

_LASTSYNCZeitstempel der letzten kompletten Synchronisation

_DEBUGMit dieser Variablen können sogenannte Debug-Meldungen während der Ausführung ausgegeben werden

(Bsp: debug(<variantVariable>,<cHeader>)

_SQLINFOSpeziell ab Version und Android kann man mit zwischen den Werten _SQLINFO=.t. und _SQLINFO=.f. die Ausgabe der Ergebnisse der SQL-Statements als Meldungsfenster anzeigen lassen. Dies hilft bei der Fehlersuche bei der Umstellung auf die SQLite Datenbank

Zudem stehen vor dem Start des eigentlichen Programms und damit der Synchronisation alle Variablen aus der Konfigurationsdatei „swsmde.ini“ zur Verfügung.

Eine Systemvariable im weiteren Sinne lässt sich über die Funktion func() abfragen. Diese Funktion gibt als Rückgabe-Wert zurück, ob die aktuelle Maske direkt aus dem Hauptmenü aufgerufen wurde (.f.) oder aus einer anderen MDE-Maske gestartet wurde (.t.). Damit kann der Ablauf im Programm gesteuert werden.

Beispiel:

Dieses Beispiel steuert im Suchdialog, ob bereits eine Eingabe auf der Vormaske gemacht wurde. Wenn ja wird die Suche sofort durchgeführt. Wenn nein, dann wird erst nach einem Suchbegriff gefragt.

* Maske aufbauen

if func() and !empty(cSearchArtNr)

   * Externer Aufruf mit Artikelnummer gleich weiter 

   Do("SEARCH")

else

   * Direkter Start oder Suche extern

   cSearchArtNr=""

   Do("MASK")

endif

Konfigurationsvariablen lesen und setzten

Jedes mobile Endgerät hat eine eigene Konfigurationsdatei „swsmde.ini“. In dieser Datei werden die Zugriffe auf den Webserver, die lokale Datenbank und sonstige persönliche Eigenschaften gespeichert.

Diese Datei befindet sich bei den MDE in „My Documents“. Bei der PC-Version im Programmverzeichnis der mobilen Installation.

Um auf diese Werte zugreifen zu können gibt es die Funktion

getcfg()
.

cMDE=getcfg("MDEID","MDE0")

Liest den im ersten Parameter angegebenen Wert aus der INI. Wird dieser gefunden erhält man diesen als Rückgabewert. Ist keiner definiert, so wird der Defaultwert, der als 2. Parameter angegeben wurde verwendet.

Mit der Funktion

setcfg()
können Werte auch in die INI zurück geschrieben werden.

setcfg("MDEID",
c
AktMDE)

Schreibt den 2. Parameter zu der Eigenschaft des 1. Parameters in die INI-Datei.

Alternativ können Variablen zur Programmausführung auch an in die Registry des Systems geschrieben werden

Set
R
eg
K
ey()

Schreibt Werte in die Registry

1. Wert: Baum in der Registry

2. Wert: Name des Wertes

3. Wert: Wert der gesetzt werden soll

Der 1. Wert wird in einer neuen Version optional und somit vom System gesetzt.

SetReg()

Im Gegensatz zu SetRegKey muss hier zum setzten der Variable nicht der komplette Registry-Baum angegeben werden. Hier reicht es aus erst die Pfade unterhalb von

CURRENT_USER\Software\Software Schmiede\Professional\MDE
anzugeben.

1. Wert: Pfad unterhalb …\MDE\

2. Wert: Name des Wertes

3. Wert: Wert der gesetzt werden soll

Beispiel:

SetReg("Vars","TEST","123")

cRet=
Get
R
eg
K
ey()

Liest Werte aus der Registry

1. Wert: Baum in der Registry

2. Wert: Name des gesuchten Wertes

3. Wert: Default-Wert, sofern der Wert nicht gesetzt wurde.

Der 1. Wert wird in einer neuen Version optional und somit vom System gesetzt.

cRet=
Get
R
eg ()

Analog zu SetReg() für das lesen der gesetzten Variablen.

Beispiel:

cRet=G
etReg("Vars","TEST","1
11
")

Wichtige Inhalte der INI-Datei:

WEBUSER=PROFESS

Name des registrierten Users im Webdienst. Dieser muss passen, sonst wird der Zugriff verweigert.

WEBPW=SWS

Passwort des registrierten Users im Webdienst. Dieser muss passen, sonst wird der Zugriff verweigert.

DBLOCATION=___

Pfad zur lokalen Datenbank. Hier kann auch mit virtuellen Pfaden gearbeitet werden:

DBLOCATION=H:\User\%USERNAME%\Week

Wird hier ein Pfad angegeben, so wird in diesem eine FoxPro Datenbank aufgebaut.

Wird hingegen eine Provider-Info angegeben, so wir diese als Basis für eine SQL-Datenbank genommen:

DBLOCATION=Provider=SQLOLEDB;DRIVER=SQL Server;Address=
<<Server>>
;Trusted_Connection=Yes;
 
SERVER=
<<Server>>
;DataBase=MDESTD

WEBSERVICE=http://...

WEBSERVICE2= http://...

Internet-Adresse des Webservers. Wird der erste nicht gefunden / erreicht, wird versucht über den Zweiten eine Verbindung herzustellen (Intern / extern).

Dies wird aber nur einmal beim Start des Programmes geprüft. Ändert sich während des Einsatzes etwas, so muss das Professional-MDE neu gestartet werden.

DBSIZE=512

Mit diesem Schalter kann die maximale DB-Größe angegeben.

Default ist 128MB

PW=DBPW

Passwort für die lokale Datenbank. Default ist swsmde.

PW=c3dz

Passwort für den Zugriff auf die lokale Datenbank, sofern hier SQL zum Einsatz kommt.

Eingetragen wird der Verschlüsselte Wert nach Base64

Verschlüsseln:

strconv("sws",13) c3dz

Entschlüsseln:

strconv("c3dz",14) sws

MAXPWERROR

Anzahl, wie oft ein User sein Passwort falsch eingeben kann, bevor die Datenbank lokal gelöscht wird.

Default: 3

DBDEL

Ist dieser Schalter gesetzt, so wird nach 3 maliger falscher Eingabe der Login-Daten die lokale Datenbank gelöscht.

Die Ausprägung JOB löscht die Datenbank ebenfalls, lässt aber die Tabelle MDEJOB mit eventuell unverarbeiteten Jobs stehen (nur bei lokale SQL-Datenbank)

Default: YES

DBINIDEL

Wird die lokale Datenbank gelöscht, so wird diese mit der nächsten Anmeldung wieder aufgebaut. Mit diesem Schalter kann beim Löschen der Datenbank auch gleich die INI mit gelöscht werden. Damit ist ein Neuaufbau der Datenbank ohne neues Einkopieren nicht mehr möglich.

Default: YES

LOGFILE

Gibt die Datei und das Verzeichnis an, wohin das System die Anmeldeversuche und eventuelle Löschungen der lokalen Datenbank protokolliert.

Standard: <<Programm-Verzeichnis>>\SWSMDE.log

LOGLEVEL

Level der Protokollierung

5 – nur Fehler werden protokolliert

8 – auch erfolgreiche Anmeldzungen werden aufgezeichnet

MAXSQL

Gibt an, wieviel Zeichen beim Sync pro Insert verwendet werden. Problem ist, dass bei den Funktionen der SQL-Code für den Insert des kompletten Text zu lange würde. So wird der Text zerlegt und beim ersten Mal ein Teil über Insert eingefügt und danach als Update (Text=Al+Neu) upgedatet. So wird die Befehlszeile nicht zu lang. (Geht so nicht am MDE, da nText+Neu ein Fehler aufwirft)

=20.000

MDEID=MDE1

Eindeutiger Name des mobilen Gerätes

SYNCINTERVAL=60

Angabe der Zeit in Sekunden zwischen zwei Synchronisationsprozessen mit dem Webservice

SYNCROWS=nnn

Der Schalter gibt die Vorgabe der maximalen Anzahl der zu synchronisierenden Sätze für Tabellen an.

Ist der Schalter nicht gesetzt, werden auf dem PC 5000 und auf dem MDE 2000 Sätze auf einmal geholt.

Für die Tabelle MDECODE gibt es bereits ein Sonderhandling, dass hier immer nur 50 Sätze auf einmal geholt werden. (Siehe Schalter SYNCROWSCODE)

SYNCROWSCODE=nnn

Der Schalter gibt die Vorgabe der maximalen Anzahl der zu synchronisierenden Sätze für Funktionen an.

Ist der Schalter nicht gesetzt, werden auf dem PC 50 und auf dem MDE 10 Sätze auf einmal geholt.

WEBTIMEOUT=8000

Zeitdauer, die das Gerät auf eine Datenbankabfrage vom Webservice wartet. Angegeben ist der Default-Wert.

CONNECTTIMEOUT=2000

Zeitdauer, die das Gerät wartet, um die Info zu bekommen, ob eine Verbindung zum Webservice besteht. Angegeben ist der Default-Wert. Wird benötigt, wenn der SYNCINTERVAL zu groß ist und die Verbindung neu etabliert werden muss. Meldung am MDE: „Verbinde mit Server…“

POCKET=YES

Hierüber werden das Betriebssystem, und damit der unterschiedliche Maskenaufbau bezüglich der Position des Menüs, definiert.

YES = Windows mobile PC

NO = Pocket PC

SCALE=2

1 – bei Auflösung 320*

2 – bei Auflösung 640*

FONTBOLD=YES

Damit wird die Schrift der Maskenobjekte fett dargestellt

ERRPROT=____

Schaltet eine Fehlerprotokollierung der SQL-Befehle ein. Angegeben wir Pfad und Dateiname der entstehenden Textdatei.

COVERAGE=_____

Ermittelt bei der Ausführung auf dem Gerät die Zeitdauern pro Zeile und gibt diese in dem angegebenen Textdokument aus.

SQLTEST=_______

Ermittelt bei der Ausführung auf dem Gerät die Zeitdauern je SQL-Statement und gibt diese in dem angegebenen Textdokument aus.

PROTMINMS=<Millisekunden>

Wurde die Protokollierung von Sql-Abfragen auf den MDE-Geräten durch Setzen des Schalters SQLTEST aktiviert, so werden alle Sql-Abfragen in einer Textdatei protokolliert. Oft ist es jedoch interessant, nur die Abfragen zu protokollieren, die eine längere Ausführungszeit besitzen. Ist dies gewünscht, so kann dies erfolgen durch Setzen des Schalters PROTMINMS auf die gewünschte Anzahl an Millisekunden, die überschritten werden muss.

SYNCMULTITS=NO

Wird bei einer Synchronisationsabfrage über mehrere Tabellen gejoint, so werden die Daten der aktuellen Tabelle auch upgedatet, wenn sich der referenzierte Datensatz in der anderen Tabelle geändert hat.

Mit setzten des Schalters auf „NO“, kann dies wieder deaktiviert werden.

SYNCBACK=5

Anzahl von Sekunden, die beim Sync vor dem gespeicherten CURTIMEST noch mit geschaut wird.

MDELANG=E

Mit diesem Parameter wird die Sprache für das MDE gesetzt. Das System versucht nun alle Texte aus der Tabelle LANGUAGE zu ermitteln.

Wird kein Eintrag für das Objekt gefunden, so wird ein Pflegesatz auf dem Server angelegt. Angezeigt wird in diesem Fall der deutsche Text.

Als Voraussetzung muss in der SWSWEB.INI über den Schalter SYSCON die Verbindung zur Systemdatenbank hinterlegt werden und in der Tabelle LANGUAGE muss das Feld LAN_MDE N(1) angelegt sein. Siehe Kapitel „Fremdsprachigkeit“

SWSMDEINI=<Ort der Datei>

Es ist möglich mit einer INI, die nur diesen Inhalt hat, auf die korrekte INI zu verweisen.

Die INI liegt normalerweise im Verzeichnis \My Documents. Macht man die Geräte Kaltstartsicher, so wird diese immer wieder neu aufgebaut. Daher legt man die korrekt INI auf die Storage Card und verweist in der ursprünglichen auf die aktuelle:

SWSMDEINI=\Storage Card\<Pfad>\swsmde.ini

BUTTONMENU=NO

Hiermit kann das Aussehen des Menüs gesteuert werden.

NO - Klassisches TreeView-Menü mit Hauptmenüpunkten

YES – Buttonmenü 2-stufig mit der Ebene der Hauptmenüpunkte

2 - Buttonmenü ohne Hauptmenüebene

Default ist „NO“

LOGINKEYBOARD=YES

Blendet die Tastatur beim Login-Dialog ein

CLOSEPWKEYBOAR

D=YES

Blendet die Tastatur beim Logout mit Passwortaufforderung eine

SYNCLOGOUT=YES

Automatische Abmeldung wenn gedockt (eigentlich CONNECTED – als für WLan-Lösungen unbrauchbar.). In diesen Fällen wird der Einsprungspunkt _SHUTDOWN in der jeweiligen Funktion (auch MENU) ausgeführt

CLOSEPW=<PW>

Passwortabfrage, wenn man Professional-MDE beenden möchte. Geht nur nach Eingabe des korrekten Passworts

LOGOUTUSERCLEAR

=YES

Löscht den Benutzernamen beim Ausloggen

CHANGEPW=YES

Blendet auf der Anmeldemaske den Button zur Passwortänderung ein

SYNCLOGOUT=YES

bewirkt ein automatisches Abmelden sobald Client gedockt ist und synchronisiert.

LSYNCCHECK=2

Steuert die Prüfung, wann zuletzt synchronisiert wurde (Vergleich Zeitstempel letzte Synchronisation Client mit Zeitstempel letzte Synchronisation Server / Soll Probleme unterbinden, wenn auf dem Server eine ältere Datensicherung eingespielt wurde). Ist der Client-TS neuer als der TS auf dem Server, greift der Schalter:

0 = keine Prüfung auf letzte Synchronisation

1 = Meldung Zeitstempelfehler und Abbruch der Synchronisation

2 = Rückfrage ob auf Client alles gelöscht und neu synchronisiert werden soll

3 = Rückfrage ob auf Client alles gelöscht und neu synchronisiert werden soll, bei Verneinung weitere Rückfrage ob Synchronisation trotzdem durchgeführt werden soll

4 = Meldung Zeitstempelfehler und Löschen aller Daten auf Client und neu synchronisieren

5 = sofort alle Daten löschen und neu synchronisieren ohne Meldung

SYNCDEL=YES

Durch setzen dieses Schalters werden auf dem Client bei der Synchronisation die Datensätze gelöscht, die nicht mehr der Sync-Bedingung entsprechen.

MDEDEL=YES

Ist dieser Schalter auf NO gesetzt, so werden auf dem Client keine Sätze mehr aus der Tabelle MDEDEL gelöscht.

Default: YES.

RECDEL=<Prot-File>

Wird dieser Schalter gesetzt (Protokolldatei mit Pfad), so werden alle Satzlöschungen auf dem Client von Sätzen, die nicht mehr auf dem Server vorhanden sind, protokolliert. Standardmäßig werden die Satzlöschungen nicht protokolliert

LABELBOLD=YES

Setzt alle Labels automatisch auf Fett-Schrift

LABELCOLOR=255,255,255

Definiert die Default Label-Farbe auf den angegebenen Wert.

SWSGUI=x

Steuert den MDE-PC Modus

3 = Dynamic Oberfläche

SCALEDEFAULT=120

Setz im Dynamic Mode die Maskengröße der Masken mit Ribbonbar auf 120%, sofern keine Registry-Speicherung für den Dialog vorliegt.

ASKPW=n

Steuert das Verhalten bei der Anmeldung

0 = Default (Anmeldung mit Passwortabfrage)

1 = Ist der Benutzer aus der Betriebssystemanmeldung in Professional angelegt, steht der Maus-Cursor direkt im Passwortfeld

2 = Ist der Benutzer aus der Betriebssystemanmeldung in Professional angelegt, erscheint kein

USER=xxx

Mit der Konfigurationsvariable USER kann der Benutzer bei der Anmeldung am MDE-Gerät vorbelegt werden. Hierzu kann ein fixer Benutzer angegeben werden oder auch durch Angabe von )( erreicht werden, dass der Benutzer der Windows-Anmeldung verwendet wird.

WFPPRIVAT=YES

Die Konfigurationsvariable steuert die Sichtbarkeit privater Termine Anderer im Kalender-Control.

Ist diese nicht gesetzt, so werden diese Termine nicht angezeigt. Mit YES wird der Block angezeigt, aber kein Inhalt.

NEXTID=yymmddnnn

Diese Variable setzt das Programm automatisch, wenn vom MDE-Client neue Sätze in die Datenbank eingefügt werden. Dabei wird der Aufzählung immer das aktuelle Tagesdatum vorangesetzt.

Wird ein MDE neu aufgesetzt und die INI verändert, so muss dieser Wert gelöscht werden, sofern an diesem Tag noch kein Insert stattgefunden hat. Ansonsten muss der Zähler entsprechend hochgesetzt werden,

FILESROOT

=<<MDEDB>>+\FILES

Über diese Variable kann der Speicherort der vom Server synchronisierten Dateien definiert werden.

Default ist das Verzeichnis FILES im Unterordner der MDE-Datenbank.

Ansonsten ist der komplette Pfad in der Variablen anzugeben. Z.B.: C:\Temp\Dateien

FILESDIRLENGTH=3

Über diesen Schalter kann die Zerstückelung der MDI_REFID in eine Ordnerstruktur beeinflusst werden.

Im Standard nimmt das System diese ID, die numerisch 15-stellig ist, und zerteil diese in Pakete der Länge 3. Daraus entsteht die Ordnerstruktur unter der MDI_REFTAB

Bsp: REFID=1234567890123145 REFTAB=ART

\FILES\ART\123\456\789\012\345\

LOGINPWCODE=<Code>

Wird das MDE-Gerät in der Hosentasche transportiert, so kann es relativ schnell vorkommen, dass mehrfach hintereinander ein Login ohne Passwort ausgeführt wird. Da standardmäßig die Datenbank nach der dritten falschen Anmeldung gelöscht wird, kann es hierdurch zu einem Datenverlust kommen. Um dies zu verhindern kann man über den Schalter LOGINPWCODE erreichen, dass der Benutzer vor einem erneuten Anmeldeversuch erst einen Sicherheitscode eingeben muss. Über den Schalter kann definiert werden, was der Benutzer in diesem Fall eingeben muss. Ist der Schalter nicht gesetzt, so erfolgt auch keine Sicherheitsabfrage.

USERFIELDS

Standardmäßig werden nur bestimmte Felder, die für die Anmeldung auf den MDE-Geräten notwendig sind, auf die Geräte synchronisiert.

Erweiterung ist mit diesem Schalter möglich:

USERFIELDS=* // alle Felder synchronisieren

USERFIELDS=USR_TELE,USR_EMAIL //zusätzliche Felder

Protkollierung

Funktion

WriteLog(<cText>[,<nLevel>[,<cLogFile>]])

(auch bei der Funktion ist der Default-Level 5, wenn dieser nicht angegeben wird)

LOGFILE=<Pfad+Dateiname>

Log-Einträge vom Programm und über den Befehl WriteLog werden standardmäßig im Programmverzeichnis (PC) oder unter MyDocuments (MDE) in der Datei swsmde.log gespeichert. Soll der Speicherort geändert werden, so kann dies über den Schalter LOGFILE entsprechend eingestellt werden.

Default: swsmde.log

LOGLEVEL=<LEVEL>

Log-Einträge vom System sowie Einträge über WriteLog werden nur dann weggeschrieben, wenn der Level des Eintrags größer als oder gleich wie der Wert im Schalter LOGLEVEL ist. Der Default des Wertes ist 5. Wird der Schalter auf 0 gesetzt, so wird grundsätzlich nichts mehr protokolliert.

Beispiel für Protokollierung im Standard:

Anmeldungen mit falschem Passwort werden ab Level 4 protokolliert.

Erfolgreiche Anmeldungen werden erst ab dem Level 8 protokolliert.

Default: 5

Arrays

Ein Sonderfall der Variablen stellen sogenannte Arrays dar.

Im MDE-Toolkit sind die Arrays zweidimensional definiert. Das heißt ein Array kann n-Zeilen mit jeweils m-Spalten aufnehmen.

Ein Array muss anfänglich einmal dimensioniert werden:

adim("aVar",6,2)

Dimensioniert das Array „aVar“ mit 6 Zeilen und dazu jeweils 2 Spalten (Werte).

Die Anzahl der Spalten und Zeilen lässt sich über die Funktion alen() ermitteln:

a
len
("aVar",
1
)

Gibt die Anzahl der Zeilen des Arrays zurück.

a
len
("aVar",
2
)

Gibt die Anzahl der Spalten des Arrays zurück.

Wurde ein Array nicht dimensioniert oder gefüllt, so wird der Wert 0 zurück gegeben.

Suchen im Array

n=ascan("aQS",nID,1)

Gibt die Zeile im Array zurück

1. Wert: Array-Name

2. Wert: Such-Wert

3. Wert: Spalte in der gesucht wird (optional – Default: 1)

B
eachte
, 
Werte 
in 
einem 
Grid-
Array 
sind 
Strings

Die Initialisierung und Befüllung der Werte erfolgt am besten in einer Schleife:

nZaehler=1

do while nZaehler<=alen("aVar",1)

 
aVar[nZaehler,1]=“Wert “+v2c(nZaehler)

   aVar[nZaehler,2]=nZaehler

   nZaehler=nZaehler+1
enddo

Der Zugriff auf die Werte und deren Darstellung auf der Maske erfolgt wie folgt:

nZaehler=1
do while nZaehler<=alen("aVar",1)
   Ask(aVar[nZaehler,1],"aVar["+str(nZaehler)+",2]") 

   nZaehler=nZaehler+1

enddo

Zu beachten ist, dass der Typ jedes Arraywertes von Zeile zu Zeile unterschiedlich sein darf. So kann z.B. der Inhalt der Arrayelements aVar[1,2] numerisch sein, der Typ von aVar[2,2] ein Datum und aVar[3,1] einen String darstellen.

meld("Typ: "+type("aVar["+str(nZaehler)+",2]"))

Properties

Um Eigenschaften der Maske zentral zu definieren sollten diese an einem zentralen Punkt während des Aufbaus der Maske definiert werden. Hierzu sollte der Einsprungspunkt „SETPROP“ verwendet werden.

Diese Variablen werden für die gesamte Lebensdauer der Masken definiert.

Beispiel:

* Setzte Properties

oProp=CreateObject("PROPS")

oProp.lUserBestKorr=.f.

oProp.
ActiveBusiness
=
1

* Prüfe Recht auf Dialog

oProp.lUserBestKorr=Do("CHECK_RIGHTS")

Beachte, dass die Properties in aufgerufenen Dialogen auch existieren und eventuell überschrieben werden. Daher sin diese sicherheitshalber vor dem Aufruf zu speichern und danach wieder zurück zu sichern.

Beispiel:

Private oPropLocal

* Sichere Properties des aktuellen Dialogs

oPropLocal=oProp

lRet=DoDialog(„L_TEST“)

oProp=oPropLocal

Kommentare

Kommentarzeilen beginnen mit “

*
” bzw. „
//

Innerhalb einer Programmzeile kann der Rest mit „

&&
“ deaktiviert werden.

Die Funktion ASK()

Mit der Funktion ASK() werden die Objekte in einem Maskencontainer definiert. Hier kurz der allgemeine Aufbau:

Die Funktion hat 7 Parameter.

1. Wert

Bezeichnung auf Maske (Label)

2. Wert

Variable – Einsprungspunkt (CMD,CNT) – Cursor (GRD)

3. Wert

Die ersten 3 Buchstaben geben den Typ des Feldes an (wenn leer=“TXT“)

Die Zeichen dahinter ergeben dann den Objektnamen, anhand dessen die Übersetzung der Labels, Buttons etc. gesteuert wird.

Bsp: TXTART_ARTNR

Hierüber werden auch die Einsprungspunkte WH_, VA_ und VI_ angesprochen.Zur Bewahrung der Updatefähigkeit wird bei der Angabe des Typs mit nur 3 Zeichen, nach wie vor der Variablenname akzeptiert.

4. Wert

Breite des Feldes

5. Wert

Höhe des Feldes

6. Wert

Abstand vom linken Rand

7. Wert

Abstand vom oberen Rand

Wird der 7. Parameter weggelassen, so werden alle ASK() untereinander dargestellt. Möchte man mehrere Elemente in der gleichen Zeile ausgeben, so ist dieser zu setzten. Um hier variable zu bleiben stellt das System die Funktion AskTop() zur Verfügung. So macht es nichts aus, wenn weiter oben neue Felder eingebunden werden.

nAskValue=AskTop()

Gibt den aktuellen Abstand zum oberen Rand des übergeordneten Objekts (Maske, Container, …)

Die Funktion AddField()

Alternativ zu der Funktion ASK() kann auch über die AddField-Methode Objekte auf die Maske gebracht werden.

Im Unterschied zum ASK muss der Programmierer die Positionierung der Felder auf der Maske selbst in die Hand nehmen und Pixelgenau angeben. Dafür können in einer Programmzeile wesentlich mehr Eigenschaften für die Felder gesetzt werden. Beim ASK müssen diese als Eigenschaften des Feldes separat gesetzt werden.

Die Funktion hat 13 Parameter.

1. Wert

Name des Feldes

2. Wert

Angezeigter Wert

3. Wert

Tabreihenfolge – Reihenfolge der Felder

4. Wert

Pixelposition von oben

5. Wert

Pixelposition von links

6. Wert

Breite des Feldes in Pixel

7. Wert

Höhe des Feldes in Pixel

8. Wert

Feldbreite variabel (.t. oder .f.)

9. Wert

Feldhöhe variabel (.t. oder .f.)

10. Wert

Wievieltes Feld in der gleichen Zeile links von diesem Feld, das seine Größe ändern kann + 1

11. Wert

Anzahl der Elemente in dieser Zeile, die Ihre Breite ändern können

12. Wert

Anzahl der Objekte die oberhalb liegen und in der Höhe geändert werden können + 1

13. Wert

Anzahl der Objekte in vertikaler Richtung die Ihre Höhe ändern können.

Beispiele:

cWert="ABC"

AddField("LBLADR_TEXT","TEXT",1,40,10,80,24)

AddField("TXTADR_TEXT",cWert,2,40,100,144,24)

Maske, Reiter und Container

Bei der PC-Variante wird zuerst eine Maske definiert. Auf dieser werden dann diverse Reiter gelegt. Innerhalb der Reiter können über Container die Felder übersichtlich angepasst werden.

PGF

Eigentliche Maske

CNT

Container

Beispiele:

Ask("","Main","PGF",,332)   

nAskTop=AskTop()

Ask("","Adr1","CNT",380,300)

Ask("","Adr2","CNT",380,300,388,nAskTop)

Funktionen des Maskenobjektes (PGF)

Die Maske kann nach der Definition mit diversen Eigenschaften gestaltet werden:

Beispiel:

Ask("","Main","PGF",,332)   

pgfMain.AddPage("Adressen","Adr")

Damit wird auf die Maske ein Reiterobjekt gelegt, das mit ADR referenziert wird.

Damit steht nun ein Einsprungspunkt IN_CNTPAGEADR zur Verfügung, in dem die weiteren Objekte platziert werden können.

In einem Einsprungspunkt _PAGECHANGE kann auf den Wechsel zwischen den Registern regiert werden. Folgende Parameter stehen hier zur Verfügung:

vParam1

Name des Page-Frames

vParam2

Nummer des angeklickten Page-Frames

vParam3

Nummer des aktuell aktiven Page-Frames

Scrollbalken für einen Container (SCR)

Um einen Container auf der Maske mit einem Scrollbalken zu versehen ist ein SCR-Objekt mit dem gleichen Namen wie der Container zu definieren.

Beispiel:

* Container zur Anzeige der QS-Prüfpunkte

nAskCNTPAGE=askTop()

Ask("","QSVALUE","CNTQSVALUE",804,452)

cntQSVALUE.WResize=.t.

cntQSVALUE.HResize=.t.

cntArtikel.VPos=2

* Scrolbar

Ask("","QSVALUE","SCRQSVALUE",16,452,804,nAskCNTPAGE)

SCRQSVALUE.HPos=2

SCRQSVALUE.HResize=.t.

cntxxx.ScrollPos= nPixel

Damit kann man per Code in einem Container mit Scrollbalken positionieren.

Beispiel:

 
nAskCNTValue=AskTop()

 
* Speichere Maskenposition         

 
aQS[nZaehler,17]=nAskCNTValue

 
Ask("","aQS["+str(nZaehler)+
",6]",cFieldName,10 &_ 

 
,,250,nAskCNTValue)

 
cPosFocus="CHK"+padl(aQS[nZaehler,1],20,"0")

 
&cPosFocus.
SetFocus
()

   CNTQSVALUE.ScrollPos=aQS[nZaehler,17]

Funktioniert so auch am MDE. Allerdings wurde der Test nur am PC-Client im MDE-Modus durchgeführt!

Syntax:

nTop=AskTop()

Ask("","VALUES","CNTVALUES",218,224)

Ask("","VALUES","SCRVALUES",20,224,218,nTop)

SCRVALUES.HPos=2

nTop=230
Ask("Beenden","CLOSE","CMD",,40,,nTop)

Felder auf die Maske bringen

Über die Variablennamen werden hier auch die Felder definiert.

Prinzipiell gibt es folgende Feldarten

TXT

einzeiliges Textfeld – erzeugt zusätzlich ein gleichnamiges LBL

LBL

Bezeichnungsfeld

Beachte die Angabe der Länge im Ask() wird immer von einem längeren Inhalt übersteuert.

CBO

Combobox (manuell bestückt – 2 dimensional) benötigt zur Füllung die Methode AddItem(<cWert>,<cAnzeige>)

Der Wert, muss immer als String angegeben werden.

Zum leeren des Arrays gibt es die Methode Clear().

CBK

Combobox auf Basis der synchronisierten Tabelle Wahl.

Ist bei den Eigenschaften als CBO anzusprechen.

Angezeigt wird das Kürzel.

CBT

Combobox auf Basis der synchronisierten Tabelle Wahl.

Ist bei den Eigenschaften als CBO anzusprechen.

Angezeigt wird der Text des Kürzels

OPT

Option-Buttons

EDT

mehrzeiliges Textfeld

CMD

Schaltfläche

GRD

Tabelle im klassischen Stil (Basis von .Net)

GRH

SWS-Tabelle mit Überschrift ohne transparenten Hintergrund

GRS

SWS-Tabelle ohne Überschrift ohne transparenten Hintergrund

GRT

SWS-Tabelle ohne Überschrift ohne transparenten Hintergrund

CHK

Checkbox

CAL

Kalender-Control (Termine)

CHA

Grafik-Objekt

Ask("","cData","CHAUMSATZ",820,240,4,4)

cData=“Was“+_TAB+“Wert1“+_TAB_+“Wert2“

Ask("Zeit","Wert","CHADATA",,200)

IMG

Objekt zur Bilder-Darstellung

Ask(cFile,"Image","IMGFile",232,130)

IMGFile.Image=cFile

Beispiele:

Ask("Artikel","cArtNr",,70)

Ask("Suche"
,"SEARCHART","CMD",,,192,nTop)

Ask("cCols","oSearch","GRD")

Ask("Adressgruppe","cAdrGrp","CBKADR_GRUPPE")

* Da Feldnamen eindeutig sein müssen kann über ~ der gleiche Inhalt ein 2. Mal auf die Maske gebracht werden

Ask("Adressgruppe","cAdrGrp","CBKADR_GRP2~ADR_GRUPPE")

Ask()

Ask("Vertreter","nVertr","CBO")

cbonVertr.Clear()

cbonVertr.AddItem("0","kein Vertreter")

cbonVertr.ShowValue=.f.

für die Tabelle Wahl:

Ask("Sprache","cAdrSpr","CBKADR_SPR",60)

Mit Arrays:

cboCombo.RowSource="aValues"

Alter Definition ohne Objektnamen (nicht Fremdsprachfähig)

Ask("Artikel","cArtNr","TXT")

Neue Definition mit Objektnamen (Fremdsprachfähig)

Ask("Artikel","cArtNr","TXTART_ARTNR")

Wird

Ask()
ohne Parameter aufgerufen, so wird lediglich ein kleiner Absatz auf der Maske angezeigt.

Prinzipiell ermittelt sich das Programm die aktuelle Position und die optimale Breite aus der Reihenfolge der Definitionen und aus den Geräteparametern.

Möchte man jedoch zwei Elemente auf eine Zeile stellen, so kann man sich über die Funktion

AskTop()
den aktuellen Abstand zum oberen Bildschirmrand zurückgeben lassen.

nTop=AskTop()

Zur Korrektur oder fixen Einstellung kann der Funktion auch ein Wert zugeordnet werden:

nTopKorr=AskTop(AskTop()+30)

Hotkeys z.B. für Buttons kann man mit dem kaufmännischen UND („&“) vor dem gewünschten Buchstaben erzeugen. Diese werden dann zur Laufzeit nach dem Betätigen der Taste „ALT“ angezeigt.

Eigenschaften der Textfelder (TXT und EDT)

Die Textfelder kann man nach der Definition mit diversen Eigenschaften belegen:

Beispiel:

Ask("Lagerort","cLager")

txtcLager.Upper=.t.

.UPPER

Eingabe nur in Großbuchstaben möglich

.READONLY

Sperrt das Feld gegen Eingaben

.SELECT

Markiert den Inhalt beim Klick in das Feld

.ENABLED

Aktiviert oder Deaktiviert das Feld

.TABSTOP

Steuert die Reihenfolge der Felder

.FONTSIZE

Angabe der Schriftgröße

.FONTBOLD

Setzt den Inhalt auf fette Schriftart

.FORECOLOR

Angabe der Schriftfarbe

.BACKCOLOR

Angabe der Hintergrundfarbe

.CHANGE=.f.

(generell für alle Felder) Nimmt das Feld aus der Automatik, dass bei einer Änderung das Diskettensymbol aktiv wird (oFields-Bezug) oder umgekehrt (bei fehlendem Bezug)

Anmerkung:

Die Farben werden über die Funktion

RGB
(<Rotwert>,<Grünwert>,<Blauwert>) übergeben und vom Programm gewandelt. So wird der Hintergrund des Feldes Lager hier rot dargestellt.

txt
cLager
.Backcolor=RGB(255,0,0)

Bei der Änderung der Schriftgröße ist darauf zu achten, dass die Abstände auf der Maske vom oberen Rand durch das Programm zu steuern sind. Automatisch werden immer 24 Punkte Abstand eingehalten!

nAskCNT1=AskTop()

Ask("Feld groß","cFeld1","TXTABC_FELD1",60)

TXTABC_FELD1.FontSite=48

nAskCNT1= nAskCNT1+36

Ask("Feld klein","cFeld2","TXTABC_FELD2",60,,,, nAskCNT1)

Eigenschaften der Bezeichnungsfelder (LBL)

Beispiel:

lblcLager.Caption="Lager"

.CAPTION

Bezeichnung des Feldes

.FONTSIZE

Angabe de Schriftgröße

.FONTBOLD

Setzt den Inhalt auf fette Schriftart

.FORECOLOR

Angabe der Schriftfarbe

.BACKCOLOR

Angabe der Hintergrundfarbe

Es gibt leider keinen direkten Weg die Schriftgröße zu verändern. Dies kann aber über folgenden Umweg erreicht werden:

setcfg("FontSize","16")

Ask("Prüfungszustand",,"LBLPRU_ZUST")

LBLPRU_ZUST.FontBold

setcfg("FontSize","0")

Hier wird zuerst die globale Schriftgröße hoch gesetzt, das Label auf der Maske dargestellt und gleich danach wieder zurück gesetzt auf den Default-Wert.

Eigenschaften von Schaltflächen (CMD, CHK)

Beispiel:

cmdUPLOAD.Caption="Zugang buchen"

.CAPTION

Bezeichnung des Feldes

.TABSTOP

Steuert die Reihenfolge der Felder

.FONTSIZE

Angabe der Schriftgröße

.FONTBOLD

Setzt den Inhalt auf fette Schriftart

.FORECOLOR

Angabe der Schriftfarbe

.BACKCOLOR

Angabe der Hintergrundfarbe

Eigenschaften von Comboboxen (CBO, CBK, CBT)

Beispiel:

cboLager.Enabled=.f.

.ENABLED

Aktiviert oder Deaktiviert das Feld

.TABSTOP

Steuert die Reihenfolge der Felder

.Style=0

Hiermit wird der Wert der Combobox editierbar für Freitexteingabe (nur im Dynamic-Mode)

.InputMask=“!!!!“

Definierung einer Maskierung der Eingabe in Verbindung mit .Style=0 (nur im Dynamic-Mode)

9 = numerisch

! = Großbuchstaben

Eigenschaften von Comboboxen (OPT)

Option-Buttons haben alle das selbe Datenfeld. Den Wert, der pro Options-Feld zurück gegeben wird kann über den „:“ bei der Definition mit angegeben werden oder später über die Methode .Value definiert werden.

Aktuell können nur numerische Werte zurückgegeben werden.

Beispiel:

nOption=2   
 
&& Setzt in der Anzeige die 2. Option

Ask("Option 1","nOption:1","opt1")

ask("Option 2","nOption:2","opt2")

ask("Option 3","nOption:3","opt3")

opt3.Value=4
 
 
&& ab jetzt gibt opt3 4 zurück

.Value

Setzt den Wert, der zurückgegeben wird, wenn die Option gewählt wurde.

Dies ist eine Alternative zu der direkten Angabe des Wertes bei der Definition der Darstellung

.Group

Ordnet das Objekt der entsprechenden Gruppe zu, die dann über WH_ und VI_ summarisch angesprochen werden kann.

Eigenschaften der Grids (GRD, GRH, GRS,GRT und GRM)

Die Eigenschaften der Grids werden immer mit Grundtyp GRD… angesprochen. Lediglich die Definition unterscheidet das spätere grundlegende Aussehen.

Beispiel:

* Definition der anzuzeigenden Spalten mit den Breiten

cCols="ID;ID;0"+_CR

cCols=cCols+"MPO_KKS;KKS;150"+_CR

cCols=cCols+"MPW_POS;Pos;76"+_CR

* 
Mit Nachkommastellen (APP)

cCols=cCols+"
POS
_
STCK
;
Anz.
;76
 ;999,999.99
"+_CR

* Als Checkbox maskieren

cCols=cCols+"ADR_HANS;H;20;;;#CHK"+_CR

* Anzeige des Grids mit den zugehörigen Eigenschafte
n

Ask("cCols","oSearchRound","GRSROUND",,nGridHeight)

grdRound.NoRefresh=.t.

.RowHeight

Mit dieser Eigenschaft kann die Zeilenhöhe im Grid angepasst werden (nur Dynamic)

.NoRefresh

Unterbindet die Aktualisierung des Grids bei einem Refresh. Dies verhindert das Neuzeichnen und damit ein eventuelles Flackern am MDE

. ActiveRow

Weist dem Grid die zu markierende Zeile zu. Diese erhält man beim _GRIDSELECT als vParam2 zurück

.GoRec(nZahl)

Markiert im Grid die entsprechende Zeile und führt das _GRIDCHANGE Event aus. Bei gesetztem 2. Parameter wird zusätzlich das _GRIDSELECT Event ausgeführt.

.DelayedChange=.t.

Nur im Multiselect-Grid. Führt nach einem bestimmten Intervall (Default1000 ms) das _GridSelect Event aus.

.TimerInterval=5000

Nur im Multiselect-Grid. Definiert die Zeit für die Verzögerung in ms.

.ActivePos=0

0 = Standardverhalten (wie bisher)

1 = aktive Zeile immer am oberen Gridrand anzeigen

2 = aktive Zeile immer am unteren Gridrand anzeigen

.Multiselect=.f.

Macht aus dem GRH ein Grid mit nur einer Auswahl

.SetCols(cCols2)

Mit dieser Zuweisung kann man im Nachhinein einem Grid neue Spaltendefinitionen zuweisen. Z.B.: umsortieren oder eine andere Ansicht

GRDARTPREIS.SetCols(cColsPreis2)

.SetGrid(„oSearch“)

Mit dieser Zuweisung kann man dem Grid ein neuer Cursor / neue Datenmenge zuweisen.

GRDARTPREIS.SetGrid("oSearchPreis2")

.SetValue(nRow,cField ,nWert)

Mit dieser Anweisung kann man Werte im Grid ändern ohne diese neu aufzubauen.

1. Wert: Zeile im Grid

2. Wert: Spaltename im Grid (ergibt sich aus cCols)

3. Wert: Wert der neu angezeigt werden soll

GRDSEARCHANS.SetValue(1,"ADR_FIRMA2","Test")

Es ist zu beachten, dass diese Werte nicht an den Cursor übergeben werden. Es handelt sich um eine reine Ansicht.

.SelectColor=.f.

Bei Tabellen mit Hintergrundfarbe (Spalte _RGB/_BACK) kann es störend sein, dass die aktiven Zeilen immer mit einem grünen Hintergrund dargestellt wird. Dieser kann über diesen Schalter ausgeschaltet werden.

.SelectRow(n,.f.)

Mit Parameter wird die n.-te Zeile markiert (.f.) bzw. demarkiert (.t.)

Ohne Parameter werden alle Zeilen-Markierungen im Grid (Multiselektions-Grid) entfernt

Über die VI_ Eigenschaft kann ein Grid über Bedingungen auch ausgeblendet werden.

Eine WH_ Funktionalität gibt es nicht.

Farblicher Hintergrund:

Den Hintergrund der neuen Tabellen kann man durch die Definition der Gridspalte _RGB (_BACK sollte nicht mehr verwendet werden) definieren.

cFields="iif(x=1,"+v2c(nRGB1)+","+v2c(nRGB2)+")
+00000000000
 as _
RGB

Der Spaltenname im Dynamic-Mode ist hingegen _RGB!!!

Anmerkung:

Die Auswertung des Grids erfolgt über zwei spezielle Einsprungspunkte_GRIDCHANGE – wird beim Klick in das Grid ausgeführt

_GRIDSELECT – wird bei einem Doppel-Klick in das Grid ausgeführt

Anmerkung:

In nicht DynaicMode wird das _GRIDSELECT nach dem _GRIDCHANGE auch bei einem Einfach-Klick ausgeführt.

Dabei werden 3 Parameter belegt:

vParam1

Gibt den Namen des Grids aus der Definition der Anzeige zurück. Darüber wird es möglich auf mehrere Grids auf einer Maske zu reagieren.

vParam2

Enthält die Zeilennummer aus dem, dem Grid zugeordneten Cursor, und dient als Sprungmarke zur Anzeige / Ermittlung der gewünschten Daten.

vParam3

Enthält den Spaltennamen auf dem der Klick ausgeführt wurde.

if vParam1="GRDDONE"

   oRowDone=GetRow(oSearchDone,vParam2)

 
nWertID=GetField(oRowDone,"ID")

e
ndif

Anmerkung:

Analog den Parametern in aufgerufenen Masken können die Variablen auch bei Standard-Events zur besseren Lesbarkeit mit Namen versehen werden und auch die Gültigkeit von private auf local geändert werden (z.B. lparameter lcGrid,lnRecNo)

Eigenschaften des Kalenders (CAL)

Ask("","Main","CALMAIN",988,480,4,4)

calMain.WResize=.t.

calMain.HResize=.t.

calMain.User=cActUser

* Setzte Starttag

calMain.Date=dAktAnzeigeStart

* Tagesstart und Ende

calMain.DayStart="08:00"

calMain.DayEnd="18:00"

Notwendige Felder:

Für das Kalender-Control sind bestimmte WFPOS-Felder notwendig:

WFP_WFTID,WFP_GRPID,WFP_GRPSTA,WFP_ADAUER,WFP_RDAUER,WFP_RAUM,WFP_BERTYP

Sichtbarkeit privater Termine:

Hier ist die Konfigurationsvariable WFPPRIVAT wichtig. Ist diese nicht gesetzt, so werden private Termine Anderer gar nicht angezeigt. Mit YES wird der Block angezeigt, aber kein Inhalt.

Eigenschaften des Grafikobjektes (CHA)

Mit diesem Objekt können einfache grafische Darstellungen realisiert werden.

Der Aufruf des Ask erfolgt mit den folgenden Parametern

Name der x-Achse

Name der y-Achse

Name des Objektes

Positionierung und Größe wie üblich

Dazu werden dem Grafikobjekt die einzelnen Messreihen als 2-dimensionale Array zugeordnet (ADD). Der erste Wert entspricht dem x-Wert und der zweite dem y-Wert der Grafik

.Add()

Mit dieser Methode wird diesem Grafikobjekt eine Messreihe zugeordnet.

1. Wert: Bezeichnung der Messreihe

2. Wert: Gibt die Form der Grafikdarstellung

/// 1 = Balken

/// 2 = Linien

/// 3 = Linie mit Punkten und Stufen

3. Wert: Farbe der Messreihe in rgb()

4. wert: Das Werte-Paare-Array als String

.Draw()

Zeichnet die Grafik

.Clear()

Löscht die komplette Grafik

.Show0

Über diese logische Variable kann angegeben werden, ob die Null-Linie angezeigt werden soll.

.YDecimals

Gibt an, wieviel Dezimalstellen bei der y-Achsen-Beschriftung verwendet werden sollen

Beispiel:

Ask("Zeit","Wert","CHADATA",,200)

chaData.Show0=.f.

chaData.YDecimals=1

* Arrays wurden im SEARCH belegt

if recc
(oSearchUGOG)>0

   chaData.
Add
("Max. Messbereich",2,
rgb
(255,0,0),"aDataOG")

   chaData.
Add
("Min. Messbereich",2,
rgb
(0,255,0),"aDataUG")

endif

chaData.
Add
("Messwerte",3,
rgb
(0,0,255),"aData")

chaData.
Draw
()

Unable to render {include} The included page could not be found.

Felder gruppieren

Allen Feldern gemein ist die Möglichkeit diese zu Gruppen zusammen zu fassen.

txtFeld1.Group="grpContainer1"

So vereinfach es sich gemeinsame Eigenschaften und Verhalten zu realisieren.

In dem nun zur Verfügung stehenden Einsprungspunkten

VA_GRPxxx
,
WH_GRPxxx
und
VI_GRPxxx
kann nun das gemeinsam Verhalten eingestellt werden.

Tipp: Innerhalb des Einsprungspunktes stehen die beiden Parameter vParam1 und vParam2 zur weiteren Unterscheidung zur Verfügung.

vParam1

Enthält den Feldnamen, der den Einsprungspunkt ausgelöst hat. Z.B.:

txtFeld1

vParam2

Enthält die übergeordnete Einheit zum Feld. Z.B.: den Container

cntEigensch1

Masken und Container leeren

Mit der Funktion CLEAR() können alle Elemente der Maske oder mit Angabe eines Container-Objektes des Containers wieder entfernt werden.

Die Funktion Clear für die Maske hat 4 Parameter.

1. Wert

Name der Maske

2. Wert

Breite der Maske in Pixel

3. Wert

Höhe der Maske in Pixel

4. Wert

Name unter welchem die Maskeneinstellungen in der Registry gespeichert werden.

Die Funktion zum leeren eines Containers hat keine Parameter. Zu beachten ist, dass nach dem leeren des Container auch noch zusätzlich der vorhandene Einsprungspunkt IN_<<Containername>> aufgerufen wird, der die Informationen zum Neuzeichnen enthält.

Beispiele:

Clear("Terminverwaltung",1020,900,"frmP_WFL_FRM")

cntAdresse.Clear()

Hotkeys

In der PC-Version, kann man einigen Objekten einen Hotkey zuordnen, der bei der Bedienung über die Tastenkombination Alt+<Hotkey> alternativ zum Anklicken des Objektes verwendet kann.

Der Hotkey wird bei der entsprechenden Caption durch Voranstellen des kaufmännischen und („&“).

Folgende Objekte werden unterstützt:

CMD

AddBar

Beispiel:

Ask("&Suche","SEARCHART","CMD",,,192,nTop)

Veränderung von Werten

Wird ein Wert auf der Maske verändert, so wird die Eigenschaft _CHANGED vom Programm gesetzt. Diese wird im DynamicMode dazu genutzt die Speicher- und Undo_Buttons zu aktivieren. Achtung aber nur wenn die Referenz für die Masken-Felder ein oFields Bezug ist.

Dies gilt, sofern nicht das Feld explizit von dieser Methodik ausgenommen ist

CBOANZEIGE.
Change=.f.

Pro Feld wird die globale Eigenschaft lChanged beim Betreten auf .f. gesetzt. Wird nun eine Änderung an dem Feld gemacht, so wird diese Variable .f. und kann somit im entsprechenden Valid ausgenutzt werden.

Wird das Feld jedoch aus dem Code gefüllt und soll über eine VA_-Funktion geprüft werden, so muss diese Variable manuell gesetzt werden.

Beispiel Adress-Suche mit nachgestellten VA_:

oFieldsVOR.VOR_KDNR=DoFunc("P_ADRSEARCH",.t.) 

* Setzte Änderung, so dass Valid reagiert

lChanged=.t.

DO("VA_TXTVOR_KDNR")

Dass die Valids der Felder auch immer durchlaufen wurden, das heisst die Variable lChanged (die über den return Wert beeinflusst werden kann) wieder auf .t. steht kann über die Funktion Valid() geprüft werden. Diese Funktion sorgt dafür, dass zuerst die VA_ durchgeführt wurden. Somit kann z.B das parallel abarbeiten eines VA_ und eines WRITE() unterbunden werden.

Valid sorgt dafür dass in dem Feld, in dem aktuell der Cursor steht das Valid durchlaufen wird. Sollte das Valid .f. zurückgeben und das Feld somit nicht verlassen werden können, so gibt auch die Valid-Funktion .f. zurück.

Beispiel beim Start der Speicherroutine:

* Abfangen, dass sich VA_ und Speichern überholen

if !Valid()

   return

endif

do case

   case pgfMain.ActivePage=1

        Do("WRITEVOR")

   case pgfMain.ActivePage=2

        Do("WRITEPOS")

endcase

Funktions-Menüs auf Maske

Mit der Addbar-Methode kann man auf der Maske in die Menüstruktur „Extras“ weitere Funktionen integrieren.

Beispiel:

AddBar("Ersatzetikett Lager","LABEL")

Die Funktion hat 2 Parameter

1. Wert

Angezeigter Name

2. Wert

Einsprungspunkt der bei Aktivierung gestartet wird

Eigenschaften:

.Text

Damit kann der Text der Funktion im Programmablauf beeinflusst werden.

.Enabled

Bietet die Möglichkeit Funktionspunkte auf Inaktiv zu setzten.

In der PC-Version lassen sich über die AddBar-Methode ganze Strukturen von Menüs aufbauen.

Zuerst wird auf oberster Ebene der angezeigte Menüpunkt definiert

AddBar("Datei","","barFile")

1. Wert

Angezeigter Name

2. Wert

Einsprungspunkt der bei Aktivierung gestartet wird. Dieser ist hier leer.

3. Wert

Gruppennname der Struktur

In diese Struktur hinein können nun weiter Menüpunkte gelegt werden.

AddBar("Speichern","Save","barSave","barFile","F10")

AddBar("Verwerfen","Undo","barUndo","barFile")

1. Wert

Angezeigter Name

2. Wert

Einsprungspunkt der bei Aktivierung gestartet wird.

3. Wert

Gruppennname der damit erzeugter Struktur

4. Wert

Gibt an zu welcher übergeordneten Struktur der Menüpunkt gehört.

5. Wert

Funktionstaste um den Menüpunkt direkt starten zu können.

Die einzelnen Punkte können über den Einsprungspunkt VI_BARxxxx aktiv bzw inaktiv gesetzt werden.

Zudem besteht in diesem Bereich die Möglichkeit den Text durch setzen der Variablen cBarText zu verändern.

if cISOTyp="ISO"

   cBarText="&Kalibrierungstyp auf nonISO ändern"

else

   cBarText="&Kalibrierungstyp auf ISO ändern"

endif

Ribbon-Bar auf Maske (Dynamic-Mode)

Im Dynamic-Mode verwendet man anstatt den Menüstrukturen sogenannten Ribbon-Bars.

Unable to render {include} The included page could not be found.

Für den Aufbau dieser Ribbons stehenfolgende Funktionen / Befehlse zur Verfügung:

AddPage("padRecord", 

"Datensatz")

Erzeugt einen leeren Ribbon Datensatz auf der Maske

Der erste Parameter gibt an, wie dieser im Programmcode angesprochen werden kann.

Der Zweite gibt die Bezeichnung im Reiter des Ribbon auf der Maske an.

AddGroup("padRecord",

"popAllg","Allgemein"

Mit dieser Funktion richtet man eine logische Gruppe im Ribbon ein. Erkenntlich wird dies auf der Maske durch die senkrechten Teilungsstriche.

Parameter gibt den Bezug zur Page an

Parameter gibt an, wie die Gruppe weiter im Programmcode angesprochen wird

Parameter gibt die Bezeichnung unterhalb der Gruppe an

AddButton("popAllg",

"barNew","Neu","F9",

"F9","NEW",1,1)

Mit dieser Funktion können nun Funktions-Buttons auf dem Ribbon platziert werden.

Parameter gibt den Bezug zur Gruppe im Ribbon an

Parameter gibt an, wie der Button weiter im Programmcode angesprochen wird

Parameter gibt die Beschriftung unterhalb des Buttons an

Parameter gibt den Shortcut zur Bedienung des Ribbons mit der Tastatur an

Parameter ist der Anzeigetext (Tooltip) für die Shortcuts

Parameter gibt den Einsprungspunkt an, hinter dem sich der Code zur Ausführung nach dem Betätigen dieser Auswahl verbirgt

Parameter ist die Nummer des anzuzeigenden Icons.

Parameter: letKeysActive (bool) Ist der Wert gesetzt (.t.), so bleiben Hotkeys bei der Anzeige in der Ribbonbar auch mit Betätigen des aktuellen Hotkeys stehen. So kann über die Tastatur eine ganze Reihe von Befehlen Funktionsaufrufen nacheinander ausgeführt werden. Dies geht so lange, bis ein Button der Ribbonbar betätigt wird, bei welchem die Eigenschaft auf .f. gesetzt ist.

AddSubButton("barNewPos",

"barNewP","Position","F12",

"F12","NEWPOS",1)

Soll sich hinter einem Button eine weitere Auswahl zur Verfügung gestellt werden, so wird dies über die Sub-Buttons gemacht. Dies bedingt zuerst einen neutralen Button Funktionsaufruf. Auf diesen referenzieren dann die Sub-Buttons.

Parameter gibt den Bezug zur Hauptbutton der Gruppe an

Parameter gibt an, wie der Sub-Button weiter im Programmcode angesprochen wird

Parameter gibt die Beschriftung neben dem Button an

Die weiteren Parameter sind identisch mit denen des Buttons

REFRESHSTATUS()

Mit dieser Funktion wird die Ribbon-Bar neu gezeichnet und alle Valids- und Whens- der einzelnen Objekte neu durchlaufen.

Beispiel des Aufbaus der Ribbon-Bar im Vorgang (P_VOR_FRM)

Clear
("Auftragsbearbeitung",956,760,"frmP_VOR_FRM")

* Ribbon-Bar

AddPage("padRecord","Datensatz")

AddGroup("padRecord","popAllg","Allgemein")

AddButton("popAllg","barNewVor","Neu","F9","F9","NEWVOR",1,1)

AddButton("popAllg","barNewPos","Neu","","","",1,1)

AddSubButton("barNewPos","barNewP","Position","F12","F12","NEWPOS",1)

AddSubButton("barNewPos","barNewT","Textposition","","","NEWTEXT",1)

AddButton("popAllg","barWrite","Speichern","F10","F10","WRITE",3,1)

AddButton("popAllg","barUndo","Änderungen verwerfen","ESC
APE
","ESC","UNDO",4,1)

AddButton("popAllg","barSearchVor","Suchen","F7","F7","VORSEARCH",12,1)

AddGroup("padRecord","popRecord","Datensatz")

AddButton("popRecord","barDel","Löschen",,,"DELREC",5,1)

AddGroup("padRecord","popAnsicht","Ansicht")

AddButton("popAnsicht","barRefresh","Aktualisieren",,,"UNDO",28,1)

Panels auf der Maske

In der PC Version und im Dynamic-Mode (SWSGUI=3) können Panels in der Masken-Statuszeile eingebaut werden.

AddPanel(1)

Definiert ein Panelobjekt. Am besten in dem Einsprungspunkt _INIT.

Der Parameter gibt die Ausrichtung an

0 = links

1 = rechts

Eine Benamung der Panels erfolgt hier nicht. Die Reihenfolge der Definition ist hier ausschlaggebend.

SetPanel(1,2,cInhalt)

Füllt das Panel

1. Parameter – links oder rechts

2. Parameter – Welches Panel

3. Parameter - Inhalt

AddPanel darf nur einmal aufgerufen werden und sollte somit im _INIT benutzt werden. Hiermit werden die Panels definiert.

*Panels für VOR_NETTO und VOR_BRUTTO

AddPanel(1)

AddPanel(1)

* Fülle Panels

SetPanel(1,1,"Netto "+
allt
(
transform
(oFieldsVOR.VOR_NETTO,"999,999,990.99"))+" "+
allt
(oFieldsVOR.VOR_WAEHR))

SetPanel(1,2,"Brutto "+
allt
(
transform
(oFieldsVOR.VOR_BRUTTO,"999,999,990.99"))+" "+
allt
(oFieldsVOR.VOR_WAEHR))

Maskensteuerung

Folgende Möglichkeiten existieren um die Maske neu aufzubauen, mit aktuellen Werten zu versehen und den Cursor durch Programmlogik zu setzen:

Clear(„Lager-Info“
,800,640
, “frmABC“
)

Leert die Maske komplett und setzt die Überschrift in den Programmkopf.

Der zweite Parameter gibt die Breite der Maske an. Der dritte die Höhe.

Der letzte Parameter kann angegeben werden, wenn sich das System die letzte Position und Größe in der Registry speichern soll.

_SCREEN.Height=600

Mit dieser Eigenschaft kann zur Laufzeit die Höhe der Maske vergrößert oder verkleinert werden - z.B.: Zur Anzeige der Suchergebnisse.

_SCREEN.Width=800

Mit dieser Eigenschaft kann zur Laufzeit die Breite der Maske vergrößert oder verkleinert werden.

_SCREEN.Caption=xxx

Überschrift der Maske neu zuweisen.

Aktuell ist kein Auslesen des Wertes möglich.

_screen.MaxHeight=260

_screen.MinHeight=260

   _screen.MinWidth=nStartWidth

_screen.MaxWith=0
 

Über diese Werte lassen sich die Maskenbegrenzungen einstellen. Das heißt die Masken können dann nicht größer als …MAX und nicht kleiner …MIN werden.

Die Werte lassen sich nicht nur setzen, sondern auch abfragen.

_screen.WindowState=0

Über diese Methode lässt sich der Fensterstatus abfragen / setzten:

0 = Normal

1 = Minimiert

2 = Maximiert (bis …MAX)

_screen.scale=1.3

Setzt die Skalierung der Maske auf 130%

_SCREEN.CLOSABLE

Verhindert das Schließen der Maske über das rote X

Da man am MDE immer nur eine Maske hat, führt das Entfernen des X dazu, dass es bei einer eventuellen Rückkehr in einen Vordialog dort auch verschwunden ist. Möchte man den Wert nicht bei jedem Dialog auf .t. setzen, so empfiehlt es sich das Handling um den Funktionsaufruf zu bauen.

_screen.clos
able=.f.

lRet=DoFunc(…)

_
screen.clos
able=.t.

Refresh()

Ordnet allen Feldern auf der Maske die neuen Inhalte zu.

cntAdresse.Refresh()

Ordnet allen Feldern in dem angegebenen Objekt die neuen Werte zu. Ohne die gesamte Maske neu aufzubauen.

txtcLager.Focus()

Setzt den Focus in das Feld „cLager“

close()

Schliesst die aktuelle Maske

logout()

Schliesst nicht nur die Maske, sondern führt darüber hinaus noch einen Logout durch. Man findet sich danach wieder in der Anmeldemaske für Professional-MDE

Beispiel für einen einfachen Maskenaufbau:

Clear("Lager-Info")

nTop=AskTop()

Ask("Lagerort","cLager",,70)

txtcLager.Upper=.t.

Ask("Suche","SEARCH","CMD",,,182,nTop)

AddBar("Ersatzetikett Lager","LABEL")

Refresh()

txtcLager.Focus()

Beispiel: Variable Größenveränderliche Masken (Suchmasken)

nStartWidth=656

nStartHeight=564

Clear("Adresse suchen",_screen.Width,_screen.Height)

Ask("Matchcode","cSearchMatch","TXTSEARCH_MATCH",382+(_screen.Width-nStartWidth))

Tastatursteuerung

Da ein Programm nicht nur über die Buttons und Funktionen auf der Maske bedient werden kann gibt es die Möglichkeit die Tastatur mit einzusetzen.

KEY_xxx
 

Einsprungspunkt, der beim Druck der Taste <xxx> aufgerufen wird.

Bsp: KEY_ENTER oder KEY_ESCAPE

KEY_F15=ESCAPE

In der SWSMDE.INI kann über diese Zuweisung einer speziellen Funktionstaste das Verhalten der dahinter angegebenen Taste zugeordnet werden.

Erläuterung: Da es auf manchen MDE die ESC-Taste nicht gibt, wird hier der Taste F15 das Verhalten für ESC zugeordnet. Im Code wird der Einsprungspunkt KEY_ESCAPE ausprogrammiert. Wird nun die taste F15 betätigt wird der Code hinter KEY_ESCAPE ausgeführt.

KeyStop()

Verwirft ab dem Setzten die Eingaben über die Tastatur. Dies verhindert Eingaben im Textfeldern und die Einsprungs-Punkte KEY_xxx.

KeyResume()

Nimmt nun wieder die Eingaben über die Tasten entgegen.

KeyClear()

Löscht den aktuellen Tastatur-Puffer

Kommunikation / Rückfragen

Zur Interaktion mit dem User gibt es folgende Funktionen:

meld("Bitte Artikel scannen") 

meld(„Vorgang $AUF$ gelöscht“,poropob(„AUF“ ,VOR_AUF),“Info“)

Meldung an den User durch einen einfachen Meldungsdialog.

Parameter ist der anzuzeigende Text

Parameter wird nur ausgewertet wenn dieser mit

poropob()
beginnt und ersetzt damit die Felder im 1. Parameter. (optional)

Wichtig ist die bei fremdsprachigen Versionen, da die Texte der Meldungsfenster automatisch in die LANGUAGE eingehen. Wird hier der variable Textteil mit im 1. Parameter angegeben, so würde der Text in jeder Variation in der LANGUAGE zum übersetzten ankommen.

In der Funktion

poropob()
können beliebig viele Ersetzungspaare kommagetrennt anggegeben werden. Dies ist nicht auf einen Wert beschränkt.

Parameter bzw. wenn dieser nicht angegeben wird und der 2. Parameter ein String ist, ist dies die Headline der Meldung (optional)

nRet=j_n_a(„Wirklich löschen?“,“J“)

Rückfragedialog mit den zwei Schaltflächen „JA“ und „NEIN“.

Zurückgegeben wird bei „JA“ eine 1 – bei „NEIN“ eine 0.

Anzuzeigender Text

Default-Button – mit Semikolon getrennt kann die Zeit angegeben werden, wann die Frage automatisch beantwortet wird ("J;25") (Zeitsteuerung geht nur, wenn auch der 3.Parameter = Neuer Rückfragedialog)

Kopfzeile der Maske

Ausrichtung 1=zentriert

Hintergrundfarbe als RGB angegeben

Farbe des 1. Buttons als RGB

Farbe des 2. Buttons als RGB

Schriftgröße der Buttons

Im PC-Modus wird bei einer Parameterzahl <=3 die kleine Messagebox() aus dem Standard verwendet.

nRet=show(cKKSInfo,
"Info",1)

Infodialog analog meld(), nur dass hier die ganze Maske dazu verwendet wird und weitere Buttons zur Verfügung stehen.

Parameter:

Anzuzeigender Text

Kopfzeile der Maske

Text-Ausrichtung (0=links – 1=mittig – 2=rechts)

Hintergrundfarbe als RGB angegeben

Farbe des Buttons als RGB

Schriftgröße des Textes

Showme(cVars)

Showme zur Anzeige von Variablen (Arrays) analog Standard-Showme

n=adir("aFiles",cFilePath)

showme("aFiles[3]")

BEEP
(nInt)

Spielt einen Ton. Es stehen 6 verschiedene Töne zur Verfügung

 (nInt von 1 bis 6)

SOUND
(cWave)

Spielt die angegebene Wave-Datei ab. Die Angabe muss inkl. Pfad erfolgen.

Waitstart()

Setzt den Cursor als symbolische Sanduhr. Die Sanduhr wird automatisch vor einem meld() beendet.

Waitstop()

Setzt den Cursor wieder zurück

Operatoren

Folgende Operatoren werden unterstützt:

^

Potenz

*

Multiplikation

/

Division

+

Addition

-

Subtraktion

=

Gleich (Werte)

==

Identisch (Stringvergleich)

#

ungleich

>

Größer (Alle Stringvergleiche ab 2.6.006 auch für Alpha-Werte)

<

kleiner

>=

größer gleich

<= 

kleiner gleich

and 

logisches und

or

logisches oder

!

logisches nicht

empty()

Zeigt an, ob der übergebene Wert gefüllt ist

min/max

Mit den Funktionen kann der Mnimal- bzw. Maximalwert von zwei numerischen Werten ermittelt werden

nErg=min(nWert1,nWert2)

Es gelten die üblichen mathematischen Priorisierungen im Ablauf: „Punkt vor Strich“ und „Zusammenfassung von Klammerungen“

Stringoperationen

Um Inhalte von Feldern zu vergleichen gibt es einige Funktionen:

type(„
cText
“)

Gibt den Typ des angefragten Objektes zurück:

U – undefiniert

C – Character

N – Numerisch

T – Date-Time

D - Datum

O - Objekt

v2c(nMenge
)
 

Wandelt jeden Variablen-Typ in einen String um.

v2c
(dInvDat,
nWert
)

Wandelt ein Datumswert in einen String.

Wird der zweite Parameter weggelassen, so findet lediglich eine Typwandlung statt.

Mit 1 werden die Hochkommata für Abfragen gesetzt

Mit 2 wird ein Datumswert in das SQL-CE Format gewandelt

Mit 4 entscheidet der Parser, ob es eine PC-Umgebung auf FoxPro-Tabellen oder eine SQL-CE Datenbank ist. Dementsprechend ist der String wie 1 oder 2 formatiert.

str(nWert,12,2)

Wandelt den Wert aus nWert in einen String mit 12 Vorkomma- und 2 Nachkommastellen um

allt(
cText
)

Schneidet Leerzeichen am Anfang und Ende des Strings ab.

ltrim(cText)

Schneidet Leerzeichen am Anfang des Strings ab.

rtrim(cText)

Schneidet Leerzeichen am Ende des Strings ab.

Transform(nWert,“999,999.99“)

Wandelt einen Wert in einen String um unter Berücksichtigung der Picture-Klausel (nur Numerische Werte umgesetzt!)

padr(
cText
,
3
0
,
“-„)

Ergänzt den übergebenen String rechts bis zur angegeben Anzahl (2. Parameter) mit Leerzeichen bzw. dem Zeichen an 3. Stelle.

padl(cText,30
,
“-„)

Ergänzt den übergebenen String links bis zur angegeben Anzahl (2. Parameter) mit Leerzeichen bzw. dem Zeichen an 3. Stelle.

upper(cText)

Wandelt den angegeben String in Großbuchstaben um.

lower(cText)

Wandelt den angegeben String in Kleinbuchstaben um.

len(cText)

Gibt die Anzahl von Zeichen des übergeben Strings zurück.

at(cSuch,cText), atc(cSuch,cText)

Gibt die Stelle im String cText zurück, an der die Such-Zeichenkette cSuch das erste Mal auftritt. at() ist dabei Case-Sensitiv.

substr(cText,10,2)

Gibt die Zeichen aus dem String (1. Parameter) ab dem zweiten Wert aus. Ist kein dritter Parameter angegeben, dann bis zum Ende des Strings, ansonsten so viele Zeichen, wie der 3. Wert angibt (hier also 2).

left(cText,5)

Gibt die 5 (2 Parameter) linken Zeichen aus dem String zurück

right(cText,5)

Gibt die 5 (2 Parameter) rechten Zeichen aus dem String zurück

inlist(cLand,“D“,“E“,“F“)

Prüft, ob der Wert in ersten Parameter (cLand) in der weiteren Aufzählung (ab 2. Parameter) vorhanden ist. Wenn Ja, so gibt die Funktion .t. zurück. Sonst .f.

Strtran(cText,cSuche,cErsetze)

Ersetzt in dem String

cText
die Zeichenfolge
cSuche
durch
cErsetze
.

Subv(cText,nVorkommen,“,“)

Gibt den Teilstring aus

cText
zurück der zwischen den Vorkommen des Separators (hier: Komma) nVorkommen und nVorkommen-1 steht.

Addbs(cText)

Prüft den übergebenen Text, ob dieser mit einem \ endet. Ist dies nicht der Fall, so wird der String um einen \ erweitert.

va
l
l
(cText)

Wandelt eine Zahl in den numerischen Wert unter Berücksichtigung der länderspezifischen Trenner („.“, „,“) um.

Handelt es sich um keinen numerischen Ausdruck, so wird 0 zurückgeliefert. (SWS-UDF!)

v
al(cText)

Interne Funktion von Foxpro und die gibt einen Fehler aus, wenn cText nicht numerisch darstellbar ist.

asc(cBuchstabe)

Gibt den ASCII-Wert des Arguments zurück

chr(nAscii)

Gibt das Zeichen für den ASCII-Wert zurück

eval(
"oFields."+aFields[nField,1]
)

Wertet den als String übergebenen Ausdruck aus und gibt den Wert zurück.

In diesem Beispiel interpretiert

eval
zuerst den Inhalt von
aFields[nField,1]
z.B.
VOR_AUF
. Dann setzt das System die beiden Teilstrings zusammen zu
oFields.VOR_AUF
. Das
eval(oFields.VOR_AUF)
gibt dann den Wert zurück z.B.
602100
.

chr(13)+chr(10)

Wandelt den angegeben ASCII-Code in das korrekte Zeichen um. Dies auch für nicht druckbare Zeichen.

vget(@cParam,"
MNG
",
str(nMNG)
)

nMenge=

vget(@cParam,"MNG", ,“N“)

Setzt bzw. fragt Werte aus einer Wertepaarstruktur ab. Die Werte werden immer als String gespeichert.

1. Parameter – Name der Struktur

2. Parameter – Name des Wertepaares

3. Parameter – Wert

4. Parameter – Erwarteter Typ der Abfrage

N = numerisch

D = Datum

T = Datum mit Uhrzeit

Wir der 3. Parameter angegeben, so wird dieser gesetzt.

Runden

Numerische Werte können mit folgenden Funktionen gerundet werden:

int(nWert)

Schneidet den Nachkommawert ab

round(nWert,nStelle)

Rundet den Wert mathematisch auf bzw. ab. Der Wert nStelle ist optional und gibt die Nachkommastelle auf welche gerundet werden soll. Default ist 0. Negative Werte wirken sich auf die Vorkomastellen aus.

ceil(
nWert
)

ceil
ing
(nWert)

Gibt den nächst höheren Integerwert in Bezug auf nWert zurück.

abs(nWert)

Gibt den positiven Wert zurück als INT bzw. Double.

Arbeiten mit Datumswerten

Zum Arbeiten gibt es einige Funktionen:

dDate=date()

Ordnet der Variablen das heutige Datum zu

dDate=date(2011,12,24)

Ordnet der Variablen das kurze Datum {24.12.2011} zu. Werte müssen numerisch übergeben werden (vall())

dDate=datetime()

Ordnet der Variablen das heutige Datum inklusive der Uhrzeit zu

cTime=Time()

Gibt die aktuelle Uhrzeit als String zurück

Ticks(tDateTime)

64-Bit-10tel Mikrosekunden seit 1. Januar 0001

S
econd(dDate)

Ergibt die Sekunden aus der datetime-Variablen

M
inute(dDate)

Ergibt die Minuten aus der datetime-Variablen

hour(dDate)

Ergibt die Stunden aus der datetime-Variablen

day(dDate)

Ergibt den Tag aus einer date- bzw. datetime-Variablen

m
onth(dDate)

Ergibt den Monat aus einer date- bzw. datetime-Variablen

year(dDate)

Ergibt das Jahr aus einer date- bzw. datetime-Variablen

ttod(t
Date
Time
)

Gibt aus dem Datumsstring inkl. Zeit (31.12.2010 12:01:25) nur das Datum (31.12.2010) zurück.

D.h. es findet einen Konvertierung des Typs von DAteTime zu Date statt

ttoc(tDateTime)

Konvertiert den Date-Time-Wert in einen Text-String

dtoc(dDate)

Konvertiert den Datumswert in einen Text-String

dDat=date()+nTage

Addiert die Anzahl Tage zu dem aktuellen Tagesdatum

dDat=goMonth (date(), nMonate)

Addiert die angegebene Anzahl von Monaten zum Tagesdatum

dDatetime()+nSeconds

Addiert die Anzahl der Sekunden zu einem Date-Time-Wert

Dow(date())

Gibt den Wochentag des Datums als Zahl zurück (Sonntag=1)

Arbeiten mit Binärfeldern

Zum Auswerten von Binärfelder gibt es die Funktion bittest. Mit dieser kann ermittelt werden, ob in einem String das x. Bit gesetzt ist.

bittest(b
Wert,nBit)=1

Ermittelt aus dem binären Wert

b
Wert
, ob das Bit an der Stelle
nBit
gesetzt ist. Der Rückgabewert ist logisch und kann mit 1=Ja oder 0=Nein abgefragt werden.

bWert=bitset(bWert,nBit)

Setzten des Bits

nBit
in der Variable
bWert

bWert=

b
itclear
(bWert,nBit)

Löscht in der Variablen

bWert
das gesetzte Bit
nBit

b
itand
(bWert1,bWert2)

Gibt den Bitwert zurück der in beiden Bitwerten gemeinsam gesetzt ist

bitor
(bWert1,bWert2)

Gibt den Bitwert zurück der entweder in dem einen oder anderen Bitwert gesetzt ist

Beispiel / Exkurs:

Aufbau Bitzahlen: Bit 0 (2^0) ungesetzt 0 Bit 0 gesetzt 1

Bit 1 (2^1) gesetzt addiert 2

Bit 2 (2^2) gesetzt addiert 4 etc

Sind 1 und 2 gesetzt, so hat bWert den Wert 6

Bei bitand(5,6) ergibt sich 4, da 5 binär = 101 und 6 binär = 011. Das heisst bei beiden ist nur das 3. Bit gemeinsam gesetzt also 2^2=4

Bei bitor(5,6) sind dies die Bits 1,2,3 also 2^0+2^1+2^2=7

Schleifen und Verzweigungen

Es gibt folgende Schleifen-Konstrukte

If - Schleife

If lOk=.t.

   nMenge=1

else

  nMenge=0

endi
f

Einzeilige if-Abfrage

nRet=iif(nTest=1,“Ok“,“nOk“)

Do Case

do case 

   case nMenge<100

      nRabatt=5

   case nMenge<1000

      nRabatt=8

   otherwise

      nRabatt=10

endcase

Do-While – Scheife

do while nMenge<100

   nMenge=nMenge+1

enddo

Do-While Schleifen können mit

exit
verlassen werden. Dabei setzt das Programm mit der Zeile nach dem
enddo
fort.

Prüfung auf Funktionen

Es gibt zwei Funktionen, mit denen geprüft werden kann, ob in der aktuellen Installation Funktionen beziehungsweise Einsprungspunkte vorhanden sind:

HasFunc("P_XXX")

Prüft, ob die Funktion „P_XXX“ importiert wurde und vorhanden ist.

HasCode(„P_XXX._INIT“)

Prüft, ob es den Einsprungspunkt _INIT in der Funktion P_XXX gibt.

Zugriff auf die lokale Datenbank

Zur Arbeit mit der lokalen Datenbank gibt es einige Befehle:

oSearch=GetC
ursor()

Erzeugt einen Cursor als Rückgabewert.

1. Parameter - 0=lokale Datenbank

1=Serverdatenbank

2. Parameter – Felder

3. Parameter – Tabelle (from)

4. Parameter – Bedingung (where)

5. Parameter – Sortierung (order by)

6. Parameter – Gruppierung (group by)

7. Parameter – Having

8. Parameter – Name des Cursors für SQL-Server. „~“ Erzeugt den Cursor auf dem SQL-Server. „~NAME“ vergibt zusätz-lich den Namen.

Anmerkung: Ist der erste Parameter nicht numerisch, so wird dieser als “Felder” genommen und alle Parameter verschieben sich nach vorne.

Wird der 8. Parameter gesetzt kommt kein Cursor zurück, sondern ein String. Auf den Cursor auf dem Server kann dann über einen weiteren Select auf ~NAME eventuell #NAME zugegriffen werden.

In diesem Fall muss der Speicher wieder manuell freigegeben werden mit:

DelCursor(oSearch)

DelCursor(oSearch)

Löscht den angegebenen Cursor-Inhalt. Vernichtet aber das Objekt nicht.

Wichtig ist dies vor allem für Cursor, die auf dem SQL-Server erzeugt wurden. Aber auch bei lokale – sehr großen – Cursorn kann hiermit der Speicherbedarf reduziert werden.

Lokal: DelCursor(oSearch)

SQL: DelCursor(„~NAME“)

recc(oSearch)

Gibt die Anzahl der Ergebnissätze des angegebenen Cursors an.

oRow=GetRow(oSearch,1) 

Liest aus dem angegeben Cursor die Zeile, die an zweiter Stelle übergeben wird.

lRet=DelRow(
oSearch
,nRow)

Löscht die angegebene Zeile (nRow) aus dem lokalen Cursor.

Als Rückgabewert wird der Erfolg der Löschung erhalten.

cBez=GetField(oRow,"ART_BEZ1")

Liest das an zweiter Stelle angegeben Datenbankfeld aus der übergebenen Reihe und übergibt diesen als Rückgabewert,

lRet=
sqlexec(
“<<SQL>>”
)

Befehl zur direkten Ausführung von SQL-Statements. Beginnt der Befehl mit #, so werden Fehler nicht ausgegeben, sondern ignoriert.

create("TAB","ID","N(15)")

Erzeugt eine lokale Tabelle.

1. Parameter - Tabellenname

Danach kommen paarweise, Komma getrennt angegeben die Feldnamen und Feldtypen.

index(
“TAB”,”FELD”
)

Legt für die Tabelle

TAB
einen Index auf das Feld
FELD
an.

i
nsert("TAB","ID",123)

Fügt in die Tabelle “

TAB
” eine Zeile ein. Die Werte werden wieder paarweise, Komma getrennt angegeben (Feldname, Wert).Ab Version 2 wird beim Insert die erzeugte ID als Rückgabewert zurückgegeben. Dabei wird immer die ID des MDE als Präfix verwendet. Zudem wird, sofern in der Tabelle das Feld MDEID identisch der ID gefüllt.

UPDATE("TAB",123,"ID","321")

Ändert in der Tabelle „TAB“ den Satz mit der ID=123 (immer 2. Parameter ist die ID) ab. Dabei werden die Felder und Werte wieder paarweise, Komma getrennt angegeben.

UPDATE(„TAB“,cWhere,“WERT“,0)

Ändert in der Tabelle „TAB“ für alle Sätze die der Where-Bedingung unterliegen, das angegeben Feld auf den angegebenen Wert. Dabei werden die Felder und Werte wieder paarweise, Komma getrennt angegeben. (Massenupdate)

d
elete("TAB",321)

Löscht den Satz der ID=321 (immer 2. Parameter ist die ID) in der Tabelle „TAB“

drop("TAB")

Löscht die Tabelle „TAB“ aus der lokalen Datenbank.

pofda(„TAB.ID“
,.t.
)

Prüft, ob das angegebene Feld

 
ID
in der Tabelle
TAB
der Datenbank existiert.

Der zweite Parameter ist optional. Normalerweise werden die Werte von pofda() gecacht. Möchte man das umgehen (z.B. für temporäre Tabellen, so übersteuert der 2. Parameter = .t. das Caching.

wah_get(„ADR_LAND“,„DE “)

Funktion gibt in Abhängigkeit des optionaen 3. Parameters den Wert aus der Tabelle WAHL zurück.

Achtung immer als String!

Parameter gibt den Bereich (WAH_FELD) in dem gesucht wird

Parameter ist das Kürzel als Suchbegriff

Parameter. Ist dieser nicht gefüllt, so wird die Bezeichnung des Kürzels zurück gegeben. Wird hier ein Wert übergeben, so wird dieser als Feldname in der Tabelle WAHL gewertet und der entsprechende Feldinhalt dieser Spalte zurückgegeben.

Beispiel zur Artikelsuche:

* Prüfe Artikel

cFields="ART_BEZ1"

cWhere="ART_ARTNR='"+cArtNr+"'"

cFrom="ARTIKEL"

oSearch=GetC
ursor(0,cFields,cFrom,cWhere,"ART_ARTNR",)

if recc(oSearch)#1

* Kein gültiger Artikel

  meld(“Kein gültiger Artikel”)

  return

endif

oRow=GetRow(oSearch,1) 

c
Bez=GetField(oRow,"ART_BEZ1")

Beispiel Massenupdate

cWhere="MPW_MPKID="+v2c(nKopfID)

cWhere=cWhere+" and MPW_MPMID>0"

cWhere=cWhere+" and MPW_ZEIT="+v2c(d1900,4)

UPDATE("MP_WERT",cWhere,"MPW_MPMID",0,"MPW_USRID",_USERID)

Zugriff auf die lokale Datenbank mit oFields

Um mit dem Datenobjekt oFields zu arbeiten, stehen zwei weitere Funktionen zur Verfügung:

oFields=

GetFields
(
oSearch
 ,nRow
)

Für jedes Feld das in der Feldliste des Cursor steht wird nun im Objekt oFields ein Wertepaar definiert. (Name der Spalte, Inhalt der Spalte).

Der zweite Parameter ist optional und gibt den Datensatz im Cursors an, die eingelesen wird. Wird der Parameter weggelassen, so werden die Werte des ersten Datensatz gesetzt.

oFields=

GETBLANK

 
(oSearch
)

Im Falle, dass man für jedes Feld einen leeren Wert initialisieren möchte, so kann dieser Aufruf genutzt werden.

aFields 
=
afields
(oFields)

Erstellt eine mehrdimensionales Array(n,2) aus dem oFields-Objekt.

aFields[n,1]Name der Tabellenspalte

aFields[n,2]Wert in der Tabellenspalte

Der Zugriff auf die Werte erfolgt dann einfach durch Angabe des oFields-Objektes und dem Tabellenfeld angehängt durch einen Punkt.

Zu beachten gilt, dass die Zuweisung von zwei oFields Objekten keine getrennten Objekte erzeugt, sondern nur Referenzen. Möchte man zwei eigenständige Objekte erhalten, so muss der Cursor zwei Mal explizit zugewiesen werden.

Zwei unabhängige Objekte:

 
oFieldsVOR=GetFields(oSearch
,nRow
)

      oFieldsVOROld=GetFields(oSearch
,nRow
)

Objekte verbunden über eine Referenz. Das heißt eine Änderung im Einen führt diese automatisch im anderen nach!

 
oFieldsVOR=GetFields(oSearch
,nRow
)

 
oFieldsVOR
1
=
oFieldsVOR

Beispiel:

* Lese nun Adressinfos nach - VOR_KDNR

oSearch=
GetCursor
(0,"*","ADRESSEN","ADR_NUMMER="+v2c(oFieldsVOR.VOR_KDNR))

if recc
(oSearch)>0

   oFieldsADR=GetFields(oSearch
,nRow
)

else

 
oFieldsADR=GetBlank(oSearch
,nRow
)   

endif

cSprache=oFieldsADR.ADR_SPR

MS Sql-Server als lokale Datenbank

Das „group by“ geht auf die Daten vor der Gruppierung. Aus diesem Grund kann man noch keine Aliase verwenden. Aber dennoch Qulifiziert zugreifen!

„Order by“ und „Having“ wiederum werden erst nach der Gruppierung ausgeführt, wodurch auf die Alias zugegriffen werden kann bzw. beim Having sogar muss.

Beispiel:

if 
_PC

   cFields=cFields+",nvl(LAP2.LAP_NAME,' ') as STELLP"

else

 
cFields=cFields+",case when LAP2.LAP_NAME is null then ' ' else LAP2.LAP_NAME end as STELLP"

endif

cFrom=cFrom+" left join LAGPLATZ LAP2 on LAP2.ID=LAGPLATZ.LAP_LAPID"

cOrder=cOrder+",STELLP"

cGroup=cGroup+",LAP2.LAP_NAME"

Zugriff auf Serverprozess

Der Austausch zwischen den mobilen Geräten und dem Server erfolgt über den Austausch von Wertepaaren, die in einer Zeilenstruktur abgebildet werden.

Dazu wird auf dem mobilen Gerät diese Struktur gefüllt und dann dem Server übergeben. Hier unterscheidet man zwei Arten.

ASyncJob()

Die Aufgabe wird an den Server übergeben. Auf eine Antwort wird nicht gewartet. Das heißt am MDE geht die Arbeit sofort weiter.

Folgende Parameter hat die Funktion

Serverfunktion, die aufgerufen werden soll

Parameter-Liste zur Übergabe

Bei mehreren Job-Verarbeitungen kann die Gruppe für die Jobverarbeitung mitgegeben werden.

lNoSync – Nach der Übergabe startet nicht ein Sync, sondern erst, wenn wieder die Zeitspanne für den nächsten Sync erreicht ist. Ohne Gruppe kann der Wert auch als 3. Parameter übergeben werden. (Default ist .f.)

Beispiel

A
SyncJob("PONLINE",cParam,
“ADM“
,
.t.
)

SyncJob()

Die Aufgabe wird an den Server übergeben. Der aktuelle Prozess wartet nun bis eine Antwort vom Server kommt. Erst dann geht die Arbeit am MDE weiter.

Folgende Parameter hat die Funktion

Serverfunktion, die aufgerufen werden soll

Parameter-Liste zur Übergabe

Bei mehreren Job-Verarbeitungen kann die Gruppe für die Jobverarbeitung mitgegeben werden.

Zeit in Millisekunden, wann der Job bei einer eventuellen Nicht-Erreichbarkeit abbricht.

Beispiel

cOutput=SyncJob("PONLINE",cParam,,1000)

lRet=Checkweb()

Mit diesem Aufruf kann der aktuelle Verbindungsstatus zum Webserver abgefragt werden.

Der Rückgabewert ist wahr, wenn aktuell eine Verbindung zustande kommt.

Veraltet – bitte Online() verwenden

lRet=
Online()

Mit diesem Aufruf kann der aktuelle Verbindungsstatus zum Webserver abgefragt werden.

Der Rückgabewert ist wahr, wenn aktuell eine Verbindung zustande kommt.

Syncstop
(
.t./.f.
)

Stoppt die Synchronisation. Als Parameter existieren folgende Werte:

.f. oder ohne Parameter = Sync-Abbruch wenn aktueller Sync beendet

.t. oder 1 = Sync-Abbruch nach aktueller Tabelle

2 = Sync-Abbruch sofort

3 = Sync-Abbruch durch Benutzer (kann nur durch SyncStart(.t.) wieder aufgehoben werden. Beendet Sync nach aktueller Tabelle.

Default ist .f.

Syncstart()

Startet die Synchronisation wieder, sofern diese nicht mit SyncStop(3) generell gestoppt wurde.

Sync
S
tart
(
.t.
)

Startet die Synchronisation wieder, sofern diese mit SyncStop(3) generell gestoppt wurde.

SyncStatus()

Funktion zur Abfrage des aktuellen Sync-Zustandes. Folgende Rückgabewerte wurden definiert:0 = Sync ist angeschaltet1 = Sync wird nach Abschluss des aktuellen Sync-Laufs beendet2 = Sync wird nach Abschluss aktuelle Tabelle beendet3 = Sync wird sofort beendet4 = Sync ist angehalten5 = Sync wurde durch den Benutzer angehalten (StopSync(3))

SyncReset(nMinuten)

Korrigiert die interne Zeit der letzten Synchronisation um n Minuten. So können fehlende Sätze neu synchronisiert werden. – Sonderfall

SyncResume()

Ist als Alternative zu SyncStart() zu sehen. Währen bei SyncStart() sofort die nächste Synchronisation beginnt, läuft bei SyncResume() der Timer zur nächsten Synchronisation im Hintergrund weiter. Erst wenn dieser 0 wird, beginnt der nächste Sync.

Die Aufgaben werden am Server in der Tabelle MDEJOB gespeichert und über den Serverdienst, der über „Dienste“ – „MDE Job-Verarbeitung“ gestartet wird.

Beispiel für die Inventurübergabe:

vget(@cParam,"INVDAT",dInvDat,"D")

vget(@cParam,"INVLIST",cInvList)

vget(@cParam,"LAGER",cLager)

vget(@cParam,"ARTNR",cArtNr)

vget(@cParam,"MENGE",str(nMenge))

vget(@cParam,"SNR",cSNr)

ASyncJob("INSERT_INV",cParam)

Kapselung von Servercode

Immer wieder kommt es vor, dass man einen Codeteil aus mehreren Serverfunktionen aufrufen möchte. Dies geht hier über die Anlage einer separaten Serverfunktion, die dann über eine Datenermittlung in den aktuellen Code eingebunden wird.

Der gewünschte Code sei in unserem Beispiel in der Funktion „KAPSELUNG“ unter dem Server-Einsprungspunkt „KASPELCODE“ gespeichert.

Die eigentliche Funktion ist in der Tabelle „MDEFUNC“ definiert. Der Code an sich ist in der Tabelle „MDECODE“ mit dem internen Bezug MDEFUNC.ID = MDECODE.MDC_MDFID und dem entsprechenden MDC_TODO-Namen zu finden.

* Lese gekapselte Datenregel ein

cDR=RefThis.
Load
("KAPSELCODE.KAPSELFUNC")

* 
Führe
 Datenregel mit übergebenen Parameter aus
 

cError=ex(cDR,,,
P1
,P2,P3,…)

Über die Standard-Funktion

ex()
kann dann der Code an der aufgerufenen Stelle zur Laufzeit eingebunden werden.

Alternativ kann auch folgender Aufruf verwendet werden_

*Alternative

RefThis.Do("
KAPSELCODE.KAPSELFUNC
",
P1,P2,P3,…
)

*
Oder

RefThis.SyncJob("_EXT
_
LAGER.UMLAGERN",cParam)

Bei Timer-Funktionen ist Stand Mai 2019 kein

RefThis.Do
möglich. Daher hier der Aufruf über
RefForm.oBusiness.Do
realisieren!

Zugriff auf das Dateisystem - Dateiupload

Für den Austausch von Dateien zwischen dem Client und dem Server, wie zB der Übertragung einer Unterschrift oder PDF wurden einige Befehle umgesetzt:

file(cFile)

Diese Funktion prüft, ob die Datei, die inklusive Pfad in der Variablen cFile existiert. Der Rückgabewert ist .t. oder .f.

d=
AttrFile(cFile, nMode)

Gibt zu einer übergeben Datei die Zeitattribute je nach Modus zurück

nMode = 1: Erzeugung (wird beim Kopieren verändert)

nMode = 2: Letzter lesender Zugriff

nMode = 3: Letzter schreibender Zugriff

makedir(cPath)

Legt das in cPath angegeben Verzeichnis an. Existiert dies bereits macht das Programm nichts

removedir(cPath)

Löscht das angegeben Verzeichnis

fcopy(cFile,cDest)

Kopiert die Datei cFile, die inklusive Pfad anzugeben ist, in das Zielverzeichnis.

Als 3. Parameter kann angegeben werden, ob die Datei im Zielverzeichnis einfach überschrieben wird (Default .f.)

Mit dem 4. Parameter kann man sagen, ob die Datei kopiert (.f.) oder aber verschoben (.t.) werden soll. Default ist hier .f.

n=
adir(
aFiles
,cPath
F
,
lRecursive
)

Die Funktion gibt die Anzahl der Dateien als Rückgabewert zurück.

Parallel erhält man die Dateinamen im angegebenen Array zurück.

Enthält der 2. Parameter einen „.“ (Punkt), so wertet das System diesen Teilstring als Filter auf die Dateien (also auch ‚R*.pdf‘). Wenn nicht, werden alle Dateien im Verzeichnis zurück gegeben.

Der 3. Parameter ist optional und gibt an, ob auch Unterverzeichnisse mit durchsucht werden sollen. Wird dieser nicht angegeben, dann wird nur das angegebene Verzeichnis durchsucht.

cFilePath="C:\temp"
 
oder
 
cFilePath="C:\temp
\*.pdf
"

aFiles=““

adim(„aFiles“,0)

n=adir("aFiles",cFilePath)

meld("Anz.
:
"+v2c(n
)+"/
"+v2c(aFiles[1
])+"
/"+v2c(alen("aFiles")))

DelFile(cFile)

Löscht die angegebene Datei.

Veraltet – bitte

FDEL
verwenden

fdel(cFile)

Löscht die angegebene Datei.

cGUID=addupload(cFile)

Diese Funktion wandelt die Datei in übertragbares Format und trägt automatisch die Werte für den Upload in die Tabelle MDEUPLD ein.

Über einen zweiten Parameter kann angegeben werden, ob die Datei kopiert oder verschoben wird (lMove)???

Als Rückgabewert erhält man dann den Namen, den die Datei später im Upload-Verzeichnis (UPLOADPATH in SWSWEB.ini) auf dem Server haben wird.

Die Realisierung der Umwandlung passiert während der normalen Jobverarbeitung, wenn diese ausgeführt wird.

cImage=FileToHex(cFile)

Wandelt die Datei cFile in einen Hex-String um und speichert diesen in der Variablen z.B.: cImage.

Diese kann dann als Parameter in cParams an den Serverprozess übertragen werden. (ALT – neu siehe addupload)

cStr=FileToString(cFile)

Wandelt die Datei cFile in einen String um und speichert diesen in der Variablen z.B.: cStr.

Beispiel für die Rückwandlung des Hex-Strings in das Ursprungsbild:

cHex=vget(@cInput,"Sign")

cFile=tmp_file("jpg")

hex2file(cHex,cFile)

shellex(cFile)

Beispiel für die Übertragung einer PDF-Datei :

Am MDE-PC

cGUID=addupload(cPDFFile

* Übergebe Daten an Server

cParam=""

vget(@cParam,"FILE",cGUID)

ASyncJob("REPUPLOAD",cParam)

Am Server

cUplFile=vget(@cInput,"FILE",,"C")

cZielPfad=
“E:\
....\mdereport\"

makedir(cZielPfad)

fcopy(cQuellPfad+cUplFile,cZielFile)

Dateisynchronisation auf alle MDE

Eine weitere Variante Dateien zwischen den beiden Enden (Clients und Server) zu tauschen und synchron zu halten stellt die Verwendung der Tabelle MDEFILE dar.

Alle Dateien, die in der Tabelle MDEFILE auf dem Server vermerkt sind, werden automatisch über den Synchronisationsprozess der Clients auf diese übertragen.

In diesem Fall ist die Tabelle auf dem Server führend. Wird hier z.B. eine neuere Version eingetragen, so führt dies automatisch zum Download auf den Client.

Dennoch gibt es auch auf dem Client Möglichkeiten die Struktur zu analysieren und gegebenen Falls die Synchronität wieder herzustellen.

Sollen Dateien aus der Synchronisation herausgenommen werden, so muss der Datensatz aus der MDEFILE über das Datenobjekt

goData.MDEFILE.Delete(
ID=xxx
)
gelöscht werden, dass ein entsprechender MDEDEL Satz erzeugt wird und damit auch die Datei auf dem MDE’s gelöscht werden.

Funktionen auf dem Server:

nID=setMDEfile()

Mit dieser Server-Funktion wird die Tabelle MDEFILE gefüllt bzw. aktualisiert, wenn die Datei bereits vorhanden war.

Der Rückgabewert der Funktion ist die ID des Satzes in der MDEFILE

1. Wert: Datei inkl. Pfad die synchronisiert werden soll

2. Wert: Referenztabelle – Oberste Ebene des Speicherortes auf den MDE

3. Wert:Referenz-ID – Fasst alle zusammengehörigen Dateien in der Speicherstruktur auf dem MDE zusammen. Die ID wird in Teilstrings zerlegt und ergibt daraus die Speicherstruktur. Siehe dazu die Konfigurationsvariable FILESDIRLENGTH (3)

4. Wert:Dateiname zur Identifikation der Datei in der MDEFILE

5. Wert:Typ der Datei – Eine weitere Möglichkeit Dateien zusammen zu fassen. Siehe hier auch die Anmerkungen zu aDirMDE()

6. Wert:Filterbedingung für Geräte und Gerätegruppen

7. Wert:Info über den Erfolg der Funktion als Referenz<leer> wenn alles OK-3 wenn Datei oder Verzeichnis nicht existieren

Beispiel:

?setmdefile("c:\temp\
art
.txt","
ART
",123,

"DGDAT1","INFO",,@cMessage)

CheckMDEFile()

Future Wish

Diese Funktion sollte auch als Server-Funktion bereit gestellt werden. Die Funktionsweise sollte wie folgt sein:

Die Funktion nimmt alle in der Servertabelle MDEFILE hinterlegten Sätze und prüft

Ist Datei vorhanden und Zeitstempel passt – mache nichts

Ist die Datei vorhanden und Zeitstempel ist falsch (egal wie) – Schreibe Zeitstempel neu in Datei und setze curtimest hoch

Ist die Datei nicht mehr vorhanden – lösche den Satz in MDEFILE und schreibe einen MDEDEL-Satz

Somit wären auch Änderungen am Server einfach wieder den MDE’s mitteilbar

Wenn nicht dann:

cPathF="C:\TEMP\*.*"

dime aFiles(1,5)

adir("aFiles",cPathF)

?aFiles[x,1]Name der Datei

?aFiles[x,2]Größe der Datei in Bytes

?aFiles[x,3]Datum des Zeitstempels

?aFiles[x,4]Uhrzeit des Zeitstempels

?aFiles[x,5]Dateiattribute

Für die Übertragung vom Server gibt es noch die Konfigurationsvariable MDEDLB64.

Dieser Schalter ist bei SQL-Servern per Default auf YES. Ansonsten NO.

In diesem Fall erfolgt der Dateitransfer über ein Base64-Feld. Dieses ist Bestandsteil der Tabelle MDEFILE (MDI_DATA).

Funktionen auf dem Client:

Auf dem Gerät sind nun die Dateien in einem Verzeichnis FILES unterhalb des Datenbank-Ordners zu finden. Dieser Ordner kann über die Konfigurationsvariable FILESROOT auch an jeden beliebigen Ort verschoben werden.

C
heck
MDEF
iles
()

Diese Funktion prüft alle Dateien, die in der lokalen Tabelle MDEFILES stehen, ob diese auf dem MDE vorhanden sind und ob der Zeitstempel in der Tabelle mit der Datei zusammenpasst. Ist dies nicht der Fall, so wird die Datei erneut zum Download angefordert. Dies erfolgt über das Feld Status=0.

cPath=

GetMDIPath(nID)

Diese Funktion gibt den kompletten Pfad inkl. Dateiname zu der gespeicherten Datei als String zurück.

E:\MDEDB_DM\FILES\
ART
\000\000\000\000\123
\ART.TXT

cGUID=GetGUID()

Gibt als String die nächste GUID (Dateiname für Dateiupload) zurück.

Diese Funktion wird nicht mehr benötigt, da dies heute bereits automatisch in der Funktion addUpload() integriert ist.

nFiles=aDirMDI

(<Array>, <MDI_REFTAB>, <MDI_REFID>, <Dat.Typ>)

Analog dem Befehl adir() mit dem man Dateien in einem Verzeichnis auflisten kann.

Dieser Parameter nimmt einem im Prinzip die Aufsplittung der Unterverzeichnisse ab.

1. Wert: Array zur Aufnahme der Dateinamen

2. Wert: Referenztabelle – Oberste Ebene des Speicherortes auf den MDE

3. Wert:Referenz-ID

4. Wert:Hierüber kann man einen Filter definieren, welche Files gewünscht werdenA*.pdf – alle Dateien beginnen mit A und der Endung PDF*.pdf – Alle PDF-DateienINFO – alle Dateien mit MDI_TYPE=INFO

Beispiel:

* Lese Dateien auf dem MDE

nFiles=0

* 
Definiere das Array

aDir
=""

adim("aDir",0)

* Lese gewünschte Dateien ein

nFiles=adirmdi("aDir","ART",123,"*")

* Prüfe Ergebnis 

meld("Anz.: "+v2c(nFiles)+" / "+v2c(
alen
("aDir")))

meld("Datei 1: "+v2c(
aDir
[1])
)

Fotofunktionen – nur für das MDE

Für das MDE gibt es die Möglichkeit Fotos von der Kamera aufzunehmen…

vRet=
CaptureImage()

Diese Funktion ist speziell für die Geräte von Motorola (MC55) geschrieben und erlaubt es Fotos mit der Integrierten Kamera aufzunehmen.

Die Funktion hat 11 Parameter

1. Wert: Name der zu erzeugenden Datei

2. Wert: Auflösung des Bildes zB. „R_1024x768“

(R_1024x768, R_800x600, R_640x480, R_1280x1024, R_160x120, R_320x240, R_352x288, R_176x144, R_1280x960, R_1600x1200, R_2048x1536)

3. Wert: JPEG-Komprimierung in % von 0-100

4. Wert: Fenstertitel

5. Wert: Infotext zum Bild

6. Wert: Gibt an, ob das Fotofenster geschlossen werden darf (rotes X)

7. Wert: Text für Button „aufnehmen“

8. Wert: Text für Button „verwerfen“

9. Wert: Text für „speichern und beenden“

10. Wert: Text für „beenden ohne Bild“

11. Wert: Text für „Lampe“

vRet=GetUpload()

Erzeugt die Datei aus dem Base64-Feld der Tabelle MDEUPLD (füllt AddUpload-Funktion) bzw. kopiert diese aus dem Upload-Verzeichnis an die gegebene Stelle.

1. Wert: Dateiname aus MDEUPLD

2. Wert: Ablagepfad

3. Wert: Soll Datei in der Upload-Tabelle erhalten bleiben

4. Wert: ???

5. Wert: ???

Eventuell wird dies nicht mehr benötigt, da addUpload und die Datensynchronisation heute dafür sorgt, dass die Datei automatisch in einem Zielverzeichnis auf dem Server landet, (siehe Zugriff auf das Dateisystem)

Ask(cFile,"Image",

"IMGFile",232,130)

Erweiterung des ASK-Befehles um Bilder auf Masken darzustellen

IMGFile.Image=cFile

Weist dem IMG-Objekt z.B. beim Klick ein neues Bild zu

Beispiel:

am MDE

cFile=""

c
G
UIID
=""

lCloseButton=0

cNewFile=CaptureImage(cFile,"
R_
1024
x768
",75,"Kamera",cIMG_KKS+" - "+cIMG_Info,lCloseButton)

if 
!
empty
(cNewFile)

 
cGUIID
=AddUpload(cNewFile,1)

 

 
* Name der Datei
 
 
cFileName=
allt
(cIMG_KKS)+"_"+
left
(
TTOC
(tIMG_TS,1),8)+"_"+
substr
(
TTOC
(tIMG_TS,1),9)

   …UPLOAD…

E
ndif

Am Server

cGUIID
=vget(@cInput,"
GUIID
",,"C")

vRet=getupload(
cGUIID
,cZiel,lKeepDB,1,@cErrMsg)

Reports am MDE-PC

Mit dem vereinfachten Reportgenerator kann auch am MDE-PC ein Report gestaltet und erstellt werden:

layout("C:\
Rep
.xml")

Startet den Reportgenerator mit der angegeben XML-Datei.

Ist diese nicht vorhanden, so wird diese neu angelegt.

PDF(oRep,cLayout,cFile)

Aufruf der PDF-Erzeugung.

Parameter: Cursor für die Daten auf dem Report. Aktuell geht hier nur ein Cursor, der alle Daten pro Satz enthalten muss.

Parameter: XML-Layoutfile für den Report oder der Einsprungspunkt, wenn der Report über die MDE-Verwaltung als Report gepflegt wird.

Parameter. Zielname des fertigen Dokuments

Run(cPDFFile
,,,.t.
)

Öffnet das angegeben Dokument auf dem MDE-PC

Der 4. Parameter kann dazu genutzt werden, dass der aufrufende Dialog weiter bearbeitet werden kann (nicht modal). .f. ist der Default-Wert.

Run(cPDFFile,

"RK_KYO","printto")

Druckt das angegebene PDF-Dokument auf den angegebenen Drucker

Beispiel für die Erstellung eines PDF-Dokuments

oReport=GetCursor(0,cFields,cFrom,cWhere,cOrder)

* Layoutfile für Report

cRepLayout=getcfg("W_REPSERVXML","C:\TEMP\RepService.xml")

* Datei, die erzeugt werden soll

cPDFFile=getcfg("W_REPFILE","C:\TEMP\RepService.pdf")

* Rufe Erzeugung auf

PDF(oReport,cRepLayout,cPDFFile)

Start externer Programme:

Um externe Programme aufzurufen existiert die RUN-Funktion:

Run(“x.exe”,”a=b”)

Die Funktion RUN startet das Programm, das als erster Parameter übergeben wird.

Der zweite Parameter nimmt eventuell Übergabeparameter für das Programm auf.

Beispiel für die Unterschriftenerfassung mit SignPad:

Run(„c:\Programme\StepOver\SimpleSigner.exe“, „c:\temp\sign.jpg
 250 100 AutoSaveSig
-2
 Textout-Unterschrift 
“)

Beispiel für die Unterschriftenerfassung mit SignatureOffice:

run("C:\Program
me\StepOver\eSignatureOffice
\SOSigOffice.exe",cPDFFile+" -sss="+cTempSSS+" -scs="+cTempSCS+" -zoom=50"

Fremdsprachigkeit:

Prinzipiell sind die MDE-Programme auch dann mit einer Fremdsprachen-Oberfläche aufrufbar, sofern das Professional-ERP Modul „Fremdsprache“ eingesetzt wird.

In diesem Fall muss die Tabelle „LANGUAGE“ im SYS-Verzeichnis um folgende 2 Felder erweitert werden:

LAN_MDE

Numerisch 1

CURTIMEST

Datum / Zeit

Dem Webservice muss mitgeteilt werden, wo er die Tabelle LANGAUGE finden kann.

SYSDB= E:\ProfERP\SYS

SYSCON=PROVIDER=SQLOLEDB;DRIVER=SQL Server;Address=SWSNBDG2016;SERVER=SWSNBDG2016;Database=PERPxxx_SYS;UID=sa;PWD
=xxx

Die entsprechenden MDE werden über die SWSMDE.INI und den Parameter MDELANG auf die Fremdsprache eingestellt.

MDELANG=E

Mit der Synchronisation werden vom Server alle Spracheinträge geholt, die das Kennzeichen LAN_MDE gesetzt haben.

Bei der Darstellung der Masken prüft das Programm intern, ob der entsprechende Spracheintrag für das anzuzeigende Objekt lokal vorhanden ist. Ist dieser nicht der Fall, so wird dies dem Server über die Job-Tabelle mitgeteilt. Dieser sucht nun in der LANGUAGE-Tabelle auf dem Server, ob er für das Objekt und die deutsche Übersetzung einen Eintrag findet. Ist dieser bereits angelegt, so wird nun das MDE-Flag gesetzt. Wenn nicht, wird ein neuer Eintrag mit Flag erzeugt. Daraufhin wird der Satz wieder auf das MDE synchronisiert.

Sofern für das Objekt kein englischer text eingetragen ist, wird der deutsche Text angezeigt.

Die Pflege der Übersetzungstexte erfolgt über die Firmenparameter – Extras - Übersetzungstexte. Hier kann über das Flag „MDE“ gefiltert werden.

cTest=„~“+v2c(
n
)

Meld(cTest)

Textstrings, die mit „~“ beginnen werden nicht übersetzt.

Bei der Ausgabe wird die Tilde abgeschnitten.

GetL(<
D
efault-Text>, <Objekt>,“C“)

Ermittelt den fremdsprachigen Text für ein Objekt mit dem vorgegebenen deutschen Text.

Beispiele:

_SCREEN.Caption="~"
+_CR
+GetL("Service Auftrag","FRMMDE","C")

Anmerkungen:

Lokal wird die Tabelle LANGUAGE nur erzeugt, wenn ein Eintrag für MDELANG in der SWSMDE.ini gesetzt ist.

Die Login-Oberfläche startet, sofern ein Wert für MDELANG gesetzt ist, immer in englisch.

MDE-Jobverarbeitung

Möchte man auf eventuelle Probleme bei der Verarbeitung der MDE-Jobs reagieren, so steht hierfür die Datenregel _MDE zur Verfügung.

Im Einsprungspunkt *:VJOBUDATE hat man die Werte des aktuellen Jobs im Objekt goPData.MDEJOB.oFields.

Unterschiede in den Sprachversionen:

Die Unterschiede der Sprachversionen liegen vorwiegen im Zugriff auf die Datenbank. Da bei der PC-Version noch auf die klassischen FoxPro-Tabellen zu gegriffen wird, kommt am MDE die SQL-CE-Datenbank zum Einsatz.

Der Parameter _PC kann aber auch dazu verwendet werden um Funktionen, die am MDE nicht zur Verfügung stehen aus dem Ablauf und damit einem Absturz auszuklammern.

Anzeige <NULL> im Grid:

if _PC

 
cFields=cFields+",nvl(BE3_SNR,space(25)) as BE3_SNR"

else

   cFields=cFields+",case when BE3_SNR is null then ' ' else BE3_SNR end as BE3_SNR"

endif

iif in Abfragen

if _PC

   cFields="sum(iif(MPW_WERT>100,1,0))+0000 as PosWert"

else

   cFields=”sum(case when [MPW_WERT]=1 then 1 else 0 end)+0000 as [PosWert]”

endif

Bit-Felder validieren

if _PC

   cWhere=cWhere+" and bittest("+cMPZFeld+","+v2c(nBit)+")=1" 

else

  cWhere=cWhere+" and cast("+cMPZFeld+" as BGINT)&"+ v2c(nBit^2)+">0"

endif

Umstellung von Version 1.xxx auf Version 2.xxx

Neben dem immer erweiterten Sprachumfang gibt es im Bereich des Insert die größten Änderungen.

Die Funktion gibt nun als Rückgabewert die vom System lokal generierte ID zurück.

Gibt es zudem in der Tabelle das Feld MDEID (ohne jeden Präfix), so wird dieses Feld ebenfalls mit der ID gefüllt. Sollte dieses Synchronisations-Feld aus historischen Gründen anders heißen, so ist dieses manuell nach dem Insert Up-zu-daten.

Die ID verwendet die Konfigurationsvariable MDEID, die immer 3-stellig numerisch den jeweiligen MDE-Client eindeutig beschreiben sollte. Somit kann auf den bisherigen Offset verzichtet werden.

Die Funktion

NextID()
wird damit in diesem Bereich auch nicht mehr benötigt.

Alt:

nMDEOffset= val(alltrim(_MDEID))*1000000000
 

nPosID=val(getcfg("NEXTID","1"))

nPosMDEID=nPosID+nMDEOffset

INSERT
("POSITION","POS_AUF",nVORAUF,<…>,
"POS_MDEID",nPosMDEID
)

Neu – mit Feld MDEID:

nPosID= INSERT("POSITION","POS_AUF",nVORAUF,<…>)

Neu – mit historischem xxx_MDEID-Feld:

nPosID= INSERT("POSITION","POS_AUF",nVORAUF,<…>)

UPDATE("POSITION",nPosID,"POS_MDEID",nPosID)

Eingehend mit diesen Änderungen sind auch die _CHECK_CHANGE_ID Einsprungspunkte lokal und am Server anzupassen:

Aus

   if 
nAnsMDEID#0 and nAnsID=(nAnsMDEID-nMDEOffset)

wird

   if 
nAnsMDEID#0 and nAnsID=nAnsMDEID

Umstellung von Version 2.xxx auf Version 3.xxx

Folgende Einstellungen sollten für eine MDE (Honeywell) im klassischen gemacht werden, dass das Look and Feel gleich bleibt

ScaleFont=1,5

Bringt die Schriftgröße in ein Format, das gut lesbar ist

ScaleX=1,1

Verbreitert die Maske so, dass es füllend auf dem Honeywell angezeigt wird

ScaleY=1,2

Skaliert die Höhe der Felder der Maske so, dass es füllend auf dem Honeywell angezeigt wird

ScaleGrid=1,2

Damit können die Spalten im Grid um einen Faktor vergrößert werden, so dass diese zum PC-Client passen.

MENULOGO=NO

Entfernt auf der Menüebene das Professional-Logo, so dass 4 Menüpunkte auf die Seite des Honeywell passen

PROGLOVE=NO

Hiermit kann der Button „Proglove verbinden“ auf der Login-Maske ausgeblendet werden

LOGFILE=YES

Schafft die Möglichkeit Events auf dem Gerät zum Ablauf zu speichern. Die Datei mit dem Namen LogFile<datum> wird im LOG-Verzeichnis des ProfessionaERP-Ordners abgelegt

LOGVERBOSITY=E

Gibt das Level für das Logging an (E=Error)

Kurzform

Beschreibung

V

Verbose

D

Debug

I

Info

W

Warnung

E

Error

F

Fataler Error

S

Still

Kurze Infos zur Installation der APP:

Unable to render {include} The included page could not be found.

Umstellung auf Dynamic-Mode

Anhand des Projektes PMT hier eine Auflistung der Arbeiten, die im Zuge einer Umstellung auf den Dynamic-Mode zu machen sind.

Es kommt dabei sicher auf den Zustand des Ausgangscodes an und die Features, die genutzt werden.

Die Liste ist sicher nicht vollständig, beschreibt aber die Probleme / Umstellungen, die in dem Projekt zu machen waren.

_INIT

Der Offset zur Prüfung der Datensynchronisation kann entfernt werden

   nMDEOffset= val(alltrim(_MDEID))*1000000000

Für die Farben (show-Dialoge) sollte eine Sektion zur Definition der Farben eingeführt werden:

   * Farben

   nRGBHGMeld=rgb(184,206,225)

MASK

Speicherung der Maskendarstellung

Die individuellen Änderungen der Maske können im Dynamic-Mode ebenfalls in der Registry gespeichert werden. Dazu muss bei der Maskendefinition der Zweig mit angegeben werden:

Aus

   Clear("Geräteverwaltung",860,698)

Wird nun

   Clear("Geräteverwaltung",860,698,"frmSNRFFRM")

Menü vs. Ribbon

Die bisherigen Menüs führen im Dynamic-Mode zu einer Fehlermeldung und müssen daher entfernt werden

Unable to render {include} The included page could not be found.

Alt:

   AddBar("Datei","","barFile")

   AddBar("Verwerfen","Undo","barUndo","barFile")

Neu:

   * Ribbon-Bar

   AddPage("padRecord","\<Datensatz")

   AddGroup("padRecord","popAllg","Allgemein")

   AddButton("popAllg","barUndo","Verwerfen","ESC
APE
","ESC","UNDO",4,1)

Die Namen für bar___ und die Funktonsaufrufe können einfach so übernommen werden.

Beachte: Die Ribbons werden durch Clear() nicht zerstört. Daher dürfen diese nur einmal erzeugt werden!

Headline über Reiter

Um das Aussehen an Professional-ERP anzupassen, sollte eine Headline unter der Ribbon-Bar eingefügt werden. Die Befüllung erfolgt dann üblicherweise im READ der Funktion

   * Headline

   nAskMASK=AskTop()

 
Ask("","cHeadInfo","TXTHEADLINE",,,4)

 
TXTHEADLINE.FontBold=.t.

 
TXTHEADLINE.FontSize=15

 
TXTHEADLINE.ForeColor=rgb(52,97,140)

 
TXTHEADLINE.WResize=.t.

   Ask()

   Ask()

 
Ask()

Basis-Container - Reiter oder nur einen Container einfügen

Wird mit einer Ribbon-Bar gearbeitet, so ist immer mindestens ein größenveränderlicher Reiter einzufügen:

 
* Basis-Container als Reiter

 
Ask("","Main","PGF",,492)

 
pgfMain.WResize=.t.

   pgfMain.HResize=.t.

Ohne Ribbon-Bar reicht ein Größenveränderlicher Container

   * Container zur Anzeige der ToDo

   Ask("","AdrUms","CNTSUCH",,444)

   CNTSUCH.WResize=.t.

   CNTSUCH.HResize=.t.

Objekte größenveränderlich gestalten

Alle Objekte können nun auf das Vergrößern und Verkleinern der Maske eingestellt werden. Hier eine Auswahl der Befehle:

<…

>.WResize=.t.

Vergrößert das Objekt in der Breite

<…

>.HResize=.t.

Vergrößert das Objekt in der Höhe

<…

>.HCount=3

Gibt an wie viele größenveränderliche Objekte in der Horizontalen vergrößerbar sind (Achtung nicht Höhe sondern in der Breite) – muss bei jedem Objekt angegeben werden

<…

>.HPos=3

Gibt die Position an, an der wievielten Stelle das Objekt in Bezug auf die horizontalen größenveränderlichen Objekte liegt.

<…

>.VCount=2

Gibt an wie viele größenveränderliche Objekte in der Vertikalnen vergrößerbar sind

<…

>.VHPos=2

Gibt die Position an, an der wievielten Stelle das Objekt in Bezug auf die vertikalen größenveränderlichen Objekte liegt.

Meldungen:

Die Meldungen, die dem User mehr als eine Einfache Info geben sollen, sollten von einem

Meld()
in ein
Show()
geändert werden:

Aus

 
meld("Kein Termin gefunden")

wird

 
show
(_CR+"Kein Termin gefunden!","Info",1,nRGBHGMeld,nRGBGruen,16)

Der Vorteil des Show ist, dass dieses auch im Bereich der Validierungen <VA_xxx> eingesetzt werden kann, da hier das Bestätigen der Meldung nicht erneut zu einem Focus-Erhalt in dem Feld führt, was im Normalfall dazu führt, dass das Valid erneut schießt. Endlosschleife

Analog wurde die

J_N_A()
-
Funktion um weitere Parameter zur Gestaltung erweitert.

Aus

   nJNA=j_n_a("Servicezeit übernehmen?","J","Service Auftrag")

kann folgendes werden

 
nJNA=j_n_a("Es sind noch..."+_CR+cAnzText+_CR+"Trotzdem weiter?“

        ,"N","Info",1,nRGBGruen,nRGBRot,nRGBBlau,12)

Sonstige Änderungen:

Shortcuts auf Buttons, Reiter etc. sind von der bisherigen Schreibform

„&Speichern“
umgestellt werden aus die Schreibweise
„\<Speichern
“.

Die bisherige Funktion

<…>.SetFocus()
gibt es nicht mehr. Ab sofort ersetzten durch
<…>.Focus()

Bei Comboboxen steht nur die neue Eigenschaft

<cboxxxx>.ShowValue=.f.
zur Verfügung. Damit kann der gespeicherte Wert in der Anzeige unterdrückt werden.

Die alte Version konnte den Exakt-Vergleich auch auf numerische Werte verarbeiten. Dies ist in der neuen Version nicht mehr zulässig. Also nach „==“ suchen und ggf. ersetzten.

Der Serverprozess

Die Abarbeitung der Aufgaben von den verschiedenen MDE erfolgt über einen Serverdienst. Dieser wird in den Diensten über den Menüpunkt „MDE Job-Verarbeitung“ gestartet.

Unable to render {include} The included page could not be found.

In dem angezeigten Meldungsfenster werden alle verarbeiteten Aufgaben protokolliert und eventuelle Fehler angezeigt.

Parallel zur on Screen Dokumentation werden die abgearbeiteten Aufgaben in der Datei MDEPROT.TXT im Weekend-Verzeichnis aufgezeichnet.

Beispiel der Protokollierung

05.04.2011 08:04:22.03 Verarbeitung gestartet [PC08 # Maier]

UTC 05.04.2011 06:06:05 123 WFPSELECT(SERVANNEHM)

Programmstarte auf Desktop legen

Man kann den Programmteil per Rechtsklick auf den Desktop legen.

Möchte man zudem eine Startmöglichkeit ohne User- und Passworteingabe ermöglichen, so kann der Zieleintrag im Icon um den Parameter

/v<USER>$<PASSWORT>
ergänzt werden. Das Passwort muss dabei zwingend in ProfessionalERP in Großbuchstaben angelegt sein.

Beispiel:

C:\xxx\PROFERP.EXE -t -c"C:\xxx\CONFIG.FP" .t. $ID998ID$ MDE_FRM$with$'MDE_JOB',,'*,KO' "C:\xxx\WEEKEND.DAT\" 
/vmde§MDE
 3721

Der MDE-Systemstatus

Einen Überblick über die Jobverarbeitung und die angeschlossenen Geräte erhält man über die Maske „MDE-Systemstatus“.

Unable to render {include} The included page could not be found.

Die Schalter zum Beeinflussen der Aktualisierungsfrequenz lauten:

MDEACTIVE_C für MDE-Clients

MDEACTIVE_T Topas-Server

MDEACTIVE_* für restliche Geräte

MDEJOBACTIVE Prüfung ob Jobverarbeitung noch aktiv (eventuell MDEACTIVE_V?)

Die SWSWeb.ini

Damit der Webservice korrekt mit der ProfessionalERP-Datenbank zusammenarbeiten kann, müssen hierzu grundlegende Einstellungen in der SWSWeb.ini hinterlegt werden.

Beispiel:

WEBUSER=PROFESS

WEBPW=SWS

DB=E:\WEEK60\weekend.dat

PROT=C:\TEMP\swsweb.txt

WEBUSER=PROFESS

Name des erwarteten Users vom MDE aus dessen Ini-Datei. Dieser muss passen, sonst wird der Zugriff verweigert.

WEBPW=SWS

Passwort des registrierten Users vom MDE. Dieser muss passen, sonst wird der Zugriff verweigert.

DB=___

Pfad zur Professional-ERP – Datenbank. Als Laufwerk oder als UNC-Pfad

CON=___

SQL bei SQL-Datenbank. Benötigt DB=___ nicht mehr

*CON=PROVIDER=SQLOLEDB;DRIVER=SQL Server;Address=<<IP,Port>>;SERVER=<<IP>>;Database=<<DB-Name>>;UID=<<DB-User>>;PWD=<<DB-PW>>

Prot=___

Dieser Schaltet die SQL-Protokollierung ein. Hiermit können Fehler in der Synchronisation festgestellt bzw. die Statements bei der Verarbeitung überprüft werden.

PROTMDE=x,y

Protokollierung nur für die angegebenen MDE

IDPRAEFIX=1

Steuert die ID-Vergabe in der MDEJOB je nach Webservice über den der Job ankommt.

JOBCHECK=YES

Prüft ob JOBS mit identischem Inhalt im Feld MDJ_PARAM, ToDo-Art und Gerät schon da ist. Wenn ja, so wird der zweite nicht beachtet.

Default ist YES

UPLOADPATH=E:\...

Verzeichnis in welches die addUpload(cFile) bei der Jobverarbeitung gespeichert werden

HASH=NO

Ab der Version 2.5. wird bei der Anfrage an den Webservice vom Client immer ein Overhead plus einem Hash-Wert mit übergeben. Der Webservice nimmt somit nur noch Datenanfragen entgegen, wenn der Hashwert passt. Dies dient hauptsächlich zur Sicherheit für unbefugten Datenzugriff von außen.

Das heißt aber auch, dass man den Webservice bzgl. GetCursor() nicht mehr testen kann, wenn der Schalter nicht gesetzt ist.

Default ist YES

Auch der Webservice kann von einem Server auf den anderen Server weitergeleitet werden. In dem Fall muss in der INI des ersten Webservice nur die Adresse des zweiten angegeben werden:

WEBSE
R
VICE=http:
//
webserv2
/
SWSWeb
/
SWSWebService.asmx

Installationsprobleme

Kommt bei der Synchronisation der Fehler, dass in der MDETABLE das Feld CURTIMEST fehlt (Fehler beim Insert), dann ist der DATA_TYP beim GETSTRUCT im Webservice falsch.

Ursache ist die fehlende oder falsche Version des OLEDB \MDAC\OLDEDBVFP9…

GetStruct muss bei DateTime Feldern den DATA_TYP = 135 zurückliefern

 
<TABLE_NAME>mdetable</TABLE_NAME>

 
<COLUMN_NAME>curtimest</COLUMN_NAME>

 
<ORDINAL_POSITION>8</ORDINAL_POSITION>

 
<COLUMN_HASDEFAULT>false</COLUMN_HASDEFAULT>

 
<COLUMN_FLAGS>88</COLUMN_FLAGS>

 
<IS_NULLABLE>false</IS_NULLABLE>

 
<DATA_TYPE>135</DATA_TYPE>

Fallen in der Programmierung SQL

OR-Abfragen

Die folgende Abfrage mit der oder-Verknüpfung benötigte über 2100 ms obwohl beide Felder einen Index hatten

select ID from ARTIKEL where ART_ARTNR='Z1033 ' or ART_EAN='Z1033  '

Aufgebohrt in zwei Abfragen

select ART_ARTNR from ARTIKEL where ART_EAN='403884702184 '

und danach

select ID from ARTIKEL where ART_ARTNR='Z1033    '

ergab jeweils eine Zeitdauer von 40 ms.

Programmhistorie

Im Laufe der Zeit wurde das Modul immer weiter ausgebaut und um neue Funktionen erweitert. Hier die Eckpfeiler der unterschiedlichen Versionen.

Version

1.0.

Alle Grundfunktionen für MDE und Laptop

Synchronisation und Serverprozesse

1.1.

Verbesserte Synchronisation (Abbrüche und Rücksetzung)

Gekoppelte Webservices

1.2.

Objektnamen und Fremdsprachigkeit

MDT_TABLE auf Memofeld umgestellt um Tabellenjoins zu ermöglichen.

1.3.

Einsprungspunkt _EXIT beim Verlassen Maske

Release(„nWert“,“cString“, ….) eingebaut

Empty(dWert) für Datumswerte

Checkbox korrigiert

1.4.

Abs()

VA_ und WH_ für CBK, CBT und CHK bei Änderung Wert

Sortierung in CBK nach WAH_TEXT

Time-Funktion

1.5.

Coverage-Funktion – Protokolliert die Dauern der einzelnen Zeiten auf dem MDE und protokolliert diese in die angegebene Datei.

INI: COVERGAE=My Documents\coverage.txt

1.6.

Ttod() - Wandelt Zeit in ein Datumsformat um

1.7.

Verbesserte Synchronisation der Jobs bei Verbindungsabbrüchen.Download neuer Softwarestände durch Doppel auf Logo der Software-Schmiede. SWS.CAB-File muss im Verzeichnis des Webservices liegen.

1.8.

Synchronisations-Bedingungen für die Geräte (MDT_MDE und MDF-MDE) werden nun unterstützt.

1.9.

_WHEN und _VISIBLE als globale Einsprungspunkte eingeführt. (varams2 enthält nun den Wert für die übergeordnete Einheit z.B. den Container)

Alle Objekte haben nun die Eigenschaft .FontSize

1.10.

Checkbox und Combobox werden nun auch über WHEN gesperrt.

1.11.

Synchronisation über mehrere Tabellen bei gejointen Abfragen

1.12.

EDT-Felder Zeilenumbruch mit Eingabetaste

Sichtbarkeit der Reiter (nicht machbar) aber die Aktivierung wird unterdrückt.

SyncStop und SyncStart überarbeitet

Über cBarText kann im VI_ der Text der Menüs geändert werden.

Arrays nun verfügbar.

1.13.

Masken schließbar per Programm: Close()

Funktion der aufrufenden Maske ausführbar:

ParentDo("SEARCH")

Fremdsprachigkeit

Klick ins Grid an allen Stellen ermöglicht die Verarbeitung der Zeile

1.14.

1.15.

goMonth

1.16.

Beim Löschen von Sätzen durch die Synchronisation am PC wird die ID des Satzes zuerst fortlaufend negativ belegt. Wert wird in der INI gespeichert (Verletzung Unique Key)

Der Klick ins leere Grid führt zu keinem Absturz mehr

1.17.

Korrektur des Filters für MDF_BED, damit alle Funktionen wieder sichtbar sind nach Import.

Neuer Befehl zur Bereinigung von Arrays. CBOxxx_xxx.Clear()

Mehrstufige SWSMDEINI durch den Eintrag SWSMDEINI=<neuer Ort>

1.19.

Verteilte SWSMDEINI auch für Laptop

1.20.

OK-Button auskommentiert auf den MDE-Masken

1.24

SUBV – alle Felder des Users

1.25

Neues Logo, at(), atc(), bar<xxx>.text und –enabled, PKEY_<xxx> und logout()

1.3.10

DelRow, KeyStop und KeyResume

1.3.11

INI-Werte: MDEDEL und RECDEL

1.3.14

Synchrolücke geschlossen: Sofortiger Abbruch der Synchronisation, wenn neuer unverarbeiteter Job vorliegt

1.4.00

Zugirff auf WebServer mit Komprimierung (abschaltbar durch WEBCOMP=NO)

1.4.01

verarbeitete Jobs aus Tabelle MDEJOB löschen / abschaltbar durch DELJOBS=NO

1.4.02

Syncabbruch, wenn der erste Job nicht an den Webservice übergeben werden konnte (Überholen von Jobs)

1.4.03

Einsprungspunkte _LOGOUT/_EXIT in _MENU

Bei erstem Sync keine Sätze für MDEJOB abfragen

1.4.04

Einsprungspunkte _INIT und VI_CMDxxxx für Hauptmenü auch bei erneuter Anmeldung aufrufen

Schalter DOPROT zur Protokollierung der aufgerufenen Funktionen

Text „keine Verbindung“ wenn keine Verbindung zur Datenbank

Bei Abbruch Sync: Meldung Syncabbruch Tabelle mit gelbem Hintergrund

Anzeige WebService und Datenbank bei Doppelklick auf Versions-Container

Server:

Try/catch bei aktiver Protokollierung auf WebServer

Default von JobCheck=YES

Sonderzeichen vor Übergabe ausfiltern

1.4.05

Anzeige Zeitpunkt letzte Synchronisation bei „keine Verbindung“

Variablen _LASTSYNC und _LASTSYNCUTC

Funktionen ttoc, dtoc

Empfehlungen

Parameter in MDE-Jobs

Vom Client werden an den Server Jobs in der Tabelle MDEJOB übergeben. Im Feld MDJ_PARAM stehen hier im Klartext die Übergabeparameter anhand denen der Serverjob die Abarbeitung vornimmt.

Dies sieht für das Befüllen von Boxen im Kommissionier-Rundgang in etwa wie folgt aussehen:

BOOKTYPE=I

BOXID=100161018001

BOXMDEID=100161018001

VORID=86501000129568

LAPID=86501000000929

LAPNAME=BOX-006

PICKER=SUPERUSER

KOBRDAT=18.10.2016 14:23:25

KOBSTATUS=1

USER=SUPERUSER

MDEID=100

_UTC={^2016-10-18 12:23:25}

_USER=SUPERUSER

Diese Informationen sind für den Server-Prozess ausreichend. Für ein einfacheres Nachvollziehen oder für einen einfacheren Support wäre es sehr hilfreich, wenn man z.B. neben der VORID auch noch die Vorgangsnummer mitzugeben. Im Beispiel oben wurde so schon der Lagerplatzname für die Box schon mitgegeben.

Dadurch würde bei der Untersuchung der MDE-Jobs einfacher klar welche Buchungen zu welchem Vorgang gehören.

Hilfsfunktionen zur Analyse

Für die Fehlersuche in einer bestimmten MDE-Anwendung ergeben sich mit der Zeit immer wieder dieselben Fragestellungen. Z.B. bei einer MDE-Lagerlösung: Alle MDE-Jobs zu einer Auftragsnummer.

Hier empfiehlt es sich Hilfsfunktionen oder Auswertungen zu schreiben, mit denen diese Fragen beantwortet werden können ohne ständig im DBC komplexe Abfragen schreiben zu müssen.

Granularität von MDE-Jobs

Bei der Abarbeitung von MDE-Serverjobs sollte bei der Entwicklung auf eine passende Granularität geachtet werden. Es kann sinnvoll sein, einen MDE-Serverjob in mehrere kleinere Jobs auf zu spalten.

Beispiel: Aufspaltung eines Jobs der einen Lieferschein drucken soll.

Hier könnte es sinnvoll sein, diesen Job serverseitig zu teilen in den Wechsel des Vorgangsstatus auf Lieferschein und in einen Job der nur den Lieferscheindruck vornimmt.

Potentielle Vorteile:

Der Code ist übersichtlicher und damit wartungsfreundlicher, da der allgemeinen Programmierempfehlung gefolgt wird das eine Funktion nur eine Verantwortlichkeit hat.

Besseres Fehlerhandling. Wenn z.B. nur der Druckjob fehlschlägt weil der Drucker nicht bereit ist, kann dieser Job einfacher wiederholt werden.

Eventuell bessere Performance: Falls Prozessbeteiligte nur Teilergebnisse brauchen, können diese schneller weiterarbeiten da die anderen Teilergebnisse in anderen MDE-Jobs abgearbeitet werden.

Je nach Anwendung ist es sogar denkbar, mehrere MDE-Jobverarbeitungen zu nutzen – z.B. eine zusätzliche MDE-Jobverarbeitung, die nur Druckjobs abarbeitet.

Fehlerbehandlung und Stabilität

MDE-Lösungen werden in einem ganz anderen Kontext ausgeführt als die Professional ERP Anwendung (Asynchronität der MDE-Clients, Kommunikation über Netzwerke, Job-Abarbeitung ohne menschlichen Nutzer) und haben damit das Potential zu lange Zeit unbemerkten und sehr komplexen Fehlerbildern. Dies muss in der Programmierung beachtet werden. Es ist aber auch unvermeidlich organisatorische Maßnahmen zur Überwachung der MDE-Lösung und Fehlerbehandlung zu treffen. Dazu gilt:

1. Es muss sichergestellt werden, dass eine verantwortliche Stelle beim Kunden existiert, die das Laufen des Webservice und der Jobverarbeitung überwacht. Hierzu muss es zwingend Dokumentation geben, damit Support durch Mitarbeiter der SWS geleistet werden kann, die den Kunden nicht kennen.

2. Es müssen in der MDE-Lösung bereits Mechanismus zum Abfangen von Fehlern eingebaut werden. Falls die Fehler nicht in der Anwendung selbst behandelt werden können, muss ein Verantwortlicher informiert werden. Es ist fatal wenn die Jobverarbeitung zwar stabil läuft, aber ständig Fehler produziert die niemand mitbekommt. Am wichtigsten ist fehlerhaft verarbeitete MDE-Jobs an einen Verantwortlichen zu melden. Hier haben sich automatisch generierte Todos oder Emails bewährt.

Es gilt zu beachten, dass fehlerhaft verarbeitete MDE-Jobs jederzeit auftreten können, auch bei MDE-Lösungen die bereits seit Jahren fehlerfrei laufen. Grund dafür können nicht nur Programmierfehler oder nicht beachtete Sonderfälle sein, sondern auch ausgefallene Infrastruktur oder Änderungen im Professional-ERP Umfeld.

Beispiel: Fällt die Verbindung zum Archivsystem aus, können MDE-Jobs fehlschlagen die an sich korrekt programmiert sind. Es muss dann auch zwingend ein Verantwortlicher informiert werden, da die Verbindung zu Docuware i.d.R. nur durch einen menschlichen Eingriff wiederhergestellt werden kann.

Es ist nicht notwendig und auch unmöglich für jeden möglichen Fehler eine vorbereitete Lösung zu haben, es ist aber im Vorfeld organisatorisch zu klären, wie aufgetretene Fehler entdeckt, klassifiziert, bewertet und behandelt werden können. Dies geht nicht ohne einen Verantwortlichen beim Kunden, dem aufgetretene Fehler automatisch gemeldet werden bzw. der das Laufen der Automatismen überwacht.

Dieser Verantwortliche muss auch entscheiden können wie Fehler behandelt werden. Denkbar sind zum Beispiel folgende Behandlungsebenen:

1. Ignorieren, da Job nicht wichtig genug ist. Beispiel: Interner Versand einer Infomail schlug fehl weil der Mailserver vorübergehend nicht erreichbar war.

2. Beseitigung von Problemen und Wiederholung von Jobs (über ein Tool oder mit Hilfe der SWS)

3. Korrekturmaßnahmen, z.B. Lagerumbuchungen oder manuelles Erfassen von den Informationen die der MDE-Job in die Datenbank schreiben sollte

4. Eskalation an den Support der Software-Schmiede zur Untersuchung und Beseitigung der Fehlerursachen

Offensichtlich ist dies alles abhängig von der jeweiligen Lösung sowie der Auswirkung möglicher Fehler beim Kunden. Mehr Sicherheit verursacht auch immer mehr Kosten. Es kann aber zu fatalen Folgen führen, wenn keinerlei Strategie für die Überwachung und die Behandlung von Fehlern existiert.

Für weitere Infos zu Professional ERP steht Ihnen das Team der Software-Schmiede Vogler & Hauke GmbH gerne zur Verfügung

Besuchen Sie uns doch mal im Internet!

Unable to render {include} The included page could not be found.

Auf dieser Seite enthalten:

Weitere hilfreiche Seiten:

Filter by label

There are no items with the selected labels at this time.