FUNCTION_BLOCK "MasterCom"
{ S7_Optimized_Access := 'FALSE' }
AUTHOR : Vor
FAMILY : Com
VERSION : 1.11
// Dieser Baustein regelt die Kommunikation zu einer Partnerstation Slave
// Es ist pro S7 Verbindung nur ein MasterCom zulässig da die Koppel_ID im Baustein vergeben wird
// Da BSend und BRCV verwendet werden sind nur S7 Verbindungen erlaubt
// Nach möglichkeit sind die SFB12/13 BSEND/BRCV zu verwenden, wenn nicht durch die CPU unterstützt
// dann die FB12/13 aus der Kommunikationsbibliothek Step7 in die CPU laden.
// SFB und FB arbeiten zusammen
//
// V1.0 Startversion
// V1.1 Umgebaut auf ANY_Pointer
// V1.2 ANY in Baustein Integriert (Structur gebildet)
// V1.10 Baustein erkennt jetzt selbstständig ob Ein DB, Pointer oder Structur angehängt wurde
//v1.11 Angsstimer hinzugefügt. Resetet den Request wenn er länger als 10sek ansteht.
VAR_INPUT
DB_1_Recv : Any; // Daten die empfangen werden sollen DB, ANY oder Struct
DB_1_Send : Any; // Daten die gesendet werden
ID : Word; // Lokale VerbindungsID aus Netpro
Master : Bool; // Eine seite des OBJ_MasterCom muss master sein, sonst passen die KoppelID nicht
END_VAR
VAR_OUTPUT
zykl : Time; // Zyklus in denen ein Telegramm gesendet wird beendet wird
WD : Bool; // Wenn Senden oder Empfangen länger als 5sek kein Done liefert, Error.
END_VAR
VAR
SEND {OriginalPartName := 'BSEND_SFB_PART'; LibVersion := '1.2'} : BSEND;
RECV {OriginalPartName := 'BRCV_SFB_PART'; LibVersion := '1.2'} : BRCV;
save_status_SEND : Word;
save_status_RECV : Word;
Zeit_alt : Time;
Watchdog_Empfangen {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME;
Watchdog_Senden {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME;
Angsttimer {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME;
tReset : Bool;
END_VAR
VAR_TEMP
zDB_1_Recv : Any;
pDB_1_Recv AT zDB_1_Recv : Struct
SyntaxID : Byte;
Bereichstyp : Byte;
Anzahl_Werte : Int;
DB_Nr : Int;
Startadresse : DWord;
END_STRUCT;
zDB_1_Send : Any;
pDB_1_Send AT zDB_1_Send : Struct
SyntaxID : Byte;
Bereichstyp : Byte;
Anzahl_Werte : Int;
DB_Nr : Int;
Startadresse : DWord;
END_STRUCT;
wDummy : Word;
RetVAL : Int;
Write_Prot : Bool;
Zeit : Time;
Anzahl_Werte_RECV : Word;
Anzahl_Werte_SEND : Word;
SCL_MIGRA_TEMP_INT_1 : Int;
Migra_DB_LENGTH : UDInt;
Attrib_RCV : Byte;
Attrib_SND : Byte;
uiAnzahl_Werte_RECV : UDInt;
uiAnzahl_Werte_SEND : UDInt;
END_VAR
BEGIN
#zDB_1_Recv := #DB_1_Recv;
#zDB_1_Send := #DB_1_Send;
#Zeit := TIME_TCK();
(*Pointer aufbereiten*)
IF #pDB_1_Recv.Bereichstyp = B#16#19 THEN
(*Empfangen*)
#pDB_1_Recv.SyntaxID := B#16#10; // Typ des Bereichs soll Byte sein (INT = 2)
#pDB_1_Recv.Bereichstyp := 2; // Typ des Bereichs soll Byte sein (INT = 2)
#pDB_1_Recv.DB_Nr := DWORD_TO_INT(#pDB_1_Recv.Startadresse); // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
(* classic code: #RetVAL := TEST_DB(DB_NUMBER := DWORD_TO_WORD(#pDB_1_Recv.Startadresse), DB_LENGTH => #Anzahl_Werte_RECV, WRITE_PROT => #Write_Prot);*)
#SCL_MIGRA_TEMP_INT_1:=ATTR_DB(REQ:=True,DB_NUMBER:=UINT_TO_DB_ANY(DWORD_TO_UINT(#pDB_1_Recv.Startadresse)) ,DB_LENGTH=>#uiAnzahl_Werte_RECV, ATTRIB=>#Attrib_RCV);
#Anzahl_Werte_RECV:=UDINT_TO_WORD(#uiAnzahl_Werte_RECV);
#RetVAL := #SCL_MIGRA_TEMP_INT_1;
#pDB_1_Recv.Anzahl_Werte := UDINT_TO_INT(#Anzahl_Werte_RECV); // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
#pDB_1_Recv.Startadresse := DW#16#84000000;
END_IF;
IF #pDB_1_Send.Bereichstyp = B#16#19 THEN
(*Senden*)
#pDB_1_Send.SyntaxID := B#16#10; // Typ des Bereichs soll Byte sein (INT = 2)
#pDB_1_Send.Bereichstyp := 2; // Typ des Bereichs soll Byte sein (INT = 2)
#pDB_1_Send.DB_Nr := DWORD_TO_INT(#pDB_1_Send.Startadresse); // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
(* classic code: #RetVAL := TEST_DB(DB_NUMBER := DWORD_TO_WORD(#pDB_1_Send.Startadresse), DB_LENGTH => #Anzahl_Werte_SEND, WRITE_PROT => #Write_Prot);*)
#SCL_MIGRA_TEMP_INT_1:=ATTR_DB(REQ:=True,DB_NUMBER:=UINT_TO_DB_ANY(DWORD_TO_UINT(#pDB_1_Send.Startadresse)) ,DB_LENGTH=>#uiAnzahl_Werte_SEND,ATTRIB=>#Attrib_SND);
#Anzahl_Werte_SEND:=UDINT_TO_WORD(#uiAnzahl_Werte_SEND);
#RetVAL := #SCL_MIGRA_TEMP_INT_1;
#pDB_1_Send.Anzahl_Werte := UDINT_TO_INT(#Anzahl_Werte_SEND); // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
#pDB_1_Send.Startadresse := DW#16#84000000;
END_IF;
// Senden des Datenbausteins
#Anzahl_Werte_SEND := INT_TO_WORD(#pDB_1_Send.Anzahl_Werte);
IF #Master THEN
#SEND.R_ID := 1;
ELSE
#SEND.R_ID := 2;
END_IF;
#SEND(ID:=#ID,SD_1:=#zDB_1_Send,LEN:=#Anzahl_Werte_SEND);
#Angsttimer(IN :=#SEND.REQ,
PT := t#10s,
Q=>#tReset);
#SEND.REQ := TRUE;
IF #SEND.ERROR OR #SEND.DONE OR #tReset THEN
#SEND.REQ := FALSE;
END_IF;
IF #SEND.ERROR THEN
#save_status_SEND := #SEND.STATUS;
END_IF;
IF #SEND.DONE THEN
#zykl := #Zeit - #Zeit_alt;
#Zeit_alt := #Zeit;
END_IF;
#Watchdog_Senden(IN := NOT #SEND.DONE,
PT := t#5s);
// Empfangen des Datenbausteins
IF #Master THEN
#RECV.R_ID := 2;
ELSE
#RECV.R_ID := 1;
END_IF;
#RECV(EN_R:=TRUE,ID:=#ID,
RD_1:=#zDB_1_Recv,
LEN:=#Anzahl_Werte_RECV);
IF #RECV.ERROR THEN
#save_status_RECV := #RECV.STATUS;
END_IF;
#Watchdog_Empfangen(IN := NOT #RECV.NDR,
PT := t#5s);
#WD := #Watchdog_Empfangen.Q OR #Watchdog_Senden.Q;
END_FUNCTION_BLOCK