In VARIABLES.CSV, RETAIN variable were only flagged as such for GLOBALS, now local POU variable shoud be as well.
/*
* copyright 2008 Edouard TISSERANT
* copyright 2011 Mario de Sousa (msousa@fe.up.pt)
*
* Offered to the public under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* This code is made available on the understanding that it will not be
* used in safety-critical situations without a full and competent review.
*/
/****
* IEC 61131-3 standard function library
*/
/* NOTE: This file is full of (what may seem at first) very strange macros.
* If you want to know what all these strange macros are doing,
* just parse this file through a C preprocessor (e.g. cpp),
* and analyse the output!
* $gcc -E iec_std_lib.h
*/
#ifndef _IEC_STD_LIB_H
#define _IEC_STD_LIB_H
#include <limits.h>
#include <float.h>
#include <math.h>
#include <stdint.h>
#include <ctype.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifdef DEBUG_IEC
#define DBG(...) printf(__VA_ARGS__);
#define DBG_TYPE(TYPENAME, name) __print_##TYPENAME(name);
#else
#define DBG(...)
#define DBG_TYPE(TYPENAME, name)
#endif
/*
* Include type defs.
*/
#include "iec_types_all.h"
extern TIME __CURRENT_TIME;
extern BOOL __DEBUG;
/* TODO
typedef struct {
__strlen_t len;
u_int16_t body[STR_MAX_LEN];
} WSTRING;
*/
/*
# if __WORDSIZE == 64
#define __32b_sufix
#define __64b_sufix L
#else
#define __32b_sufix L
#define __64b_sufix LL
#endif
*/
# if __WORDSIZE == 64
#define __32b_sufix
#define __64b_sufix L
#else
#define __32b_sufix L
/* changed this from LL to L temporarily. It was causing a bug when compiling resulting code with gcc.
* I have other things to worry about at the moment..
*/
#define __64b_sufix L
#endif
#define __lit(type,value,...) (type)value##__VA_ARGS__
// Keep this macro expention step to let sfx(__VA_ARGS__) change into L or LL
#define __literal(type,value,...) __lit(type,value,__VA_ARGS__)
#define __BOOL_LITERAL(value) __literal(BOOL,value)
#define __SINT_LITERAL(value) __literal(SINT,value)
#define __INT_LITERAL(value) __literal(INT,value)
#define __DINT_LITERAL(value) __literal(DINT,value,__32b_sufix)
#define __LINT_LITERAL(value) __literal(LINT,value,__64b_sufix)
#define __USINT_LITERAL(value) __literal(USINT,value)
#define __UINT_LITERAL(value) __literal(UINT,value)
#define __UDINT_LITERAL(value) __literal(UDINT,value,__32b_sufix)
#define __ULINT_LITERAL(value) __literal(ULINT,value,__64b_sufix)
#define __REAL_LITERAL(value) __literal(REAL,value,__32b_sufix)
#define __LREAL_LITERAL(value) __literal(LREAL,value,__64b_sufix)
#define __TIME_LITERAL(value) __literal(TIME,value)
#define __DATE_LITERAL(value) __literal(DATE,value)
#define __TOD_LITERAL(value) __literal(TOD,value)
#define __DT_LITERAL(value) __literal(DT,value)
#define __STRING_LITERAL(count,value) (STRING){count,value}
#define __BYTE_LITERAL(value) __literal(BYTE,value)
#define __WORD_LITERAL(value) __literal(WORD,value)
#define __DWORD_LITERAL(value) __literal(DWORD,value,__32b_sufix)
#define __LWORD_LITERAL(value) __literal(LWORD,value,__64b_sufix)
typedef union __IL_DEFVAR_T {
BOOL BOOLvar;
SINT SINTvar;
INT INTvar;
DINT DINTvar;
LINT LINTvar;
USINT USINTvar;
UINT UINTvar;
UDINT UDINTvar;
ULINT ULINTvar;
BYTE BYTEvar;
WORD WORDvar;
DWORD DWORDvar;
LWORD LWORDvar;
REAL REALvar;
LREAL LREALvar;
TIME TIMEvar;
TOD TODvar;
DT DTvar;
DATE DATEvar;
} __IL_DEFVAR_T;
/**********************************************************************/
/**********************************************************************/
/***** *****/
/***** Some helper functions... *****/
/***** ...used later: *****/
/***** - when declaring the IEC 61131-3 standard functions *****/
/***** - in the C source code itself in SFC and ST expressions *****/
/***** *****/
/**********************************************************************/
/**********************************************************************/
/****************************/
/* Notify IEC runtime error */
/****************************/
/* function that generates an IEC runtime error */
static inline void __iec_error(void) {
/* TODO... */
fprintf(stderr, "IEC 61131-3 runtime error.\n");
/*exit(1);*/
}
/*******************/
/* Math Operations */
/*******************/
static inline double __expt(double in1, double in2) {
return pow(in1, in2);
}
/*******************************/
/* Time normalization function */
/*******************************/
static inline void __normalize_timespec (IEC_TIMESPEC *ts) {
if( ts->tv_nsec < -1000000000 || (( ts->tv_sec > 0 ) && ( ts->tv_nsec < 0 ))){
ts->tv_sec--;
ts->tv_nsec += 1000000000;
}
if( ts->tv_nsec > 1000000000 || (( ts->tv_sec < 0 ) && ( ts->tv_nsec > 0 ))){
ts->tv_sec++;
ts->tv_nsec -= 1000000000;
}
}
/**********************************************/
/* 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...
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;
ts.tv_nsec = sign * (long int)((total_sec - ts.tv_sec)*1e9);
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 __time_to_timespec(sign,mseconds,seconds,minutes,hours,days) \
((IEC_TIMESPEC){\
/*tv_sec =*/ ((long int) (((sign>=0)?1:-1)*((((long double)days*24 + (long double)hours)*60 + (long double)minutes)*60 + (long double)seconds + (long double)mseconds/1e3))), \
/*tv_nsec =*/ ((long int)(( \
((long double)(((sign>=0)?1:-1)*((((long double)days*24 + (long double)hours)*60 + (long double)minutes)*60 + (long double)seconds + (long double)mseconds/1e3))) - \
((long int) (((sign>=0)?1:-1)*((((long double)days*24 + (long double)hours)*60 + (long double)minutes)*60 + (long double)seconds + (long double)mseconds/1e3))) \
)*1e9))\
})
/* 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;
long double total_sec = (hours*60 + minutes)*60 + seconds;
ts.tv_sec = (long int)total_sec;
ts.tv_nsec = (long int)((total_sec - ts.tv_sec)*1e9);
return ts;
}
*/
#define __tod_to_timespec(seconds,minutes,hours) \
((IEC_TIMESPEC){\
/*tv_sec =*/ ((long int) ((((long double)hours)*60 + (long double)minutes)*60 + (long double)seconds)), \
/*tv_nsec =*/ ((long int)(( \
((long double)((((long double)hours)*60 + (long double)minutes)*60 + (long double)seconds)) - \
((long int) ((((long double)hours)*60 + (long double)minutes)*60 + (long double)seconds)) \
)*1e9))\
})
#define EPOCH_YEAR 1970
#define SECONDS_PER_MINUTE 60
#define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE)
#define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR)
#define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
static const unsigned short int __mon_yday[2][13] =
{
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
};
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_day; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year */
} tm;
static inline tm convert_seconds_to_date_and_time(long int seconds) {
tm dt;
long int days, rem;
days = seconds / SECONDS_PER_DAY;
rem = seconds % SECONDS_PER_DAY;
if (rem < 0) {
rem += SECONDS_PER_DAY;
days--;
}
// time of day
dt.tm_hour = rem / SECONDS_PER_HOUR;
rem %= SECONDS_PER_HOUR;
dt.tm_min = rem / 60;
dt.tm_sec = rem % 60;
// date
dt.tm_year = EPOCH_YEAR;
while (days >= (rem = __isleap(dt.tm_year) ? 366 : 365)) {
dt.tm_year++;
days -= rem;
}
while (days < 0) {
dt.tm_year--;
days += __isleap(dt.tm_year) ? 366 : 365;
}
dt.tm_mon = 1;
while (days >= __mon_yday[__isleap(dt.tm_year)][dt.tm_mon]) {
dt.tm_mon += 1;
}
dt.tm_day = days - __mon_yday[__isleap(dt.tm_year)][dt.tm_mon - 1] + 1;
return dt;
}
static inline IEC_TIMESPEC __date_to_timespec(int day, int month, int year) {
IEC_TIMESPEC ts;
int a4, b4, a100, b100, a400, b400;
int yday;
int intervening_leap_days;
if (month < 1 || month > 12)
__iec_error();
yday = __mon_yday[__isleap(year)][month - 1] + day;
if (yday > __mon_yday[__isleap(year)][month])
__iec_error();
a4 = (year >> 2) - ! (year & 3);
b4 = (EPOCH_YEAR >> 2) - ! (EPOCH_YEAR & 3);
a100 = a4 / 25 - (a4 % 25 < 0);
b100 = b4 / 25 - (b4 % 25 < 0);
a400 = a100 >> 2;
b400 = b100 >> 2;
intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
ts.tv_sec = ((year - EPOCH_YEAR) * 365 + intervening_leap_days + yday - 1) * 24 * 60 * 60;
ts.tv_nsec = 0;
return ts;
}
static inline IEC_TIMESPEC __dt_to_timespec(double seconds, double minutes, double hours, int day, int month, int year) {
IEC_TIMESPEC ts_date = __date_to_timespec(day, month, year);
IEC_TIMESPEC ts = __tod_to_timespec(seconds, minutes, hours);
ts.tv_sec += ts_date.tv_sec;
return ts;
}
/*******************/
/* Time operations */
/*******************/
#define __time_cmp(t1, t2) (t2.tv_sec == t1.tv_sec ? t1.tv_nsec - t2.tv_nsec : t1.tv_sec - t2.tv_sec)
static inline TIME __time_add(TIME IN1, TIME IN2){
TIME res ={IN1.tv_sec + IN2.tv_sec,
IN1.tv_nsec + IN2.tv_nsec };
__normalize_timespec(&res);
return res;
}
static inline TIME __time_sub(TIME IN1, TIME IN2){
TIME res ={IN1.tv_sec - IN2.tv_sec,
IN1.tv_nsec - IN2.tv_nsec };
__normalize_timespec(&res);
return res;
}
static inline TIME __time_mul(TIME IN1, LREAL IN2){
LREAL s_f = IN1.tv_sec * IN2;
time_t s = (time_t)s_f;
div_t ns = div((int)((LREAL)IN1.tv_nsec * IN2), 1000000000);
TIME res = {(long)s + ns.quot,
(long)ns.rem + (s_f - s) * 1000000000 };
__normalize_timespec(&res);
return res;
}
static inline TIME __time_div(TIME IN1, LREAL IN2){
LREAL s_f = IN1.tv_sec / IN2;
time_t s = (time_t)s_f;
TIME res = {(long)s,
(long)(IN1.tv_nsec / IN2 + (s_f - s) * 1000000000) };
__normalize_timespec(&res);
return res;
}
/***************/
/* Convertions */
/***************/
/*****************/
/* REAL_TO_INT */
/*****************/
static inline LINT __real_round(LREAL IN) {
return fmod(IN, 1) == 0 ? ((LINT)IN / 2) * 2 : (LINT)IN;
}
static inline LINT __preal_to_sint(LREAL IN) {
return IN >= 0 ? __real_round(IN + 0.5) : __real_round(IN - 0.5);
}
static inline LINT __preal_to_uint(LREAL IN) {
return IN >= 0 ? __real_round(IN + 0.5) : 0;
}
static inline LINT __real_to_sint(LREAL IN) {return (LINT)__preal_to_sint(IN);}
static inline LWORD __real_to_bit(LREAL IN) {return (LWORD)__preal_to_uint(IN);}
static inline ULINT __real_to_uint(LREAL IN) {return (ULINT)__preal_to_uint(IN);}
/***************/
/* TO_STRING */
/***************/
static inline STRING __bool_to_string(BOOL IN) {
if(IN) return (STRING){4, "TRUE"};
return (STRING){5,"FALSE"};
}
static inline STRING __bit_to_string(LWORD IN) {
STRING res;
res = __INIT_STRING;
res.len = snprintf((char*)res.body, STR_MAX_LEN, "16#%llx",(long long unsigned int)IN);
if(res.len > STR_MAX_LEN) res.len = STR_MAX_LEN;
return res;
}
static inline STRING __real_to_string(LREAL IN) {
STRING res;
res = __INIT_STRING;
res.len = snprintf((char*)res.body, STR_MAX_LEN, "%.10g", IN);
if(res.len > STR_MAX_LEN) res.len = STR_MAX_LEN;
return res;
}
static inline STRING __sint_to_string(LINT IN) {
STRING res;
res = __INIT_STRING;
res.len = snprintf((char*)res.body, STR_MAX_LEN, "%lld", (long long int)IN);
if(res.len > STR_MAX_LEN) res.len = STR_MAX_LEN;
return res;
}
static inline STRING __uint_to_string(ULINT IN) {
STRING res;
res = __INIT_STRING;
res.len = snprintf((char*)res.body, STR_MAX_LEN, "%llu", (long long unsigned int)IN);
if(res.len > STR_MAX_LEN) res.len = STR_MAX_LEN;
return res;
}
/***************/
/* FROM_STRING */
/***************/
static inline BOOL __string_to_bool(STRING IN) {
int i;
if (IN.len == 1) return !memcmp(&IN.body,"1", IN.len);
for (i = 0; i < IN.len; i++) IN.body[i] = toupper(IN.body[i]);
return IN.len == 4 ? !memcmp(&IN.body,"TRUE", IN.len) : 0;
}
static inline LINT __pstring_to_sint(STRING* IN) {
LINT res = 0;
__strlen_t l;
unsigned int shift = 0;
if(IN->body[0]=='2' && IN->body[1]=='#'){
/* 2#0101_1010_1011_1111 */
for(l = IN->len - 1; l >= 2 && shift < 64; l--)
{
char c = IN->body[l];
if( c >= '0' && c <= '1'){
res |= ( c - '0') << shift;
shift += 1;
}
}
}else if(IN->body[0]=='8' && IN->body[1]=='#'){
/* 8#1234_5665_4321 */
for(l = IN->len - 1; l >= 2 && shift < 64; l--)
{
char c = IN->body[l];
if( c >= '0' && c <= '7'){
res |= ( c - '0') << shift;
shift += 3;
}
}
}else if(IN->body[0]=='1' && IN->body[1]=='6' && IN->body[2]=='#'){
/* 16#1234_5678_9abc_DEFG */
for(l = IN->len - 1; l >= 3 && shift < 64; l--)
{
char c = IN->body[l];
if( c >= '0' && c <= '9'){
res |= (LWORD)( c - '0') << shift;
shift += 4;
}else if( c >= 'a' && c <= 'f'){
res |= (LWORD)( c - 'a' + 10 ) << shift;
shift += 4;
}else if( c >= 'A' && c <= 'F'){
res |= (LWORD)( c - 'A' + 10 ) << shift;
shift += 4;
}
}
}else{
/* -123456789 */
LINT fac = IN->body[0] == '-' ? -1 : 1;
for(l = IN->len - 1; l >= 0 && shift < 20; l--)
{
char c = IN->body[l];
if( c >= '0' && c <= '9'){
res += ( c - '0') * fac;
fac *= 10;
shift += 1;
}else if( c >= '.' ){ /* reset value */
res = 0;
fac = IN->body[0] == '-' ? -1 : 1;
shift = 0;
}
}
}
return res;
}
static inline LINT __string_to_sint(STRING IN) {return (LINT)__pstring_to_sint(&IN);}
static inline LWORD __string_to_bit (STRING IN) {return (LWORD)__pstring_to_sint(&IN);}
static inline ULINT __string_to_uint(STRING IN) {return (ULINT)__pstring_to_sint(&IN);}
static inline LREAL __string_to_real(STRING IN) {
__strlen_t l;
l = IN.len;
/* search the dot */
while(--l > 0 && IN.body[l] != '.');
if(l != 0){
return atof((const char *)&IN.body);
}else{
return (LREAL)__pstring_to_sint(&IN);
}
}
/***************/
/* TO_TIME */
/***************/
static inline TIME __int_to_time(LINT IN) {return (TIME){IN, 0};}
static inline TIME __real_to_time(LREAL IN) {return (TIME){IN, (IN - (LINT)IN) * 1000000000};}
static inline TIME __string_to_time(STRING IN){
__strlen_t l;
/* TODO :
*
* Duration literals without underlines: T#14ms T#-14ms T#14.7s T#14.7m
* short prefix T#14.7h t#14.7d t#25h15m
* t#5d14h12m18s3.5ms
* long prefix TIME#14ms TIME#-14ms time#14.7s
* Duration literals with underlines:
* short prefix t#25h_15m t#5d_14h_12m_18s_3.5ms
* long prefix TIME#25h_15m
* time#5d_14h_12m_18s_3.5ms
*
* Long prefix notation Short prefix notation
* DATE#1984-06-25 D#1984-06-25
* date#1984-06-25 d#1984-06-25
* TIME_OF_DAY#15:36:55.36 TOD#15:36:55.36
* time_of_day#15:36:55.36 tod#15:36:55.36
* DATE_AND_TIME#1984-06-25-15:36:55.36 DT#1984-06-25-15:36:55.36
* date_and_time#1984-06-25-15:36:55.36 dt#1984-06-25-15:36:55.36
*
*/
/* Quick hack : only transform seconds */
/* search the dot */
l = IN.len;
while(--l > 0 && IN.body[l] != '.');
if(l != 0){
LREAL IN_val = atof((const char *)&IN.body);
return (TIME){(long)IN_val, (long)(IN_val - (LINT)IN_val)*1000000000};
}else{
return (TIME){(long)__pstring_to_sint(&IN), 0};
}
}
/***************/
/* FROM_TIME */
/***************/
static inline LREAL __time_to_real(TIME IN){
return (LREAL)IN.tv_sec + ((LREAL)IN.tv_nsec/1000000000);
}
static inline LINT __time_to_int(TIME IN) {return IN.tv_sec;}
static inline STRING __time_to_string(TIME IN){
STRING res;
div_t days;
/*t#5d14h12m18s3.5ms*/
res = __INIT_STRING;
days = div(IN.tv_sec, SECONDS_PER_DAY);
if(!days.rem && IN.tv_nsec == 0){
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "T#%dd", days.quot);
}else{
div_t hours = div(days.rem, SECONDS_PER_HOUR);
if(!hours.rem && IN.tv_nsec == 0){
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "T#%dd%dh", days.quot, hours.quot);
}else{
div_t minuts = div(hours.rem, SECONDS_PER_MINUTE);
if(!minuts.rem && IN.tv_nsec == 0){
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "T#%dd%dh%dm", days.quot, hours.quot, minuts.quot);
}else{
if(IN.tv_nsec == 0){
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "T#%dd%dh%dm%ds", days.quot, hours.quot, minuts.quot, minuts.rem);
}else{
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "T#%dd%dh%dm%ds%gms", days.quot, hours.quot, minuts.quot, minuts.rem, (LREAL)IN.tv_nsec / 1000000);
}
}
}
}
if(res.len > STR_MAX_LEN) res.len = STR_MAX_LEN;
return res;
}
static inline STRING __date_to_string(DATE IN){
STRING res;
tm broken_down_time;
/* D#1984-06-25 */
broken_down_time = convert_seconds_to_date_and_time(IN.tv_sec);
res = __INIT_STRING;
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "D#%d-%2.2d-%2.2d",
broken_down_time.tm_year,
broken_down_time.tm_mon,
broken_down_time.tm_day);
if(res.len > STR_MAX_LEN) res.len = STR_MAX_LEN;
return res;
}
static inline STRING __tod_to_string(TOD IN){
STRING res;
tm broken_down_time;
time_t seconds;
/* TOD#15:36:55.36 */
seconds = IN.tv_sec;
if (seconds >= SECONDS_PER_DAY){
__iec_error();
return (STRING){9,"TOD#ERROR"};
}
broken_down_time = convert_seconds_to_date_and_time(seconds);
res = __INIT_STRING;
if(IN.tv_nsec == 0){
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "TOD#%2.2d:%2.2d:%2.2d",
broken_down_time.tm_hour,
broken_down_time.tm_min,
broken_down_time.tm_sec);
}else{
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "TOD#%2.2d:%2.2d:%09.6f",
broken_down_time.tm_hour,
broken_down_time.tm_min,
(LREAL)broken_down_time.tm_sec + (LREAL)IN.tv_nsec / 1e9);
}
if(res.len > STR_MAX_LEN) res.len = STR_MAX_LEN;
return res;
}
static inline STRING __dt_to_string(DT IN){
STRING res;
tm broken_down_time;
/* DT#1984-06-25-15:36:55.36 */
broken_down_time = convert_seconds_to_date_and_time(IN.tv_sec);
if(IN.tv_nsec == 0){
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "DT#%d-%2.2d-%2.2d-%2.2d:%2.2d:%2.2d",
broken_down_time.tm_year,
broken_down_time.tm_mon,
broken_down_time.tm_day,
broken_down_time.tm_hour,
broken_down_time.tm_min,
broken_down_time.tm_sec);
}else{
res.len = snprintf((char*)&res.body, STR_MAX_LEN, "DT#%d-%2.2d-%2.2d-%2.2d:%2.2d:%09.6f",
broken_down_time.tm_year,
broken_down_time.tm_mon,
broken_down_time.tm_day,
broken_down_time.tm_hour,
broken_down_time.tm_min,
(LREAL)broken_down_time.tm_sec + ((LREAL)IN.tv_nsec / 1e9));
}
if(res.len > STR_MAX_LEN) res.len = STR_MAX_LEN;
return res;
}
/**********************************************/
/* [ANY_DATE | TIME] _TO_ [ANY_DATE | TIME] */
/**********************************************/
static inline TOD __date_and_time_to_time_of_day(DT IN) {
return (TOD){
IN.tv_sec % SECONDS_PER_DAY + (IN.tv_sec < 0 ? SECONDS_PER_DAY : 0),
IN.tv_nsec};
}
static inline DATE __date_and_time_to_date(DT IN){
return (DATE){
IN.tv_sec - IN.tv_sec % SECONDS_PER_DAY - (IN.tv_sec < 0 ? SECONDS_PER_DAY : 0),
0};
}
/*****************/
/* FROM/TO BCD */
/*****************/
static inline BOOL __test_bcd(LWORD IN) {
while (IN) {
if ((IN & 0xf) > 9) return 1;
IN >>= 4;
}
return 0;
}
static inline ULINT __bcd_to_uint(LWORD IN){
ULINT res = IN & 0xf;
ULINT factor = 10ULL;
while (IN >>= 4) {
res += (IN & 0xf) * factor;
factor *= 10;
}
return res;
}
static inline LWORD __uint_to_bcd(ULINT IN){
LWORD res = IN % 10;
USINT shift = 4;
while (IN /= 10) {
res |= (IN % 10) << shift;
shift += 4;
}
return res;
}
/************/
/* MOVE_* */
/************/
/* some helpful __move_[ANY] functions, used in the *_TO_** and MOVE standard functions */
/* e.g. __move_BOOL, __move_BYTE, __move_REAL, __move_TIME, ... */
#define __move_(TYPENAME)\
static inline TYPENAME __move_##TYPENAME(TYPENAME op1) {return op1;}
__ANY(__move_)
#include "iec_std_functions.h"
#ifdef DISABLE_EN_ENO_PARAMETERS
#include "iec_std_FB_no_ENENO.h"
#else
#include "iec_std_FB.h"
#endif
#endif /* _IEC_STD_LIB_H */