ich bin neu hier im Forum und habe leider keinerlei Programmierererfahrung - na ja, stimmt nicht ganz vor 25 Jahren habe ich etwas Tubo-Pascal in der Schule gelernt. Nun stehe ich aber vor einer Programmieraufgabe ...
Also ich habe ein Messprogramm (Abacom Profilab, http://www.abacom-onine.de" onclick="window.open(this.href);return false;), welches Daten aus allen möglichen Messinstrumenten erfasst, verarbeitet und darstellt. Ich möchte nun die mir erfasste Messdaten (in diesem Fall Wetterdaten) an einen SQL-Server übertragen. Das Problem: Profilab selbst bietet diese Funktion nicht, stellt aber eine DLL-Schnittstelle zur Verfügung. Leider sind die Beispiele in Profilab nur für Delphi abgefasst.
Bisher ist es mir gelungen folgendes Herauszufinden:
A) Ich konnte einen Beispiel-Code einfach in Lazarus per Copy-Paste einfügen, den Delphi Kompatiblitätsmodus einschalten und das mitgelieferte Beispiel - siehe unten - selbst zu einer DLL compilieren. Anliessend habe ich die DLL in Profilab importiert und siehe da - alles funktioniert einwandfrei....
B) Ich konnte den Beispiel-Code modifizieren, sodass ich erfolgreich erste Geh-Versuche unternommen habe habe und so einfache Berechnungen erstellen konnte
C) Mir ist es bereits gelungen (aus dem Englischen Wiki) einen Free-Pascal Code so zu erstellt, dass dieser mir eine Verbindung mit meinem SQL-Server aufbaut und Daten in einge gegebene Tabelle einer existierenden Datenbank schreibt auch das klappt bereits ....
Nun ist aber A) in Delphi und C) in Free-Pascal abgefasst und irgendwie bringe ich beides nicht zusammen. Daher meine Bitte: Kann mir jemand helfen, den folgenden Delphi Code sauber in Free-Pascal zu übersetzen, sodass ich ein Beispiel habe, an dem ich mich entlang hangeln kann? Das wäre wirklich toll, den Rest würde ich dann wohl wieder irgendwie schaffen, da ich gerne solche Beispiele Zerlege, abändere und nach meinen Bedürfnissen zurechtbastele ...
Vielen Dank im Voraus
Thorsten
Hier nun das Beispiel für Delphi aus Profilab (mit freundiche Genehmigung der Fa. Abacom):
Code: Alles auswählen
library Counter;
// Delphi 5 DLL-source (COUNTER.DLL)
// Defines a simple 8-Bit binary counter component for ProfiLab
uses
SysUtils,Windows,Classes;
{$R *.RES}
Const Inputs = 2; // number of inputs
Outputs = 8; // number of outputs
{INPUTS}
CLK = 0; // index of input variable CLK
RST = 1; // index of input variable RST
{OUTPUTS}
{USER}
CLK_old = 0; // index user variable CLK_old
RST_old = 1; // index user variable RST_old
Count = 2; // index user variable COUNT
Type TDLLParams = array[0..100] of extended; //Type of ProfiLab DLL parameters
PDLLParams = ^TDLLParams; // Pointer to ProfiLab DLL parameters
function NumInputs: Byte;
begin
result:=Inputs; //Define number of component input pins
end;
function NumOutputs: Byte;
begin
result:=Outputs; //Define number of component output pins
end;
Function InputName(Channel: Byte): ShortString; // Return name for each component input pin
begin
case Channel of
CLK: result:='CLK'; // "CLK" (Clock)
RST: result:='/RST'; // "/RST" (NOT RESET)
end;
end;
Function OutputName(Channel: Byte): ShortString; // Return name for each component output pin
begin
result:='Q'+intToStr(Channel); //"Q0".."Q7" (Binary count)
end;
Procedure SimStart(PInput,POutput,PUser: PDLLParams); //called when ProfiLab enters RUN mode
var i: Integer;
begin
PUser^[Count]:=0; //RESET COUNTER
For i:=0 to Outputs do
begin
POutput[i]:=0; //Set binary outputs with COUNT=0
end;
end;
Procedure SimStop(PInput,POutput,PUser: PDLLParams); //called when ProfiLab RUN mode is terminated
begin
// nothing to be done
end;
Procedure Calculate(PInput,POutput,PUser: PDLLParams); //called regularly from ProfiLab
var i: Integer;
begin
if PInput^[RST]<2.5 then //check RST input HIGH or LOW
begin
if (not (PInput^[RST]>=2.5)) and (PUser^[RST_old]>2.5) then //check out falling edge at RST input
begin
PUser^[Count]:=0; //RESET COUNT
For i:=0 to Outputs do
begin
POutput[i]:=0; //Set binary outputs with COUNT=0
end;
end;
exit;
end;
PUser^[RST_old]:=PInput^[RST]; //Remember RST status for next call
if PInput^[CLK]>2.5 then //check CLK input HIGH or LOW
begin
if (PInput^[CLK]>2.5) and not(PUser^[CLK_old]>2.5) then //check out rising edge at CLK input
begin
PUser^[Count]:=PUser^[Count]+1; // increase COUNT
if PUser^[Count]>255 then PUser^[Count]:=0; //check overflow
For i:=0 to Outputs do
begin
if (round(PUser^[Count]) and (1 shl i))>0 then POutput^[i]:=5 else POutput[i]:=0; //Set binary outputs with current COUNT
end;
end;
end;
PUser^[CLK_old]:=PInput^[CLK]; //Remember CLK status for next call
end;
//export methods for ProfiLab
exports SimStart,
SimStop,
NumInputs,
NumOutputs,
Calculate,
InputName,
OutputName;
begin
end.
Bei der Programmierung der DLL müssen Sie sich an bestimmte Vorgaben von ProfiLab halten. So muss Ihre DLL bestimmte Routinen enthalten und exportieren, mit denen Sie die Anzahl der Ein- und Ausgänge, deren Bezeichnung und natürlich die interne Funktion des Bauteils bestimmen. Folgende Funktionen muss Ihre DLL bereitstellen:
Code: Alles auswählen
Delphi: function NumInputs: Byte;
Alternativ:
function NumInputsEx(PUser: PDLLParams): Byte;
Code: Alles auswählen
Delphi: function NumOutputs: Byte;
Alternativ:
function NumOutputsEx(PUser: PDLLParams): Byte;
Code: Alles auswählen
Delphi: function InputName(Channel: Byte): ShortString;
Code: Alles auswählen
Delphi: function OutputName(Channel: Byte): ShortString;
Code: Alles auswählen
Delphi: Procedure Calculate(PInput,POutput,PUser: PDLLParams);
Der Pointer PINPUT zeigt auf einen Speicherbereich, der dazu dient die Eingangswerte des Bauteils an die DLL zu übergeben.
Der Pointer POUTPUT zeigt auf einen Speicherbereich, der dazu dient die berechneten Ausgangswerte der DLL an das Bauteil zurückzugeben.
Der Pointer PUSER stellt einen Pointer auf einen Speicherbereich zur Verfügung, in dem die DLL eigene (lokale) Werte speichern kann. Hintergrund: Variablen, die die DLL selbst deklariert sind globale Variablen. Werte die in diesen Variablen gespeichrt werden, würden sich gegenseitig überschreiben, wenn man dieselbe DLL mehr als einmal in einem ProfiLab-Projekt verwendet. Um lokale Werte speichern zu können, stellt ProfiLab der DLL den lokalen Speicherbereich zur Verfügung, auf den PUSER zeigt. (Alternativ kann man auch Variablen in der DLL selbst deklarieren. Dann muß man die DLL aber mehrfach unter verschiedenen Dateinamen speichern und importieren, um sie mehrfach in einem ProfiLab-Projekt verwenden zu können.)
Alle drei Pointer PINPUT, POUTPUT und PUSER zeigen jeweils auf einen Speicherbereich in dem 100 Variablen vom Typ EXTENDED in einem Array abgelegt sind. Die Pointer sind vom Typ PDLLParams, dessen Deklaration in Delphi so aussieht:
Code: Alles auswählen
type TDLLParams = array[0..100] of extended;
PDLLParams = ^TDLLParams;
Die Extended-Variablen des Pointers PINPUT enthalten die Eingangszustände des Bauteils. Auf den Wert eines Eingangs des Bauteils können Sie wie folgt zugreifen:
PInput^[0] enthält den numerischen Eingangswert des 1. Eingangs,
PInput^[1] enthält den numerischen Eingangswert des 2. Eingangs, usw.
Die Extended-Variablen des Pointers POUTPUT nehmen die Ausgangszustände des Bauteils auf. Um den Wert der Ausgänge des Bauteils zu setzen, benutzen Sie:
POutput^[0] nimmt den numerischen Ausgangswert des 1. Ausgangs auf,
POutput^[1] nimmt den numerischen Ausgangswert des 2. Ausgangs, usw.
Entsprechend können Sie mit PUser^[0] bis PUser^[99] frei verwenden um eigene Werte zu speichern. Die Werte in diesen Variablen werden von ProfiLab mit in der Projektdatei abgespeichert, und sind so beim nächsten Laden des Projekts wieder verfügbar. Die Variable PUser^[100] wird von ProfiLab gesetzt, und enthält die Nummer des DLL-Bauteils in Ihrem Projekt: 1 für DLL1, 2 für DLL2, usw.
Die Routine Calculate wird im RUN-Modus ständig von ProfiLab aufgerufen, um neue Eingangswerte zu übergeben und neue Ausgangswerte für das Bauteil abzurufen. Diese Routine muss daher unbedingt zeitoptimiert programmiert werden, und sollte auf keinen Fall Pausen (in Form von SLEEP-Befehlen oder Warteschleifen) enthalten. Nach dem Abfragen der Eingangswerte und dem Setzen der Ausgangswerte sollte die Routine so schnell wie möglich wieder verlassen werden. Die Rechenzeit für die Routine wirkt sich unmittelbar auf die Simulationsfrequenz von ProfiLab aus.
Code: Alles auswählen
Delphi: Procedure CalculateEx(PInput,POutput,PUser: PDLLParams; PStrings: PStringParams);
Code: Alles auswählen
type TStringParams = array[0..100] of PChar;
PStringParams = ^TStringParams;
Jedem Eingang/Ausgang (max. 100) der DLL ist ein Zeiger auf einen nullterminierten String (PChar) zugeordnet. Der Speicher auf den die PChar zeigen wird von ProfiLab bereitgestellt (max. 1000 Zeichen pro PChar). Bevor ProfiLab die Methode anspringt, werden die PChar mit den Werten von den $Eingängen belegt. Nach Verlassen der Methode gibt ProfiLab die Inhalte der PChar über die $Ausgänge aus. Ein Unterscheidung in Eingang und Ausgang findet hier nicht statt. So teilen sich z.B. Eingang 0 und Ausgang 0 den gleichen PChar. Um einen Eingang/Ausgang für Strings zu verwenden muss der zugehörige Pinname mit einem '$'-Zeichen beginnen. Beispiele für die Verarbeitung von Strings mit einer DLL und mit ProfiLab sind vorhanden.
Code: Alles auswählen
Delphi: Procedure SimStart(PInput,POutput,PUser: PDLLParams);
Code: Alles auswählen
Delphi: Procedure SimStop(PInput,POutput,PUser: PDLLParams);
Code: Alles auswählen
Delphi: Procedure Configure(UserValues: PDLLParam);