lib/iec_std_lib.h
changeset 900 1e749c7b70f8
parent 818 2a3f34967cae
child 903 f712705bef65
equal deleted inserted replaced
899:a3f734a23566 900:1e749c7b70f8
   178 }
   178 }
   179 
   179 
   180 /**********************************************/
   180 /**********************************************/
   181 /* Time conversion to/from timespec functions */
   181 /* Time conversion to/from timespec functions */
   182 /**********************************************/
   182 /**********************************************/
   183 
   183 /* NOTE: The following function was turned into a macro, so it could be used to initialize the initial value of TIME variables.
       
   184  *       Since each macro parameter is evaluated several times, the macro may result in multiple function invocations if an expression
       
   185  *       containing a function invocation is passed as a parameter. However, currently matiec only uses this conversion macro with 
       
   186  *       constant literals, so it is safe to change it into a macro.
       
   187  */
       
   188 /* NOTE: I (Mario - msousa@fe.up.pt) believe that the following function contains a bug when handling negative times.
       
   189  *       The equivalent macro has this bug fixed.
       
   190  *       e.g.;
       
   191  *          T#3.8s
       
   192  *       using the function, will result in a timespec of 3.8s !!!: 
       
   193  *          tv_sec  =  4               <-----  1 *  3.8           is rounded up when converting a double to an int!
       
   194  *          tv_nsec = -200 000 000     <-----  1 * (3.8 - 4)*1e9
       
   195  * 
       
   196  *         -T#3.8s
       
   197  *       using the function, will result in a timespec of -11.8s !!!: 
       
   198  *          tv_sec  = -4                 <-----  -1 *  3.8 is rounded down when converting a double to an int!
       
   199  *          tv_nsec = -7 800 000 000     <-----  -1 * (3.8 - -4)*1e9
       
   200  */
       
   201 /* NOTE: Due to the fact that the C compiler may round a tv_sec number away from zero, 
       
   202  *       the following macro may result in a timespec that is not normalized, i.e. with a tv_sec > 0, and a tv_nsec < 0 !!!!
       
   203  *       This is due to the rounding that C compiler applies when converting a (long double) to a (long int).
       
   204  *       To produce normalized timespec's we need to use floor(), but we cannot call any library functions since we want this macro to be 
       
   205  *       useable as a variable initializer.
       
   206  *       VAR x : TIME = T#3.5h; END_VAR --->  IEC_TIME x = __time_to_timespec(1, 0, 0, 0, 3.5, 0);
       
   207  */
       
   208 /*
   184 static inline IEC_TIMESPEC __time_to_timespec(int sign, double mseconds, double seconds, double minutes, double hours, double days) {
   209 static inline IEC_TIMESPEC __time_to_timespec(int sign, double mseconds, double seconds, double minutes, double hours, double days) {
   185   IEC_TIMESPEC ts;
   210   IEC_TIMESPEC ts;
   186 
   211 
   187   /* sign is 1 for positive values, -1 for negative time... */
   212   // sign is 1 for positive values, -1 for negative time...
   188   long double total_sec = ((days*24 + hours)*60 + minutes)*60 + seconds + mseconds/1e3;
   213   long double total_sec = ((days*24 + hours)*60 + minutes)*60 + seconds + mseconds/1e3;
   189   if (sign >= 0) sign = 1; else sign = -1;
   214   if (sign >= 0) sign = 1; else sign = -1;
   190   ts.tv_sec = sign * (long int)total_sec;
   215   ts.tv_sec = sign * (long int)total_sec;
   191   ts.tv_nsec = sign * (long int)((total_sec - ts.tv_sec)*1e9);
   216   ts.tv_nsec = sign * (long int)((total_sec - ts.tv_sec)*1e9);
   192 
   217 
   193   return ts;
   218   return ts;
   194 }
   219 }
   195 
   220 */
   196 
   221 /* NOTE: Unfortunately older versions of ANSI C (e.g. C99) do not allow explicit identification of elements in initializers
       
   222  *         e.g.  {tv_sec = 1, tv_nsec = 300}
       
   223  *       They are therefore commented out. This however means that any change to the definition of IEC_TIMESPEC may require this
       
   224  *       macro to be updated too!
       
   225  */
       
   226 #define ld long double
       
   227 #define __time_to_timespec(sign,mseconds,seconds,minutes,hours,days) \
       
   228           ((IEC_TIMESPEC){\
       
   229               /*tv_sec  =*/ ((long int)   (((sign>=0)?1:-1)*((((ld)days*24 + (ld)hours)*60 + (ld)minutes)*60 + (ld)seconds + (ld)mseconds/1e3))), \
       
   230               /*tv_nsec =*/ ((long int)(( \
       
   231                             ((long double)(((sign>=0)?1:-1)*((((ld)days*24 + (ld)hours)*60 + (ld)minutes)*60 + (ld)seconds + (ld)mseconds/1e3))) - \
       
   232                             ((long int)   (((sign>=0)?1:-1)*((((ld)days*24 + (ld)hours)*60 + (ld)minutes)*60 + (ld)seconds + (ld)mseconds/1e3)))   \
       
   233                             )*1e9))\
       
   234         })
       
   235 #undef ld
       
   236 
       
   237 
       
   238 
       
   239 
       
   240 /* NOTE: The following function was turned into a macro, so it could be used to initialize the initial value of TOD (TIME_OF_DAY) variables */
       
   241 /* NOTE: many (but not all) of the same comments made regarding __time_to_timespec() are also valid here, so go and read those comments too!
       
   242 /*
   197 static inline IEC_TIMESPEC __tod_to_timespec(double seconds, double minutes, double hours) {
   243 static inline IEC_TIMESPEC __tod_to_timespec(double seconds, double minutes, double hours) {
   198   IEC_TIMESPEC ts;
   244   IEC_TIMESPEC ts;
   199 
   245 
   200   long double total_sec = (hours*60 + minutes)*60 + seconds;
   246   long double total_sec = (hours*60 + minutes)*60 + seconds;
   201   ts.tv_sec = (long int)total_sec;
   247   ts.tv_sec = (long int)total_sec;
   202   ts.tv_nsec = (long int)((total_sec - ts.tv_sec)*1e9);
   248   ts.tv_nsec = (long int)((total_sec - ts.tv_sec)*1e9);
   203 
   249 
   204   return ts;
   250   return ts;
   205 }
   251 }
       
   252 */
       
   253 #define ld long double
       
   254 #define __tod_to_timespec(seconds,minutes,hours) \
       
   255           ((IEC_TIMESPEC){\
       
   256               /*tv_sec  =*/ ((long int)   ((((ld)hours)*60 + (ld)minutes)*60 + (ld)seconds)), \
       
   257               /*tv_nsec =*/ ((long int)(( \
       
   258                             ((long double)((((ld)hours)*60 + (ld)minutes)*60 + (ld)seconds)) - \
       
   259                             ((long int)   ((((ld)hours)*60 + (ld)minutes)*60 + (ld)seconds))   \
       
   260                             )*1e9))\
       
   261         })
       
   262 #undef ld
       
   263 
   206 
   264 
   207 #define EPOCH_YEAR 1970
   265 #define EPOCH_YEAR 1970
   208 #define SECONDS_PER_MINUTE 60
   266 #define SECONDS_PER_MINUTE 60
   209 #define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE)
   267 #define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE)
   210 #define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR)
   268 #define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR)
   219 };
   277 };
   220 
   278 
   221 typedef struct {
   279 typedef struct {
   222 	int tm_sec;			/* Seconds.	[0-60] (1 leap second) */
   280 	int tm_sec;			/* Seconds.	[0-60] (1 leap second) */
   223 	int tm_min;			/* Minutes.	[0-59] */
   281 	int tm_min;			/* Minutes.	[0-59] */
   224 	int tm_hour;		/* Hours.	[0-23] */
   282 	int tm_hour;			/* Hours.	[0-23] */
   225 	int tm_day;			/* Day.		[1-31] */
   283 	int tm_day;			/* Day.		[1-31] */
   226 	int tm_mon;			/* Month.	[0-11] */
   284 	int tm_mon;			/* Month.	[0-11] */
   227 	int tm_year;		/* Year	*/
   285 	int tm_year;			/* Year	*/
   228 } tm;
   286 } tm;
   229 
   287 
   230 static inline tm convert_seconds_to_date_and_time(long int seconds) {
   288 static inline tm convert_seconds_to_date_and_time(long int seconds) {
   231   tm dt;
   289   tm dt;
   232   long int days, rem;
   290   long int days, rem;