Step 7 SCL indirekte Adressierung array of udt

michdan

Level-2
Beiträge
37
Reaktionspunkte
1
Zuviel Werbung?
-> Hier kostenlos registrieren
hallo
Ich habe eine kleine udt (16 bytes) fur Rezept daten in mehrere DB's. Die daten sind als array of udt im DB angebracht.

Ich möchte jetzt alle DB's dursuchen um ein bestimmten Eintrag 'ProductID' zu finden.

Code:
TYPE UDT6
    STRUCT
    // Type Description
        ProductID       :DINT;
        Adj_1_0_20      :INT;    
        Adj_2_20_40     :INT;    
        Adj_3_40_60     :INT;    
        Adj_4_60_80     :INT;    
        Adj_5_80_100    :INT;    
        Adj_6_OverAll   :INT;   
    END_STRUCT
END_TYPE


DATA_BLOCK DB701
//
// Block Comment ...
//
    STRUCT
        Recipes :ARRAY[1..4000] OF UDT6;
    END_STRUCT
BEGIN

END_DATA_BLOCK



FUNCTION FC700 : VOID

VAR_INPUT
    iDBNR_Start    : INT;
    iDBNR_Slut     : INT;
    iDW            : INT;
    ProductID      : DINT;
END_VAR

VAR_OUTPUT
    FoundDBNR       : INT;
    FoundRecipeNO   : INT;
    RecipeFound     : BOOL;
    RecipeNotFound  : BOOL;  
END_VAR
        
VAR_TEMP
    iDBNR,j           : INT;
END_VAR

    RecipeFound := false;
    RecipeNotFound := false;
   
    FOR iDBNR:= iDBNR_Start TO iDBNR_Slut BY 1 DO
          
        // We have max 4000 recipes pr DB Block 
        // Search DB for ProductID   
        FOR j:= 1 TO 4000 BY 1 DO
            // Statement Section
            
            IF ( WORD_TO_BLOCK_DB(INT_TO_WORD(iDBNR)).Recipes[j].ProductID = 0 ) THEN
                // ProductID does not exist
                FoundDBNR := iDBNR;
                FoundRecipeNO := j;                
                RecipeFound := false;
                EXIT;
            ELSIF ( WORD_TO_BLOCK_DB(INT_TO_WORD(iDBNR)).Recipes[j].ProductID = ProductID ) THEN
                // ProductID found
                FoundDBNR := iDBNR;
                FoundRecipeNO := j;
                RecipeFound := true;
                EXIT;
            END_IF;
                
        END_FOR; //FOR j:= 1 TO 4000....
    END_FOR; //FOR iDBNR:= iDBNR.....
 
    FoundDBNR := 0;
    FoundRecipeNO := 0;

    RecipeFound := false;
    RecipeNotFound := true;
    
END_FUNCTION

Ich bekomme aber 4 Fehler beim compilieren.
Diese adressiereung scheint nicht zu funktionieren WORD_TO_BLOCK_DB(INT_TO_WORD(iDBNR)).Recipes[j].ProductID

wenn ich den ersten zeil der die DB adresseriung ersetze durch einen DB701.Recipes[j].ProductID klapt es aber dann kann ich nicht die adressirung vom DB-bausten indirekt machen.


Geht diese indirekte adressirung vom DB-bausten und array von strukturen in SCL??

/michael

 
Hallo Michael,

halb symbolisch und halb absolut geht nicht wirklich gut.
Die einzige Lösung die mir einfällt, ist folgende:

Code:
    ... FOR j:= 1 TO 4000 BY 1 DO
            // Statement Section
            BYTEADRESSE := (j-1) * 16;
            IF WORD_TO_BLOCK_DB(INT_TO_WORD(iDBNR)).DD[BYTEADRESSE] = 0 THEN ...

Man muss dann aber alles sorgfältig kommentieren.


Gruß, Onkel
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Wie der Onkel schon schrieb: Das geht nicht. In DBs, deren Nummer erst zur Laufzeit übergeben wird, kann SCL nicht symbolisch adressieren. Da kann nur die Speicheradresse (Byteadresse) indiziert adressiert werden, was ziemlich "unschön" ist. ( WORD_TO_BLOCK_DB(INT_TO_WORD(iDBNR)).DW[Adresse] )
Wie ein zur Laufzeit übergebener DB bzw. Speicherbereich symbolisch strukturiert ist, kann man nur per strukturiertem IN- oder IN_OUT-Parameter dem SCL mitteilen.

Du könntest "Recipes : ARRAY[1..4000] OF UDT6;" als IN_OUT übergeben und dann den FC für jeden DB einzeln aufrufen. Entweder alle nacheinander im selben OB1-Zyklus oder in mehreren Zyklen nacheinander.
Oder wenn es nur wenige DB sind und das Durchsuchen etwas performanter sein muß, dann könntest Du für jeden DB einen eigenen FC mit fester DB-Nummer erstellen.

Was für eine CPU soll eigentlich Deinen Code ausführen?
Wieviele DB willst Du denn durchsuchen lassen?
Sind die Daten vielleicht irgendwie sortiert?

Das Durchsuchen von mehreren 64kB großen DBs gehört nicht zu den klassischen Aufgaben einer SPS. Unabhängig davon, ob Deine CPU vielleicht eine schnelle Rennsau ist, würde ich das Durchsuchen auf mehrere OB1-Zyklen aufteilen (je Zyklus höchstens 1 DB), weil so ein Durchsuchen einen großen Zykluszeit-Peak erzeugt. Was macht Deine CPU sonst noch so? Stark schwankende Zykluszeiten sind in der Regel Gift für Anlagensteuerungen.


PS: wo kommt diese enorme Menge an Datensätzen eigentlich her? Wie ist für ein Backup der Daten gesorgt?
Kann vielleicht ein externer Datenbank-Server das Suchen übernehmen?

Harald
 
Hallo Onkel

Das war mir auch eingefallen aber ich finde den code unschön und ich denke es ist einfacher zu lesen wenn es symbolisch ist, aber wenn es nicht so geht ist deine lösung auch ok,

Danke

/michael
 
hallo Harald

Ich arbeite mit einer s7-315 PN/DP. Die Maschine erhellt ihre Betriebs Parameter von einen zentralen PC aber ich musste ein paar neue Parameter dazu machen die zurzeit nicht im PC sind.
Darum habe ich mich ein paar Gedanken gemacht ob ich diese neuen Parameter lokal in der Steuerung machen könnte.
Ich bekomme die ProductID vom PC und muss dann die übrigen Parameter lokal in der Steuerung suchen. Ich kenne noch nicht die Anzahl von ProductID's aber ich denke die 4000 die ich in einem DB unterbringen kann reicht aber ich wollte sicher sein dass ich auch mehrere DB durchsuchen könnte falls es notwendig wird.

Deine Idee mit übergeben des Speicherbereich per IN- oder IN_OUT-parameter werde ich mal probieren.

Danke für deinen Beitrag...

/michael
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe mal so eine Art Lagerspiegel in C# programmiert. Die Technologen hatten das vergessen zu bestellen und die Herstellerfirma hat da keine Seite in der Visu erstellt, da nicht bestellt. An das Visu Projekt kam ich nicht ran, deswegen was auf dem PC.
Die Firma hatte sich da ein schickes System ausgedacht. Die Datensätze waren Arrays of UDTs auf mehrere fortlaufend nummerierte DBs verteilt. Ein Kollege hatte bei der IBN über die Schulter geschaut und mit den Augen das System für die indirekte Adressierung abgeschaut bzw. später dann daraus hergeleitet. Ich habe mir das dann zu Nutze gemacht.
Die haben die Datensätze fortlaufend nummeriert. Anhand dieser Nummer wurde dann mit Hilfe der Menge Datensätze pro DB, der Bytlänge pro Datensatz im DB und der Nummer des ersten DBs mit Datensätzen folgende Werte errechnet, Nummer das Datensatzes im DB (Arrayindex), der Offset für das erste Byte des Datensatzes und die DB Nummer in der der Datensatz liegt.
Das alles in eine Funktion gepackt, kann man wunderbar indirekt zugreifen, suchen usw.

Falls das interessiert kann ich mal die Codestelle in C# raussuchen, damit als Vorlage sollte sich das recht einfach in SCL umsetzen lassen.
 
hallo Bapho

Das würde mich bestimmt interessieren, wie dass in C# programiert wurde.

Danke für dein beitrag...
 
Deine Idee mit übergeben des Speicherbereich per IN- oder IN_OUT-parameter werde ich mal probieren.

Bedenke. Wenn du den Speicherbereich als Struktur an IN anhängst um nachher Symbolisch drauf zuzugreifen, wird eine Kopie in der Instanz angelegt, was einen nicht unerheblichen Speicherbedarf bedeuten kann.
An IN_OUT wird nur ein Zeiger übergeben. An den möglichen Zugriffen per Symbolik ändert sich dabei nichts. Man spart also viel Speicher, arbeitet aber dann direkt mit dem Speicherbereich. Was man bei Schreibzugriffen natürlich beachten muss (umsortierung z.B.)

mfG René
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Code:
const Int32 _dataset_db = 20; //Anzahl Datensätze pro DB
const Int32 _length_dataset = 2846; //Länge eines Datensatzes in Byte 
const Int32 _first_db = 221; // Nummer des 1. DB mit Datensätzen

//Berechnung der Datensatznummer im DB
Int32 _nr_dataset = Convert.ToInt32((bkn - ((bkn - 1) / _dataset_db) * _dataset_db) - 1);
//Berechnung des Offsets für das Startbyte des Datensatzes, Start Datensatz + Offset von 150 Bytes
Int32 _start = (_nr_dataset * _length_dataset) + 150;
//Berechnung der DB Nr
Int32 _dbnr = Convert.ToInt32(((bkn - 1) / _dataset_db) + _first_db);

bkn ist die "globale" fortlaufende Datensatznummer
 
Zurück
Oben