Convert System Date Time to DINT or REAL, RSLOGIX

busarider29

Lifetime Supporting Member
Join Date
Aug 2013
Location
Midland, MI
Posts
398
Greetings fellow PLC programmers,

I know there is a simple way to get the system date time of the RSLogix system. But, is there then a way to convert that system time to a REAL or UDINT?

The end goal of this programming task is to calculate machine utilization %. Part of an accurate calculation is the need to account for the time when/if the machine is powered off and then add that amount of time to the "Total time monitored in seconds" after the machine is powered back up.

I've completed the code for a Codesys base platform machine and it was easy enough and fairly straight forward to do. I'm wondering if it will be a similar process in RSLogix5000 machine, much different, or not possible(?).

The basis for the Codesys code was to first get the SystemDateTime and convert that time to a LREAL. The SystemDateTime is incremented by 1 every 1 second. So the LREAL is also incremented by 1 every 1 second. It was straight forward from there. Can I do a similar code in RSLogix5000? Or will it be much different, or not possible? Has someone coded something similar to this in RSLogix5000?

Thanks
 
Last edited:
Hello busarider29:
The program I am showing below may be a strong candidate of the worst Logix5000 program ever, but it could be helpful for you if only as an example of how not to solve your problem.

I did not do the conversion to REAL, but I think this could help. I needed to measure the time it takes for a device to communicate with the scanner after powering it on with one of the DOs of the CompaclLogix local IO. So I use the function GSV (Get System Value) at every scan to get time until the power is supplied and store this in one DINT[7] array, as supported by GSV function. On a different variable I store the time until the connection is established, then I calculate the difference between these two times stamps. Since these delays is in the range of several hundred milliseconds I only converted the second, milliseconds and microseconds part to a millisecond common unit. For longer intervals you would need to select the unit best suited for your application (seconds?, minutes?) and calculate the difference in between the two intervals you measure, and as I did, convert to a common unit of time adding up all the differences to a DINT variable.
I hope my explanation is clear (I suspect it could be better) and I hope that if my program does not help, at least it can provide some laughter.

20201020_TimeStampDifference1.jpg
 
Last edited:
In looking in https://literature.rockwellautomation.com/idc/groups/literature/documents/rm/1756-rm003_-en-p.pdf at the various parts of the system time available via GSV, there appear to be both CurrentValue (LINT microseconds since the epoch 1970-01-01T00:00:00) and DateTime (DINT[7] as year, month, day, hour, minute, seconds, microseconds). Perhaps CurrentValue is the equivalent to what is in the OP's existing CodeSys program. For using DateTime, I submit the following as a very simple approach.
Code:
########################################################################
### Get WallClock as DINT[7] Date_Time


                   _________________________
------------------[GSV                     ]----
                  [Class Name WallClockTime|
                  [Dest           Date_Time|
                   ------------------------


########################################################################
### Detect change in the seconds value => 1s has passed on this scan
### - Update Save_sec, so this will not execute on following scans
###   until next second has passed

    _____________________            1s_this_scan
---[EQU                  ]----+----------( )---------------+---
   [SourceA  Date_Time[5]]    |                            |
   [SourceB      Save_sec]    |    ____________________    |
    ---------------------     +---[MOV                 ]---+
                                  [Source  Date_Time[5]]
                                  [Dest        Save_sec]
                                   --------------------


########################################################################
### If 1s has passed, increment total time

 1s_this_scan      ________________________________________
----] [------------[ADD                                     ]---
                   [SourceA  Total_time_monitored_in_seconds]
                   [SourceB                                1]
                   [Dest     Total_time_monitored_in_seconds]
                    ----------------------------------------


########################################################################
### If 1s has passed and machine is on, increment on time as cs

 1s_this_scan  Machine_on    ________________________________________
----] [-----------] [------[ADD                                     ]---
                           [SourceA  Machine_on_time_in_centiseconds]
                           [SourceB                              100]
                           [Dest     Machine_on_time_in_centiseconds]
                            ----------------------------------------


########################################################################
### Calculate utilization percentage

 1s_this_scan    ________________________________________
----] [---------[DIV                                     ]---
                [SourceA  Machine_on_time_in_centiseconds]
                [SourceB  Total_time_monitored_in_seconds]
                [Dest          Machine_on_time_in_percent]
                 -----------------------------------------
Caveats
* Total_time_monitored_in_seconds and especially Machine_on_time_in_seconds need to be LREALs, which is good for about 300My.
* REALs are only good for times up to about half a year; a DINT representing cs would cover only slightly longer, or ~136y if the ADD incremented by 1 (s) instead of 100 (cs) and a MUL instruction was included to multiply the quotient by 100 to yield percent.
* Everthing could be on one or two rungs and the 1s_this_scan boolean is not needed.
* Reset logic (setting the total and on times to zero), handling PLC power cycles, etc., are left as an exercise for the user; I expect that will be the more complicated part of this application.
 
There is a possible issue with the sample code I provided there: what if the WallClock does not update at least once per second? E.g the MicroLogix RTC (Real Time Clock) updates once per two seconds (and seconds are available as RTC[0].SEC or similar; GSV is not required).



If the steps are fixed the percentage is still correct, but if not a bit more code is needed: subtract the last seconds value (Save_sec) from the current seconds value (Date_Time[5]); if that difference is less than 1, correct it by adding 60 to it; then use that (possibly corrected) difference in the ADD instructions instead of 1 to increment [Total_time_monitored_in_seconds], and multiply that difference by 100 and use that product instead of the 100 in the ADD instruction that increments [Machine_on_time_in_centiseconds].


Note that this only works if the clock updates at least once per minute; another approach would be to change the original code to use use Date_Time[4] instead and make the calculation using minutes instead of seconds.
 
Thanks to those that provided feedback and even spent some time doing some code. Very much appreciated!!
It appears from the feedback provided, that this should be straight forward in RSLogix5000, albeit slightly different than in Codesys but almost the same thing. I just wasn't sure how "tricky" it was going to be to convert that system time (or wall clock time) to a numerical data type. As long as that can be done and the numerical datatype will never overflow with incrementing every 1 second, then I believe we're good.
DrBitBoy, thanks for the "heads-up" with the time possibly updating every 2 seconds. I'll take some code from all and use it as a basis to begin.

Thanks everyone!!
 
Greetings fellow PLC programmers,

I know there is a simple way to get the system date time of the RSLogix system. But, is there then a way to convert that system time to a REAL or UDINT?

The end goal of this programming task is to calculate machine utilization %. Part of an accurate calculation is the need to account for the time when/if the machine is powered off and then add that amount of time to the "Total time monitored in seconds" after the machine is powered back up.

I've completed the code for a Codesys base platform machine and it was easy enough and fairly straight forward to do. I'm wondering if it will be a similar process in RSLogix5000 machine, much different, or not possible(?).

The basis for the Codesys code was to first get the SystemDateTime and convert that time to a LREAL. The SystemDateTime is incremented by 1 every 1 second. So the LREAL is also incremented by 1 every 1 second. It was straight forward from there. Can I do a similar code in RSLogix5000? Or will it be much different, or not possible? Has someone coded something similar to this in RSLogix5000?

Thanks


Does "real time" even enter into the discussion ?

All you need is a measure of "active" time and "productive" time, and you don't need to inspect the controller's wallclock for that data, just a couple of RTO's will do it .
 
Does "real time" even enter into the discussion ?

All you need is a measure of "active" time and "productive" time, and you don't need to inspect the controller's wallclock for that data, just a couple of RTO's will do it .


Read the sentence after the one you highlighted… "Part of an accurate calculation is the need to account for the time when/if the machine is powered off"


I've yet to meet an RTO that will count time while powered off.
 
Read the sentence after the one you highlighted… "Part of an accurate calculation is the need to account for the time when/if the machine is powered off"


I've yet to meet an RTO that will count time while powered off.




".. when/if the machine is powered off ..."


If "machine" is the process being monitored, then a couple of RTOs, as suggested, will work.


If "machine" is the PLC, I have yet to meet any PLC algorithm that will keep track of a process' state while the PLC is off.
 
".. when/if the machine is powered off ..."


If "machine" is the process being monitored, then a couple of RTOs, as suggested, will work.


If "machine" is the PLC, I have yet to meet any PLC algorithm that will keep track of a process' state while the PLC is off.

Yes, the "machine" is the PLC. The way that I've done it in TwinCAT (Codesys) is show below. If you have Codesys or TwinCAT, you can should be able to copy and paste this code right in and run it. Other than the string concatenations, I've converted this code to Ladder just for the sake of converting it. I've not shown that code here though. Any variable ending in "LD" is not needed here. I used those in the Ladder equivalent program.

These are the global variables. They are within a global variable list called "GV_Stations".
Code:
VAR_GLOBAL
    bSTA1Started                :BOOL;
    bSTA2Started                :BOOL;
    bSTA3Started                :BOOL;
    bSTA4Started                :BOOL;
    bSTA5Started                :BOOL;
    bSTA6Started                :BOOL;
    Sta1Util                    :LREAL;
    Sta1UtilLD                  :LREAL;
    Sta2Util                    :LREAL;
    Sta2UtilLD                  :LREAL;
    Sta3Util                    :LREAL;
    Sta3UtilLD                  :LREAL;
    Sta4Util                    :LREAL;
    Sta4UtilLD                  :LREAL;
    Sta5Util                    :LREAL;
    Sta5UtilLD                  :LREAL;
    Sta6Util                    :LREAL;
    Sta6UtilLD                  :LREAL;
    
    MachineUtil     		:LREAL;
    MachineUtilLD               :LREAL;
    Date_Started			:STRING;
    Date_StartedLD              :STRING;
    Station1Utilization         :STRING;
    Station2Utilization         :STRING;
    Station3Utilization         :STRING;
    Station4Utilization         :STRING;
    Station5Utilization         :STRING;
    Station6Utilization         :STRING;
    TotalMachineUtilization     :STRING;
END_VAR

These are the local variables. The function block "FB_TIMETOREAL" is a custom function block that simply converts a TIMESTRUCT datatype to a LREAL. The function block saves me 5 lines of code any time I have to do that conversion, hence I wrote a function block for it.
Code:
PROGRAM DOWNTIME_TRACKING_ST
VAR PERSISTENT
	tsDateTime					: TIMESTRUCT;
	tsTrackTimeStartTime			        : TIMESTRUCT;
	stDateTime					: STRING;
        lrTrackTimeStartTime                         : LREAL;	
	lrStation1RunningTime			: LREAL;
	lrStation2RunningTime			: LREAL;
	lrStation3RunningTime			: LREAL;
	lrStation4RunningTime			: LREAL;
	lrStation5RunningTime			: LREAL;
	lrStation6RunningTime			: LREAL;
        lrTotalTimeMonitored                          : LREAL;
        lrTimeRecordedAtOff                           : LREAL;    
END_VAR

VAR
        fbGetCurTaskIndex                            : GETCURTASKINDEX;
	fbGetSysTime			               : FB_LOCALSYSTEMTIME;
        fbTimeToReal_1                                 : FB_TIMETOREAL;
	fbTimeToReal_2			               : FB_TIMETOREAL;
	nCase				               : INT := 0;	
	bResetTracking					: BOOL;
	ONS1						: R_TRIG;
	ONS2						: R_TRIG;
    tmrTon1                                                : TON;
	tmr_OnTime				        : TON;
	tmr_OffTime					: TON;
    lrSysTimeCurrent				        : LREAL;
    lrSecondsWhileOff                                  : LREAL;
    stStation1Util                                         : STRING;
    stStation2Util                                         : STRING;
    stStation3Util                                         : STRING;
    stStation4Util                                         : STRING;
    stStation5Util                                         : STRING;
    stStation6Util                                         : STRING;
    stMachineUtil                                         : STRING;
    stMachine1Util                                       : STRING;
    stStation1aUtil                                       : STRING;
    stStation2aUtil                                       : STRING;
    stStation3aUtil                                       : STRING;
    stStation4aUtil                                       : STRING;
    stStation5aUtil                                       : STRING;
    stStation6aUtil                                       : STRING;
    stUtilization                                  : STRING := '% utilization since: ';    
END_VAR
Program is in the following post....
 
Last edited:
This is the program in Structured Text. Because I'm using a CASE SELECT statement, conditioning on the first scan bit is really not necessary, so I'll probably remove that. As stated in original post, being able to get the system time and converting that to some datatype that won't roll over any time soon, was the basis for this code.

Code:
fbGetSysTime(sNetID:='', bEnable:=TRUE, dwCycle:=1); //Call the function block to get System Time.  Set the FB's required parameters.
tsDateTime:= fbGetSysTime.systemTime; //From the function block call, get the systemTime and copy it into a TIMESTRUCT datatype
fbTimeToReal_1(Execute:= TRUE, DateTime:=tsDateTime, TimeLREAL=> lrSysTimeCurrent); //Convert the TIMESTRUCT to a LREAL datatype.
ONS1(CLK:= bResetTracking);//ONS1 is an R_TRIG(equivalent of a oneshot in RSLogix), and is triggered via 'bResetTracking' Bool.

fbGetCurTaskIndex(); //Function block call to get the current task index number

//If this is the first cycle of the task (just powered up after a powered down state), then set the nCase to 1
IF _TaskInfo[fbGetCurTaskIndex.index].FirstCycle THEN
	nCase := 1;
END_IF   

CASE nCase OF 
	1: //Allow 1 second to pass to ensure the FB calls above are done.  This is way more time than needed, but insignificant nonetheless.
	tmrTON1.PT:=T#1S;
	tmrTON1.IN:=TRUE;
		IF tmrTON1.Q THEN
			nCase := 2;
		END_IF
		
	2: //Calculate the amount of time the machine was powered off and add it to the total time monitored.
	lrSecondsWhileOff := lrSysTimeCurrent - lrTimeRecordedAtOff;
    lrTotalTimeMonitored := lrTotalTimeMonitored + lrSecondsWhileOff;
	nCase := 3;
	
    3: //Increment running time of each station and machine, incrementing by 1 every 1-second.
	tmr_OnTime(IN:= NOT tmr_OffTime.Q, PT:= T#500MS);
	tmr_OffTime(IN:= tmr_Ontime.Q, PT:= T#500MS);
    ONS2(CLK:=tmr_OnTime.Q);
	IF ONS2.Q THEN		
		IF GV_Stations.bSTA1Started THEN
			lrStation1RunningTime := lrStation1RunningTime + 1;
		END_IF
		IF GV_Stations.bSTA2Started THEN
			lrStation2RunningTime := lrStation2RunningTime + 1;
		END_IF
		IF GV_Stations.bSTA3Started THEN
			lrStation3RunningTime := lrStation3RunningTime + 1;
		END_IF 
		IF GV_Stations.bSTA4Started THEN
			lrStation4RunningTime := lrStation4RunningTime + 1;
		END_IF 
		IF GV_Stations.bSTA5Started THEN
			lrStation5RunningTime := lrStation5RunningTime + 1;
		END_IF
		IF GV_Stations.bSTA6Started THEN
			lrStation6RunningTime := lrStation6RunningTime + 1;
		END_IF 
		lrTimeRecordedAtOff := lrSysTimeCurrent; //Continually record the LREAL equivalent of the current System Time so that we have it when the machine is powered off.  
		lrTotalTimeMonitored := lrTotalTimeMonitored + 1; //Keep incrementing Total Time Monitored by 1, every 1-second

        //Calculate the utilization of each station and machine.  Return the values in a % format	
		GV_Stations.Sta1Util := (lrStation1RunningTime / lrTotalTimeMonitored)*100;
		GV_Stations.Sta2Util := (lrStation2RunningTime / lrTotalTimeMonitored)*100;
		GV_Stations.Sta3Util := (lrStation3RunningTime / lrTotalTimeMonitored)*100;
		GV_Stations.Sta4Util := (lrStation4RunningTime / lrTotalTimeMonitored)*100;
		GV_Stations.Sta5Util := (lrStation5RunningTime / lrTotalTimeMonitored)*100;
		GV_Stations.Sta6Util := (lrStation6RunningTime / lrTotalTimeMonitored)*100;
		GV_Stations.MachineUtil := ((GV_Stations.Sta1Util + GV_Stations.Sta2Util + GV_Stations.Sta3Util + GV_Stations.Sta4Util + GV_Stations.Sta5Util + GV_Stations.Sta6Util) / 6);
        
        //The calculation of machine utilization % is complete with the lines of code above. 
        //The following lines of code below and within the remainder of the Case statement are simply for building and concactenating the text strings, and are optional.
        stStation1Util := LREAL_TO_FMTSTR(GV_Stations.Sta1Util, 2, TRUE);
        stStation2Util := LREAL_TO_FMTSTR(GV_Stations.Sta2Util, 2, TRUE);
        stStation3Util := LREAL_TO_FMTSTR(GV_Stations.Sta3Util, 2, TRUE);
        stStation4Util := LREAL_TO_FMTSTR(GV_Stations.Sta4Util, 2, TRUE);
        stStation5Util := LREAL_TO_FMTSTR(GV_Stations.Sta5Util, 2, TRUE);
        stStation6Util := LREAL_TO_FMTSTR(GV_Stations.Sta6Util, 2, TRUE);
     	stMachineUtil := LREAL_TO_FMTSTR(GV_Stations.MachineUtil, 2, TRUE);
        stStation1aUtil := CONCAT(stStation1Util, stUtilization);
        stStation2aUtil := CONCAT(stStation2Util, stUtilization);
        stStation3aUtil := CONCAT(stStation3Util, stUtilization);
        stStation4aUtil := CONCAT(stStation4Util, stUtilization);
        stStation5aUtil := CONCAT(stStation5Util, stUtilization);
        stStation6aUtil := CONCAT(stStation6Util, stUtilization);
        stMachine1Util := CONCAT(stMachineUtil, stUtilization);
        GV_Stations.Station1Utilization:= CONCAT(stStation1aUtil, stDateTime);
        GV_Stations.Station2Utilization:= CONCAT(stStation2aUtil, stDateTime);
        GV_Stations.Station3Utilization:= CONCAT(stStation3aUtil, stDateTime);
        GV_Stations.Station4Utilization:= CONCAT(stStation4aUtil, stDateTime);
        GV_Stations.Station5Utilization:= CONCAT(stStation5aUtil, stDateTime);
        GV_Stations.Station6Utilization:= CONCAT(stStation6aUtil, stDateTime);
        GV_Stations.TotalMachineUtilization:= CONCAT(stMachine1Util, stDateTime);				
	END_IF;
END_CASE

//When this R_TRIG (oneshot) is triggered, reset the utilization % to restart it from 0.  This also resets the date started.
IF ONS1.Q THEN
    tsTrackTimeStartTime := fbGetSysTime.systemTime;
    lrTrackTimeStartTime := lrSysTimeCurrent;
	stDateTime:= SYSTEMTIME_TO_STRING(tsTrackTimeStartTime);
	lrStation1RunningTime := 0;	
	lrStation2RunningTime := 0;
	lrStation3RunningTime := 0;
	lrStation4RunningTime := 0;
	lrStation5RunningTime := 0;
	lrStation6RunningTime := 0;
	lrTotalTimeMonitored := 0;
	lrSecondsWhileOff := 0;
	bResetTracking := FALSE;    
END_IF

GV_Stations.Date_Started := stDateTime;
tmrTON1();
tmr_OnTime();
tmr_OffTime();
 
Last edited:
Yes, the "machine" is the PLC....


That being the case, and with the original query to convert system time (year, month, day, hour, minutes, seconds (, microseconds?)) to an LREAL or UDINT in RSLogix5000, does this help?


RA Technote 52347; ID: QA7905
How to convert 64-bit current time value in microseconds to seconds


(requires techconnect contract, but search for 52347 in pcltalk and you'll find some code here)


I.e. for ControlLogix, the CurrentValue (64-bit μs past Unix(tm) epoch*) of the WallClockTime (or CST) object is already in a LINT, so the fbTimeToReal_n functions from the Codesys example become simple divide-by-1e6 operation, so I think you can use that instead of the DateTime [year,month,...] of the WallClockTime, and the rest of the Codesys algorithm should be straightforward to write via RSLogix5000.





* Unix(tm) epoch is 1970-01-01 00:00:00 UTC
 
Also, if multiplying the CST/CurrentValue LINT (or UDINT[2] elements) by 1e-6 is too messy, bits 20-31 of UDINT[1] and bits 0-19 of UDINT[2] combine to make a UDINT (or DINT until 2041) within 5% of whole seconds.
 

Similar Topics

HI, my friends: i have some question. in the simense PCS7, i want to read the current system clock and convert it to the INT or WORD data type...
Replies
10
Views
4,735
Hello all, I'm currently working on a servo motor linear positioning system (ball screw). I'm all set up regarding communication between my HMI...
Replies
1
Views
87
I have an application using an incremental encoder and then I convert it to degree (0-360) using calculation program. For a while, the calculation...
Replies
7
Views
233
Hi all. Me again still learning Rockwell. So I'll be polling an INT data array from a Modbus SE power meter with a L82 (with a Modbus ProSoft in...
Replies
56
Views
1,354
Hello, could someone kindly convert the attached RSP files that are currently used for SLC 5 PLC into PDF please
Replies
6
Views
519
Back
Top Bottom