# HG changeset patch # User mjsousa # Date 1404563970 -3600 # Node ID 1e749c7b70f82671153d396e84bb15cb56fa4279 # Parent a3f734a23566f942820678702a5cfcdd30b9e408 Fix time conversion function in std library, and change into macro (so it may be used in variable initialisations) diff -r a3f734a23566 -r 1e749c7b70f8 lib/iec_std_lib.h --- a/lib/iec_std_lib.h Sun Jun 01 08:59:57 2014 +0100 +++ b/lib/iec_std_lib.h Sat Jul 05 13:39:30 2014 +0100 @@ -180,11 +180,36 @@ /**********************************************/ /* Time conversion to/from timespec functions */ /**********************************************/ - +/* NOTE: The following function was turned into a macro, so it could be used to initialize the initial value of TIME variables. + * Since each macro parameter is evaluated several times, the macro may result in multiple function invocations if an expression + * containing a function invocation is passed as a parameter. However, currently matiec only uses this conversion macro with + * constant literals, so it is safe to change it into a macro. + */ +/* NOTE: I (Mario - msousa@fe.up.pt) believe that the following function contains a bug when handling negative times. + * The equivalent macro has this bug fixed. + * e.g.; + * T#3.8s + * using the function, will result in a timespec of 3.8s !!!: + * tv_sec = 4 <----- 1 * 3.8 is rounded up when converting a double to an int! + * tv_nsec = -200 000 000 <----- 1 * (3.8 - 4)*1e9 + * + * -T#3.8s + * using the function, will result in a timespec of -11.8s !!!: + * tv_sec = -4 <----- -1 * 3.8 is rounded down when converting a double to an int! + * tv_nsec = -7 800 000 000 <----- -1 * (3.8 - -4)*1e9 + */ +/* NOTE: Due to the fact that the C compiler may round a tv_sec number away from zero, + * the following macro may result in a timespec that is not normalized, i.e. with a tv_sec > 0, and a tv_nsec < 0 !!!! + * This is due to the rounding that C compiler applies when converting a (long double) to a (long int). + * To produce normalized timespec's we need to use floor(), but we cannot call any library functions since we want this macro to be + * useable as a variable initializer. + * VAR x : TIME = T#3.5h; END_VAR ---> IEC_TIME x = __time_to_timespec(1, 0, 0, 0, 3.5, 0); + */ +/* static inline IEC_TIMESPEC __time_to_timespec(int sign, double mseconds, double seconds, double minutes, double hours, double days) { IEC_TIMESPEC ts; - /* sign is 1 for positive values, -1 for negative time... */ + // sign is 1 for positive values, -1 for negative time... long double total_sec = ((days*24 + hours)*60 + minutes)*60 + seconds + mseconds/1e3; if (sign >= 0) sign = 1; else sign = -1; ts.tv_sec = sign * (long int)total_sec; @@ -192,8 +217,29 @@ return ts; } - - +*/ +/* NOTE: Unfortunately older versions of ANSI C (e.g. C99) do not allow explicit identification of elements in initializers + * e.g. {tv_sec = 1, tv_nsec = 300} + * They are therefore commented out. This however means that any change to the definition of IEC_TIMESPEC may require this + * macro to be updated too! + */ +#define ld long double +#define __time_to_timespec(sign,mseconds,seconds,minutes,hours,days) \ + ((IEC_TIMESPEC){\ + /*tv_sec =*/ ((long int) (((sign>=0)?1:-1)*((((ld)days*24 + (ld)hours)*60 + (ld)minutes)*60 + (ld)seconds + (ld)mseconds/1e3))), \ + /*tv_nsec =*/ ((long int)(( \ + ((long double)(((sign>=0)?1:-1)*((((ld)days*24 + (ld)hours)*60 + (ld)minutes)*60 + (ld)seconds + (ld)mseconds/1e3))) - \ + ((long int) (((sign>=0)?1:-1)*((((ld)days*24 + (ld)hours)*60 + (ld)minutes)*60 + (ld)seconds + (ld)mseconds/1e3))) \ + )*1e9))\ + }) +#undef ld + + + + +/* 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 */ +/* 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! +/* static inline IEC_TIMESPEC __tod_to_timespec(double seconds, double minutes, double hours) { IEC_TIMESPEC ts; @@ -203,6 +249,18 @@ return ts; } +*/ +#define ld long double +#define __tod_to_timespec(seconds,minutes,hours) \ + ((IEC_TIMESPEC){\ + /*tv_sec =*/ ((long int) ((((ld)hours)*60 + (ld)minutes)*60 + (ld)seconds)), \ + /*tv_nsec =*/ ((long int)(( \ + ((long double)((((ld)hours)*60 + (ld)minutes)*60 + (ld)seconds)) - \ + ((long int) ((((ld)hours)*60 + (ld)minutes)*60 + (ld)seconds)) \ + )*1e9))\ + }) +#undef ld + #define EPOCH_YEAR 1970 #define SECONDS_PER_MINUTE 60 @@ -221,10 +279,10 @@ typedef struct { int tm_sec; /* Seconds. [0-60] (1 leap second) */ int tm_min; /* Minutes. [0-59] */ - int tm_hour; /* Hours. [0-23] */ + int tm_hour; /* Hours. [0-23] */ int tm_day; /* Day. [1-31] */ int tm_mon; /* Month. [0-11] */ - int tm_year; /* Year */ + int tm_year; /* Year */ } tm; static inline tm convert_seconds_to_date_and_time(long int seconds) { diff -r a3f734a23566 -r 1e749c7b70f8 lib/iec_types.h --- a/lib/iec_types.h Sun Jun 01 08:59:57 2014 +0100 +++ b/lib/iec_types.h Sat Jul 05 13:39:30 2014 +0100 @@ -29,6 +29,11 @@ typedef float IEC_REAL; typedef double IEC_LREAL; +/* WARNING: When editing the definition of IEC_TIMESPEC, take note that + * if the order of the two elements 'tv_sec' and 'tv_nsec' is changed, then the macros + * __time_to_timespec() and __tod_to_timespec() will need to be changed accordingly. + * (these macros may be found in iec_std_lib.h) + */ typedef struct { long int tv_sec; /* Seconds. */ long int tv_nsec; /* Nanoseconds. */