Plant SCADA 2023 R2 export variables current value to CSV file

Dear Experts,

We are using Plant SCADA 2023 R2. We need to take a snapshot of 100 variables and store these values into a CSV/Excel file. We will store the current data only, no need for logging and multiple entries.

What is the most efficient way to achieve that?

Thanks

Parents
  • If you turn on persistence the values are automatically stored in an XML format.

    If you need to write this data multiple times into a file I would suggest to:

    • Start a TagBrowseOpen() function and use a filter on for instance a CUSTOM field to select the right tags
    • Loop through the tags and use TagSubscribe() and store the handles in for instance a Cicode Int array
    • When you want to write to a file, process the handles and use SubscriptionGetAttribute() to get the value and write it to a file
  • Dear Bas,

    Thank you for your support,

    I am sorry but your answer was not clear to me and it didn't explain how to write the variables value into a CSV/Excel file.

    I simply need to write a script when executed to write the current values of 100 variables into a temporary file. this file will only be used once then deleted. No need to reopen the file and append new values once the code is executed again.

    Thanks

  • Hi  
    Below is the sample code and hope it would help.

    Save the following sample code to a Cicode file and add it to your project. 

    MODULE STRING My100TagNames[100];
    MODULE INT nLoggingFlag=0;
    //Call this function on Button or an event - EnableLogging(1);
    FUNCTION EnableLogging(INT nFlag) 
    	nLoggingFlag = nFlag;
    END
    
    //This function should be called by a task
    FUNCTION StartLoggingSnapshotTagValues()
    	WHILE(1) DO
    		IF nLoggingFlag <> 0 THEN
    			nLoggingFlag = 0;
    			LoggingSnapshotTagValues();
    		END
    		SleepMS(1000);
    	END
    END
    
    Function LoggingSnapshotTagValues()
    INT hFileWrite;
    INT iFileHeader = 0;
    INT Index;
    STRING sLine;
    
    	// You can specify your own file location and use TIMESTAMP as part of the file Name
    	STRING sFileName = "[RUN]:MySnapshotValues.csv"; 
    	My100TagNames[0] = "DEV01_TAG00003";
    	My100TagNames[1] = "DEV04_TAG00002";
    
    	EnterCriticalSection("Log100TagValues");
    	ErrSet(1);
    	sFileName = PathToStr(sFileName);
    
    	IF FileExist(sFileName) THEN 
    		iFileHeader = 0;
    	ELSE
    		iFileHeader = 1;
    	END
    
    	hFileWrite = FileOpen(sFileName, "a+");
    	IF hFileWrite = -1 THEN
    		ErrLog("Fileopen failed!");
    		RETURN;
    	END
    
    	IF (iFileHeader = 1) THEN
    		// If an opened file is new, generate the header for each column in this csv file.
    		sLine = "Name,Value,Quality,Timestamp";	
    		FileWriteLn(hFileWrite, sLine);
      	END	
    
    	// Note: Tag Read is a blocking function
    	FOR Index = 0 TO 1 DO
    		sLine = My100TagNames[Index] + "," + TagReadEx(My100TagNames[Index]) + "," + TagReadEx(My100TagNames[Index] + ".Q") + "," + TagReadEx(My100TagNames[Index] + ".T");
    		FileWriteLn(hFileWrite, sLine);
    	END
    
    	// Logging is completed.
    	FileClose(hFileWrite);
    	ErrSet(0);
    	LeaveCriticalSection("Log100TagValues");
    END

    • Added TaskNew("StartLoggingSnapshotTagValues", "", 0); to your startup code of the client. 
    • In the sample code, only 2 tags are populated. 
    • You should create a separate function to assign those 100 tags to array My100TagNames[100]. As  suggested, you could also use TagBrowse functions to populate the wanted tags and assign them to the array. It is up to you how to obtain those 100 tags.
    • Remove Quality and Timestamp parts if not wanted and update the header as well
  • Hi  ,

    Thank you for you reply it solved my problem efficiently.

    Can you please clarify why did you used the EnterCiticalSection and why you are suggesting to call this function as a Task?

  • Hi  , 

    A task is an asynchronized operation that will not block the main thread of Plant SCADA. For example, TagRead() is a blocking function that cannot be called directly from a page button.

    EnterCiticalSection is just like a safe lock to prevent another request from breaking the data integrity in processing the current snapshot. If you have only one set of tags to process, it is not required. Hope I have answered your questions.

  • Hi  

    Thank you. Do you mean that if I am in my code only processing one snapshot request at a time I won't need the EnterCiticalSection?

    I have multiple sets of tags but I am proccing them one by one in a single Cicode file.

  • I am not sure if there is any sleep between processing multiple sets or signal indicating the previous set is completed before he next one is sent. Anyway, if some requests are overlapped, you do need this lock to ensure the data integrity of each snapshot is maintained.

Reply Children
  •   
    -If I am calling the requests sequentially in the same file without any sleep or signal indicating the previous set is completed before the next one is sent. In this manner as far as I understand each line will only be executed if the previous line is done so in this case it is impossible two TagRead() requests to be executed in parallel. Is that true?

    -To make sure I understand the blocking function is not called in by a task the main thread of the plant SCADA will be blocked and if there is an infinite loop the plant SCADA will be stuck and nothing will be executed until the end of the blocking function. Am I right here?

  •  If the function is called multiple times from another function (without using TaskNew()), they would run sequentially and would not conflict. The problem would be if multiple tasks were calling the function, such as a button on a page that could be clicked multiple times before the task finishes.

    Plant SCADA has multitasking built in so an infinite loop is OK. The Cicode task will not block the main thread unless you specifically call an external DLL function or try to access an unavailable file across the network.