Il modello a cascata, conosciuto come “waterfall model”, è il più classico dei modelli per il ciclo di vita del software. Prevede l’esecuzione lineare di una precisa sequenza di fasi, ciascuna delle quali genera un output utilizzato come input dalla fase successiva (da qui l’origine del termine “a cascata”).
Nella librerie da noi fornite abbiamo esteso questo modello di approccio sequenziale alla esecuzione di molti blocchi funzione. I vari FB vengono eseguiti sequenzialmente, il termine della esecuzione di un FB abilita l’esecuzione di quello successivo. In questo modo avendo sempre un unico FB in esecuzione è possibile condividere la stessa risorsa (Esempio uno stream di comunicazione) tra i vari FB.
Quasi tutti i blocchi funzione hanno un ingresso di Enable che ne abilita il funzionamento ed una uscita di Done che si attiva al termine della esecuzione. Nei FB realizzati per poter essere connessi con modello a cascata l’uscita Done rimane attiva fino alla disabilitazione dell’ingresso Enable così da essere connessa all’ingresso Enable del FB successivo per abilitarne il funzionamento.

Modello a cascata in pratica
Vediamo come sia possibile realizzare un programma con modello a cascata:
- Un FB viene abiltato attivando il suo ingresso di Enable, quando il FB ha terminato il suo compito attiva la sua uscita Done che rimarrà attiva fino alla disabilitazione dell’ingresso Enable.
- L’uscita Done del primo FB viene connessa all’ingresso Enable di un secondo FB che inizierà il suo compito al termine del quale attiverà la sua uscita Done che rimarrà attiva fino alla disabilitazione dell’ingresso Enable.
- E così di seguito l’uscita Done di un FB potrà collegarsi all’ingresso Enable di un successivo FB fino a completare la catena. Quando un FB termina il suo compito il successivo inizia il suo.
- L’uscita Done dell’ultimo FB della catena sarà connessa in modo negato all’ingresso Enable del primo. In questo modo quando si attiverà si resetta l’Enable del primo che disabiliterà la sua uscita Done ed in cascata l’Enable del successivo, fino all’ultimo FB della catena, che, disabilitando l’Enable disabiliterà il Done riabilitando l’Enable del primo FB della catena. Questo farà ripartire la sequenza di esecuzione di tutti gli FB della catena.
Ma credo sia più intuitivo vederne l’applicazione pratica che non a seguirne la descrizione teorica, ecco quindi di seguito il programma FBD_SDM120Waterfall che esegue l’acquisizione di alcuni registri Modbus da un contatore di energia SDM120. Siccome i valori acquisiti hanno MSB/LSB invertiti su Ok di ogni acquisizione ne eseguo lo swap.
PROGRAM FBD_SDM120Waterfall
VAR
Sp : SysSerialPort; (* Serial port *)
Mdb0 : ModbusMaster_v3; (* Modbus master FB *)
Mdb1 : ModbusMaster_v3; (* Modbus master FB *)
Mdb2 : ModbusMaster_v3; (* Modbus master FB *)
ACQ : DWORD; (* Value acquisition *)
VOLTAGE : REAL; (* Line voltage *)
FREQUENCY : REAL; (* Line frequency *)
ACTIVEPOWER : REAL; (* Active power *)
END_VAR

Esempi
Come utilizzare gli esempi
ST_SDM120Waterfall: Lo stesso esempio realizzato precedentemente in linguaggio FBD realizzato in linguaggio ST che ne permette una facile integrazione nel proprio programma.
LogicLab (Ptp141, ST_SDM120Waterfall)
PROGRAM ST_SDM120Waterfall
VAR
i : UDINT; (* Aux counter *)
Acq : DWORD; (* Value acquisition *)
Voltage : REAL; (* Line voltage *)
Frequency : REAL; (* Line frequency *)
ActivePower : REAL; (* Active power *)
Mdb : ARRAY[0..2] OF ModbusMaster_v3; (* Modbus master FB *)
Sp : SysSerialPort; (* Serial port *)
END_VAR
// *****************************************************************************
// PROGRAM "ST_SDM120Waterfall"
// *****************************************************************************
// An example how to write a program realized with the watwrfall model.
// -----------------------------------------------------------------------------
// -------------------------------------------------------------------------
// INITIALIZATION
// -------------------------------------------------------------------------
// Program initializations.
IF (SysFirstLoop) THEN
// Serial port settings.
Sp.COM:=ADR('COM2'); //COM port definition
Sp.Baudrate:=2400; //Baudrate
Sp.Parity:='N'; //Parity
Sp.DataBits:=8; //Data bits
Sp.StopBits:=1; //Stop bits
Sp.DTRManagement:=DTR_AUTO_WO_TIMES; //DTR management
Sp.DTRComplement:=FALSE; //DTR complement
Sp.EchoFlush:=FALSE; //Received echo flush
Sp.DTROnTime:=0; //DTR On time delay (mS)
Sp.DTROffTime:=0; //DTR Off time delay (mS)
Sp.FlushTm:=0; //Flush time (mS)
Sp.RxSize:=0; //Rx buffer size
Sp.TxSize:=0; //Tx buffer size
// Modbus master settings.
FOR i:=0 TO (SIZEOF(Mdb)/SIZEOF(Mdb[0]))-1 DO
Mdb[i].Type:=MODBUS_PROTOCOL#MDB_RTU; //Modbus type
Mdb[i].Absolute:=FALSE; //Absolute addressing
Mdb[i].Node:=1; //Modbus node
Mdb[i].FCode:=16#04; //Modbus function code
Mdb[i].Timeout:=T#200ms; //Timeout time
Mdb[i].Delay:=T#100ms; //Delay time
Mdb[i].Points:=2; //Modbus register points
Mdb[i].Buffer:=ADR(Acq); //Memory buffer address
END_FOR;
// SDM120 register settings.
Mdb[0].Address:=1; //Modbus register address
Mdb[1].Address:=71; //Modbus register address
Mdb[2].Address:=13; //Modbus register address
END_IF;
// -------------------------------------------------------------------------
// MODBUS MANAGEMENT
// -------------------------------------------------------------------------
// Serial port management.
Sp(Open:=TRUE); //Serial port management
// Modbus management.
Mdb[0](Enable:=NOT(Mdb[2].Done), File:=Sp.File); //Modbus master
IF (Mdb[0].Ok) THEN eTO_JUNK(SwapData(ADR(Voltage), ADR(Acq), REAL_TYPE, SWAP_HALF)); END_IF;
Mdb[1](Enable:=Mdb[0].Done, File:=Sp.File); //Modbus master
IF (Mdb[1].Ok) THEN eTO_JUNK(SwapData(ADR(Frequency), ADR(Acq), REAL_TYPE, SWAP_HALF)); END_IF;
Mdb[2](Enable:=Mdb[1].Done, File:=Sp.File); //Modbus master
IF (Mdb[2].Ok) THEN eTO_JUNK(SwapData(ADR(ActivePower), ADR(Acq), REAL_TYPE, SWAP_HALF)); END_IF;
// [End of file]