etisserant@0: /* etisserant@0: * (c) 2003 Mario de Sousa etisserant@0: * etisserant@0: * Offered to the public under the terms of the GNU General Public License etisserant@0: * as published by the Free Software Foundation; either version 2 of the etisserant@0: * License, or (at your option) any later version. etisserant@0: * etisserant@0: * This program is distributed in the hope that it will be useful, but etisserant@0: * WITHOUT ANY WARRANTY; without even the implied warranty of etisserant@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General etisserant@0: * Public License for more details. etisserant@0: * etisserant@0: * This code is made available on the understanding that it will not be etisserant@0: * used in safety-critical situations without a full and competent review. etisserant@0: */ etisserant@0: etisserant@0: /* etisserant@0: * An IEC 61131-3 IL and ST compiler. etisserant@0: * etisserant@0: * Based on the etisserant@0: * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) etisserant@0: * etisserant@0: */ etisserant@0: etisserant@0: etisserant@0: /* etisserant@0: * Code to be included into the code generated by the 4th stage. etisserant@0: * etisserant@0: * This is part of the 4th stage that generates etisserant@0: * a c++ source program equivalent to the IL and ST etisserant@0: * code. etisserant@0: */ etisserant@0: etisserant@0: etisserant@0: #ifndef __PLCIEC_H etisserant@0: #define __PLCIEC_H etisserant@0: etisserant@0: //#include etisserant@0: #include "plc.h" etisserant@0: #include etisserant@0: #include etisserant@0: etisserant@0: etisserant@0: etisserant@0: /* function that generates an IEC runtime error */ etisserant@0: void IEC_error(void) { etisserant@0: /* TODO... */ etisserant@0: fprintf(stderr, "IEC 61131-3 runtime error.\n"); etisserant@0: exit(1); etisserant@0: } etisserant@0: etisserant@0: etisserant@0: etisserant@0: typedef bool BOOL; etisserant@0: #define TRUE true etisserant@0: #define FALSE false etisserant@0: etisserant@0: typedef i8 SINT; etisserant@0: typedef i16 INT; etisserant@0: typedef i32 DINT; etisserant@0: typedef i64 LINT; etisserant@0: etisserant@0: typedef u8 USINT; etisserant@0: typedef u16 UINT; etisserant@0: typedef u32 UDINT; etisserant@0: typedef u64 ULINT; etisserant@0: etisserant@0: typedef u8 BYTE; etisserant@0: typedef u16 WORD; etisserant@0: typedef u32 DWORD; etisserant@0: typedef u64 LWORD; etisserant@0: etisserant@0: typedef f32 REAL; etisserant@0: typedef f64 LREAL; etisserant@0: etisserant@0: etisserant@0: etisserant@0: etisserant@0: etisserant@0: etisserant@0: /*********************************************/ etisserant@0: /* TIME AND DATE data trypes */ etisserant@0: /*********************************************/ etisserant@0: etisserant@0: /* NOTE: All the time and date data types use a struct timespec etisserant@0: * internally to store the time and date. This is so as to ease all the etisserant@0: * operations (add, subtract, multiply and division) the standard defines etisserant@0: * on these same data types. etisserant@0: * However, in order to ease the implementation of the comparison operators etisserant@0: * (==, =>, <=, <, >, <>), the two elements in the timespec structure etisserant@0: * must be handled in such a way as to guarantee the following: etisserant@0: * - The stored time is always the result of the operation tv_sec + tv_nsec*1e-9 etisserant@0: * - tv_sec and tv_nsec will always have the same sign etisserant@0: * (i.e. either both positive or both negative) etisserant@0: * - tv_nsec always holds a value in the range ]-1, +1[ seconds. etisserant@0: * (note that -1 and +1 are excluded form the range) etisserant@0: */ etisserant@0: etisserant@0: etisserant@0: /* NOTE: According to the C++ standard, the result of the % and / operations is implementation dependent etisserant@0: * when the at least one of the operands is negative! However, whatever the result of the operations, we are etisserant@0: * guaranteed that (a/b)*b + (a%b) is always equal to a. etisserant@0: * This means that, even knowing that both tv_sec and tv_sec always have the same sign (we make it so this is true), etisserant@0: * we must still re-normailze the result for both the addition and subtraction operations! etisserant@0: */ etisserant@0: etisserant@0: static inline void __normalizesign_timespec (struct timespec *ts) { etisserant@0: if ((ts->tv_sec > 0) && (ts->tv_nsec < 0)) { etisserant@0: ts->tv_sec--; etisserant@0: ts->tv_nsec += 1000000000; etisserant@0: } etisserant@0: if ((ts->tv_sec < 0) && (ts->tv_nsec > 0)) { etisserant@0: ts->tv_sec++; etisserant@0: ts->tv_nsec -= 1000000000; etisserant@0: } etisserant@0: } etisserant@0: etisserant@0: etisserant@0: static inline struct timespec __add_timespec (const struct timespec &t1, const struct timespec &t2) { etisserant@0: /* NOTE the following sum works correctly because a long can hold a litle more than 2 seconds expressed in nano seconds! */ etisserant@0: long nsec; etisserant@0: nsec = t1.tv_nsec + t2.tv_nsec; etisserant@0: struct timespec ts; etisserant@0: ts.tv_sec = t1.tv_sec + t2.tv_sec + (nsec / 1000000000); etisserant@0: ts.tv_nsec = nsec % 1000000000; etisserant@0: etisserant@0: __normalizesign_timespec(&ts); etisserant@0: etisserant@0: return ts; etisserant@0: } etisserant@0: etisserant@0: etisserant@0: static inline struct timespec __sub_timespec (const struct timespec &t1, const struct timespec &t2) { etisserant@0: /* NOTE the following subtraction works correctly because a long can hold a litle more than 2 seconds expressed in nano seconds! */ etisserant@0: long nsec = t1.tv_nsec - t2.tv_nsec; etisserant@0: struct timespec ts; etisserant@0: ts.tv_sec = t1.tv_sec - t2.tv_sec + nsec / 1000000000; etisserant@0: ts.tv_nsec = nsec % 1000000000; etisserant@0: etisserant@0: __normalizesign_timespec(&ts); etisserant@0: etisserant@0: return ts; etisserant@0: } etisserant@0: etisserant@0: etisserant@0: static inline struct timespec __mul_timespec (const struct timespec &t1, const long double value) { etisserant@0: #if 0 etisserant@0: /* A simple implementation that risks reducing the precision of the TIME value */ etisserant@0: long double sec_d1 = t1.tv_nsec / (long double)1e9 * value; etisserant@0: long double sec_d2 = t1.tv_sec * value; etisserant@0: long int sec = (long int)truncl(sec_d1 + sec_d2); etisserant@0: struct timespec ts; etisserant@0: ts.tv_sec = sec; etisserant@0: ts.tv_nsec = (long int)((sec_d1 + sec_d2 - sec)*1e9); etisserant@0: return ts; etisserant@0: #else etisserant@0: /* A more robust implementation that reduces the loss of precision of the TIME value */ etisserant@0: /* NOTE: The following assumes that the value stored in tv_nsec is never larger than 1 sec etisserant@0: * and is also based on the fact that tv_nsec can safely store slighlty more thanb 2 sec. etisserant@0: */ etisserant@0: long double sec_d1 = t1.tv_nsec / (long double)1e9 * value; etisserant@0: long double sec_d2 = t1.tv_sec * value; etisserant@0: long int sec1 = (long int)sec_d1; etisserant@0: long int sec2 = (long int)sec_d2; etisserant@0: struct timespec ts; etisserant@0: ts.tv_sec = sec1 + sec2; etisserant@0: ts.tv_nsec = (long int)(((sec_d1 - sec1) + (sec_d2 - sec2))*1e9); etisserant@0: /* re-normalize the value of tv_nsec */ etisserant@0: /* i.e. guarantee that it falls in the range ]-1, +1[ seconds. */ etisserant@0: if (ts.tv_nsec >= 1000000000) {ts.tv_nsec -= 1000000000; ts.tv_sec += 1;} etisserant@0: if (ts.tv_nsec <= -1000000000) {ts.tv_nsec += 1000000000; ts.tv_sec -= 1;} etisserant@0: /* We don't need to re-normalize the sign, since we are guaranteed that tv_sec and tv_nsec etisserant@0: * will still both have the same sign after being multiplied by the same value. etisserant@0: */ etisserant@0: return ts; etisserant@0: #endif etisserant@0: } etisserant@0: etisserant@0: /* Helper Macro for the comparison operators... */ etisserant@0: #define __compare_timespec(CMP, t1, t2) ((t1.tv_sec == t2.tv_sec)? t1.tv_nsec CMP t2.tv_nsec : t1.tv_sec CMP t2.tv_sec) etisserant@0: etisserant@0: etisserant@0: etisserant@0: /* Some necessary forward declarations... */ lbessard@16: /* etisserant@0: class TIME; etisserant@0: class TOD; etisserant@0: class DT; etisserant@0: class DATE; etisserant@0: etisserant@0: typedef struct timespec __timebase_t; lbessard@16: */ lbessard@16: lbessard@16: typedef struct TIME timespec; lbessard@16: typedef struct TOD timespec; lbessard@16: typedef struct DT timespec; lbessard@16: typedef struct DATE timespec; lbessard@16: lbessard@16: static inline struct timespec __time_to_timespec(int sign, double mseconds, double seconds, double minutes, double hours, double days) { lbessard@16: struct timespec ts; lbessard@16: lbessard@16: /* sign is 1 for positive values, -1 for negative time... */ lbessard@16: long double total_sec = ((days*24 + hours)*60 + minutes)*60 + seconds + mseconds/1e3; lbessard@16: if (sign >= 0) sign = 1; else sign = -1; lbessard@16: ts.tv_sec = sign * (long int)total_sec; lbessard@16: ts.tv_nsec = sign * (long int)((total_sec - ts.tv_sec)*1e9); lbessard@16: lbessard@16: return ts; lbessard@16: } lbessard@16: lbessard@16: lbessard@16: static inline struct timespec __tod_to_timespec(double seconds, double minutes, double hours) { lbessard@16: struct timespec ts; lbessard@16: lbessard@16: long double total_sec = (hours*60 + minutes)*60 + seconds; lbessard@16: ts.tv_sec = (long int)total_sec; lbessard@16: ts.tv_nsec = (long int)((total_sec - ts.tv_sec)*1e9); lbessard@16: lbessard@16: return ts; lbessard@16: } lbessard@16: lbessard@16: static inline struct timespec __date_to_timespec(int day, int month, int year) { lbessard@16: struct timespec ts; lbessard@16: struct tm broken_down_time; lbessard@16: lbessard@16: broken_down_time.tm_sec = 0; lbessard@16: broken_down_time.tm_min = 0; lbessard@16: broken_down_time.tm_hour = 0; lbessard@16: broken_down_time.tm_mday = day; /* day of month, from 1 to 31 */ lbessard@16: broken_down_time.tm_mon = month - 1; /* month since January, in the range 0 to 11 */ lbessard@16: broken_down_time.tm_year = year - 1900; /* number of years since 1900 */ lbessard@16: lbessard@16: time_t epoch_seconds = mktime(&broken_down_time); /* determine number of seconds since the epoch, i.e. Jan 1st 1970 */ lbessard@16: lbessard@16: if ((time_t)(-1) == epoch_seconds) lbessard@16: IEC_error(); lbessard@16: lbessard@16: ts.tv_sec = epoch_seconds; lbessard@16: ts.tv_nsec = 0; lbessard@16: lbessard@16: return ts; lbessard@16: } lbessard@16: lbessard@16: static inline struct timespec __dt_to_timespec(double seconds, double minutes, double hours, int day, int month, int year) { lbessard@16: struct timespec ts; lbessard@16: lbessard@16: long double total_sec = (hours*60 + minutes)*60 + seconds; lbessard@16: ts.tv_sec = (long int)total_sec; lbessard@16: ts.tv_nsec = (long int)((total_sec - ts.tv_sec)*1e9); lbessard@16: lbessard@16: struct tm broken_down_time; lbessard@16: broken_down_time.tm_sec = 0; lbessard@16: broken_down_time.tm_min = 0; lbessard@16: broken_down_time.tm_hour = 0; lbessard@16: broken_down_time.tm_mday = day; /* day of month, from 1 to 31 */ lbessard@16: broken_down_time.tm_mon = month - 1; /* month since January, in the range 0 to 11 */ lbessard@16: broken_down_time.tm_year = year - 1900; /* number of years since 1900 */ lbessard@16: lbessard@16: time_t epoch_seconds = mktime(&broken_down_time); /* determine number of seconds since the epoch, i.e. Jan 1st 1970 */ lbessard@16: if ((time_t)(-1) == epoch_seconds) lbessard@16: IEC_error(); lbessard@16: lbessard@16: ts.tv_sec += epoch_seconds; lbessard@16: if (ts.tv_sec < epoch_seconds) lbessard@16: /* since the TOD is always positive, if the above happens then we had an overflow */ lbessard@16: IEC_error(); lbessard@16: lbessard@16: return ts; lbessard@16: } lbessard@16: lbessard@16: lbessard@16: lbessard@16: lbessard@16: lbessard@16: lbessard@16: lbessard@16: lbessard@16: #ifdef 0 etisserant@0: class TIME{ etisserant@0: private: etisserant@0: /* private variable that contains the value of time. */ etisserant@0: /* NOTE: The stored value is _always_ (time.tv_sec + time.tv_nsec), etisserant@0: no matter whether tv_sec is positive or negative, or tv_nsec is positive or negative. etisserant@0: */ etisserant@0: __timebase_t time; etisserant@0: etisserant@0: public: etisserant@0: /* conversion to __timebase_t */ etisserant@0: operator __timebase_t(void) {return time;} etisserant@0: etisserant@0: /* constructors... */ etisserant@0: TIME (void) {time.tv_sec = 0; time.tv_nsec = 0;} etisserant@0: TIME (__timebase_t time) {this->time = time;} etisserant@0: TIME (const TIME &time) {this->time = time.time;} /* copy constructor */ etisserant@0: TIME(int sign, double mseconds, double seconds=0, double minutes=0, double hours=0, double days=0) { etisserant@0: /* sign is 1 for positive values, -1 for negative time... */ etisserant@0: long double total_sec = ((days*24 + hours)*60 + minutes)*60 + seconds + mseconds/1e3; etisserant@0: if (sign >= 0) sign = 1; else sign = -1; etisserant@0: time.tv_sec = sign * (long int)total_sec; etisserant@0: time.tv_nsec = sign * (long int)((total_sec - time.tv_sec)*1e9); etisserant@0: } etisserant@0: etisserant@0: /* used in plciec.cc to set the value of the __CURRENT_TIME variable without having to call a etisserant@0: * TIME((__timebase_t time) constructor followed by the copy constructor. etisserant@0: */ etisserant@0: void operator= (__timebase_t time) {this->time = time;} etisserant@0: etisserant@0: /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ etisserant@0: TIME operator+ (const TIME &time) {return TIME(__add_timespec(this->time, time.time));} etisserant@0: TIME operator- (const TIME &time) {return TIME(__sub_timespec(this->time, time.time));} etisserant@0: etisserant@0: friend TOD operator+ (const TIME &time, const TOD &tod); etisserant@0: friend TOD operator+ (const TOD &tod, const TIME &time); etisserant@0: friend TOD operator- (const TOD &tod, const TIME &time); etisserant@0: etisserant@0: friend DT operator+ (const TIME &time, const DT &dt); etisserant@0: friend DT operator+ (const DT &dt, const TIME &time); etisserant@0: friend DT operator- (const DT &dt, const TIME &time); etisserant@0: etisserant@0: friend TIME operator* (const TIME &time, const long double value); etisserant@0: friend TIME operator* (const long double value, const TIME &time); etisserant@0: friend TIME operator/ (const TIME &time, const long double value); etisserant@0: etisserant@0: /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ etisserant@0: BOOL operator> (const TIME &time) {return __compare_timespec(>, this->time, time.time);} etisserant@0: BOOL operator>= (const TIME &time) {return __compare_timespec(>=, this->time, time.time);} etisserant@0: BOOL operator< (const TIME &time) {return __compare_timespec(<, this->time, time.time);} etisserant@0: BOOL operator<= (const TIME &time) {return __compare_timespec(<=, this->time, time.time);} etisserant@0: BOOL operator== (const TIME &time) {return __compare_timespec(==, this->time, time.time);} etisserant@0: BOOL operator!= (const TIME &time) {return !__compare_timespec(==, this->time, time.time);} etisserant@0: }; etisserant@0: etisserant@0: etisserant@0: etisserant@0: /* Time of Day */ etisserant@0: class TOD { etisserant@0: private: etisserant@0: __timebase_t time; etisserant@0: etisserant@0: public: etisserant@0: /* conversion to __timebase_t */ etisserant@0: operator __timebase_t(void) {return time;} etisserant@0: etisserant@0: /* constructors... */ etisserant@0: TOD (void) {time.tv_sec = 0; time.tv_nsec = 0;} etisserant@0: TOD (__timebase_t time) {this->time = time;} etisserant@0: TOD (const TOD &tod) {this->time = tod.time;} /* copy constructor */ etisserant@0: TOD (double seconds, double minutes=0, double hours=0) { etisserant@0: long double total_sec = (hours*60 + minutes)*60 + seconds; etisserant@0: time.tv_sec = (long int)total_sec; etisserant@0: time.tv_nsec = (long int)((total_sec - time.tv_sec)*1e9); etisserant@0: } etisserant@0: etisserant@0: /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ etisserant@0: TIME operator- (const TOD &tod) {return TIME(__sub_timespec(this->time, tod.time));} etisserant@0: etisserant@0: friend TOD operator+ (const TIME &time, const TOD &tod); etisserant@0: friend TOD operator+ (const TOD &tod, const TIME &time); etisserant@0: friend TOD operator- (const TOD &tod, const TIME &time); etisserant@0: etisserant@0: /* The following operation is not in the standard, etisserant@0: * but will ease the implementation of the default function CONCAT_DATE_TOD etisserant@0: */ etisserant@0: friend DT operator+ (const DATE &date, const TOD &tod); etisserant@0: friend DT operator+ (const TOD &tod, const DATE &date); etisserant@0: etisserant@0: /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ etisserant@0: BOOL operator> (const TOD &tod) {return __compare_timespec(>, this->time, tod.time);} etisserant@0: BOOL operator>= (const TOD &tod) {return __compare_timespec(>=, this->time, tod.time);} etisserant@0: BOOL operator< (const TOD &tod) {return __compare_timespec(<, this->time, tod.time);} etisserant@0: BOOL operator<= (const TOD &tod) {return __compare_timespec(<=, this->time, tod.time);} etisserant@0: BOOL operator== (const TOD &tod) {return __compare_timespec(==, this->time, tod.time);} etisserant@0: BOOL operator!= (const TOD &tod) {return !__compare_timespec(==, this->time, tod.time);} etisserant@0: }; etisserant@0: etisserant@0: etisserant@0: etisserant@0: //typedef DATE; etisserant@0: class DATE { etisserant@0: private: etisserant@0: __timebase_t time; etisserant@0: etisserant@0: public: etisserant@0: /* conversion to __timebase_t */ etisserant@0: operator __timebase_t(void) {return time;} etisserant@0: etisserant@0: /* constructors... */ etisserant@0: DATE (void) {time.tv_sec = 0; time.tv_nsec = 0;} etisserant@0: DATE (__timebase_t time) {this->time = time;} etisserant@0: DATE (const DATE &date) {this->time = date.time;} /* copy constructor */ etisserant@0: DATE (int day, int month, int year) { etisserant@0: struct tm broken_down_time; etisserant@0: etisserant@0: broken_down_time.tm_sec = 0; etisserant@0: broken_down_time.tm_min = 0; etisserant@0: broken_down_time.tm_hour = 0; etisserant@0: broken_down_time.tm_mday = day; /* day of month, from 1 to 31 */ etisserant@0: broken_down_time.tm_mon = month - 1; /* month since January, in the range 0 to 11 */ etisserant@0: broken_down_time.tm_year = year - 1900; /* number of years since 1900 */ etisserant@0: etisserant@0: time_t epoch_seconds = mktime(&broken_down_time); /* determine number of seconds since the epoch, i.e. Jan 1st 1970 */ etisserant@0: etisserant@0: if ((time_t)(-1) == epoch_seconds) etisserant@0: IEC_error(); etisserant@0: etisserant@0: time.tv_sec = epoch_seconds; etisserant@0: time.tv_nsec = 0; etisserant@0: } etisserant@0: etisserant@0: /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ etisserant@0: TIME operator- (const DATE &date) {return TIME(__sub_timespec(this->time, date.time));} etisserant@0: /* The following operation is not in the standard, etisserant@0: * but will ease the implementation of the default function CONCAT_DATE_TOD etisserant@0: */ etisserant@0: friend DT operator+ (const DATE &date, const TOD &tod); etisserant@0: friend DT operator+ (const TOD &tod, const DATE &date); etisserant@0: etisserant@0: /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ etisserant@0: BOOL operator> (const DATE &date) {return __compare_timespec(>, this->time, date.time);} etisserant@0: BOOL operator>= (const DATE &date) {return __compare_timespec(>=, this->time, date.time);} etisserant@0: BOOL operator< (const DATE &date) {return __compare_timespec(<, this->time, date.time);} etisserant@0: BOOL operator<= (const DATE &date) {return __compare_timespec(<=, this->time, date.time);} etisserant@0: BOOL operator== (const DATE &date) {return __compare_timespec(==, this->time, date.time);} etisserant@0: BOOL operator!= (const DATE &date) {return !__compare_timespec(==, this->time, date.time);} etisserant@0: }; etisserant@0: etisserant@0: etisserant@0: etisserant@0: etisserant@0: etisserant@0: class DT { etisserant@0: private: etisserant@0: __timebase_t time; etisserant@0: etisserant@0: public: etisserant@0: /* conversion to __timebase_t */ etisserant@0: operator __timebase_t(void) {return time;} etisserant@0: etisserant@0: /* constructors... */ etisserant@0: DT (void) {time.tv_sec = 0; time.tv_nsec = 0;} etisserant@0: DT (__timebase_t time) {this->time = time;} etisserant@0: DT (const DT &dt) {this->time = dt.time;} /* copy constructor */ etisserant@0: DT (double seconds, double minutes, double hours, int day, int month, int year) { etisserant@0: long double total_sec = (hours*60 + minutes)*60 + seconds; etisserant@0: time.tv_sec = (long int)total_sec; etisserant@0: time.tv_nsec = (long int)((total_sec - time.tv_sec)*1e9); etisserant@0: etisserant@0: struct tm broken_down_time; etisserant@0: broken_down_time.tm_sec = 0; etisserant@0: broken_down_time.tm_min = 0; etisserant@0: broken_down_time.tm_hour = 0; etisserant@0: broken_down_time.tm_mday = day; /* day of month, from 1 to 31 */ etisserant@0: broken_down_time.tm_mon = month - 1; /* month since January, in the range 0 to 11 */ etisserant@0: broken_down_time.tm_year = year - 1900; /* number of years since 1900 */ etisserant@0: etisserant@0: time_t epoch_seconds = mktime(&broken_down_time); /* determine number of seconds since the epoch, i.e. Jan 1st 1970 */ etisserant@0: if ((time_t)(-1) == epoch_seconds) etisserant@0: IEC_error(); etisserant@0: etisserant@0: time.tv_sec += epoch_seconds; etisserant@0: if (time.tv_sec < epoch_seconds) etisserant@0: /* since the TOD is always positive, if the above happens then we had an overflow */ etisserant@0: IEC_error(); etisserant@0: } etisserant@0: etisserant@0: /* Helpers to conversion operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ etisserant@0: DATE __to_DATE(void) { etisserant@0: #if 0 etisserant@0: /* slow version */ etisserant@0: struct tm broken_down_time; etisserant@0: time_t seconds = time.tv_sec; etisserant@0: if (NULL == gmtime_r(seconds, &broken_down_time)) /* get the UTC (GMT) broken down time */ etisserant@0: IEC_error(); etisserant@0: return DATE(broken_down_time.tm_mday, broken_down_time.tm_mon, broken_down_time.tm_year); etisserant@0: #else etisserant@0: /* Faster version, based on the fact that the date will always be a multiple of 60*60*24 seconds, etisserant@0: * and that the value of tv_nsec falls in the range ]-1, +1[ etisserant@0: */ etisserant@0: /* The above is true since the Unix function mktime() seems to ignore all leap seconds! */ etisserant@0: struct timespec date_time = {time.tv_sec - (time.tv_sec % (24*60*60)), 0}; etisserant@0: return DATE(date_time); etisserant@0: #endif etisserant@0: } etisserant@0: etisserant@0: TOD __to_TOD(void) { etisserant@0: #if 0 etisserant@0: /* slow version */ etisserant@0: struct tm broken_down_time; etisserant@0: time_t seconds = time.tv_sec; etisserant@0: if (NULL == gmtime_r(seconds, &broken_down_time)) /* get the UTC (GMT) broken down time */ etisserant@0: IEC_error(); etisserant@0: return TOD(broken_down_time.tm_sec, broken_down_time.tm_min, broken_down_time.tm_hour); etisserant@0: #else etisserant@0: /* Faster version, based on the fact that the date will always be a multiple of 60*60*24 seconds etisserant@0: * and that the value of tv_nsec falls in the range ]-1, +1[ etisserant@0: */ etisserant@0: /* The above is true since the Unix function mktime() seems to ignore all leap seconds! */ etisserant@0: struct timespec time_time = {time.tv_sec % (24*60*60), time.tv_nsec}; etisserant@0: return TOD(time_time); etisserant@0: #endif etisserant@0: } etisserant@0: etisserant@0: /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ etisserant@0: TIME operator- (const DT &dt) {return TIME(__sub_timespec(this->time, dt.time));} etisserant@0: etisserant@0: friend DT operator+ (const TIME &time, const DT &dt); etisserant@0: friend DT operator+ (const DT &dt, const TIME &time); etisserant@0: friend DT operator- (const DT &dt, const TIME &time); etisserant@0: etisserant@0: /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ etisserant@0: BOOL operator> (const DT &dt) {return __compare_timespec(>, this->time, dt.time);} etisserant@0: BOOL operator>= (const DT &dt) {return __compare_timespec(>=, this->time, dt.time);} etisserant@0: BOOL operator< (const DT &dt) {return __compare_timespec(<, this->time, dt.time);} etisserant@0: BOOL operator<= (const DT &dt) {return __compare_timespec(<=, this->time, dt.time);} etisserant@0: BOOL operator== (const DT &dt) {return __compare_timespec(==, this->time, dt.time);} etisserant@0: BOOL operator!= (const DT &dt) {return !__compare_timespec(==, this->time, dt.time);} etisserant@0: }; etisserant@0: etisserant@0: etisserant@0: /* The operations on time and data types... */ etisserant@0: TOD operator+ (const TIME &time, const TOD &tod) {return TOD(__add_timespec(tod.time, time.time));}; etisserant@0: TOD operator+ (const TOD &tod, const TIME &time) {return TOD(__add_timespec(tod.time, time.time));}; etisserant@0: TOD operator- (const TOD &tod, const TIME &time) {return TOD(__sub_timespec(tod.time, time.time));}; etisserant@0: etisserant@0: DT operator+ (const TIME &time, const DT &dt) {return DT(__add_timespec(dt.time, time.time));}; etisserant@0: DT operator+ (const DT &dt, const TIME &time) {return DT(__add_timespec(dt.time, time.time));}; etisserant@0: DT operator- (const DT &dt, const TIME &time) {return DT(__sub_timespec(dt.time, time.time));}; etisserant@0: etisserant@0: TIME operator* (const TIME &time, const long double value) {return TIME(__mul_timespec(time.time, value));} etisserant@0: TIME operator* (const long double value, const TIME &time) {return TIME(__mul_timespec(time.time, value));} etisserant@0: TIME operator/ (const TIME &time, const long double value) {return TIME(__mul_timespec(time.time, 1.0/value));} etisserant@0: etisserant@0: /* The following operation is not in the standard, etisserant@0: * but will ease the implementation of the default function CONCAT_DATE_TOD etisserant@0: */ etisserant@0: DT operator+ (const DATE &date, const TOD &tod) {return DT(__add_timespec(date.time, tod.time));}; etisserant@0: DT operator+ (const TOD &tod, const DATE &date) {return DT(__add_timespec(date.time, tod.time));}; etisserant@0: lbessard@16: #endif etisserant@0: etisserant@0: /* global variable that will be used to implement the timers TON, TOFF and TP */ etisserant@0: extern TIME __CURRENT_TIME; etisserant@0: etisserant@0: etisserant@0: etisserant@0: //typedef STRING; etisserant@0: //typedef WSTRING; etisserant@0: etisserant@0: etisserant@0: etisserant@0: typedef union __IL_DEFVAR_T { etisserant@0: BOOL BOOLvar; etisserant@0: etisserant@0: SINT SINTvar; etisserant@0: INT INTvar; etisserant@0: DINT DINTvar; etisserant@0: LINT LINTvar; etisserant@0: etisserant@0: USINT USINTvar; etisserant@0: UINT UINTvar; etisserant@0: UDINT UDINTvar; etisserant@0: ULINT ULINTvar; etisserant@0: etisserant@0: BYTE BYTEvar; etisserant@0: WORD WORDvar; etisserant@0: DWORD DWORDvar; etisserant@0: LWORD LWORDvar; etisserant@0: etisserant@0: REAL REALvar; etisserant@0: LREAL LREALvar; etisserant@0: etisserant@0: /* NOTE: since the TIME, DATE, ... classes all have constructors, etisserant@0: * C++ does not allow them to be used as members of a union. etisserant@0: * The workaround is to use a base data type (in this case __timebase_t) that etisserant@0: * contains all the internal data these classes require, and then add an operator etisserant@0: * member function to each class that allows it to be converted to that same base data type, etisserant@0: * acompanied by a constructor using that data type. etisserant@0: */ lbessard@16: etisserant@0: TIME TIMEvar; etisserant@0: TOD TODvar; etisserant@0: DT DTvar; etisserant@0: DATE DATEvar; lbessard@16: lbessard@16: /* etisserant@0: __timebase_t TIMEvar; etisserant@0: __timebase_t TODvar; etisserant@0: __timebase_t DTvar; etisserant@0: __timebase_t DATEvar; lbessard@16: */ etisserant@0: } __IL_DEFVAR_T; etisserant@0: /*TODO TODO TODO TODO TODO TODO TODO TODO TODO etisserant@0: * How do we add support for the possibility of storing etisserant@0: * data values of derived data types into the default register, etisserant@0: * to be later used for calling functions, stroing in another etisserant@0: * variable, etc...? etisserant@0: * etisserant@0: * For example: etisserant@0: * TYPE etisserant@0: * point_t : STRUCT etisserant@0: * x : INT; etisserant@0: * y : INT; etisserant@0: * END_STRUCT; etisserant@0: * END_TYPE etisserant@0: * etisserant@0: * VAR p1, p2, p3 : point_t; etisserant@0: * etisserant@0: * LD p1 etisserant@0: * ST p2 etisserant@0: * etisserant@0: * etisserant@0: * We could do it with a pointer to void, that would contain not etisserant@0: * the value itself, but rather the address in which the value etisserant@0: * is currently stored. etisserant@0: * For example, we could add a etisserant@0: * void *generic_ptr etisserant@0: * to this union, and then have the above LD and ST instructions etisserant@0: * converted to: etisserant@0: * __IL_DEFVAR.generic_ptr = (void *)(&p1); etisserant@0: * p2 = *((point_t *)__IL_DEFVAR.generic_ptr); etisserant@0: * etisserant@0: * Unfortunately the above will only work as long as the p1 variable etisserant@0: * does not get a chance to change its value before the default register etisserant@0: * gets loaded with something esle (and therefore the value is no etisserant@0: * longer needed). etisserant@0: * Additionally, a scenario where the value of p1 may change before the etisserant@0: * default register gets a new value is if p1 is used in a function block etisserant@0: * call for an output parameter! etisserant@0: * For example: etisserant@0: * etisserant@0: * LD p1 etisserant@0: * CAL funcblock( etisserant@0: * param1 => p1 etisserant@0: * ) etisserant@0: * ST p2 etisserant@0: * etisserant@0: * In the above scenario, p1 gets a new value when the function block etisserant@0: * funcblock is called. When we get to copy the default register to etisserant@0: * p2, we will no longer be copying the value that got stored in the default etisserant@0: * register when we did 'LD p1', but rather the value returned by the etisserant@0: * function block call!!! etisserant@0: * etisserant@0: * How the do we implement this??? etisserant@0: * We will probably need to declare a default variable of the correct data etisserant@0: * type whenever we get these values stored to the default register. etisserant@0: * For example etisserant@0: * LD p1 etisserant@0: * ST p2 etisserant@0: * etisserant@0: * would be converted to: etisserant@0: * union { etisserant@0: * point_tvar point_t; etisserant@0: * } __IL_DEFVAR_special ; etisserant@0: * etisserant@0: * __IL_DEFVAR_special.point_tvar = p1; etisserant@0: * p2 = __IL_DEFVAR_special.point_tvar; etisserant@0: * etisserant@0: * The above requires that we iterate through the whole Instruction list etisserant@0: * before we start the conversion, in order to first determine if we need etisserant@0: * to declare that new variable for the default register. etisserant@0: * etisserant@0: * Since we have to do this, it would probaly be a better idea to simply etisserant@0: * do away with the __IL_DEFVAR_T data type we declare here, and etisserant@0: * declare the __IL_DEFVAR at the begining of each IL code segment etisserant@0: * with all the data types that get used in that segment! etisserant@0: */ etisserant@0: etisserant@0: etisserant@0: /* Names start with double underscore so as not to clash with etisserant@0: * names in ST or IL source code! Names including a double underscore are etisserant@0: * ilegal under IL and ST! etisserant@0: */ etisserant@0: etisserant@0: /* This is an abstract base class, that cannot be instantiated... */ etisserant@0: template class __ext_ref_c { etisserant@0: public: etisserant@0: virtual void operator= (value_type value) = 0; etisserant@0: virtual operator value_type(void) = 0; etisserant@0: }; etisserant@0: etisserant@0: etisserant@0: etisserant@0: etisserant@0: etisserant@0: etisserant@0: /* Names start with double underscore so as not to clash with etisserant@0: * names in ST or IL source code! Names including a double underscore are etisserant@0: * ilegal under IL and ST! etisserant@0: */ etisserant@0: template class __ext_element_c etisserant@0: : public __ext_ref_c { etisserant@0: //{ etisserant@0: etisserant@0: private: etisserant@0: value_type value; etisserant@0: etisserant@0: public: etisserant@0: virtual void operator= (value_type value) { etisserant@0: this->value = value; etisserant@0: } etisserant@0: etisserant@0: virtual operator value_type(void) { etisserant@0: return value; etisserant@0: } etisserant@0: etisserant@0: __ext_element_c(void) {} etisserant@0: etisserant@0: __ext_element_c(value_type value) { etisserant@0: this->value = value; etisserant@0: } etisserant@0: }; etisserant@0: etisserant@0: etisserant@0: /* Names start with double underscore so as not to clash with etisserant@0: * names in ST or IL source code! Names including a double underscore are etisserant@0: * ilegal under IL and ST! etisserant@0: */ etisserant@0: template class __plc_pt_c etisserant@0: : public __ext_ref_c { etisserant@0: etisserant@0: private: etisserant@0: plc_pt_t plc_pt; etisserant@0: bool valid_plc_pt; etisserant@0: etisserant@0: private: etisserant@0: void init_name(const char *pt_name) { etisserant@0: /* assume error! */ etisserant@0: valid_plc_pt = false; etisserant@0: plc_pt = plc_pt_by_name(pt_name); etisserant@0: if (plc_pt.valid == 0) { etisserant@0: plc_pt = plc_pt_null(); etisserant@0: return; etisserant@0: } etisserant@0: /* We can't have this check here, otherwise the boolean variables won't work correctly, etisserant@0: * since MatPLC uses 1 bit for boolean variables, whereas g++ uses 8 bits. etisserant@0: */ etisserant@0: /* etisserant@0: if (plc_pt_len(plc_pt) != size) { etisserant@0: plc_pt = plc_pt_null(); etisserant@0: return; etisserant@0: } etisserant@0: */ etisserant@0: valid_plc_pt = true; etisserant@0: } etisserant@0: etisserant@0: public: etisserant@0: virtual void operator= (value_type value) { etisserant@0: plc_set(plc_pt, *((u32 *)&value)); etisserant@0: } etisserant@0: etisserant@0: virtual operator value_type(void) { etisserant@0: u32 tmp_val = plc_get(plc_pt); etisserant@0: return *((value_type *)&tmp_val); etisserant@0: } etisserant@0: etisserant@0: __plc_pt_c(const char *pt_name) { etisserant@0: init_name(pt_name); etisserant@0: } etisserant@0: etisserant@0: __plc_pt_c(const char *pt_name, value_type init_value) { etisserant@0: init_name(pt_name); etisserant@0: *this = init_value; etisserant@0: } etisserant@0: etisserant@0: bool valid(void) {return valid_plc_pt;} etisserant@0: }; etisserant@0: etisserant@0: etisserant@0: etisserant@0: #define DEFAULT_MODULE_NAME "iec" etisserant@0: etisserant@0: etisserant@0: etisserant@0: #endif /* __PLCIEC_H */ etisserant@0: etisserant@0: