Best practice to serialize data into the long Data Block on Siemens Simatic S7-300 PLC

67 Views Asked by At

I need to serialize the data from many smaller Data Blocks and working memory via ANY pointer to the single long DB for on-PLC filtering and transferring. Now I use a routine utilizing the special dedicated DB called DATASET_REGISTRAR to organize the data into array-like structures, and then the second DB called DATASET_LIST to grab and write them into the proper place over BLKMOV system routine. Here is a commented sample of the code I use to do this (the code is real, taken from the testing station PLC).

FUNCTION "NET_TX_SERIALIZER" : WORD
TITLE =Serialize data to store them in the specified data block
//Dynamic spec: DATASET_REGISTRAR stores standardized structures defining so 
//called "datasets" to collect the data from DBs or peripheral memory, then the 
//data will be mapped over the area in the output DB. The dataset_list DB stores 
//the sequence of datasets which will be mapped. The structure of the output DB 
//should match tiled datasets as they are defined in DATASET_REGISTRAR by total 
//length.
VERSION : 0.1


VAR_INPUT
  dataset_list : BLOCK_DB ;               // List of the datasets for serialization: homogeneous sequence of WORD elements containing dataset IDs
  serialized_data : BLOCK_DB ;                // Output DB: The ID is determined automatically, and it should be system-wide writable   
END_VAR

VAR_TEMP 
  dataset_index : WORD ;                      // Index of the current dataset
  general_offset : WORD ;                 // Offset in DATASET_REGISTRAR to read a spec for the current dataset
  dataset_loop_iter : WORD ;                  // Loop counter
  dataset_id_pointer : DWORD ;                // Pointer to read actual dataset ID
  dataset_record : STRUCT                 // Local copy of the spec stored in DATASET_REGISTRAR
   dataset_id : WORD ;  
   datablock_id : WORD ;    
   type_id : WORD ; 
   start_offset : DWORD ;   
   dataset_length : DWORD ; 
   end_offset : DWORD ; 
  END_STRUCT ;  
  dataset_registrar_dbid : WORD ;         // DATASET_REGISTRAR DB ID
  ret_error_status : INT ;                // Error code for BLKMOV
  dataset_pointer : ANY ;                 // Pointer to the actual data stored within the dataset addressed by the spec
  serialized_pointer : ANY ;                  // Pointer to the currently written tile in the output DB
  serialized_offset : DWORD ;                 // Current position in the output DB
  serialized_dbid : WORD ;                // Output DB ID
  prev_ar1 : DWORD ;    
  prev_ar2 : DWORD ;    
  actual_dataset_length : WORD ;          // Actual length of the current dataset, in BYTES
  dataset_spec_pointer : ANY ;                // Pointer to read the spec for the current dataset 
END_VAR

BEGIN
NETWORK
TITLE =Initialization

      TAR1  #prev_ar1;                         // Preserving address registers
      TAR2  #prev_ar2; 

      L     0;                                 // Initializing index for loop over all specified datasets
      T     #dataset_index; 

      L     0;                                 // Initializing index for serializer's tile counter with wariable dataset length
      T     #serialized_offset; 

      OPN   "DATASET_REGISTRAR";               // Activating registrar to gather dataset specs
      L     DBNO; 
      T     #dataset_registrar_dbid; 

      OPN   #serialized_data;                  // Retrieving number of serializer output DB
      L     DBNO; 
      T     #serialized_dbid; 

NETWORK
TITLE =Loop over dataset list

      OPN   #dataset_list;                     // Open list DB
      L     DBLG;                              // Load length
      L     2; 
      /I    ; 
iter: T     #dataset_loop_iter;                // Start loop

      L     #dataset_index;                    // Indexing incrementally
      L     2;                                 // Size factor is 2
      *I    ; 
      SLD   3;                                 // Formulating pointer
      L     P#0.0; 
      +D    ;                                  // Commutative addition
      T     #dataset_id_pointer; 
      L     DBW [#dataset_id_pointer];         // ACCU1 - current ID to extract dataset structure from DATASET_REGISTRAR
      L     18;                                // Dataset description structure length is 18 bytes
      *D    ; 
      T     #general_offset;                   // Offset in DATASET_REGISTRAR

      LAR1  P##dataset_spec_pointer;           // Constructing ANY pointer to static registrar DB for spec
      L     B#16#10;                           // 0x10 - SIEMENS ANY POINTER HEADER OPCODE
      T     LB [AR1,P#0.0]; 
      L     B#16#2;                            // BYTE
      T     LB [AR1,P#1.0]; 
      L     18;                                // 18 bytes of spec structure
      T     LW [AR1,P#2.0]; 
      L     #dataset_registrar_dbid;           // Load from dynamic DATASET_REGISTRAR by ID
      T     LW [AR1,P#4.0]; 
      L     P#DBX 0.0;                         // Formulating internal pointer
      L     #general_offset; 
      SLD   3; 
      +D    ; 
      T     LD [AR1,P#6.0];                    // AR1 - current pointer to the registrar record containing the dataset spec structure
      CALL "BLKMOV" (                          // Copying spec to the internal record
           SRCBLK                   := #dataset_spec_pointer,
           RET_VAL                  := #ret_error_status,
           DSTBLK                   := #dataset_record);

      LAR1  P##dataset_pointer;                // Constructing ANY pointer to the actual data
      L     B#16#10;                           // 0x10 - SIEMENS ANY POINTER HEADER OPCODE
      T     LB [AR1,P#0.0]; 
      L     B#16#2;                            // Copying byte-wise
      T     LB [AR1,P#1.0]; 
      L     #dataset_record.end_offset; 
      L     #dataset_record.start_offset; 
      -D    ;                                  // ACCU1 - actual number of aligned bytes to copy
      T     #actual_dataset_length; 
      T     LW [AR1,P#2.0]; 
      L     0; 
      L     #dataset_record.datablock_id;      // 0 as DB ID leads to copying from the peripheral memory
      ==I   ; 
      JC    mcp; 
      T     LW [AR1,P#4.0];                    // Load from the specified DB by ID
      L     P#DBX 0.0; 
      JU    ofst;                              // Switch unconditionally to offset calculation
mcp:  L     P#M 0.0;                           // Bit Memory area access
      T     LW [AR1,P#4.0]; 
ofst: L     #dataset_record.start_offset;      // Read from specified area by offset
      SLD   3; 
      +D    ; 
      T     LD [AR1,P#6.0]; 

      L     P##serialized_pointer;             // Constructing ANY pointer for the serialized DB memory area
      LAR1  ; 
      L     B#16#10;                           // 0x10 - SIEMENS ANY POINTER HEADER OPCODE
      T     LB [AR1,P#0.0]; 
      L     B#16#2;                            // Copying byte-wise
      T     LB [AR1,P#1.0]; 
      L     #actual_dataset_length;            // Actual length, previously calculated
      T     LW [AR1,P#2.0]; 
      L     #serialized_dbid;                  // Serialized data storage enclosure ID, read from initial test in NW 1
      T     LW [AR1,P#4.0]; 
      L     P#DBX 0.0; 
      L     #serialized_offset;                // Tiling offset
      SLD   3; 
      +D    ; 
      T     LD [AR1,P#6.0]; 

      CALL "BLKMOV" (                          // Copying actual data from the source area to the serialized DB
           SRCBLK                   := #dataset_pointer,
           RET_VAL                  := #ret_error_status,
           DSTBLK                   := #serialized_pointer);


      L     #serialized_offset;                // Offset increment for the output area
      L     #actual_dataset_length; 
      +D    ; 
      T     #serialized_offset; 

      L     #dataset_index;                    // Looping over the next dataset (the same one in the example)
      L     1; 
      +I    ; 
      T     #dataset_index; 

      L     #dataset_loop_iter;                // Closing loop
      LOOP  iter; 


NETWORK
TITLE =Restoring address registers

      LAR1  #prev_ar1; 
      LAR2  #prev_ar2; 

END_FUNCTION

Could you please share your best practice tips and experience of doing exactly this on the PLCs? Is there an easier example to implement and debug?

0

There are 0 best solutions below