Step 7 12 Stellige BCD Zahl in DINT wandeln

Zuviel Werbung?
-> Hier kostenlos registrieren
Habs jetzt nochmal getestet und angepasst!
Ziffern 11/12, welche bei einem 10-Ziffern DINT eigentlich nur das '-' enthalten können dürfen
werden vorab geprüft und ein Fehler ausgelöst, wenn da was anders als ein 'F' drinsteht.
Das lässt sich dann ganz einfach durch auskommentieren der Schleife entfernen.

Hier geht es wirklich nur um die Fehleranzeige: Da ein 32-DINT nicht mehr als 10 Ziffern enhalten kann,
kann man getrost abbrechen. Die Frage ist nur will man einen Fehler haben, weil man das vorher nicht
geteset hat oder will man einfach die BCDs konvertiert haben!

Mittlerweile sehe ich die Stringversion tatsächlich als die einfachere Version an. Das macht
programmiertechnisch definitiv weniger Probleme.
Die Frage bei der Stringversion ist nur, was passiert, wenn man STRING_TO_DINT auf ungültige Werte
loslässt! Wie reagiert da Step7, TIA, CodeSys

// hier nochmal der überarbeitete Code
Code:
FUNCTION m7_BCDL_TO_DINT : DINT
    TITLE ='Converts Long BCD (10 digits and sign) to a DINT'

// ==============================================================================
// Converts long BCD value (up to 10 digits and sign) to a DINT
// The BCD must be provided in an ARRAY[1..] OF BYTE in little endian Format
// BCD[1] contains the lowes digit
// each BYTE represents 2 digits of the BCD. 5 Bytes = 10 digits
// plus the possible minus '-' ('-' is B#16#F in BCD code)
// the '-' can be anywhere in the BCD. If a '-' is detected the
// conversion will stop at this position. 
// For BCD{#0F, #F0) what both is '-0', the FUNCTION returns 0.
// IF an Error occures ,the Function returns -2147483648 / DW#16#80000000
//    and FC-ENO := FALSE  // the ok-Flag
// ELSE it returns the DINT value of the BCD {-2147483647 .. 2147483647}  
//    and FC-ENO := TRUE;  // the ok-Flag
// ==============================================================================
//   
// AUTHOR: S.Maag
// DATE: 4/2020
//
// CHANGELOG:
// ------------------------------------------------------------------------------
// DATE         NAME        DESCRIPTION
// ------------------------------------------------------------------------------
// 06.04.2020   S.Maag      Added Error check for digit 11/12 which
//                          will exceed 32-Bit DINT range. So far digit 11/12
//                          hve been ignored because they exceed the DINT-range.
//                          If we want to ignore again, we have to remove
//                          Err:=TRUE; at Byte6 (digit 11/12) check.
//                          See comment in the code! 
// ------------------------------------------------------------------------------

    VERSION : '1.0'
    AUTHOR  : 'S.Maag'
    FAMILY  : 'Maagic7'

VAR_INPUT
    inBCDs : ARRAY[1..6] OF BYTE;  // 1:Lo..6:Hi; Array with the BCD digits (with sign max 12 digits)
END_VAR

VAR_OUTPUT

END_VAR

VAR_TEMP
    I      : DINT;   // LOOP COUNTER
    tmpDI  : DINT;   // Our DINT value
    diBCD  : DINT;   // Value of a 2-digit BCD value
    Err    : BOOL;   // Error (BCD-digit not valid; NOT{0..9})
    negative: BOOL;  // BCD is negative
    dFact  : DINT;   // Factor for BCD_TO_INT (1_digit, 10_digit, 100_digit ...)

    BCDhi  : WORD;   // Value of hi BCD digit of a BYTE
    BCDlo  : WORD;   // Value of lo BCD digit of a BYTE
END_VAR

CONST
   cstReturnValIfError := -2147483648; // -2147483648 / DW#16#80000000  we use the -2147483648 as Error indication
END_CONST

BEGIN

    Err := FALSE;
    negative := FALSE;
    tmpDI := 0;
    dFact := 1;  // Faktor für (1er, 10er),(100er, 1000er) ...
   
    // **********************************************************************
    // ***  A BCD can contain a max. of 10 digits and a '-' to fit into   ***
    // ***  a DINT. (At a 10digit BCD the '-' is in digit 11 of 12)       ***
    // ***  If you want to ignore numbers in digit 11/12, then remove     ***
    // ***  this 1st check for valid digit 11/12.                         ***
    // ***  Removing this 1st check do not affect the processing of the   ***
    // ***  the 'F', BCD '-'                                              ***
    // ***  With this check an Error is fired when digit 11/12 contains   ***
    // ***  digits different from 'F'/'-', without the check              ***
    // ***  only 10 digits & '-' are processed                            ***
    // **********************************************************************
    
    // (*    // remove this 'IF THEN', if you want to ignore numbers in digit 11/12

    // check first digit 11/12: If it contains a number, we will exceed DINT range
    BCDhi := BYTE_TO_WORD( SHR(IN:=inBCDs[6], N:=4) );   // Value of hi BCD digit
    BCDlo := BYTE_TO_WORD( inBCDs[6] AND B#16#F);        // Value of lo BCD digit
    IF BCDlo <> W#16#0 AND BCDlo <> W#16#F THEN
        Err := TRUE;
    ELSIF BCDHi <> W#16#0 AND BCDHi <> W#16#F THEN
        Err := TRUE;
    END_IF;
    // **********************************************************************

    // *)
    
    I := 0;
    REPEAT 
        I := I + 1;   // inBCDs[1..6]

        BCDhi := BYTE_TO_WORD( SHR(IN:=inBCDs[I], N:=4) );   // Value of hi BCD digit
        BCDlo := BYTE_TO_WORD( inBCDs[I] AND B#16#F);        // Value of lo BCD digit
        
        // Caution BCD digits must be checked for correct range {0..9}
        // PLCs may stop if BCD contains invalid digits
    
        IF WORD_TO_INT(BCDhi) > 9 THEN          // IF BCD_DIGIT IS NOT VALID
            IF BCDhi = B#16#F THEN
                negative := TRUE;
            ELSE
                Err := TRUE;                    // invalid BCD digit
            END_IF;
            BCDhi := 0;                                   
        END_IF;
  
        IF WORD_TO_INT(BCDlo) > 9 THEN          // IF BCD_DIGIT IS NOT VALID
            IF BCDlo = B#16#F THEN
                negative := TRUE;
                BCDHi := 0;                     // ignore BCDhi if BCDlo is the sign '-'
            ELSE
                Err := TRUE;                    // invalid BCD digit
            END_IF;
            BCDlo := 0;     
        END_IF;
 
        // calculate the DINT value of the 2 BCD digits only up to 10 digits max
        // digit 11..12 is the '-' at 10 digit long BCDs
        IF I<= 5 THEN
            diBCD := INT_TO_DINT(WORD_TO_INT(BCDhi)*10) + INT_TO_DINT(WORD_TO_INT(BCDlo));
  
            IF I = 5 THEN           // Check overflow if 10digit BCD
                IF diBCD > 21 THEN  // 21-47-48-36-47 = DW#16#7FFFFFFF; maxDINT
                    Err := TRUE;
                ELSIF diBCD = 21 THEN
                    IF tmpDI > 47483647 THEN ERR := TRUE; END_IF;
                END_IF;
            END_IF;         

            tmpDi := tmpDi + diBCD * dFact;  // add the value of the 2 digits in the right position of the DINT   
            dFact := dFact *100;             // 100 it's ShiftLeftBCD(myBCD,2_digits) as DINT
            // dFact = {1, 100, 10.000, 1.000.000, 100.000.000} fact = 10.000.000.000 will exceed the DINT range 
        END_IF;
    
    UNTIL negative OR Err OR (I = 6)
    END_REPEAT;
   
    IF negative THEN        // add the negative sign to the DINT
        tmpDI := -tmpDI;
    END_IF;                

    IF Err THEN
       m7_BCDL_TO_DINT := cstReturnValIfError; // -2147483648; DW#16#80000000
    ELSE
       m7_BCDL_TO_DINT := tmpDI;
    END_IF;
    
    OK := NOT Err;   //  OK-Bit = TRUE if BCD is converted without an Error! If OK is FALSE, the returned DINT is not valid!
    
END_FUNCTION
 
Und dann verlässt Du Dich darauf, dass nur negative Zahlen drin stehen, damit Du weisst, wo die eine BCD-Zahl aufhört und die nächste anfängt???

Nein, darauf brauch ich mich nicht verlassen. Insgesamt ist das defintiv nur eine einzige zusammenhängende BCD-Zahl. Entweder negativ oder positiv!
Die Zahl startet definitiv auf der rechten Seite mit den Einern und geht max. bis 10 Stellen, Stelle 11/12 muss spätestens '-' oder eben 0 enthalten.

In der Praxis sieht es so aus, dass das '-' immer an Stelle 12 ist, dann ist mit '0'en aufgefüllt, bis die erste wertige Ziffer kommt.
So ist das beim original Code mit den Stringoperationen interpretiert (eine Fehlerabfrage gibt's im original überhaupt nicht)

Mein Code akzeptiert das '-' an jeder beliegigen Stelle, was erst mal nicht falsch ist, sondern einfach nur mehr Varianten zulässt, ohne
nicht mehr zu konvertieren.
Ich breche in der ersten Version nach 10 Stellen mit der Übersetzung einfach ab und prüfe 11/12 nur noch auf ein '-'
per Definition des DINT kann 11/12 nur '-' oder eben nichts enhalten!
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Nein, darauf brauch ich mich nicht verlassen. Insgesamt ist das defintiv nur eine einzige zusammenhängende BCD-Zahl. Entweder negativ oder positiv!
Die Zahl startet definitiv auf der rechten Seite mit den Einern und geht max. bis 10 Stellen, Stelle 11/12 muss spätestens '-' oder eben 0 enthalten.

In der Praxis sieht es so aus, dass das '-' immer an Stelle 12 ist, dann ist mit '0'en aufgefüllt, bis die erste wertige Ziffer kommt.
So ist das beim original Code mit den Stringoperationen interpretiert (eine Fehlerabfrage gibt's im original überhaupt nicht)

Mein Code akzeptiert das '-' an jeder beliegigen Stelle, was erst mal nicht falsch ist, sondern einfach nur mehr Varianten zulässt, ohne
nicht mehr zu konvertieren.
Ich breche in der ersten Version nach 10 Stellen mit der Übersetzung einfach ab und prüfe 11/12 nur noch auf ein '-'
per Definition des DINT kann 11/12 nur '-' oder eben nichts enhalten!

Wenn geht, dann hau dem Entwickler einer, dr das verbrohen hat. Ein Trennzeichen war wohl zu schwierig für den Kollegen? :ROFLMAO:
 
Zurück
Oben