Skip to content

Commit

Permalink
Merge pull request #60 from simatic-ax/add-time-conversion
Browse files Browse the repository at this point in the history
Add time conversion
  • Loading branch information
ThKindler authored Nov 30, 2023
2 parents e7a7828 + 7159dc1 commit 5baf162
Show file tree
Hide file tree
Showing 34 changed files with 341 additions and 7 deletions.
49 changes: 42 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

## Description

This library provides some functions to convert Integer values to Strings and Strings to Integer values.
This library provides some functions to convert various data types and formats into other formats that are not covered by the built-in functionalities.

## Install this package tesxt
Currently, there are two categories:

- Converting integer values to Strings and Strings to Integer values
- Converting time formats from different systems

## Install this package

Enter:

Expand All @@ -20,15 +25,16 @@ apax add @simatic-ax/conversion
Simatic.Ax.Conversion;
Simatic.Ax.Conversion.Integer;
Simatic.Ax.Conversion.Strings;
Simatic.Ax.Conversion.Times;
```

## ConversionMode
## ConversionMode for strings

```iecst
NAMESPACE Simatic.Ax.Conversion
TYPE
ConversionMode : WORD (
NONE := WORD#16#0000,
NONE := WORD#16#0000,
FORCE_SIGN := WORD#16#0001
END_TYPE
END_NAMESPACE
Expand All @@ -38,12 +44,12 @@ END_NAMESPACE
|-|-|-|
|NONE | Integer.ToString(value := 123) | '123'
|FORCE_SIGN | Integer.ToString(value := 123, mode := ConversionMode#FORCE_SIGN) | '+123'
<!-- |FORCE_LEADING_ZEROS | Integer.ToString(value := INT#123) | '+00123'
<!-- |FORCE_LEADING_ZEROS | Integer.ToString(value := INT#123) | '+00123'
|FORCE_LEADING_ZEROS | Integer.ToString(value := DINT#123) | '+0000000123' -->

<!-- > Modes can be combined. Example:
> ```
> Integer.ToString(value := 123, mode := ConversionMode#FORCE_SIGN)
> Integer.ToString(value := 123, mode := ConversionMode#FORCE_SIGN)
> ```
> -->

Expand Down Expand Up @@ -82,6 +88,14 @@ Strings.ToInt(str : STRING, value => ULINT) : BOOL;

> Values > MAX ULINT will handled as MOD MAX_ULINT (MAX_ULINT = 18446744073709551615);
### Simotion Date and Time of Day <--> LDT

```iecst
LDateAndTimeToSimotionDateToD(SimaticTime : LDATE_AND_TIME, SimotionTime => DWORD, SimotionDate => DWORD);
SimotionDateToDToLDateAndTime(SimotionTime := DWORD, SimotionDate := DWORD) : LDATE_AND_TIME;
```

## Strings

### Strings.ToHex
Expand Down Expand Up @@ -153,13 +167,34 @@ Convert a String "[123, 456, 789]" to an ARRAY[*] OF LINT and returns the number
> - startIdx out of the array boundaries startIdx : 1 and Array[5..10]
> - endIdx out of the array boundaries endIdx : 15 and Array[0..10]
### Times

Convert the date and time of a SIMOTION system into the data type LDATE_AND_TIME (LDT) and back. The SIMOTION format is a structured data type consisting of two 32-bit values. For the sake of simplicity, they are interpeted as DWORD.

```iecst
NAMESPACE Simatic.Ax.Conversion.Times
TYPE
SimotionDateTime : STRUCT
SimotionTime : DWORD;
SimotionDate : DWORD;
END_STRUCT;
END_TYPE
END_NAMESPACE
```

|||
|-|-|
|SimotionTime : DWORD|Milliseconds that have passed on the current day|
|SimotionDate : DWORD|Days that have passed since 1992-01-01|
|SimaticTime : LDATE_AND_TIME|Nanoseconds that have passed since 1970-01-01-00:00:00.000|

## Contribution

Thanks for your interest in contributing. Anybody is free to report bugs, unclear documentation, and other problems regarding this repository in the Issues section or, even better, is free to propose any changes to this repository using Merge Requests.

## Markdownlint-cli

This workspace will be checked by the [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli) (there is also documented ho to install the tool) tool in the CI workflow automatically.
This workspace will be checked by the [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli) (there is also documented ho to install the tool) tool in the CI workflow automatically.
To avoid, that the CI workflow fails because of the markdown linter, you can check all markdown files locally by running the markdownlint with:

```sh
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
131 changes: 131 additions & 0 deletions src/times/ToLDateAndTime.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
USING System.DateTime;
NAMESPACE Simatic.Ax.Conversion.Times

FUNCTION ToLDateAndTime : LDATE_AND_TIME

VAR_INPUT
SimotionDateTime : SimotionDateTime;
END_VAR

VAR_TEMP
u32Date : UDINT;
u32Year : UDInt;
u32Month : UDInt;
u32Days : UDInt;
b32AuxHours : UDInt;
boDateReady : Bool;
HOUR : UDINT;
MINUTE : UDINT;
SECOND : UDINT;
NANOSECOND : UDINT;
END_VAR

u32Date := TO_UDINT(SimotionDateTime.SimotionDate);// + UDINT#1;

// calculation of hours
HOUR := TO_UDINT(SimotionDateTime.SimotionTime) / UDINT#3600000;
b32AuxHours := TO_UDINT(SimotionDateTime.SimotionTime) - HOUR * UDINT#3600000;

// calculation of minutes
MINUTE := b32AuxHours / UDINT#60000;

// calculation of seconds
SECOND := (b32AuxHours - MINUTE * UDINT#60000) / UDINT#1000;

// calculation of mseconds
NANOSECOND := ( TO_UDINT(SimotionDateTime.SimotionTime) - (HOUR * UDINT#3600000) - (MINUTE * UDINT#60000) - (SECOND * UDINT#1000)) * UDINT#1000000;

//Reset values to calculate actual year
u32Year := UDINT#1991;
u32Days := UDINT#0;

//Detect actual year by adding the days since 01.01.1992
REPEAT
//Increase number of years
u32Year := u32Year + UDINT#1;
//If year is a leap-year
IF (u32Year MOD UDINT#4 = UDINT#0) AND (u32Year <> UDINT#2100) THEN //only 2100 is not a leap-year between 1992-2200
u32Days := u32Days + UDINT#366;
ELSE
u32Days := u32Days + UDINT#365;
END_IF;
//Number of days is greater or equals number of days of SIMOTION system
UNTIL u32Days >= u32Date
END_REPEAT;

u32Days := u32Days;

//Decrease number of days by number of days of one year
//If year is a leap-year
IF (u32Year MOD UDINT#4 = UDINT#0) AND (u32Year <> UDINT#2100) THEN //only 2100 is not a leap-year between 1992-2200
u32Date := u32Date - (u32Days - UDINT#366);
ELSE
u32Date := u32Date - (u32Days - UDINT#365);
END_IF;

//Reset values to calculate actual month and day
u32Month := UDINT#1;
boDateReady := FALSE;

//Start CASE with value "1" (-> january)
REPEAT
CASE u32Month OF
// for months with 31 days
UDINT#1, UDINT#3, UDINT#5, UDINT#7, UDINT#8, UDINT#10:
IF (u32Date <= UDINT#31) THEN
u32Days := u32Date;
boDateReady := TRUE;
ELSE
u32Month := u32Month + UDINT#1;
u32Date := u32Date - UDINT#31;
END_IF;
//for february
UDINT#2:
IF (u32Year MOD UDINT#4 = UDINT#0) AND (u32Year <> UDINT#2100) THEN //only 2100 is not a leap-year between 1992-2200
IF (u32Date <= UDINT#29) THEN
u32Days := u32Date;
boDateReady := TRUE;
ELSE
u32Month := u32Month + UDINT#1;
u32Date := u32Date - UDINT#29;
END_IF;
ELSE
IF (u32Date <= UDINT#28) THEN
u32Days := u32Date;
boDateReady := TRUE;
ELSE
u32Month := u32Month + UDINT#1;
u32Date := u32Date - UDINT#28;
END_IF;
END_IF;
//for months with 30 days
UDINT#4, UDINT#6, UDINT#9, UDINT#11:
IF (u32Date <= UDINT#30) THEN
u32Days := u32Date;
boDateReady := TRUE;
ELSE
u32Month := u32Month + UDINT#1;
u32Date := u32Date - UDINT#30;
END_IF;
//for december
UDINT#12:
u32Days := u32Date;
boDateReady := TRUE;
END_CASE;
//Correct month and day found
UNTIL boDateReady = TRUE
END_REPEAT;

NewDateAndTime(
year := to_int(u32Year),
month := to_int(u32Month),
day := to_int(u32Days),
hour := to_int(hour),
minute := to_int(MINUTE),
second := to_int(second),
millisecond := to_int(NANOSECOND/UDINT#1000000),
value => ToLDateAndTime);

END_FUNCTION

END_NAMESPACE
87 changes: 87 additions & 0 deletions src/times/ToSimotionDateTime.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
USING System.DateTime;
NAMESPACE Simatic.Ax.Conversion.Times
FUNCTION ToSimotionDateTime : SimotionDateTime
VAR_INPUT
SimaticTime : LDATE_AND_TIME;
END_VAR

VAR_TEMP
actDays : INT;
actMonth : INT;
actYear : INT;
actHour : INT;
actMinute : INT;
actSecond : INT;
actMillisecond : INT;
elapsedYears : INT;
elapsedDays : DINT;
loop : DINT;
END_VAR

//we ignore microseconds and nanoseconds, since they are supported by the destination data type
SplitDateAndTime(value := SimaticTime,
year => actYear,
month => actMonth,
day => actDays,
hour => actHour,
minute => actMinute,
second => actSecond,
millisecond => actMillisecond);

//add number of days from full years, subtracting the current year
FOR loop := 1992 TO (actYear - 1) DO
IF loop MOD 4 = 0 AND loop <> 2100 THEN
elapsedDays := elapsedDays + 366;
ELSE
elapsedDays := elapsedDays + 365;
END_IF;
END_FOR;

//add days from full months of the current year
IF actMonth > 1 THEN
elapsedDays := elapsedDays + 31;
END_IF;
IF actMonth > 2 THEN
IF actYear MOD 4 = 0 AND NOT (actYear = 2100) THEN
elapsedDays := elapsedDays + 29;
ELSE
elapsedDays := elapsedDays + 28;
END_IF;
END_IF;
IF actMonth > 3 THEN
elapsedDays := elapsedDays + 31;
END_IF;
IF actMonth > 4 THEN
elapsedDays := elapsedDays + 30;
END_IF;
IF actMonth > 5 THEN
elapsedDays := elapsedDays + 31;
END_IF;
IF actMonth > 6 THEN
elapsedDays := elapsedDays + 30;
END_IF;
IF actMonth > 7 THEN
elapsedDays := elapsedDays + 31;
END_IF;
IF actMonth > 8 THEN
elapsedDays := elapsedDays + 31;
END_IF;
IF actMonth > 9 THEN
elapsedDays := elapsedDays + 30;
END_IF;
IF actMonth > 10 THEN
elapsedDays := elapsedDays + 31;
END_IF;
IF actMonth > 11 THEN
elapsedDays := elapsedDays + 30;
END_IF;

//add days from current month
elapsedDays := elapsedDays + actDays;

ToSimotionDateTime.SimotionDate := TO_DWORD(elapsedDays);
ToSimotionDateTime.SimotionTime := TO_DWORD (TO_UDINT(actMillisecond) + UDINT#1000 * (TO_UDINT(actSecond) + UDINT#60 * (TO_UDINT(actMinute + 60 * ( actHour)))));

END_FUNCTION

END_NAMESPACE
10 changes: 10 additions & 0 deletions src/times/types.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
NAMESPACE Simatic.Ax.Conversion.Times

TYPE
SimotionDateTime : STRUCT
SimotionTime : DWORD;
SimotionDate : DWORD;
END_STRUCT;
END_TYPE

END_NAMESPACE
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
35 changes: 35 additions & 0 deletions test/times/ToLDateAndTime.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
NAMESPACE Simatic.Ax.Conversion.Times

{TestFixture}
CLASS TestSimotionDateTimeToLDateAndTime

{Test}
METHOD PUBLIC CheckStartTime
//test with 1ms added from initial time
VAR_TEMP
timestamp : LDATE_AND_TIME;
SimotionDateTime : SimotionDateTime;
END_VAR
SimotionDateTime.SimotionDate := DWORD#1;
SimotionDateTime.SimotionTime := DWORD#1;
timestamp := ToLDateAndTime(SimotionDateTime);
AxUnit.Assert.Equal(timestamp = LDATE_AND_TIME#1992-01-01-00:00:00.001, TRUE);
END_METHOD

{Test}
METHOD PUBLIC CheckCurrentTime
VAR_TEMP
timestamp : LDATE_AND_TIME;
SimotionDateTime : SimotionDateTime;
END_VAR
SimotionDateTime.SimotionDate := DWORD#11627;
SimotionDateTime.SimotionTime := DWORD#57423483;
timestamp := ToLDateAndTime(SimotionDateTime);
//the values have been read from a simotion plc and converted, so they proven to be valid
AxUnit.Assert.Equal(timestamp = LDT#2023-10-31-15:57:03.483, TRUE);

END_METHOD

END_CLASS

END_NAMESPACE
Loading

0 comments on commit 5baf162

Please sign in to comment.