Citect SCADA 2018 Disk I/O Devices and redundant servers - Migrating to Persisted Memory I/O Device

i have raised this with support but though i may use the collective knowledge on here as well.

I have Citect SCADA 2018 system with redundant servers configuration (Primary and Standby) with hard Disk Drive I/O device

Variables.

 

I have found that these 2 devices do not fully synchronise, the original project when these iO device were added was in 1996 on very early version (V3.21)

 

Reading the Help directory I now see improved performance and synchronisation is available from V7.2 onwards by using Persisted Memory I/O Device

I will implement this change as soon as I have a down day by the following procedure.

 

To migrate disk PLC devices to persisted I/O memory mode:

  1. 1.       Perform the procedures listed in Upgrading to Citect SCADA 7.20, as is appropriate. Refer to the v7.20 documentation for more information.
  2. 2.       For disk I/O devices, set the Background Poll property to TRUE and the Persist property to TRUE (the default setting). See Add an I/O Device) for more information.
  3. 3.       Start the I/O server up and wait for a few minutes so that the background poll updates every tag.
  4. 4.       Shutdown the I/O server.
  5. 5.       On the I/O Device dialog, for disk I/O devices set Background Poll back to FALSE, set Memory to TRUE and clear out the Port Name field as it is not necessary for Memory Mode.
  6. 6.       Restart the I/O server to finish the migration to persisted memory I/O mode, with your devices now containing your original values from the disk I/O device.

MY Questions are

Q1  The variables I have in these I/O devices are VERY critical to my process.  are these variable Values backed up as part of a Project backup ? if Not is there a way to back up the Values of Disk IO Device Variables

Q2 As I have redundant servers in operation. I would assume I do the above by

  1. shutting down my Standby server
  2. do the Above Migration procedure
  3. restart the  Standby server
  4. shutting down my Primary server
  5. do the Above Migration procedure
  6. restart the  Primary server

is this correct ? anything else I need to be aware of

 

 

Regards

 Alan

  • Hi ,
    Q1\A1: The Disk Device I/O Device address definition defines where these disk files reside. It typically is in the [DATA] directory, so not backed up with a project backup. I don't recommend customers to put runtime data files into project directories, since you can accidentally override them when you restore project backup.
    Q2\A2: That procedure looks correct to me. You can test this out on your test bench with two machines to refine this procedure. You could also copy the production Disk Device file to your test environment and confirm all values are migrated correctly. This will same you any headache with the upgrade.
    Kind regards
    Olivier

  • Many Thanks for the prompt response Olivier.

    Regards
    Alan
  • You can back up disk tag values using Cicode functions I wrote. Download my Cicode Tools TagTools library from the Citect Toolbox. The TagTools.ci file has TagExport() and TagImport(). They have filtering so you can export tags from a specific I/O Device to a CSV file. For example, if your disk devices all had names ending in _DSK, you'd use the command: 

    TagExport("IODEV=*_DSK")

    The functions require Citect 7.50 or later, but I've attached a different version that works in 7.20. The export filtering is not as advanced, but it allows you to export from 7.20, then use the 7.50+ version of the function to import the data in the new release.

    This Cicode should only be necessary if there's a problem doing the normal upgrade process, such as a corrupt disk device (.CDK) file.

    TagTools 7.20.ci

  • Thanks Eric,
    i will explore this next week. i appreciate your Reply.
  • Hi Eric Black,

    i copied the TagTools.ci file into my Scada 2018 project and got a few compile errors.

    looks like there are other fuctions this file calls which i do not have. are these functions in the toolbox as well ?
  • Sorry, I forgot about that. As the toolbox post says, "Most of the functions are stand-alone, but some do call other functions in the same file or other files in this set."

    Here are the additional functions my export code calls...from the Date, File, String, and SuperGenie Tools files.

    STRING	mcCRLF = "^0x0D^0x0A";	//Carriage Return & Line Feed characters
    STRING	msFileBuffer;
    
    
    //Returns the specified string with nCharsToTrim removed from the left (beginning)
    //or "" for out of range values
    STRING FUNCTION StrTrimLeft(STRING sText, INT nCharsToTrim)
    	INT nTextLen = StrLength(sText);
    	
    	IF (nCharsToTrim >= 0) AND nCharsToTrim < nTextLen THEN
    		RETURN StrMid(sText, nCharsToTrim, StrLength(sText) - nCharsToTrim);
    	ELSE
    		RETURN "";
    	END
    END
    
    
    //Returns TRUE if the specified tag name includes a cluster name prefix
    INT FUNCTION TagHasCluster(STRING sTag)
    	RETURN StrSearch(1, sTag, ".") > 0;
    END
    
    
    //Wrapper for FileWriteLn() using FileWriteBuf to minimize disk access
    FUNCTION FileWriteLnBuf(INT hFile, STRING sText)
    	FileWriteBuf(hFile, sText + mcCRLF);
    END
    
    //Wrapper for FileWrite() uses buffering to mimimize disk access
    FUNCTION FileWriteBuf(INT hFile, STRING sText)
    	INT		cMaxChars = 254;	//In Citect 7.20 SP5 and later this can be increased to 255
    	INT		nBufferLen;
    	INT		nError;
    	STRING	sRemainingText;
    
    	nBufferLen = StrLength(msFileBuffer);
    	msFileBuffer = StrLeft(msFileBuffer + sText, cMaxChars);					//Copy whatever will fit in buffer
    	sRemainingText = StrTrimLeft(sText, StrLength(msFileBuffer) - nBufferLen);		//Remove any text that was copied to buffer
    
    	//Dump full buffer to disk
    	IF StrLength(msFileBuffer) = cMaxChars THEN
    		IsError();		//Reset error code
    		FileWrite(hFile, msFileBuffer);
    		nError = IsError();
    		
    		IF nError <> 0 THEN
    			ErrLog("FileWriteBuf() Failed to write buffer to file. Error:" + nError:#);
    			RETURN;
    		END
    		msFileBuffer = sRemainingText;
    	END
    END
    
    
    //Wrapper for FileClose() also writes any text remaining in buffer
    FUNCTION FileCloseBuf(INT hFile)
    	INT		nError;
    	
    	IsError();	//Clear last error
    	FileWrite(hFile, msFileBuffer);
    	nError = IsError();
    	
    	IF nError <> 0 THEN
    		ErrLog("FileCloseBuf() Failed to write buffer to file. Error:" + nError:#);
    	END
    
    	msFileBuffer = "";
    	FileClose(hFile);
    END
    
    
    //Convert a Citect date/time integer value into a formatted string (see Q6123)
    //Similar to TimeToStr() but allows custom formats to be specified like TimestampFormat()
    //
    //sFormat	Custom time/date format specification. Case sensitive. See below.
    //nTime		Citect integer time/date value (default is the current time)
    //nMS		Milliseconds to add to Citect time (default = 0)
    //
    //Example: sText = TimeFormat("dd/MM/yyyy hh:mm:ss.fff");
    //
    STRING FUNCTION TimeFormat(STRING sFormat, INT nTime = TimeCurrent(), INT nMS = 0)
    	RETURN TimestampFormat(TimeIntToTimestamp(nTime, nMS, TRUE), sFormat);
    END
    
    
  • Hi Eric,

    ok, all good added those in it compiles all ok,

    i placed a button in my project with the following call

    TagExport("IODEV=*_DSK", "C:\Temp\DISKPLC2.csv") (my Disk I/O Device is DiskPLC2.DSK)

    the csv file is successfully created with headers Tag & Value but no data is propagated to the file?

    i have missed something with sFilter syntax by the looks of it.

    can you help please?
  • DiskPLC2.dsk looks like the I/O Device Address, not the Name. What is the Name of your disk I/O device? You have to specify a filter string to match all the disk device names, if they have something in common, like beginning with "Disk_" or ending with "_DSK". Otherwise you need to specify the full name of the device to just export that one device, or leave the filter string blank to export all tags/devices. Another option shown in the documentation for my function is to filter by tagname, like: TagExport("TAG=*\Bkr_Name_*")

  • Thanks Eric, this now works a treat. many thanks for your help on this its very much appreciated.

    i have a shutdown in 6 days and wanted a record or my tag values as its critical to our process before i change my IO device.

    Thanks Again.

    regards
    Alan