Sign in to follow this  
Followers 0
Marco8037

Converting DATE_AND_TIME variable value to year, month, day, etc

3 posts in this topic

Hello guys: I would like to get an way to get the year, month, day, hour, minute, and seccond in different variables, coming from a value on a DATE_AND_TIME variable. For example, using an FB like the following one: Where the IN and Output data of this FB is like this: FUNCTION_BLOCK DT_TO_year_month_day_hour_and_minute VAR_INPUT In_DT: DATE_AND_TIME; END_VAR VAR_OUTPUT Year: UINT; (*value from 1970 to 2106*) Month: USINT; (*value from 1 to 12*) Day: USINT; (*value from 1 to 31*) Hour: USINT; (*value from 0 to 23*) minute: USINT; (*value from 0 to 59*) secconds: USINT; (*value from 0 to 50*) END_VAR VAR END_VAR However, I only have the variables declaration. The programming code to do this seems very complicated for me. Does anybody have this code already done? Or, does anybody known an easiest way to do this? Thank you very much. Marco

Share this post


Link to post
Share on other sites
Well, all the "DT" variable is is the number of seconds since 1970. This means by using simple division you should be able to come up with each one of your variable outputs. DT / number of seconds in a year = Number of years Then you take the remainder of that division and divide it to get months MOD(DT / Number Seconds in a year) / Number Seconds in a month = Number of Months And so on.... Since there would be quite a few calculations that won't change most of the day, depending on what hardware platform you're using you may want to use a simplified timer to make sure you don't recalculate the year/month/date every single scan of the PLC. Hope it helps! Nick

Share this post


Link to post
Share on other sites
Hello Nick. Thanks for your quick answer. Yes, you are right when you say that a "DT" variable is is the number of seconds since 1970. By the way I shall be able to calculate each one of my variable outputs. However, it is not so easy as we can think at a first time, because for an exact calculation of the variable outputs we have to consider that there are a different number of secconds in defferent years, because of the leap years (years with 366 days). About the months, it happens the same: Months with 30 and 31 days (different number of secconds in different months). By the way my problem cannot be solved with some simple divisions and simple MOD (Instruction to calculate the remainder). We have also to consider that on February we sometimes have 28 days and sometimes 29. However, I have considered all this situations and I have made a programming code to calculate my variables. I have tested it and it is running well. But I still have a little problem (I will talk about it in some lines after). Let me explain first how I have made the code: At the first time I have created a Struct like this: TYPE MY_Own_time_struct : (* MY_Own_time_struct - similar to "System Time Structure" on TwinCAT PLC Utilities Library *) STRUCT wYear : UINT; (* Year: 1970..2106 *) bMonth : USINT; (* Month: 1..12 (January = 1, February = 2 and so on )*) bDay : USINT; (* Day of the month: 1..31 *) bHour : USINT; (* Hour: 0..23 *) bMinute : USINT; (* Munute: 0..59 *) bSecond : USINT; (* Second: 0..59 *) END_STRUCT END_TYPE That's easiest to check it in the following drawing (Because the forum erases the spaces in the programming code): Then, I have created my Function block to calculate my outputs (Year, month, day, etc...). That Fb puts the calculation result on an output variable of "MY_Own_time_struct " type. I gave "Calculate_YEAR_MONTH_DAY_ETC" name to that FB. The variables declaration is the following one: FUNCTION_BLOCK Calculate_YEAR_MONTH_DAY_ETC VAR_INPUT PDT: DT; (*Preset date and time*) END_VAR VAR_OUTPUT Calendar: MY_Own_time_struct; (*Struct with YEAR, MONTH, DAY, Hour, minute and seccond*) END_VAR VAR Consec_4_YEARS: USINT; (*Nr. of set (group) of 4 consecutive years*) Nr_secconds: UDINT; (*Number of secconds*) Remain_secconds: UDINT; leap_year: BOOL; years_more: USINT; sec_in_this_year: UDINT; (*Nr. of secconds on the actual year*) sec_in_this_month: UDINT; END_VAR That's easiest to check it in the following drawing: Then, I have made the following programming code: Nr_secconds:= DT_TO_UDINT(PDT); (*Calculation of the year*) Consec_4_YEARS:= UDINT_TO_USINT(Nr_secconds / 126230400); Remain_secconds:= Nr_secconds MOD 126230400; CASE Remain_secconds OF 0..31535999: leap_year:=FALSE; years_more:= 1; sec_in_this_year:=Remain_secconds; (*useful for the calculation of the month*) 31536000..63071999: leap_year:=FALSE; years_more:= 2; sec_in_this_year:=Remain_secconds - 31536000; (*useful for the calculation of the month*) 63072000..94694399: leap_year:=TRUE; years_more:= 3; sec_in_this_year:=Remain_secconds - 63072000; (*useful for the calculation of the month*) 94694400..126230399: leap_year:=FALSE; years_more:= 4; sec_in_this_year:=Remain_secconds - 94694400; (*useful for the calculation of the month*) END_CASE Calendar.wYear:= 1969 + (Consec_4_YEARS * 4) + years_more; (*Calculation of the month*) CASE sec_in_this_year OF 0..2678399: (*There are 86400 secconds in a whole day. There are 2678400 secconds on a month with 31 days. On seccond nr. 2678399, we are still in January. When 2678400 secconds are reached, it means we are already on February month*) Calendar.bMonth:= 1; sec_in_this_month:= sec_in_this_year; 2678400..5183999: (*considering a leap year - month with 29 days*) Calendar.bMonth:= 2; sec_in_this_month:= sec_in_this_year - 2678400; IF NOT leap_year THEN (*considering a no leap year - month with 28 days*) IF sec_in_this_year > 5097599 THEN (*considering a no leap year - Day 29 --- Replaced by 1st of March*) Calendar.bMonth:= 3; sec_in_this_month:= sec_in_this_month - 2419200; END_IF END_IF END_CASE (*It's necessary to close dthe CASE instruction to the 86400 increment has effect*) IF NOT leap_year THEN (*considering a no leap year - month with 28 days*) sec_in_this_year:= sec_in_this_year + 86400; (*Useful for using the same calculation method for the other months*) END_IF (*from now on, we will consider a leap year, even it is not true - February month with 29 days*) CASE sec_in_this_year OF 5184000..7862399: Calendar.bMonth:= 3; sec_in_this_month:= sec_in_this_year - 5184000; 7862400..10454399: Calendar.bMonth:= 4; sec_in_this_month:= sec_in_this_year - 7862400; 10454400..13132799: Calendar.bMonth:= 5; sec_in_this_month:= sec_in_this_year - 10454400; 13132800..15724799: Calendar.bMonth:= 6; sec_in_this_month:= sec_in_this_year - 13132800; 15724800..18403199: Calendar.bMonth:= 7; sec_in_this_month:= sec_in_this_year - 15724800; 18403200..21081599: Calendar.bMonth:= 8; sec_in_this_month:= sec_in_this_year - 18403200; 21081600..23673599: Calendar.bMonth:= 9; sec_in_this_month:= sec_in_this_year - 21081600; 23673600..26351999: Calendar.bMonth:= 10; sec_in_this_month:= sec_in_this_year - 23673600; 26352000..28943999: Calendar.bMonth:= 11; sec_in_this_month:= sec_in_this_year - 26352000; 28944000..31622399: Calendar.bMonth:= 12; sec_in_this_month:= sec_in_this_year - 28944000; END_CASE (*Calculation of the day*) Calendar.bDay:= UDINT_TO_USINT((sec_in_this_month / 86400) + 1); (*There is 86400 secconds per day. It is necessary to increment 1 unit because the day nr. 0 doesn't exist*) Remain_secconds:= sec_in_this_month MOD 86400; (*The variable "Remain_secconds" has now the nr. of secconds in this day*) Calendar.bHour:= UDINT_TO_USINT(Remain_secconds / 3600); (*There is 3600 secconds per hour. It is not necessary to increment 1 unit because the hour nr. 0 exists*) Remain_secconds:= Remain_secconds MOD 3600; (*The variable "Remain_secconds" has now the nr. of secconds in this hour*) Calendar.bMinute:= UDINT_TO_USINT(Remain_secconds / 60); (*There is 60 secconds per minute. It is not necessary to increment 1 unit because the minute nr. 0 exists*) Calendar.bSecond:= UDINT_TO_USINT(Remain_secconds MOD 60); That's a code too large to make a Print screen and show with a drawing, but you can easily make a Copy paste to twincat a see the effect. Then, I have made this Fb runs on the Main POU. If you want you can copy and paste a see it running as well. I've tested it and it runs very well with all the entered input dates, as you can see in the following example: As you can see on this example, all the calculated values are rigth. You can test with some other Date_and_time input values and the output will be always correct, independently of the year, month, etc... (It doesn´t matter if iot is a leap year or not. And it doesn't matter if there are 28, 29, 30 or 31 days on the month of proccessing date and time). The output result is always right. About the calculation method for my outputs, I have my problem solved. At this time, the problem is the following one: I intend to use this code on an application that needs an internal running clock. I need to know the exact actual date and time to make the calculation of the input variable of DT in the FB. For example: -I Know that the actual Date and time is, for example: DT#2010-01-26-20:15 -My programme determines that it will take for example 7 223 700 secconds to finish a parcel. What I need to know is: What will be the date and time when the parcel is finished? It will be easy to calculate: Converting "DT#2010-01-26-20:15" to UDINT and adding 7 223 700 secconds. Then, I will just need to convert the addition result to DATE_AND_TIME format and put that result on the input variable of the Function block that I have created. To do that, I only need an internal running clock that gives me the actual date and time. It should not be a problem if I would use an hardware of the CX family, since the CX has an internal clock running all the time. However, I will use an HMI operator terminal from beijer electronics that communicates with the beckhoff hardware with the ethernet ADS protocol. The problem is that the HMI operator terminal also have an internal running clock. When the operators set the date and hour, they will make it on the HMI operator terminal. By the way it will not be setted on the internal running clock of the beckhoff hardware. What I have to do is to synchronize the 2 clocks. To do it, I have spent some of my time reading the help menu of the twincat and I have found a code similar to the following one to do that: The function blocks "NT_GetTime" and "RTC" can be found on the twincat "TcUtilities" library. My doubt is if it only runs well with a Terminal AMS Net ID of a beckhoff hardware or if it will also runs well with the Net ID of the beijer HMI terminal. I still don't know if I am going to use the CX or the BC or BX family. Twincat help says that when using a BC family the RTC instruction has a potential error of about 1 minute in each 24 hours. If I can cyclicaly syncronise it with the HMI operator terminal, it will not be a problem for my application, because the error will also be cyclicaly corrected. At this moment, I cannot try it on because I have no one hardware to test. I'm almost finishing my programme code and I will choose an beckoff hardware platform soon. Of course that the beckoff hardware platform will depend on the size of the programming code and on the size of used data. Choosing it will also be something a little difficult for me: I think that when I make "Rebuild all" on twincat, the compiler should tell me if the proposed hardware was enough or not for the programming code that I have made. However, it happens something strange: On the PLC configuration I have selected PC or CX, like the following picture: I make "rebuild all", and the result is the following one: Then, I replace the library "TcUtilities" by the library "PlcSystemBC.Lb6". However, when I use the BC family, the Fb "NT_GetTime" is not available. By the way, how can I syncronise the BC real time clock with the HMI real clock? Twincat help says that, when using the BC family, " the RTC can be cyclically synchronised (e.g. with a radio clock or with a TwinCAT PC via the fieldbus).". Could anyone explain me this a little better, please? However, to see if the proposed PLC type is enough or not, for my aplication, I remove the Fb "NT_GetTime", and I select the PLC type BC or BX like the following drawing: I make "Rebuild all" and it appears the following error: Then, I keep the same PLC type (BC or BX) and I go to the "Resources" --> "Workspace" --> "Controller settings" and it is selected: "Defaut: BX5100" Then, I unselect the BX5100 and I select BX5200. Then I select BX5100 again and i make "rebuild all" again. It gives me the following 4 errors. I don't know how to eliminate them, but since they are about the RTC function block, I erase the RTC FB in my programming code and I eliminate the libraries about this FB and i make "rebuild all"again. the result is the following one: Now, my question is: Is the BX5100 enough to proccess my programming code or not? Thanks everyone. Marco Edited by Marco8037

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0