├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── UTILITIES.st ├── UTILITIES_ARRAY.st ├── UTILITIES_BYTE.st ├── UTILITIES_MATH.st ├── UTILITIES_STRING.st └── UTILITIES_TIME.st /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. 16 | 2. 17 | 3. 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Thank you for your interest in contributing to this utilities collection! 4 | 5 | ## Testing 6 | 7 | Unfortunately, we don't know a tool to test the code automatically. Therefore, every change needs to be tested manually on one of our development PLCs. 8 | 9 | Please test your changes if you have access to one or more PLCs. 10 | 11 | ## Submitting changes 12 | 13 | Please send a [GitHub Pull Request to structured-text-utilities](https://github.com/WengerAG/structured-text-utilities/pull/new/master) with a clear list of what you've done (read more about [pull requests](http://help.github.com/pull-requests/)). Please follow our coding conventions (below) and make sure all of your commits are atomic (one feature per commit). 14 | 15 | Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this: 16 | 17 | $ git commit -m "A brief summary of the commit 18 | > 19 | > A paragraph describing what changed and its impact." 20 | 21 | ## Coding conventions 22 | 23 | Start reading our code and you'll get the hang of it. We optimize for readability: 24 | 25 | * We indent using tabs. 26 | * We ALWAYS put spaces after list items and method parameters (`[1, 2, 3]`, not `[1,2,3]`) and around operators (`x + 1`, not `x+1`). 27 | * In most cases, function names starts with a verb (e.g. `CALCULATE_LREAL_ARRAY_STATISTICS`), exceptions are acceptable if internal functions are similar (e.g. `STRING_TO_INT`) or an adjective after a noun suits better (e.g. `STRING_ENDSWITH`). 28 | * We avoid BOOL parameters in functions. If a function provides different modes or formats (or will do it in near future), add an ENUM type parameter with meaningful names (e.g. `TIME_FORMAT : (ISO8601);`). 29 | * This is open source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as smooth as possible. 30 | 31 | Thanks! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Wenger Automation & Engineering AG, Winterthur, Switzerland 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # structured-text-utilities 2 | Utilities for Structured Text (IEC 61131-3) 3 | 4 | This collection of Structured Text utilities has the following design goals: 5 | * compiles on every device that complies to IEC 61131-3 - even without 2nd and 3rd language extensions, forward declarations and OOP 6 | * provides standard functions to handle arrays, numbers, strings and date/time values 7 | * provides some statistical calculation functions 8 | * static connascence (https://en.wikipedia.org/wiki/Connascence) -------------------------------------------------------------------------------- /UTILITIES.st: -------------------------------------------------------------------------------- 1 | UNIT UTILITIES; 2 | 3 | // This unit 'UTILITIES' uses all related units and can be used in a short-hand USES statement. 4 | // Please check Git repository for updates from time to time. 5 | 6 | INTERFACE 7 | USES UTILITIES_ARRAY, UTILITIES_BYTE, UTILITIES_MATH, UTILITIES_STRING, UTILITIES_TIME; 8 | END_INTERFACE 9 | 10 | 11 | IMPLEMENTATION 12 | END_IMPLEMENTATION 13 | -------------------------------------------------------------------------------- /UTILITIES_ARRAY.st: -------------------------------------------------------------------------------- 1 | UNIT UTILITIES_ARRAY; 2 | 3 | // This unit 'UTILITIES_ARRAY' contains some common array functions and types which are missing in standard library. 4 | // Please check Git repository for updates from time to time. 5 | 6 | INTERFACE 7 | USES UTILITIES_STRING; 8 | 9 | FUNCTION SORT_LREAL_ARRAY; 10 | FUNCTION SORT_REAL_ARRAY; 11 | 12 | FUNCTION SORT_DINT_ARRAY; 13 | FUNCTION SORT_UDINT_ARRAY; 14 | FUNCTION SORT_INT_ARRAY; 15 | FUNCTION SORT_UINT_ARRAY; 16 | FUNCTION SORT_SINT_ARRAY; 17 | FUNCTION SORT_USINT_ARRAY; 18 | 19 | FUNCTION SORT_STRING_ARRAY; 20 | 21 | FUNCTION REVERSE_LREAL_ARRAY; 22 | FUNCTION REVERSE_REAL_ARRAY; 23 | 24 | FUNCTION REVERSE_DINT_ARRAY; 25 | FUNCTION REVERSE_UDINT_ARRAY; 26 | FUNCTION REVERSE_INT_ARRAY; 27 | FUNCTION REVERSE_UINT_ARRAY; 28 | FUNCTION REVERSE_SINT_ARRAY; 29 | FUNCTION REVERSE_USINT_ARRAY; 30 | 31 | FUNCTION REVERSE_BYTE_ARRAY; 32 | FUNCTION REVERSE_WORD_ARRAY; 33 | FUNCTION REVERSE_DWORD_ARRAY; 34 | 35 | FUNCTION REVERSE_STRING_ARRAY; 36 | 37 | FUNCTION GET_FIRST_INDEX_OF_LREAL_IN_ARRAY; 38 | FUNCTION GET_FIRST_INDEX_OF_REAL_IN_ARRAY; 39 | 40 | FUNCTION GET_FIRST_INDEX_OF_DINT_IN_ARRAY; 41 | FUNCTION GET_FIRST_INDEX_OF_UDINT_IN_ARRAY; 42 | FUNCTION GET_FIRST_INDEX_OF_INT_IN_ARRAY; 43 | FUNCTION GET_FIRST_INDEX_OF_UINT_IN_ARRAY; 44 | FUNCTION GET_FIRST_INDEX_OF_SINT_IN_ARRAY; 45 | FUNCTION GET_FIRST_INDEX_OF_USINT_IN_ARRAY; 46 | 47 | FUNCTION GET_FIRST_INDEX_OF_BYTE_IN_ARRAY; 48 | FUNCTION GET_FIRST_INDEX_OF_WORD_IN_ARRAY; 49 | FUNCTION GET_FIRST_INDEX_OF_DWORD_IN_ARRAY; 50 | 51 | FUNCTION GET_FIRST_INDEX_OF_STRING_IN_ARRAY; 52 | END_INTERFACE 53 | 54 | 55 | IMPLEMENTATION 56 | FUNCTION SORT_LREAL_ARRAY : VOID 57 | VAR_IN_OUT 58 | data : ARRAY[..] OF LREAL; 59 | END_VAR 60 | 61 | VAR_TEMP 62 | i, j : DINT; 63 | i_start : DINT; 64 | i_end : DINT; 65 | temp : LREAL; 66 | END_VAR 67 | 68 | i_start := _firstIndexOf(data); 69 | i_end := _lastIndexOf(data); 70 | 71 | FOR i := i_start TO i_end DO 72 | FOR j := i+1 TO i_end DO 73 | IF data[j] < data[i] THEN 74 | temp := data[i]; 75 | data[i] := data[j]; 76 | data[j] := temp; 77 | END_IF; 78 | END_FOR; 79 | END_FOR; 80 | END_FUNCTION 81 | 82 | FUNCTION SORT_REAL_ARRAY : VOID 83 | VAR_IN_OUT 84 | data : ARRAY[..] OF REAL; 85 | END_VAR 86 | 87 | VAR_TEMP 88 | i, j : DINT; 89 | i_start : DINT; 90 | i_end : DINT; 91 | temp : REAL; 92 | END_VAR 93 | 94 | i_start := _firstIndexOf(data); 95 | i_end := _lastIndexOf(data); 96 | 97 | FOR i := i_start TO i_end DO 98 | FOR j := i+1 TO i_end DO 99 | IF data[j] < data[i] THEN 100 | temp := data[i]; 101 | data[i] := data[j]; 102 | data[j] := temp; 103 | END_IF; 104 | END_FOR; 105 | END_FOR; 106 | END_FUNCTION 107 | 108 | 109 | FUNCTION SORT_DINT_ARRAY : VOID 110 | VAR_IN_OUT 111 | data : ARRAY[..] OF DINT; 112 | END_VAR 113 | 114 | VAR_TEMP 115 | i, j : DINT; 116 | i_start : DINT; 117 | i_end : DINT; 118 | temp : DINT; 119 | END_VAR 120 | 121 | i_start := _firstIndexOf(data); 122 | i_end := _lastIndexOf(data); 123 | 124 | FOR i := i_start TO i_end DO 125 | FOR j := i+1 TO i_end DO 126 | IF data[j] < data[i] THEN 127 | temp := data[i]; 128 | data[i] := data[j]; 129 | data[j] := temp; 130 | END_IF; 131 | END_FOR; 132 | END_FOR; 133 | END_FUNCTION 134 | 135 | FUNCTION SORT_UDINT_ARRAY : VOID 136 | VAR_IN_OUT 137 | data : ARRAY[..] OF UDINT; 138 | END_VAR 139 | 140 | VAR_TEMP 141 | i, j : DINT; 142 | i_start : DINT; 143 | i_end : DINT; 144 | temp : UDINT; 145 | END_VAR 146 | 147 | i_start := _firstIndexOf(data); 148 | i_end := _lastIndexOf(data); 149 | 150 | FOR i := i_start TO i_end DO 151 | FOR j := i+1 TO i_end DO 152 | IF data[j] < data[i] THEN 153 | temp := data[i]; 154 | data[i] := data[j]; 155 | data[j] := temp; 156 | END_IF; 157 | END_FOR; 158 | END_FOR; 159 | END_FUNCTION 160 | 161 | FUNCTION SORT_INT_ARRAY : VOID 162 | VAR_IN_OUT 163 | data : ARRAY[..] OF INT; 164 | END_VAR 165 | 166 | VAR_TEMP 167 | i, j : DINT; 168 | i_start : DINT; 169 | i_end : DINT; 170 | temp : INT; 171 | END_VAR 172 | 173 | i_start := _firstIndexOf(data); 174 | i_end := _lastIndexOf(data); 175 | 176 | FOR i := i_start TO i_end DO 177 | FOR j := i+1 TO i_end DO 178 | IF data[j] < data[i] THEN 179 | temp := data[i]; 180 | data[i] := data[j]; 181 | data[j] := temp; 182 | END_IF; 183 | END_FOR; 184 | END_FOR; 185 | END_FUNCTION 186 | 187 | FUNCTION SORT_UINT_ARRAY : VOID 188 | VAR_IN_OUT 189 | data : ARRAY[..] OF UINT; 190 | END_VAR 191 | 192 | VAR_TEMP 193 | i, j : DINT; 194 | i_start : DINT; 195 | i_end : DINT; 196 | temp : UINT; 197 | END_VAR 198 | 199 | i_start := _firstIndexOf(data); 200 | i_end := _lastIndexOf(data); 201 | 202 | FOR i := i_start TO i_end DO 203 | FOR j := i+1 TO i_end DO 204 | IF data[j] < data[i] THEN 205 | temp := data[i]; 206 | data[i] := data[j]; 207 | data[j] := temp; 208 | END_IF; 209 | END_FOR; 210 | END_FOR; 211 | END_FUNCTION 212 | 213 | FUNCTION SORT_SINT_ARRAY : VOID 214 | VAR_IN_OUT 215 | data : ARRAY[..] OF SINT; 216 | END_VAR 217 | 218 | VAR_TEMP 219 | i, j : DINT; 220 | i_start : DINT; 221 | i_end : DINT; 222 | temp : SINT; 223 | END_VAR 224 | 225 | i_start := _firstIndexOf(data); 226 | i_end := _lastIndexOf(data); 227 | 228 | FOR i := i_start TO i_end DO 229 | FOR j := i+1 TO i_end DO 230 | IF data[j] < data[i] THEN 231 | temp := data[i]; 232 | data[i] := data[j]; 233 | data[j] := temp; 234 | END_IF; 235 | END_FOR; 236 | END_FOR; 237 | END_FUNCTION 238 | 239 | FUNCTION SORT_USINT_ARRAY : VOID 240 | VAR_IN_OUT 241 | data : ARRAY[..] OF USINT; 242 | END_VAR 243 | 244 | VAR_TEMP 245 | i, j : DINT; 246 | i_start : DINT; 247 | i_end : DINT; 248 | temp : USINT; 249 | END_VAR 250 | 251 | i_start := _firstIndexOf(data); 252 | i_end := _lastIndexOf(data); 253 | 254 | FOR i := i_start TO i_end DO 255 | FOR j := i+1 TO i_end DO 256 | IF data[j] < data[i] THEN 257 | temp := data[i]; 258 | data[i] := data[j]; 259 | data[j] := temp; 260 | END_IF; 261 | END_FOR; 262 | END_FOR; 263 | END_FUNCTION 264 | 265 | 266 | FUNCTION SORT_STRING_ARRAY : VOID 267 | VAR_IN_OUT 268 | data : ARRAY[..] OF STRING; 269 | END_VAR 270 | 271 | VAR_TEMP 272 | i, j : DINT; 273 | i_start : DINT; 274 | i_end : DINT; 275 | temp : STRING; 276 | END_VAR 277 | 278 | i_start := _firstIndexOf(data); 279 | i_end := _lastIndexOf(data); 280 | 281 | FOR i:=i_start TO i_end DO 282 | FOR j:=i+1 TO i_end DO 283 | IF COMPARE_STRINGS(data[j], data[i]) < 0 THEN 284 | temp := data[i]; 285 | data[i] := data[j]; 286 | data[j] := temp; 287 | END_IF; 288 | END_FOR; 289 | END_FOR; 290 | END_FUNCTION 291 | 292 | 293 | FUNCTION REVERSE_LREAL_ARRAY : VOID 294 | VAR_IN_OUT 295 | data : ARRAY[..] OF LREAL; 296 | END_VAR 297 | 298 | VAR_TEMP 299 | i, j : DINT; 300 | i_start : DINT; 301 | i_end : DINT; 302 | i_center : DINT; 303 | temp : LREAL; 304 | END_VAR 305 | 306 | i_start := _firstIndexOf(data); 307 | i_end := _lastIndexOf(data); 308 | i_center := i_start + (i_end - i_start) / 2; 309 | 310 | FOR i := i_start TO i_center DO 311 | j := i_end - (i - i_start); 312 | temp := data[i]; 313 | data[i] := data[j]; 314 | data[j] := temp; 315 | END_FOR; 316 | END_FUNCTION 317 | 318 | FUNCTION REVERSE_REAL_ARRAY : VOID 319 | VAR_IN_OUT 320 | data : ARRAY[..] OF REAL; 321 | END_VAR 322 | 323 | VAR_TEMP 324 | i, j : DINT; 325 | i_start : DINT; 326 | i_end : DINT; 327 | i_center : DINT; 328 | temp : REAL; 329 | END_VAR 330 | 331 | i_start := _firstIndexOf(data); 332 | i_end := _lastIndexOf(data); 333 | i_center := i_start + (i_end - i_start) / 2; 334 | 335 | FOR i := i_start TO i_center DO 336 | j := i_end - (i - i_start); 337 | temp := data[i]; 338 | data[i] := data[j]; 339 | data[j] := temp; 340 | END_FOR; 341 | END_FUNCTION 342 | 343 | 344 | FUNCTION REVERSE_DINT_ARRAY : VOID 345 | VAR_IN_OUT 346 | data : ARRAY[..] OF DINT; 347 | END_VAR 348 | 349 | VAR_TEMP 350 | i, j : DINT; 351 | i_start : DINT; 352 | i_end : DINT; 353 | i_center : DINT; 354 | temp : DINT; 355 | END_VAR 356 | 357 | i_start := _firstIndexOf(data); 358 | i_end := _lastIndexOf(data); 359 | i_center := i_start + (i_end - i_start) / 2; 360 | 361 | FOR i := i_start TO i_center DO 362 | j := i_end - (i - i_start); 363 | temp := data[i]; 364 | data[i] := data[j]; 365 | data[j] := temp; 366 | END_FOR; 367 | END_FUNCTION 368 | 369 | FUNCTION REVERSE_UDINT_ARRAY : VOID 370 | VAR_IN_OUT 371 | data : ARRAY[..] OF UDINT; 372 | END_VAR 373 | 374 | VAR_TEMP 375 | i, j : DINT; 376 | i_start : DINT; 377 | i_end : DINT; 378 | i_center : DINT; 379 | temp : UDINT; 380 | END_VAR 381 | 382 | i_start := _firstIndexOf(data); 383 | i_end := _lastIndexOf(data); 384 | i_center := i_start + (i_end - i_start) / 2; 385 | 386 | FOR i := i_start TO i_center DO 387 | j := i_end - (i - i_start); 388 | temp := data[i]; 389 | data[i] := data[j]; 390 | data[j] := temp; 391 | END_FOR; 392 | END_FUNCTION 393 | 394 | FUNCTION REVERSE_INT_ARRAY : VOID 395 | VAR_IN_OUT 396 | data : ARRAY[..] OF INT; 397 | END_VAR 398 | 399 | VAR_TEMP 400 | i, j : DINT; 401 | i_start : DINT; 402 | i_end : DINT; 403 | i_center : DINT; 404 | temp : INT; 405 | END_VAR 406 | 407 | i_start := _firstIndexOf(data); 408 | i_end := _lastIndexOf(data); 409 | i_center := i_start + (i_end - i_start) / 2; 410 | 411 | FOR i := i_start TO i_center DO 412 | j := i_end - (i - i_start); 413 | temp := data[i]; 414 | data[i] := data[j]; 415 | data[j] := temp; 416 | END_FOR; 417 | END_FUNCTION 418 | 419 | FUNCTION REVERSE_UINT_ARRAY : VOID 420 | VAR_IN_OUT 421 | data : ARRAY[..] OF UINT; 422 | END_VAR 423 | 424 | VAR_TEMP 425 | i, j : DINT; 426 | i_start : DINT; 427 | i_end : DINT; 428 | i_center : DINT; 429 | temp : UINT; 430 | END_VAR 431 | 432 | i_start := _firstIndexOf(data); 433 | i_end := _lastIndexOf(data); 434 | i_center := i_start + (i_end - i_start) / 2; 435 | 436 | FOR i := i_start TO i_center DO 437 | j := i_end - (i - i_start); 438 | temp := data[i]; 439 | data[i] := data[j]; 440 | data[j] := temp; 441 | END_FOR; 442 | END_FUNCTION 443 | 444 | FUNCTION REVERSE_SINT_ARRAY : VOID 445 | VAR_IN_OUT 446 | data : ARRAY[..] OF SINT; 447 | END_VAR 448 | 449 | VAR_TEMP 450 | i, j : DINT; 451 | i_start : DINT; 452 | i_end : DINT; 453 | i_center : DINT; 454 | temp : SINT; 455 | END_VAR 456 | 457 | i_start := _firstIndexOf(data); 458 | i_end := _lastIndexOf(data); 459 | i_center := i_start + (i_end - i_start) / 2; 460 | 461 | FOR i := i_start TO i_center DO 462 | j := i_end - (i - i_start); 463 | temp := data[i]; 464 | data[i] := data[j]; 465 | data[j] := temp; 466 | END_FOR; 467 | END_FUNCTION 468 | 469 | FUNCTION REVERSE_USINT_ARRAY : VOID 470 | VAR_IN_OUT 471 | data : ARRAY[..] OF USINT; 472 | END_VAR 473 | 474 | VAR_TEMP 475 | i, j : DINT; 476 | i_start : DINT; 477 | i_end : DINT; 478 | i_center : DINT; 479 | temp : USINT; 480 | END_VAR 481 | 482 | i_start := _firstIndexOf(data); 483 | i_end := _lastIndexOf(data); 484 | i_center := i_start + (i_end - i_start) / 2; 485 | 486 | FOR i := i_start TO i_center DO 487 | j := i_end - (i - i_start); 488 | temp := data[i]; 489 | data[i] := data[j]; 490 | data[j] := temp; 491 | END_FOR; 492 | END_FUNCTION 493 | 494 | 495 | FUNCTION REVERSE_BYTE_ARRAY : VOID 496 | VAR_IN_OUT 497 | data : ARRAY[..] OF BYTE; 498 | END_VAR 499 | 500 | VAR_TEMP 501 | i, j : DINT; 502 | i_start : DINT; 503 | i_end : DINT; 504 | i_center : DINT; 505 | temp : BYTE; 506 | END_VAR 507 | 508 | i_start := _firstIndexOf(data); 509 | i_end := _lastIndexOf(data); 510 | i_center := i_start + (i_end - i_start) / 2; 511 | 512 | FOR i := i_start TO i_center DO 513 | j := i_end - (i - i_start); 514 | temp := data[i]; 515 | data[i] := data[j]; 516 | data[j] := temp; 517 | END_FOR; 518 | END_FUNCTION 519 | 520 | FUNCTION REVERSE_WORD_ARRAY : VOID 521 | VAR_IN_OUT 522 | data : ARRAY[..] OF WORD; 523 | END_VAR 524 | 525 | VAR_TEMP 526 | i, j : DINT; 527 | i_start : DINT; 528 | i_end : DINT; 529 | i_center : DINT; 530 | temp : WORD; 531 | END_VAR 532 | 533 | i_start := _firstIndexOf(data); 534 | i_end := _lastIndexOf(data); 535 | i_center := i_start + (i_end - i_start) / 2; 536 | 537 | FOR i := i_start TO i_center DO 538 | j := i_end - (i - i_start); 539 | temp := data[i]; 540 | data[i] := data[j]; 541 | data[j] := temp; 542 | END_FOR; 543 | END_FUNCTION 544 | 545 | FUNCTION REVERSE_DWORD_ARRAY : VOID 546 | VAR_IN_OUT 547 | data : ARRAY[..] OF DWORD; 548 | END_VAR 549 | 550 | VAR_TEMP 551 | i, j : DINT; 552 | i_start : DINT; 553 | i_end : DINT; 554 | i_center : DINT; 555 | temp : DWORD; 556 | END_VAR 557 | 558 | i_start := _firstIndexOf(data); 559 | i_end := _lastIndexOf(data); 560 | i_center := i_start + (i_end - i_start) / 2; 561 | 562 | FOR i := i_start TO i_center DO 563 | j := i_end - (i - i_start); 564 | temp := data[i]; 565 | data[i] := data[j]; 566 | data[j] := temp; 567 | END_FOR; 568 | END_FUNCTION 569 | 570 | 571 | FUNCTION REVERSE_STRING_ARRAY : VOID 572 | VAR_IN_OUT 573 | data : ARRAY[..] OF STRING; 574 | END_VAR 575 | 576 | VAR_TEMP 577 | i, j : DINT; 578 | i_start : DINT; 579 | i_end : DINT; 580 | i_center : DINT; 581 | temp : STRING; 582 | END_VAR 583 | 584 | i_start := _firstIndexOf(data); 585 | i_end := _lastIndexOf(data); 586 | i_center := i_start + (i_end - i_start) / 2; 587 | 588 | FOR i := i_start TO i_center DO 589 | j := i_end - (i - i_start); 590 | temp := data[i]; 591 | data[i] := data[j]; 592 | data[j] := temp; 593 | END_FOR; 594 | END_FUNCTION 595 | 596 | 597 | // Search for first appearance of given number in given array and return its index. 598 | // If number is not present in array, the lowest index minus 1 is returned. 599 | // ATTENTION: comparing floating point numbers is unsafe (exact equality needed here!). 600 | FUNCTION GET_FIRST_INDEX_OF_LREAL_IN_ARRAY : DINT 601 | VAR_INPUT 602 | in : LREAL; 603 | END_VAR 604 | 605 | VAR_IN_OUT 606 | data : ARRAY[..] OF LREAL; 607 | END_VAR 608 | 609 | VAR_TEMP 610 | i : DINT; 611 | i_start : DINT; 612 | i_end : DINT; 613 | END_VAR 614 | 615 | i_start := _firstIndexOf(data); 616 | i_end := _lastIndexOf(data); 617 | 618 | FOR i := i_start TO i_end DO 619 | IF data[i] = in THEN 620 | GET_FIRST_INDEX_OF_LREAL_IN_ARRAY := i; 621 | RETURN; 622 | END_IF; 623 | END_FOR; 624 | 625 | GET_FIRST_INDEX_OF_LREAL_IN_ARRAY := i_start -1; 626 | END_FUNCTION 627 | 628 | // Search for first appearance of given number in given array and return its index. 629 | // If number is not present in array, the lowest index minus 1 is returned. 630 | // ATTENTION: comparing floating point numbers is unsafe (exact equality needed here!). 631 | FUNCTION GET_FIRST_INDEX_OF_REAL_IN_ARRAY : DINT 632 | VAR_INPUT 633 | in : REAL; 634 | END_VAR 635 | 636 | VAR_IN_OUT 637 | data : ARRAY[..] OF REAL; 638 | END_VAR 639 | 640 | VAR_TEMP 641 | i : DINT; 642 | i_start : DINT; 643 | i_end : DINT; 644 | END_VAR 645 | 646 | i_start := _firstIndexOf(data); 647 | i_end := _lastIndexOf(data); 648 | 649 | FOR i := i_start TO i_end DO 650 | IF data[i] = in THEN 651 | GET_FIRST_INDEX_OF_REAL_IN_ARRAY := i; 652 | RETURN; 653 | END_IF; 654 | END_FOR; 655 | 656 | GET_FIRST_INDEX_OF_REAL_IN_ARRAY := i_start -1; 657 | END_FUNCTION 658 | 659 | 660 | // Search for first appearance of given number in given array and return its index. 661 | // If number is not present in array, the lowest index minus 1 is returned. 662 | FUNCTION GET_FIRST_INDEX_OF_DINT_IN_ARRAY : DINT 663 | VAR_INPUT 664 | in : DINT; 665 | END_VAR 666 | 667 | VAR_IN_OUT 668 | data : ARRAY[..] OF DINT; 669 | END_VAR 670 | 671 | VAR_TEMP 672 | i : DINT; 673 | i_start : DINT; 674 | i_end : DINT; 675 | END_VAR 676 | 677 | i_start := _firstIndexOf(data); 678 | i_end := _lastIndexOf(data); 679 | 680 | FOR i := i_start TO i_end DO 681 | IF data[i] = in THEN 682 | GET_FIRST_INDEX_OF_DINT_IN_ARRAY := i; 683 | RETURN; 684 | END_IF; 685 | END_FOR; 686 | 687 | GET_FIRST_INDEX_OF_DINT_IN_ARRAY := i_start -1; 688 | END_FUNCTION 689 | 690 | // Search for first appearance of given number in given array and return its index. 691 | // If number is not present in array, the lowest index minus 1 is returned. 692 | FUNCTION GET_FIRST_INDEX_OF_UDINT_IN_ARRAY : DINT 693 | VAR_INPUT 694 | in : UDINT; 695 | END_VAR 696 | 697 | VAR_IN_OUT 698 | data : ARRAY[..] OF UDINT; 699 | END_VAR 700 | 701 | VAR_TEMP 702 | i : DINT; 703 | i_start : DINT; 704 | i_end : DINT; 705 | END_VAR 706 | 707 | i_start := _firstIndexOf(data); 708 | i_end := _lastIndexOf(data); 709 | 710 | FOR i := i_start TO i_end DO 711 | IF data[i] = in THEN 712 | GET_FIRST_INDEX_OF_UDINT_IN_ARRAY := i; 713 | RETURN; 714 | END_IF; 715 | END_FOR; 716 | 717 | GET_FIRST_INDEX_OF_UDINT_IN_ARRAY := i_start -1; 718 | END_FUNCTION 719 | 720 | // Search for first appearance of given number in given array and return its index. 721 | // If number is not present in array, the lowest index minus 1 is returned. 722 | FUNCTION GET_FIRST_INDEX_OF_INT_IN_ARRAY : DINT 723 | VAR_INPUT 724 | in : INT; 725 | END_VAR 726 | 727 | VAR_IN_OUT 728 | data : ARRAY[..] OF INT; 729 | END_VAR 730 | 731 | VAR_TEMP 732 | i : DINT; 733 | i_start : DINT; 734 | i_end : DINT; 735 | END_VAR 736 | 737 | i_start := _firstIndexOf(data); 738 | i_end := _lastIndexOf(data); 739 | 740 | FOR i := i_start TO i_end DO 741 | IF data[i] = in THEN 742 | GET_FIRST_INDEX_OF_INT_IN_ARRAY := i; 743 | RETURN; 744 | END_IF; 745 | END_FOR; 746 | 747 | GET_FIRST_INDEX_OF_INT_IN_ARRAY := i_start -1; 748 | END_FUNCTION 749 | 750 | // Search for first appearance of given number in given array and return its index. 751 | // If number is not present in array, the lowest index minus 1 is returned. 752 | FUNCTION GET_FIRST_INDEX_OF_UINT_IN_ARRAY : DINT 753 | VAR_INPUT 754 | in : UINT; 755 | END_VAR 756 | 757 | VAR_IN_OUT 758 | data : ARRAY[..] OF UINT; 759 | END_VAR 760 | 761 | VAR_TEMP 762 | i : DINT; 763 | i_start : DINT; 764 | i_end : DINT; 765 | END_VAR 766 | 767 | i_start := _firstIndexOf(data); 768 | i_end := _lastIndexOf(data); 769 | 770 | FOR i := i_start TO i_end DO 771 | IF data[i] = in THEN 772 | GET_FIRST_INDEX_OF_UINT_IN_ARRAY := i; 773 | RETURN; 774 | END_IF; 775 | END_FOR; 776 | 777 | GET_FIRST_INDEX_OF_UINT_IN_ARRAY := i_start -1; 778 | END_FUNCTION 779 | 780 | // Search for first appearance of given number in given array and return its index. 781 | // If number is not present in array, the lowest index minus 1 is returned. 782 | FUNCTION GET_FIRST_INDEX_OF_SINT_IN_ARRAY : DINT 783 | VAR_INPUT 784 | in : SINT; 785 | END_VAR 786 | 787 | VAR_IN_OUT 788 | data : ARRAY[..] OF SINT; 789 | END_VAR 790 | 791 | VAR_TEMP 792 | i : DINT; 793 | i_start : DINT; 794 | i_end : DINT; 795 | END_VAR 796 | 797 | i_start := _firstIndexOf(data); 798 | i_end := _lastIndexOf(data); 799 | 800 | FOR i := i_start TO i_end DO 801 | IF data[i] = in THEN 802 | GET_FIRST_INDEX_OF_SINT_IN_ARRAY := i; 803 | RETURN; 804 | END_IF; 805 | END_FOR; 806 | 807 | GET_FIRST_INDEX_OF_SINT_IN_ARRAY := i_start -1; 808 | END_FUNCTION 809 | 810 | // Search for first appearance of given number in given array and return its index. 811 | // If number is not present in array, the lowest index minus 1 is returned. 812 | FUNCTION GET_FIRST_INDEX_OF_USINT_IN_ARRAY : DINT 813 | VAR_INPUT 814 | in : USINT; 815 | END_VAR 816 | 817 | VAR_IN_OUT 818 | data : ARRAY[..] OF USINT; 819 | END_VAR 820 | 821 | VAR_TEMP 822 | i : DINT; 823 | i_start : DINT; 824 | i_end : DINT; 825 | END_VAR 826 | 827 | i_start := _firstIndexOf(data); 828 | i_end := _lastIndexOf(data); 829 | 830 | FOR i := i_start TO i_end DO 831 | IF data[i] = in THEN 832 | GET_FIRST_INDEX_OF_USINT_IN_ARRAY := i; 833 | RETURN; 834 | END_IF; 835 | END_FOR; 836 | 837 | GET_FIRST_INDEX_OF_USINT_IN_ARRAY := i_start -1; 838 | END_FUNCTION 839 | 840 | 841 | // Search for first appearance of given value in given array and return its index. 842 | // If value is not present in array, the lowest index minus 1 is returned. 843 | FUNCTION GET_FIRST_INDEX_OF_BYTE_IN_ARRAY : DINT 844 | VAR_INPUT 845 | in : BYTE; 846 | END_VAR 847 | 848 | VAR_IN_OUT 849 | data : ARRAY[..] OF BYTE; 850 | END_VAR 851 | 852 | VAR_TEMP 853 | i : DINT; 854 | i_start : DINT; 855 | i_end : DINT; 856 | END_VAR 857 | 858 | i_start := _firstIndexOf(data); 859 | i_end := _lastIndexOf(data); 860 | 861 | FOR i := i_start TO i_end DO 862 | IF data[i] = in THEN 863 | GET_FIRST_INDEX_OF_BYTE_IN_ARRAY := i; 864 | RETURN; 865 | END_IF; 866 | END_FOR; 867 | 868 | GET_FIRST_INDEX_OF_BYTE_IN_ARRAY := i_start -1; 869 | END_FUNCTION 870 | 871 | // Search for first appearance of given value in given array and return its index. 872 | // If value is not present in array, the lowest index minus 1 is returned. 873 | FUNCTION GET_FIRST_INDEX_OF_WORD_IN_ARRAY : DINT 874 | VAR_INPUT 875 | in : WORD; 876 | END_VAR 877 | 878 | VAR_IN_OUT 879 | data : ARRAY[..] OF WORD; 880 | END_VAR 881 | 882 | VAR_TEMP 883 | i : DINT; 884 | i_start : DINT; 885 | i_end : DINT; 886 | END_VAR 887 | 888 | i_start := _firstIndexOf(data); 889 | i_end := _lastIndexOf(data); 890 | 891 | FOR i := i_start TO i_end DO 892 | IF data[i] = in THEN 893 | GET_FIRST_INDEX_OF_WORD_IN_ARRAY := i; 894 | RETURN; 895 | END_IF; 896 | END_FOR; 897 | 898 | GET_FIRST_INDEX_OF_WORD_IN_ARRAY := i_start -1; 899 | END_FUNCTION 900 | 901 | // Search for first appearance of given value in given array and return its index. 902 | // If value is not present in array, the lowest index minus 1 is returned. 903 | FUNCTION GET_FIRST_INDEX_OF_DWORD_IN_ARRAY : DINT 904 | VAR_INPUT 905 | in : DWORD; 906 | END_VAR 907 | 908 | VAR_IN_OUT 909 | data : ARRAY[..] OF DWORD; 910 | END_VAR 911 | 912 | VAR_TEMP 913 | i : DINT; 914 | i_start : DINT; 915 | i_end : DINT; 916 | END_VAR 917 | 918 | i_start := _firstIndexOf(data); 919 | i_end := _lastIndexOf(data); 920 | 921 | FOR i := i_start TO i_end DO 922 | IF data[i] = in THEN 923 | GET_FIRST_INDEX_OF_DWORD_IN_ARRAY := i; 924 | RETURN; 925 | END_IF; 926 | END_FOR; 927 | 928 | GET_FIRST_INDEX_OF_DWORD_IN_ARRAY := i_start -1; 929 | END_FUNCTION 930 | 931 | 932 | // Search for first appearance of given string in given array and return its index. 933 | // If string is not present in array, the lowest index minus 1 is returned. 934 | // ATTENTION: due to limitations of ST only arrays of STRING with default length (80) can be used. 935 | FUNCTION GET_FIRST_INDEX_OF_STRING_IN_ARRAY : DINT 936 | VAR_INPUT 937 | in : STRING; 938 | END_VAR 939 | 940 | VAR_IN_OUT 941 | data : ARRAY[..] OF STRING; 942 | END_VAR 943 | 944 | VAR_TEMP 945 | i : DINT; 946 | i_start : DINT; 947 | i_end : DINT; 948 | END_VAR 949 | 950 | i_start := _firstIndexOf(data); 951 | i_end := _lastIndexOf(data); 952 | 953 | FOR i := i_start TO i_end DO 954 | IF data[i] = in THEN 955 | GET_FIRST_INDEX_OF_STRING_IN_ARRAY := i; 956 | RETURN; 957 | END_IF; 958 | END_FOR; 959 | 960 | GET_FIRST_INDEX_OF_STRING_IN_ARRAY := i_start -1; 961 | END_FUNCTION 962 | END_IMPLEMENTATION 963 | -------------------------------------------------------------------------------- /UTILITIES_BYTE.st: -------------------------------------------------------------------------------- 1 | UNIT UTILITIES_BYTE; 2 | 3 | // This unit 'UTILITIES_BYTE' contains some common byte functions and types which are missing in standard library. 4 | // Please check Git repository for updates from time to time. 5 | 6 | INTERFACE 7 | USES UTILITIES_ARRAY; 8 | 9 | FUNCTION BYTES_TO_STRING; 10 | FUNCTION STRING_TO_BYTES; 11 | 12 | 13 | TYPE 14 | BINARY_ENCODING : ( 15 | BASE64, // Base 64 Encoding as specified by RFC 4648 16 | BASE64_URL // Base 64 URL Encoding as specified by RFC 4648 17 | ); 18 | END_TYPE 19 | 20 | 21 | VAR_GLOBAL CONSTANT 22 | // This ARRAY is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" equivalents AS specified in "Table 1: The Base64 Alphabet" OF RFC 2045 (AND RFC 4648). 23 | toBase64 : ARRAY[0..63] OF BYTE := [ 24 | 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, // 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 25 | 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, // 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 26 | 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 27 | 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, // 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 28 | 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47 // '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' 29 | ]; 30 | // This ARRAY is a lookup table for "URL and Filename safe Base64" as specified in Table 2 OF the RFC 4648, WITH the '+' AND '/' changed TO '-' AND '_'. This table is used when BASE64_URL is specified. 31 | toBase64URL : ARRAY[0..63] OF BYTE := [ 32 | 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, // 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 33 | 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, // 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 34 | 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 35 | 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, // 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 36 | 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 95 // '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' 37 | ]; 38 | END_VAR 39 | END_INTERFACE 40 | 41 | 42 | IMPLEMENTATION 43 | // Converts given BYTE array to a string using given encoding. 44 | // If source array is bigger than 252 bytes, returned string will be cut to string's max. length of 252. 45 | FUNCTION BYTES_TO_STRING : STRING[252] 46 | VAR_INPUT 47 | in : ARRAY[..] OF BYTE; 48 | encoding : BINARY_ENCODING; 49 | END_VAR 50 | 51 | VAR_TEMP 52 | out : STRING[252]; 53 | in_start : DINT; 54 | in_end : DINT; 55 | in_byte1: BYTE; 56 | in_byte2: BYTE; 57 | in_byte3: BYTE; 58 | out_byte1: USINT; 59 | out_byte2: USINT; 60 | out_byte3: USINT; 61 | out_byte4: USINT; 62 | alphabet : ARRAY[0..63] OF BYTE; 63 | i_in : DINT; 64 | i_out : DINT := 1; 65 | END_VAR 66 | 67 | in_start := _firstIndexOf(in); 68 | in_end := _lastIndexOf(in); 69 | 70 | // select alphabet by given parameter (enum) 71 | IF encoding = BASE64 THEN 72 | alphabet := toBase64; 73 | ELSIF encoding = BASE64_URL THEN 74 | alphabet := toBase64URL; 75 | END_IF; 76 | 77 | // convert 3 bytes from source to 4 bytes encoded output 78 | FOR i_in := in_start TO in_end BY 3 DO 79 | // read 3 bytes from source 80 | in_byte1 := in[i_in]; 81 | IF (i_in + 1) <= in_end THEN 82 | in_byte2 := in[i_in + 1]; 83 | ELSE 84 | in_byte2 := 0; 85 | END_IF; 86 | IF (i_in + 2) <= in_end THEN 87 | in_byte3 := in[i_in + 2]; 88 | ELSE 89 | in_byte3 := 0; 90 | END_IF; 91 | 92 | // calculate 4 result bytes 93 | out_byte1 := BYTE_TO_USINT( SHR(in_byte1 & 2#11111100, 2)); // first 6 bits of byte 1 94 | out_byte2 := BYTE_TO_USINT(SHL(in_byte1 & 2#00000011, 4) OR SHR(in_byte2 & 2#11110000, 4)); // last 2 bits of byte 1 and first 4 bits of byte 2 95 | out_byte3 := BYTE_TO_USINT(SHL(in_byte2 & 2#00001111, 2) OR SHR(in_byte3 & 2#11000000, 6)); // last 4 bits of byte 2 and first 2 bits of byte 3 96 | out_byte4 := BYTE_TO_USINT( in_byte3 & 2#00111111 ); // last 6 bits of byte 3 97 | 98 | // convert result bytes using selected alphabet 99 | out[i_out ] := alphabet[out_byte1]; 100 | out[i_out + 1] := alphabet[out_byte2]; 101 | out[i_out + 2] := alphabet[out_byte3]; 102 | out[i_out + 3] := alphabet[out_byte4]; 103 | 104 | // apply padding (if required) 105 | IF (i_in + 1) > in_end THEN 106 | out[i_out + 2] := 61; // padding character '=' 107 | END_IF; 108 | IF (i_in + 2) > in_end THEN 109 | out[i_out + 3] := 61; // padding character '=' 110 | END_IF; 111 | 112 | i_out := i_out + 4; 113 | IF i_out >= 252 THEN 114 | EXIT; 115 | END_IF; 116 | END_FOR; 117 | 118 | BYTES_TO_STRING := out; 119 | END_FUNCTION 120 | 121 | // Converts given string into given BYTE array using given encoding. 122 | // If target array is too small, given string will be cut. If target array is too large, remaining bytes will be set to NULL (0). 123 | FUNCTION STRING_TO_BYTES : VOID 124 | VAR_INPUT 125 | in : STRING[254]; 126 | encoding : BINARY_ENCODING; 127 | END_VAR 128 | 129 | VAR_IN_OUT 130 | out : ARRAY[..] OF BYTE; 131 | END_VAR 132 | 133 | VAR_TEMP 134 | in_len : INT; 135 | out_len : DINT; 136 | out_start : DINT; 137 | out_end : DINT; 138 | in_char1: BYTE; 139 | in_char2: BYTE; 140 | in_char3: BYTE; 141 | in_char4: BYTE; 142 | in_byte1: USINT; 143 | in_byte2: USINT; 144 | in_byte3: USINT; 145 | in_byte4: USINT; 146 | out_byte1: BYTE; 147 | out_byte2: BYTE; 148 | out_byte3: BYTE; 149 | alphabet : ARRAY[0..63] OF BYTE; 150 | i_in : DINT; 151 | i_out : DINT; 152 | END_VAR 153 | 154 | in_len := LEN(in); 155 | 156 | out_start := _firstIndexOf(out); 157 | out_end := _lastIndexOf(out); 158 | out_len := out_end - out_start + 1; 159 | 160 | // select alphabet by given parameter (enum) 161 | IF encoding = BASE64 THEN 162 | alphabet := toBase64; 163 | ELSIF encoding = BASE64_URL THEN 164 | alphabet := toBase64URL; 165 | END_IF; 166 | 167 | // convert 4 bytes from source to 3 bytes decoded output 168 | i_out := out_start; 169 | FOR i_in := 1 TO in_len BY 4 DO 170 | // read 4 bytes from source 171 | in_char1 := in[i_in + 0]; 172 | in_char2 := in[i_in + 1]; 173 | IF (i_in + 2) <= in_len THEN 174 | in_char3 := in[i_in + 2]; 175 | ELSE 176 | in_char3 := 61; // padding character '=' 177 | END_IF; 178 | IF (i_in + 3) <= in_len THEN 179 | in_char4 := in[i_in + 3]; 180 | ELSE 181 | in_char4 := 61; // padding character '=' 182 | END_IF; 183 | 184 | // convert source bytes using selected alphabet 185 | in_byte1 := DINT_TO_USINT(GET_FIRST_INDEX_OF_BYTE_IN_ARRAY(in_char1, alphabet)); 186 | in_byte2 := DINT_TO_USINT(GET_FIRST_INDEX_OF_BYTE_IN_ARRAY(in_char2, alphabet)); 187 | IF in_char3 = 61 THEN 188 | in_byte3 := 0; 189 | ELSE 190 | in_byte3 := DINT_TO_USINT(GET_FIRST_INDEX_OF_BYTE_IN_ARRAY(in_char3, alphabet)); 191 | END_IF; 192 | IF in_char4 = 61 THEN 193 | in_byte4 := 0; 194 | ELSE 195 | in_byte4 := DINT_TO_USINT(GET_FIRST_INDEX_OF_BYTE_IN_ARRAY(in_char4, alphabet)); 196 | END_IF; 197 | 198 | // calculate 3 result bytes 199 | out_byte1 := SHL(USINT_TO_BYTE(in_byte1) , 2) OR SHR(USINT_TO_BYTE(in_byte2) & 2#00110000, 4); // last 6 bits from byte 1 and first 2 bits from byte 2 200 | out_byte2 := SHL(USINT_TO_BYTE(in_byte2) & 2#00001111, 4) OR SHR(USINT_TO_BYTE(in_byte3) & 2#00111100, 2); // last 4 bits from byte 2 and first 4 bits from byte 3 201 | out_byte3 := SHL(USINT_TO_BYTE(in_byte3) & 2#00000011, 6) OR USINT_TO_BYTE(in_byte4) ; // last 2 bits from byte 3 and first 6 bits from byte 4 202 | 203 | // write result bytes to output 204 | IF (i_out + 0) <= out_end THEN 205 | out[i_out + 0] := out_byte1; 206 | ELSE 207 | RETURN; 208 | END_IF; 209 | IF (i_out + 1) <= out_end THEN 210 | out[i_out + 1] := out_byte2; 211 | ELSE 212 | RETURN; 213 | END_IF; 214 | IF (i_out + 2) <= out_end THEN 215 | out[i_out + 2] := out_byte3; 216 | ELSE 217 | RETURN; 218 | END_IF; 219 | 220 | i_out := i_out + 3; 221 | END_FOR; 222 | 223 | // set remaining positions to NULL (0) 224 | FOR i_out := i_out TO out_end DO 225 | out[i_out] := 0; 226 | END_FOR; 227 | END_FUNCTION 228 | END_IMPLEMENTATION 229 | -------------------------------------------------------------------------------- /UTILITIES_MATH.st: -------------------------------------------------------------------------------- 1 | UNIT UTILITIES_MATH; 2 | 3 | // This unit 'UTILITIES_MATH' contains some common mathematic functions and types which are missing in standard library. 4 | // Please check Git repository for updates from time to time. 5 | 6 | INTERFACE 7 | USES UTILITIES_ARRAY, UTILITIES_STRING; 8 | 9 | FUNCTION CALCULATE_STATISTICS_OF_LREAL_ARRAY; 10 | FUNCTION CALCULATE_STATISTICS_OF_REAL_ARRAY; 11 | FUNCTION CALCULATE_STATISTICS_OF_DINT_ARRAY; 12 | FUNCTION CALCULATE_STATISTICS_OF_UDINT_ARRAY; 13 | FUNCTION CALCULATE_STATISTICS_OF_INT_ARRAY; 14 | FUNCTION CALCULATE_STATISTICS_OF_UINT_ARRAY; 15 | FUNCTION CALCULATE_STATISTICS_OF_SINT_ARRAY; 16 | FUNCTION CALCULATE_STATISTICS_OF_USINT_ARRAY; 17 | 18 | FUNCTION ROUND_LREAL; 19 | FUNCTION ROUND_REAL; 20 | 21 | 22 | TYPE 23 | // Contains statistics information of an array of floating values. 24 | FLOAT_ARRAY_STATISTICS : STRUCT 25 | count : UDINT; // count of values 26 | sum : LREAL; // sum of all values 27 | min_val : LREAL; // minimal value 28 | max_val : LREAL; // maximal value 29 | med_val : LREAL; // median value 30 | avg_val : LREAL; // average value 31 | sum_sqr : LREAL; // sum of squares 32 | std_dev : LREAL; // standard deviation 33 | END_STRUCT 34 | 35 | // Contains statistics information of an array of integral values. 36 | INTEGRAL_ARRAY_STATISTICS : STRUCT 37 | count : UDINT; // count of values 38 | sum : DINT; // sum of all values 39 | min_val : DINT; // minimal value 40 | max_val : DINT; // maximal value 41 | med_val : DINT; // (lower) median value 42 | avg_val : LREAL; // average value 43 | sum_sqr : LREAL; // sum of squares 44 | std_dev : LREAL; // standard deviation 45 | END_STRUCT 46 | 47 | // Contains statistics information of an array of unsigned integral values. 48 | UNSIGNED_INTEGRAL_ARRAY_STATISTICS : STRUCT 49 | count : UDINT; // count of values 50 | sum : UDINT; // sum of all values 51 | min_val : UDINT; // minimal value 52 | max_val : UDINT; // maximal value 53 | med_val : UDINT; // (lower) median value 54 | avg_val : LREAL; // average value 55 | sum_sqr : LREAL; // sum of squares 56 | std_dev : LREAL; // standard deviation 57 | END_STRUCT 58 | END_TYPE 59 | 60 | 61 | VAR_GLOBAL CONSTANT 62 | Pi : LREAL := 3.1415926535897932384; // ratio of a circle's circumference to its diameter 63 | Phi : LREAL := 1.6180339887498948482; // golden ratio 64 | Euler : LREAL := 2.7182818284590452354; // base of the natural logarithm 65 | END_VAR 66 | END_INTERFACE 67 | 68 | 69 | IMPLEMENTATION 70 | // Calculates statistics of given array containing LREAL values. 71 | // ATTENTION: due to limitations of ST given array will be sorted. If order should be prevented, array MUST be copied first. 72 | FUNCTION CALCULATE_STATISTICS_OF_LREAL_ARRAY : FLOAT_ARRAY_STATISTICS 73 | VAR_IN_OUT 74 | data : ARRAY[..] OF LREAL; 75 | END_VAR 76 | 77 | VAR_TEMP 78 | i : DINT; 79 | i_start : DINT; 80 | i_end : DINT; 81 | out : FLOAT_ARRAY_STATISTICS; 82 | END_VAR 83 | 84 | // sort array 85 | SORT_LREAL_ARRAY(data); 86 | 87 | // get bounds and calculate size 88 | i_start := _firstIndexOf(data); 89 | i_end := _lastIndexOf(data); 90 | out.count := DINT_TO_UDINT(i_end - i_start + 1); 91 | 92 | // determine min and max values 93 | out.min_val := data[i_start]; 94 | out.max_val := data[i_end]; 95 | 96 | // calculate sum of all values 97 | FOR i := i_start TO i_end DO 98 | out.sum := out.sum + data[i]; 99 | END_FOR; 100 | 101 | // calculate average (mean) value 102 | out.avg_val := out.sum / out.count; 103 | 104 | // calculate sum of all squares (to mean) 105 | FOR i := i_start TO i_end DO 106 | out.sum_sqr := out.sum_sqr + (data[i] - out.avg_val) ** 2; 107 | END_FOR; 108 | 109 | // calculate standard deviation 110 | out.std_dev := SQRT(out.sum_sqr / out.count); 111 | 112 | // calculate median value 113 | i := i_start + TRUNC((out.count -1) / 2); // index of (lower) median 114 | IF (out.count MOD 2) = 1 THEN 115 | out.med_val := data[i]; 116 | ELSE 117 | out.med_val := (data[i] + data[i + 1]) / 2; 118 | END_IF; 119 | 120 | CALCULATE_STATISTICS_OF_LREAL_ARRAY := out; 121 | END_FUNCTION 122 | 123 | // Calculates statistics of given array containing REAL values. 124 | // ATTENTION: due to limitations of ST given array will be sorted. If order should be prevented, array MUST be copied first. 125 | FUNCTION CALCULATE_STATISTICS_OF_REAL_ARRAY : FLOAT_ARRAY_STATISTICS 126 | VAR_IN_OUT 127 | data : ARRAY[..] OF REAL; 128 | END_VAR 129 | 130 | VAR_TEMP 131 | i : DINT; 132 | i_start : DINT; 133 | i_end : DINT; 134 | out : FLOAT_ARRAY_STATISTICS; 135 | END_VAR 136 | 137 | // sort array 138 | SORT_REAL_ARRAY(data); 139 | 140 | // get bounds and calculate size 141 | i_start := _firstIndexOf(data); 142 | i_end := _lastIndexOf(data); 143 | out.count := DINT_TO_UDINT(i_end - i_start + 1); 144 | 145 | // determine min and max values 146 | out.min_val := data[i_start]; 147 | out.max_val := data[i_end]; 148 | 149 | // calculate sum of all values 150 | FOR i := i_start TO i_end DO 151 | out.sum := out.sum + data[i]; 152 | END_FOR; 153 | 154 | // calculate average (mean) value 155 | out.avg_val := out.sum / out.count; 156 | 157 | // calculate sum of all squares (to mean) 158 | FOR i := i_start TO i_end DO 159 | out.sum_sqr := out.sum_sqr + (data[i] - out.avg_val) ** 2; 160 | END_FOR; 161 | 162 | // calculate standard deviation 163 | out.std_dev := SQRT(out.sum_sqr / out.count); 164 | 165 | // calculate median value 166 | i := i_start + TRUNC((out.count -1) / 2); // index of (lower) median 167 | IF (out.count MOD 2) = 1 THEN 168 | out.med_val := data[i]; 169 | ELSE 170 | out.med_val := (data[i] + data[i + 1]) / 2; 171 | END_IF; 172 | 173 | CALCULATE_STATISTICS_OF_REAL_ARRAY := out; 174 | END_FUNCTION 175 | 176 | // Calculates statistics of given array containing DINT values. 177 | // ATTENTION: due to limitations of ST given array will be sorted. If order should be prevented, array MUST be copied first. 178 | FUNCTION CALCULATE_STATISTICS_OF_DINT_ARRAY : INTEGRAL_ARRAY_STATISTICS 179 | VAR_IN_OUT 180 | data : ARRAY[..] OF DINT; 181 | END_VAR 182 | 183 | VAR_TEMP 184 | i : DINT; 185 | i_start : DINT; 186 | i_end : DINT; 187 | out : INTEGRAL_ARRAY_STATISTICS; 188 | END_VAR 189 | 190 | // sort source array 191 | SORT_DINT_ARRAY(data); 192 | 193 | // get bounds and calculate size 194 | i_start := _firstIndexOf(data); 195 | i_end := _lastIndexOf(data); 196 | out.count := DINT_TO_UDINT(i_end - i_start + 1); 197 | 198 | // determine min and max values 199 | out.min_val := data[i_start]; 200 | out.max_val := data[i_end]; 201 | 202 | // calculate sum of all values 203 | FOR i := i_start TO i_end DO 204 | out.sum := out.sum + data[i]; 205 | END_FOR; 206 | 207 | // calculate average (mean) value 208 | out.avg_val := DINT_TO_LREAL(out.sum) / out.count; 209 | 210 | // calculate sum of all squares (to mean) 211 | FOR i := i_start TO i_end DO 212 | out.sum_sqr := out.sum_sqr + (data[i] - out.avg_val) ** 2; 213 | END_FOR; 214 | 215 | // calculate standard deviation 216 | out.std_dev := SQRT(out.sum_sqr / out.count); 217 | 218 | // calculate median value 219 | i := i_start + TRUNC((out.count -1) / 2); // index of (lower) median 220 | out.med_val := data[i]; 221 | 222 | CALCULATE_STATISTICS_OF_DINT_ARRAY := out; 223 | END_FUNCTION 224 | 225 | // Calculates statistics of given array containing UDINT values. 226 | // ATTENTION: due to limitations of ST given array will be sorted. If order should be prevented, array MUST be copied first. 227 | FUNCTION CALCULATE_STATISTICS_OF_UDINT_ARRAY : UNSIGNED_INTEGRAL_ARRAY_STATISTICS 228 | VAR_IN_OUT 229 | data : ARRAY[..] OF UDINT; 230 | END_VAR 231 | 232 | VAR_TEMP 233 | i : DINT; 234 | i_start : DINT; 235 | i_end : DINT; 236 | out : UNSIGNED_INTEGRAL_ARRAY_STATISTICS; 237 | END_VAR 238 | 239 | // sort source array 240 | SORT_UDINT_ARRAY(data); 241 | 242 | // get bounds and calculate size 243 | i_start := _firstIndexOf(data); 244 | i_end := _lastIndexOf(data); 245 | out.count := DINT_TO_UDINT(i_end - i_start + 1); 246 | 247 | // determine min and max values 248 | out.min_val := data[i_start]; 249 | out.max_val := data[i_end]; 250 | 251 | // calculate sum of all values 252 | FOR i := i_start TO i_end DO 253 | out.sum := out.sum + data[i]; 254 | END_FOR; 255 | 256 | // calculate average (mean) value 257 | out.avg_val := UDINT_TO_LREAL(out.sum) / out.count; 258 | 259 | // calculate sum of all squares (to mean) 260 | FOR i := i_start TO i_end DO 261 | out.sum_sqr := out.sum_sqr + (data[i] - out.avg_val) ** 2; 262 | END_FOR; 263 | 264 | // calculate standard deviation 265 | out.std_dev := SQRT(out.sum_sqr / out.count); 266 | 267 | // calculate median value 268 | i := i_start + TRUNC((out.count -1) / 2); // index of (lower) median 269 | out.med_val := data[i]; 270 | 271 | CALCULATE_STATISTICS_OF_UDINT_ARRAY := out; 272 | END_FUNCTION 273 | 274 | // Calculates statistics of given array containing INT values. 275 | // ATTENTION: due to limitations of ST given array will be sorted. If order should be prevented, array MUST be copied first. 276 | FUNCTION CALCULATE_STATISTICS_OF_INT_ARRAY : INTEGRAL_ARRAY_STATISTICS 277 | VAR_IN_OUT 278 | data : ARRAY[..] OF INT; 279 | END_VAR 280 | 281 | VAR_TEMP 282 | i : DINT; 283 | i_start : DINT; 284 | i_end : DINT; 285 | out : INTEGRAL_ARRAY_STATISTICS; 286 | END_VAR 287 | 288 | // sort source array 289 | SORT_INT_ARRAY(data); 290 | 291 | // get bounds and calculate size 292 | i_start := _firstIndexOf(data); 293 | i_end := _lastIndexOf(data); 294 | out.count := DINT_TO_UDINT(i_end - i_start + 1); 295 | 296 | // determine min and max values 297 | out.min_val := data[i_start]; 298 | out.max_val := data[i_end]; 299 | 300 | // calculate sum of all values 301 | FOR i := i_start TO i_end DO 302 | out.sum := out.sum + data[i]; 303 | END_FOR; 304 | 305 | // calculate average (mean) value 306 | out.avg_val := DINT_TO_LREAL(out.sum) / out.count; 307 | 308 | // calculate sum of all squares (to mean) 309 | FOR i := i_start TO i_end DO 310 | out.sum_sqr := out.sum_sqr + (data[i] - out.avg_val) ** 2; 311 | END_FOR; 312 | 313 | // calculate standard deviation 314 | out.std_dev := SQRT(out.sum_sqr / out.count); 315 | 316 | // calculate median value 317 | i := i_start + TRUNC((out.count -1) / 2); // index of (lower) median 318 | out.med_val := data[i]; 319 | 320 | CALCULATE_STATISTICS_OF_INT_ARRAY := out; 321 | END_FUNCTION 322 | 323 | // Calculates statistics of given array containing UINT values. 324 | // ATTENTION: due to limitations of ST given array will be sorted. If order should be prevented, array MUST be copied first. 325 | FUNCTION CALCULATE_STATISTICS_OF_UINT_ARRAY : UNSIGNED_INTEGRAL_ARRAY_STATISTICS 326 | VAR_IN_OUT 327 | data : ARRAY[..] OF UINT; 328 | END_VAR 329 | 330 | VAR_TEMP 331 | i : DINT; 332 | i_start : DINT; 333 | i_end : DINT; 334 | out : UNSIGNED_INTEGRAL_ARRAY_STATISTICS; 335 | END_VAR 336 | 337 | // sort source array 338 | SORT_UINT_ARRAY(data); 339 | 340 | // get bounds and calculate size 341 | i_start := _firstIndexOf(data); 342 | i_end := _lastIndexOf(data); 343 | out.count := DINT_TO_UDINT(i_end - i_start + 1); 344 | 345 | // determine min and max values 346 | out.min_val := data[i_start]; 347 | out.max_val := data[i_end]; 348 | 349 | // calculate sum of all values 350 | FOR i := i_start TO i_end DO 351 | out.sum := out.sum + data[i]; 352 | END_FOR; 353 | 354 | // calculate average (mean) value 355 | out.avg_val := UDINT_TO_LREAL(out.sum) / out.count; 356 | 357 | // calculate sum of all squares (to mean) 358 | FOR i := i_start TO i_end DO 359 | out.sum_sqr := out.sum_sqr + (data[i] - out.avg_val) ** 2; 360 | END_FOR; 361 | 362 | // calculate standard deviation 363 | out.std_dev := SQRT(out.sum_sqr / out.count); 364 | 365 | // calculate median value 366 | i := i_start + TRUNC((out.count -1) / 2); // index of (lower) median 367 | out.med_val := data[i]; 368 | 369 | CALCULATE_STATISTICS_OF_UINT_ARRAY := out; 370 | END_FUNCTION 371 | 372 | // Calculates statistics of given array containing SINT values. 373 | // ATTENTION: due to limitations of ST given array will be sorted. If order should be prevented, array MUST be copied first. 374 | FUNCTION CALCULATE_STATISTICS_OF_SINT_ARRAY : INTEGRAL_ARRAY_STATISTICS 375 | VAR_IN_OUT 376 | data : ARRAY[..] OF SINT; 377 | END_VAR 378 | 379 | VAR_TEMP 380 | i : DINT; 381 | i_start : DINT; 382 | i_end : DINT; 383 | out : INTEGRAL_ARRAY_STATISTICS; 384 | END_VAR 385 | 386 | // sort source array 387 | SORT_SINT_ARRAY(data); 388 | 389 | // get bounds and calculate size 390 | i_start := _firstIndexOf(data); 391 | i_end := _lastIndexOf(data); 392 | out.count := DINT_TO_UDINT(i_end - i_start + 1); 393 | 394 | // determine min and max values 395 | out.min_val := data[i_start]; 396 | out.max_val := data[i_end]; 397 | 398 | // calculate sum of all values 399 | FOR i := i_start TO i_end DO 400 | out.sum := out.sum + data[i]; 401 | END_FOR; 402 | 403 | // calculate average (mean) value 404 | out.avg_val := DINT_TO_LREAL(out.sum) / out.count; 405 | 406 | // calculate sum of all squares (to mean) 407 | FOR i := i_start TO i_end DO 408 | out.sum_sqr := out.sum_sqr + (data[i] - out.avg_val) ** 2; 409 | END_FOR; 410 | 411 | // calculate standard deviation 412 | out.std_dev := SQRT(out.sum_sqr / out.count); 413 | 414 | // calculate median value 415 | i := i_start + TRUNC((out.count -1) / 2); // index of (lower) median 416 | out.med_val := data[i]; 417 | 418 | CALCULATE_STATISTICS_OF_SINT_ARRAY := out; 419 | END_FUNCTION 420 | 421 | // Calculates statistics of given array containing USINT values. 422 | // ATTENTION: due to limitations of ST given array will be sorted. If order should be prevented, array MUST be copied first. 423 | FUNCTION CALCULATE_STATISTICS_OF_USINT_ARRAY : UNSIGNED_INTEGRAL_ARRAY_STATISTICS 424 | VAR_IN_OUT 425 | data : ARRAY[..] OF USINT; 426 | END_VAR 427 | 428 | VAR_TEMP 429 | i : DINT; 430 | i_start : DINT; 431 | i_end : DINT; 432 | out : UNSIGNED_INTEGRAL_ARRAY_STATISTICS; 433 | END_VAR 434 | 435 | // sort source array 436 | SORT_USINT_ARRAY(data); 437 | 438 | // get bounds and calculate size 439 | i_start := _firstIndexOf(data); 440 | i_end := _lastIndexOf(data); 441 | out.count := DINT_TO_UDINT(i_end - i_start + 1); 442 | 443 | // determine min and max values 444 | out.min_val := data[i_start]; 445 | out.max_val := data[i_end]; 446 | 447 | // calculate sum of all values 448 | FOR i := i_start TO i_end DO 449 | out.sum := out.sum + data[i]; 450 | END_FOR; 451 | 452 | // calculate average (mean) value 453 | out.avg_val := UDINT_TO_LREAL(out.sum) / out.count; 454 | 455 | // calculate sum of all squares (to mean) 456 | FOR i := i_start TO i_end DO 457 | out.sum_sqr := out.sum_sqr + (data[i] - out.avg_val) ** 2; 458 | END_FOR; 459 | 460 | // calculate standard deviation 461 | out.std_dev := SQRT(out.sum_sqr / out.count); 462 | 463 | // calculate median value 464 | i := i_start + TRUNC((out.count -1) / 2); // index of (lower) median 465 | out.med_val := data[i]; 466 | 467 | CALCULATE_STATISTICS_OF_USINT_ARRAY := out; 468 | END_FUNCTION 469 | 470 | 471 | // Returns a rounded LREAL value with given precision. 472 | // Examples: ROUND_LREAL(1.2323, 3) => 1.232 / ROUND_LREAL(9.8765, 2) => 9.88 473 | FUNCTION ROUND_LREAL : LREAL 474 | VAR_INPUT 475 | in : LREAL; 476 | precision : USINT; 477 | END_VAR 478 | 479 | VAR_TEMP 480 | str : STRING; 481 | i_exp_delimiter : INT; 482 | i_exp_value : INT; 483 | i_decimal_point : INT; 484 | i_digit_to_round : INT; 485 | out : LREAL; 486 | END_VAR 487 | 488 | str := LREAL_TO_STRING(in); 489 | 490 | i_exp_delimiter := FIND(str, 'e'); 491 | IF i_exp_delimiter > 0 THEN 492 | i_exp_value := STRING_TO_INT(MID(str, LEN(str) - i_exp_delimiter, i_exp_delimiter + 1)); 493 | END_IF; 494 | IF i_exp_value < 0 THEN 495 | str := CONCAT3( 496 | PAD_STRING_RIGHT('0.', INT_TO_USINT(i_exp_value * -1 + 1), '0'), 497 | LEFT(str, 1), 498 | MID(str, i_exp_delimiter - 3, 3) 499 | ); 500 | END_IF; 501 | 502 | i_decimal_point := FIND(str, '.'); 503 | out := STRING_TO_LREAL(LEFT(str, i_decimal_point + precision)); 504 | 505 | i_digit_to_round := i_decimal_point + precision + 1; 506 | IF i_digit_to_round <= LEN(str) AND str[i_digit_to_round] >= 53 THEN 507 | IF in >= 0 THEN 508 | out := out + EXPD(-precision); 509 | ELSE 510 | out := out - EXPD(-precision); 511 | END_IF; 512 | END_IF; 513 | 514 | ROUND_LREAL := out; 515 | END_FUNCTION 516 | 517 | // Returns a rounded REAL value with given precision. 518 | // Examples: ROUND_REAL(1.2323, 3) => 1.232 / ROUND_REAL(9.8765, 2) => 9.88 519 | FUNCTION ROUND_REAL : REAL 520 | VAR_INPUT 521 | in : REAL; 522 | precision : USINT; 523 | END_VAR 524 | 525 | VAR_TEMP 526 | str : STRING; 527 | i_exp_delimiter : INT; 528 | i_exp_value : INT; 529 | i_decimal_point : INT; 530 | i_digit_to_round : INT; 531 | out : REAL; 532 | END_VAR 533 | 534 | str := REAL_TO_STRING(in); 535 | 536 | i_exp_delimiter := FIND(str, 'e'); 537 | IF i_exp_delimiter > 0 THEN 538 | i_exp_value := STRING_TO_INT(MID(str, LEN(str) - i_exp_delimiter, i_exp_delimiter + 1)); 539 | END_IF; 540 | IF i_exp_value < 0 THEN 541 | str := CONCAT3( 542 | PAD_STRING_RIGHT('0.', INT_TO_USINT(i_exp_value * -1 + 1), '0'), 543 | LEFT(str, 1), 544 | MID(str, i_exp_delimiter - 3, 3) 545 | ); 546 | END_IF; 547 | 548 | i_decimal_point := FIND(str, '.'); 549 | out := STRING_TO_REAL(LEFT(str, i_decimal_point + precision)); 550 | 551 | i_digit_to_round := i_decimal_point + precision + 1; 552 | IF i_digit_to_round <= LEN(str) AND str[i_digit_to_round] >= 53 THEN 553 | IF in >= 0 THEN 554 | out := out + EXPD(-precision); 555 | ELSE 556 | out := out - EXPD(-precision); 557 | END_IF; 558 | END_IF; 559 | 560 | ROUND_REAL := out; 561 | END_FUNCTION 562 | END_IMPLEMENTATION 563 | -------------------------------------------------------------------------------- /UTILITIES_STRING.st: -------------------------------------------------------------------------------- 1 | UNIT UTILITIES_STRING; 2 | 3 | // This unit 'UTILITIES_STRING' contains some common string functions and types which are missing in standard library. 4 | // Please check Git repository for updates from time to time. 5 | 6 | INTERFACE 7 | FUNCTION CONCAT3; 8 | FUNCTION CONCAT4; 9 | FUNCTION CONCAT5; 10 | FUNCTION CONCAT6; 11 | FUNCTION CONCAT7; 12 | FUNCTION CONCAT8; 13 | FUNCTION CONCAT9; 14 | 15 | FUNCTION STRING_STARTSWITH; 16 | FUNCTION STRING_ENDSWITH; 17 | 18 | FUNCTION COMPARE_STRINGS; 19 | 20 | FUNCTION PAD_STRING_LEFT; 21 | FUNCTION PAD_STRING_RIGHT; 22 | 23 | FUNCTION ENCODE_STRING; 24 | FUNCTION DECODE_STRING; 25 | 26 | FUNCTION USINT_TO_STRING; 27 | FUNCTION SINT_TO_STRING; 28 | FUNCTION UINT_TO_STRING; 29 | FUNCTION INT_TO_STRING; 30 | 31 | FUNCTION STRING_TO_USINT; 32 | FUNCTION STRING_TO_SINT; 33 | FUNCTION STRING_TO_UINT; 34 | FUNCTION STRING_TO_INT; 35 | 36 | 37 | TYPE 38 | STRING_ENCODING : ( 39 | ASCII, // 7-Bit US-ASCII (American Standard Code FOR Information Interchange) 40 | ISO8859_1 // 8-Bit ISO 8859-1 (Latin-1; Western-Europe) 41 | ); 42 | END_TYPE 43 | END_INTERFACE 44 | 45 | 46 | IMPLEMENTATION 47 | FUNCTION CONCAT3 : STRING[254] 48 | VAR_INPUT 49 | in1 : STRING[254]; 50 | in2 : STRING[254]; 51 | in3 : STRING[254]; 52 | END_VAR 53 | 54 | CONCAT3 := CONCAT(CONCAT(in1, in2), in3); 55 | END_FUNCTION 56 | 57 | FUNCTION CONCAT4 : STRING[254] 58 | VAR_INPUT 59 | in1 : STRING[254]; 60 | in2 : STRING[254]; 61 | in3 : STRING[254]; 62 | in4 : STRING[254]; 63 | END_VAR 64 | 65 | CONCAT4 := CONCAT(CONCAT(CONCAT(in1, in2), in3), in4); 66 | END_FUNCTION 67 | 68 | FUNCTION CONCAT5 : STRING[254] 69 | VAR_INPUT 70 | in1 : STRING[254]; 71 | in2 : STRING[254]; 72 | in3 : STRING[254]; 73 | in4 : STRING[254]; 74 | in5 : STRING[254]; 75 | END_VAR 76 | 77 | CONCAT5 := CONCAT(CONCAT(CONCAT(CONCAT(in1, in2), in3), in4), in5); 78 | END_FUNCTION 79 | 80 | FUNCTION CONCAT6 : STRING[254] 81 | VAR_INPUT 82 | in1 : STRING[254]; 83 | in2 : STRING[254]; 84 | in3 : STRING[254]; 85 | in4 : STRING[254]; 86 | in5 : STRING[254]; 87 | in6 : STRING[254]; 88 | END_VAR 89 | 90 | CONCAT6 := CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(in1, in2), in3), in4), in5), in6); 91 | END_FUNCTION 92 | 93 | FUNCTION CONCAT7 : STRING[254] 94 | VAR_INPUT 95 | in1 : STRING[254]; 96 | in2 : STRING[254]; 97 | in3 : STRING[254]; 98 | in4 : STRING[254]; 99 | in5 : STRING[254]; 100 | in6 : STRING[254]; 101 | in7 : STRING[254]; 102 | END_VAR 103 | 104 | CONCAT7 := CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(in1, in2), in3), in4), in5), in6), in7); 105 | END_FUNCTION 106 | 107 | FUNCTION CONCAT8 : STRING[254] 108 | VAR_INPUT 109 | in1 : STRING[254]; 110 | in2 : STRING[254]; 111 | in3 : STRING[254]; 112 | in4 : STRING[254]; 113 | in5 : STRING[254]; 114 | in6 : STRING[254]; 115 | in7 : STRING[254]; 116 | in8 : STRING[254]; 117 | END_VAR 118 | 119 | CONCAT8 := CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(in1, in2), in3), in4), in5), in6), in7), in8); 120 | END_FUNCTION 121 | 122 | FUNCTION CONCAT9 : STRING[254] 123 | VAR_INPUT 124 | in1 : STRING[254]; 125 | in2 : STRING[254]; 126 | in3 : STRING[254]; 127 | in4 : STRING[254]; 128 | in5 : STRING[254]; 129 | in6 : STRING[254]; 130 | in7 : STRING[254]; 131 | in8 : STRING[254]; 132 | in9 : STRING[254]; 133 | END_VAR 134 | 135 | CONCAT9 := CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(in1, in2), in3), in4), in5), in6), in7), in8), in9); 136 | END_FUNCTION 137 | 138 | 139 | // Returns TRUE if given string in first parameter starts with given string in second parameter. 140 | // This function is case-sensitive. 141 | FUNCTION STRING_STARTSWITH : BOOL 142 | VAR_INPUT 143 | in1 : STRING[254]; 144 | in2 : STRING[254]; 145 | END_VAR 146 | 147 | VAR_TEMP 148 | in1_len : INT; 149 | in2_len : INT; 150 | END_VAR 151 | 152 | in1_len := LEN(in1); 153 | in2_len := LEN(in2); 154 | 155 | IF in1_len < in2_len THEN 156 | STRING_STARTSWITH := FALSE; 157 | ELSE 158 | STRING_STARTSWITH := LEFT(in1, in2_len) = in2; 159 | END_IF; 160 | END_FUNCTION 161 | 162 | // Returns TRUE if given string in first parameter ends with given string in second parameter. 163 | // This function is case-sensitive. 164 | FUNCTION STRING_ENDSWITH : BOOL 165 | VAR_INPUT 166 | in1 : STRING[254]; 167 | in2 : STRING[254]; 168 | END_VAR 169 | 170 | VAR_TEMP 171 | in1_len : INT; 172 | in2_len : INT; 173 | END_VAR 174 | 175 | in1_len := LEN(in1); 176 | in2_len := LEN(in2); 177 | 178 | IF in1_len < in2_len THEN 179 | STRING_ENDSWITH := FALSE; 180 | ELSE 181 | STRING_ENDSWITH := RIGHT(in1, in2_len) = in2; 182 | END_IF; 183 | END_FUNCTION 184 | 185 | 186 | // Compares two strings lexicographically (based on the ISO 8859-1 / Latin 1 representation). 187 | // Returns 0 if the both strings are equal, a negative value if string 1 is less than the string 2 or a positive value if string 1 is greater than string 2. 188 | // This function is case-sensitive. 189 | FUNCTION COMPARE_STRINGS : INT 190 | VAR_INPUT 191 | in1 : STRING[254]; 192 | in2 : STRING[254]; 193 | END_VAR 194 | 195 | VAR_TEMP 196 | in1_len : INT; 197 | in2_len : INT; 198 | i : DINT; 199 | i_end : INT; 200 | END_VAR 201 | 202 | in1_len := LEN(in1); 203 | in2_len := LEN(in2); 204 | 205 | i_end := MIN(in1_len, in2_len); 206 | 207 | FOR i := 0 TO i_end DO 208 | IF in1[i] <> in2[i] THEN 209 | COMPARE_STRINGS := BYTE_TO_INT(in1[i]) - BYTE_TO_INT(in2[i]); 210 | RETURN; 211 | END_IF; 212 | END_FOR; 213 | 214 | COMPARE_STRINGS := in1_len - in2_len; 215 | END_FUNCTION 216 | 217 | 218 | // Pads given string to specified length with given padding (from the left). 219 | // If original string's length is already equal or greater than required length, it will be returned unchanged. 220 | FUNCTION PAD_STRING_LEFT : STRING[254] 221 | VAR_INPUT 222 | in : STRING[254]; 223 | length_req : USINT; 224 | padding : STRING; 225 | END_VAR 226 | 227 | VAR_TEMP 228 | in_len : INT; 229 | out : STRING[254]; 230 | out_len : INT; 231 | END_VAR 232 | 233 | in_len := LEN(in); 234 | 235 | IF in_len >= length_req THEN 236 | PAD_STRING_LEFT := in; 237 | RETURN; 238 | END_IF; 239 | 240 | out := in; 241 | out_len := LEN(out); 242 | 243 | WHILE out_len < length_req DO 244 | out := CONCAT(padding, out); 245 | out_len := LEN(out); 246 | END_WHILE; 247 | 248 | IF out_len > length_req THEN 249 | out := RIGHT(out, length_req); 250 | END_IF; 251 | 252 | PAD_STRING_LEFT := out; 253 | END_FUNCTION 254 | 255 | // Pads given string to specified length with given padding (from the right). 256 | // If original string's length is already equal or greater than required length, it will be returned unchanged. 257 | FUNCTION PAD_STRING_RIGHT : STRING[254] 258 | VAR_INPUT 259 | in : STRING[254]; 260 | length_req : USINT; 261 | padding : STRING; 262 | END_VAR 263 | 264 | VAR_TEMP 265 | in_len : INT; 266 | out : STRING[254]; 267 | out_len : INT; 268 | END_VAR 269 | 270 | in_len := LEN(in); 271 | 272 | IF in_len >= length_req THEN 273 | PAD_STRING_RIGHT := in; 274 | RETURN; 275 | END_IF; 276 | 277 | out := in; 278 | out_len := LEN(out); 279 | 280 | WHILE out_len < length_req DO 281 | out := CONCAT(out, padding); 282 | out_len := LEN(out); 283 | END_WHILE; 284 | 285 | IF out_len > length_req THEN 286 | out := LEFT(out, length_req); 287 | END_IF; 288 | 289 | PAD_STRING_RIGHT := out; 290 | END_FUNCTION 291 | 292 | 293 | // Encodes given string into given BYTE array using given encoding. 294 | // If target array is too small, given string will be cut. If target array is too large, remaining bytes will be set to NULL (0). 295 | FUNCTION ENCODE_STRING : VOID 296 | VAR_INPUT 297 | in : STRING[254]; 298 | encoding : STRING_ENCODING; 299 | END_VAR 300 | 301 | VAR_IN_OUT 302 | out : ARRAY[..] OF BYTE; 303 | END_VAR 304 | 305 | VAR_TEMP 306 | in_len : INT; 307 | out_len : DINT; 308 | out_start : DINT; 309 | out_end : DINT; 310 | i : DINT; 311 | END_VAR 312 | 313 | in_len := LEN(in); 314 | 315 | out_start := _firstIndexOf(out); 316 | out_end := _lastIndexOf(out); 317 | out_len := out_end - out_start + 1; 318 | 319 | FOR i := 1 TO out_len DO 320 | IF i > in_len THEN 321 | out[out_start + i - 1] := 0; // NULL 322 | ELSIF encoding = ASCII AND in[i] > 127 THEN 323 | out[out_start + i - 1] := 63; // Questionmark '?' 324 | ELSE 325 | out[out_start + i - 1] := in[i]; 326 | END_IF; 327 | END_FOR; 328 | END_FUNCTION 329 | 330 | // Decodes given BYTE array to a string using given encoding. Decoding stops on first NULL (0; if present). 331 | // If source array is bigger than 254 bytes, returned string will be cut to string's max. length of 254. 332 | FUNCTION DECODE_STRING : STRING[254] 333 | VAR_INPUT 334 | in : ARRAY[..] OF BYTE; 335 | encoding : STRING_ENCODING; 336 | END_VAR 337 | 338 | VAR_TEMP 339 | out : STRING[254]; 340 | in_start : DINT; 341 | in_end : DINT; 342 | i_in : DINT; 343 | i_out : DINT; 344 | END_VAR 345 | 346 | in_start := _firstIndexOf(in); 347 | in_end := _lastIndexOf(in); 348 | 349 | FOR i_in := in_start TO in_end DO 350 | i_out := i_in - in_start + 1; 351 | 352 | IF in[i_in] = 0 OR i_out > 254 THEN 353 | DECODE_STRING := LEFT(out, DINT_TO_INT(i_out -1)); 354 | RETURN; 355 | ELSIF encoding = ASCII AND in[i_in] > 127 THEN 356 | out[i_out] := 63; // Questionmark '?' 357 | ELSE 358 | out[i_out] := in[i_in]; 359 | END_IF; 360 | END_FOR; 361 | 362 | DECODE_STRING := LEFT(out, DINT_TO_INT(i_out)); 363 | END_FUNCTION 364 | 365 | 366 | FUNCTION USINT_TO_STRING : STRING[3] 367 | VAR_INPUT 368 | in : USINT; 369 | END_VAR 370 | 371 | USINT_TO_STRING := UDINT_TO_STRING(USINT_TO_UDINT(in)); 372 | END_FUNCTION 373 | 374 | FUNCTION SINT_TO_STRING : STRING[4] 375 | VAR_INPUT 376 | in : SINT; 377 | END_VAR 378 | 379 | SINT_TO_STRING := DINT_TO_STRING(SINT_TO_DINT(in)); 380 | END_FUNCTION 381 | 382 | FUNCTION UINT_TO_STRING : STRING[5] 383 | VAR_INPUT 384 | in : UINT; 385 | END_VAR 386 | 387 | UINT_TO_STRING := UDINT_TO_STRING(UINT_TO_UDINT(in)); 388 | END_FUNCTION 389 | 390 | FUNCTION INT_TO_STRING : STRING[6] 391 | VAR_INPUT 392 | in : INT; 393 | END_VAR 394 | 395 | INT_TO_STRING := DINT_TO_STRING(INT_TO_DINT(in)); 396 | END_FUNCTION 397 | 398 | 399 | FUNCTION STRING_TO_USINT : USINT 400 | VAR_INPUT 401 | in : STRING[3]; 402 | END_VAR 403 | 404 | STRING_TO_USINT := UDINT_TO_USINT(STRING_TO_UDINT(in)); 405 | END_FUNCTION 406 | 407 | FUNCTION STRING_TO_SINT : SINT 408 | VAR_INPUT 409 | in : STRING[4]; 410 | END_VAR 411 | 412 | STRING_TO_SINT := DINT_TO_SINT(STRING_TO_DINT(in)); 413 | END_FUNCTION 414 | 415 | FUNCTION STRING_TO_UINT : UINT 416 | VAR_INPUT 417 | in : STRING[5]; 418 | END_VAR 419 | 420 | STRING_TO_UINT := UDINT_TO_UINT(STRING_TO_UDINT(in)); 421 | END_FUNCTION 422 | 423 | FUNCTION STRING_TO_INT : INT 424 | VAR_INPUT 425 | in : STRING[6]; 426 | END_VAR 427 | 428 | STRING_TO_INT := DINT_TO_INT(STRING_TO_DINT(in)); 429 | END_FUNCTION 430 | END_IMPLEMENTATION 431 | -------------------------------------------------------------------------------- /UTILITIES_TIME.st: -------------------------------------------------------------------------------- 1 | UNIT UTILITIES_TIME; 2 | 3 | // This unit 'UTILITIES_TIME' contains some common time and date functions and types which are missing in standard library. 4 | // Please check Git repository for updates from time to time. 5 | 6 | INTERFACE 7 | USES UTILITIES_STRING; 8 | 9 | FUNCTION CREATE_TIME; 10 | FUNCTION CREATE_TIME_OF_DAY; 11 | FUNCTION CREATE_DATE; 12 | FUNCTION CREATE_DATE_AND_TIME; 13 | 14 | FUNCTION DISSECT_TIME; 15 | FUNCTION DISSECT_TIME_OF_DAY; 16 | FUNCTION DISSECT_DATE; 17 | FUNCTION DISSECT_DATE_AND_TIME; 18 | 19 | FUNCTION GET_DAY_OF_WEEK; 20 | FUNCTION GET_DAY_OF_YEAR; 21 | FUNCTION GET_WEEK_OF_YEAR; 22 | 23 | FUNCTION FORMAT_TIME; 24 | FUNCTION FORMAT_TIME_DISSECTED; 25 | FUNCTION FORMAT_TIME_OF_DAY; 26 | FUNCTION FORMAT_TIME_OF_DAY_DISSECTED; 27 | FUNCTION FORMAT_DATE; 28 | FUNCTION FORMAT_DATE_DISSECTED; 29 | FUNCTION FORMAT_DATE_AND_TIME; 30 | FUNCTION FORMAT_DATE_AND_TIME_DISSECTED; 31 | 32 | 33 | TYPE 34 | // Contains all parts of a positive duration (days, hours, minutes, seconds, etc.). 35 | TIME_DISSECTED : STRUCT 36 | day : USINT; // numeric day (e.g. 1) 37 | hour : USINT; // numeric hour (e.g. 21) 38 | minute : USINT; // numeric minute of hour (e.g. 34) 39 | second : USINT; // numeric second of minute (e.g. 56) 40 | millisecond : UINT; // numeric millisecond of second (e.g. 789) 41 | END_STRUCT 42 | 43 | TIME_FORMAT : ( 44 | ISO8601 // duration in ISO 8601 representation (e.g. 'P1DT21H34M56.789S') 45 | ); 46 | 47 | // Contains all parts of a local time (hours, minutes, seconds, etc.). 48 | TIME_OF_DAY_DISSECTED : STRUCT 49 | hour : USINT; // numeric hour of day (e.g. 21) 50 | minute : USINT; // numeric minute of hour (e.g. 34) 51 | second : USINT; // numeric second of minute (e.g. 56) 52 | millisecond : UINT; // numeric millisecond of second (e.g. 789) 53 | END_STRUCT 54 | 55 | TIME_OF_DAY_FORMAT : ( 56 | ISO8601, // time of day in ISO 8601 representation (e.g. '21:34:56.789') 57 | ISO8601_HHMM, // time of day in ISO 8601 representation without seconds and milliseconds (e.g. '21:34') 58 | ISO8601_HHMM_NODLM, // time of day in ISO 8601 representation without seconds, milliseconds and delimiters (e.g. '2134') 59 | ISO8601_HHMMSS, // time of day in ISO 8601 representation without milliseconds (e.g. '21:34:56') 60 | ISO8601_HHMMSS_NODLM // time of day in ISO 8601 representation without milliseconds and delimiters (e.g. '213456') 61 | ); 62 | 63 | // Contains all parts of a local date (year, month, day, etc.). 64 | DATE_DISSECTED : STRUCT 65 | year : UINT; // numeric year (e.g. 2019) 66 | month : USINT; // numeric month of year (e.g. 10) 67 | day : USINT; // numeric day of month (e.g. 31) 68 | END_STRUCT 69 | 70 | DATE_FORMAT : ( 71 | ISO8601, // date in ISO 8601 representation (e.g. '2019-10-31') 72 | ISO8601_NODLM, // date in ISO 8601 representation without delimiters (e.g. '20191031') 73 | DIN5008 // date in common DIN 5008:2011 representation (e.g. '31.10.2019') 74 | ); 75 | 76 | DAY_OF_WEEK_TYPE : ( 77 | ISO8601 // day of week as defined by ISO 8601 (Monday = 1 <-> Sunday = 7) 78 | ); 79 | 80 | WEEK_OF_YEAR_TYPE : ( 81 | ISO8601 // week of year as defined by ISO 8601 (week 1 starts in the week of 4th of January) 82 | ); 83 | 84 | // Contains all parts of a local date (year, month, day, etc.) and time (hours, minutes, seconds, etc.). 85 | DATE_AND_TIME_DISSECTED : STRUCT 86 | year : UINT; // numeric year (e.g. 2019) 87 | month : USINT; // numeric month of year (e.g. 10) 88 | day : USINT; // numeric day of month (e.g. 31) 89 | hour : USINT; // numeric hour of day (e.g. 21) 90 | minute : USINT; // numeric minute of hour (e.g. 34) 91 | second : USINT; // numeric second of minute (e.g. 56) 92 | millisecond : UINT; // numeric millisecond of second (e.g. 789) 93 | END_STRUCT 94 | 95 | DATE_AND_TIME_FORMAT : ( 96 | ISO8601 // date and time in ISO 8601 representation (e.g. '2019-10-31T21:34:56.789') 97 | ); 98 | 99 | START_OF_MONTHS : ARRAY[1..12] OF UDINT; 100 | START_OF_MONTHS_OF_YEARS : ARRAY[1992..2200] OF START_OF_MONTHS; 101 | END_TYPE 102 | 103 | 104 | VAR_GLOBAL CONSTANT 105 | Start_Of_Months_Of_Years_Values : START_OF_MONTHS_OF_YEARS := [ 106 | // Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec 107 | [ 1, 32, 61, 92, 122, 153, 183, 214, 245, 275, 306, 336], // 1992 108 | [ 367, 398, 426, 457, 487, 518, 548, 579, 610, 640, 671, 701], // 1993 109 | [ 732, 763, 791, 822, 852, 883, 913, 944, 975, 1005, 1036, 1066], // 1994 110 | [ 1097, 1128, 1156, 1187, 1217, 1248, 1278, 1309, 1340, 1370, 1401, 1431], // 1995 111 | [ 1462, 1493, 1522, 1553, 1583, 1614, 1644, 1675, 1706, 1736, 1767, 1797], // 1996 112 | [ 1828, 1859, 1887, 1918, 1948, 1979, 2009, 2040, 2071, 2101, 2132, 2162], // 1997 113 | [ 2193, 2224, 2252, 2283, 2313, 2344, 2374, 2405, 2436, 2466, 2497, 2527], // 1998 114 | [ 2558, 2589, 2617, 2648, 2678, 2709, 2739, 2770, 2801, 2831, 2862, 2892], // 1999 115 | [ 2923, 2954, 2983, 3014, 3044, 3075, 3105, 3136, 3167, 3197, 3228, 3258], // 2000 116 | [ 3289, 3320, 3348, 3379, 3409, 3440, 3470, 3501, 3532, 3562, 3593, 3623], // 2001 117 | [ 3654, 3685, 3713, 3744, 3774, 3805, 3835, 3866, 3897, 3927, 3958, 3988], // 2002 118 | [ 4019, 4050, 4078, 4109, 4139, 4170, 4200, 4231, 4262, 4292, 4323, 4353], // 2003 119 | [ 4384, 4415, 4444, 4475, 4505, 4536, 4566, 4597, 4628, 4658, 4689, 4719], // 2004 120 | [ 4750, 4781, 4809, 4840, 4870, 4901, 4931, 4962, 4993, 5023, 5054, 5084], // 2005 121 | [ 5115, 5146, 5174, 5205, 5235, 5266, 5296, 5327, 5358, 5388, 5419, 5449], // 2006 122 | [ 5480, 5511, 5539, 5570, 5600, 5631, 5661, 5692, 5723, 5753, 5784, 5814], // 2007 123 | [ 5845, 5876, 5905, 5936, 5966, 5997, 6027, 6058, 6089, 6119, 6150, 6180], // 2008 124 | [ 6211, 6242, 6270, 6301, 6331, 6362, 6392, 6423, 6454, 6484, 6515, 6545], // 2009 125 | [ 6576, 6607, 6635, 6666, 6696, 6727, 6757, 6788, 6819, 6849, 6880, 6910], // 2010 126 | [ 6941, 6972, 7000, 7031, 7061, 7092, 7122, 7153, 7184, 7214, 7245, 7275], // 2011 127 | [ 7306, 7337, 7366, 7397, 7427, 7458, 7488, 7519, 7550, 7580, 7611, 7641], // 2012 128 | [ 7672, 7703, 7731, 7762, 7792, 7823, 7853, 7884, 7915, 7945, 7976, 8006], // 2013 129 | [ 8037, 8068, 8096, 8127, 8157, 8188, 8218, 8249, 8280, 8310, 8341, 8371], // 2014 130 | [ 8402, 8433, 8461, 8492, 8522, 8553, 8583, 8614, 8645, 8675, 8706, 8736], // 2015 131 | [ 8767, 8798, 8827, 8858, 8888, 8919, 8949, 8980, 9011, 9041, 9072, 9102], // 2016 132 | [ 9133, 9164, 9192, 9223, 9253, 9284, 9314, 9345, 9376, 9406, 9437, 9467], // 2017 133 | [ 9498, 9529, 9557, 9588, 9618, 9649, 9679, 9710, 9741, 9771, 9802, 9832], // 2018 134 | [ 9863, 9894, 9922, 9953, 9983, 10014, 10044, 10075, 10106, 10136, 10167, 10197], // 2019 135 | [10228, 10259, 10288, 10319, 10349, 10380, 10410, 10441, 10472, 10502, 10533, 10563], // 2020 136 | [10594, 10625, 10653, 10684, 10714, 10745, 10775, 10806, 10837, 10867, 10898, 10928], // 2021 137 | [10959, 10990, 11018, 11049, 11079, 11110, 11140, 11171, 11202, 11232, 11263, 11293], // 2022 138 | [11324, 11355, 11383, 11414, 11444, 11475, 11505, 11536, 11567, 11597, 11628, 11658], // 2023 139 | [11689, 11720, 11749, 11780, 11810, 11841, 11871, 11902, 11933, 11963, 11994, 12024], // 2024 140 | [12055, 12086, 12114, 12145, 12175, 12206, 12236, 12267, 12298, 12328, 12359, 12389], // 2025 141 | [12420, 12451, 12479, 12510, 12540, 12571, 12601, 12632, 12663, 12693, 12724, 12754], // 2026 142 | [12785, 12816, 12844, 12875, 12905, 12936, 12966, 12997, 13028, 13058, 13089, 13119], // 2027 143 | [13150, 13181, 13210, 13241, 13271, 13302, 13332, 13363, 13394, 13424, 13455, 13485], // 2028 144 | [13516, 13547, 13575, 13606, 13636, 13667, 13697, 13728, 13759, 13789, 13820, 13850], // 2029 145 | [13881, 13912, 13940, 13971, 14001, 14032, 14062, 14093, 14124, 14154, 14185, 14215], // 2030 146 | [14246, 14277, 14305, 14336, 14366, 14397, 14427, 14458, 14489, 14519, 14550, 14580], // 2031 147 | [14611, 14642, 14671, 14702, 14732, 14763, 14793, 14824, 14855, 14885, 14916, 14946], // 2032 148 | [14977, 15008, 15036, 15067, 15097, 15128, 15158, 15189, 15220, 15250, 15281, 15311], // 2033 149 | [15342, 15373, 15401, 15432, 15462, 15493, 15523, 15554, 15585, 15615, 15646, 15676], // 2034 150 | [15707, 15738, 15766, 15797, 15827, 15858, 15888, 15919, 15950, 15980, 16011, 16041], // 2035 151 | [16072, 16103, 16132, 16163, 16193, 16224, 16254, 16285, 16316, 16346, 16377, 16407], // 2036 152 | [16438, 16469, 16497, 16528, 16558, 16589, 16619, 16650, 16681, 16711, 16742, 16772], // 2037 153 | [16803, 16834, 16862, 16893, 16923, 16954, 16984, 17015, 17046, 17076, 17107, 17137], // 2038 154 | [17168, 17199, 17227, 17258, 17288, 17319, 17349, 17380, 17411, 17441, 17472, 17502], // 2039 155 | [17533, 17564, 17593, 17624, 17654, 17685, 17715, 17746, 17777, 17807, 17838, 17868], // 2040 156 | [17899, 17930, 17958, 17989, 18019, 18050, 18080, 18111, 18142, 18172, 18203, 18233], // 2041 157 | [18264, 18295, 18323, 18354, 18384, 18415, 18445, 18476, 18507, 18537, 18568, 18598], // 2042 158 | [18629, 18660, 18688, 18719, 18749, 18780, 18810, 18841, 18872, 18902, 18933, 18963], // 2043 159 | [18994, 19025, 19054, 19085, 19115, 19146, 19176, 19207, 19238, 19268, 19299, 19329], // 2044 160 | [19360, 19391, 19419, 19450, 19480, 19511, 19541, 19572, 19603, 19633, 19664, 19694], // 2045 161 | [19725, 19756, 19784, 19815, 19845, 19876, 19906, 19937, 19968, 19998, 20029, 20059], // 2046 162 | [20090, 20121, 20149, 20180, 20210, 20241, 20271, 20302, 20333, 20363, 20394, 20424], // 2047 163 | [20455, 20486, 20515, 20546, 20576, 20607, 20637, 20668, 20699, 20729, 20760, 20790], // 2048 164 | [20821, 20852, 20880, 20911, 20941, 20972, 21002, 21033, 21064, 21094, 21125, 21155], // 2049 165 | [21186, 21217, 21245, 21276, 21306, 21337, 21367, 21398, 21429, 21459, 21490, 21520], // 2050 166 | [21551, 21582, 21610, 21641, 21671, 21702, 21732, 21763, 21794, 21824, 21855, 21885], // 2051 167 | [21916, 21947, 21976, 22007, 22037, 22068, 22098, 22129, 22160, 22190, 22221, 22251], // 2052 168 | [22282, 22313, 22341, 22372, 22402, 22433, 22463, 22494, 22525, 22555, 22586, 22616], // 2053 169 | [22647, 22678, 22706, 22737, 22767, 22798, 22828, 22859, 22890, 22920, 22951, 22981], // 2054 170 | [23012, 23043, 23071, 23102, 23132, 23163, 23193, 23224, 23255, 23285, 23316, 23346], // 2055 171 | [23377, 23408, 23437, 23468, 23498, 23529, 23559, 23590, 23621, 23651, 23682, 23712], // 2056 172 | [23743, 23774, 23802, 23833, 23863, 23894, 23924, 23955, 23986, 24016, 24047, 24077], // 2057 173 | [24108, 24139, 24167, 24198, 24228, 24259, 24289, 24320, 24351, 24381, 24412, 24442], // 2058 174 | [24473, 24504, 24532, 24563, 24593, 24624, 24654, 24685, 24716, 24746, 24777, 24807], // 2059 175 | [24838, 24869, 24898, 24929, 24959, 24990, 25020, 25051, 25082, 25112, 25143, 25173], // 2060 176 | [25204, 25235, 25263, 25294, 25324, 25355, 25385, 25416, 25447, 25477, 25508, 25538], // 2061 177 | [25569, 25600, 25628, 25659, 25689, 25720, 25750, 25781, 25812, 25842, 25873, 25903], // 2062 178 | [25934, 25965, 25993, 26024, 26054, 26085, 26115, 26146, 26177, 26207, 26238, 26268], // 2063 179 | [26299, 26330, 26359, 26390, 26420, 26451, 26481, 26512, 26543, 26573, 26604, 26634], // 2064 180 | [26665, 26696, 26724, 26755, 26785, 26816, 26846, 26877, 26908, 26938, 26969, 26999], // 2065 181 | [27030, 27061, 27089, 27120, 27150, 27181, 27211, 27242, 27273, 27303, 27334, 27364], // 2066 182 | [27395, 27426, 27454, 27485, 27515, 27546, 27576, 27607, 27638, 27668, 27699, 27729], // 2067 183 | [27760, 27791, 27820, 27851, 27881, 27912, 27942, 27973, 28004, 28034, 28065, 28095], // 2068 184 | [28126, 28157, 28185, 28216, 28246, 28277, 28307, 28338, 28369, 28399, 28430, 28460], // 2069 185 | [28491, 28522, 28550, 28581, 28611, 28642, 28672, 28703, 28734, 28764, 28795, 28825], // 2070 186 | [28856, 28887, 28915, 28946, 28976, 29007, 29037, 29068, 29099, 29129, 29160, 29190], // 2071 187 | [29221, 29252, 29281, 29312, 29342, 29373, 29403, 29434, 29465, 29495, 29526, 29556], // 2072 188 | [29587, 29618, 29646, 29677, 29707, 29738, 29768, 29799, 29830, 29860, 29891, 29921], // 2073 189 | [29952, 29983, 30011, 30042, 30072, 30103, 30133, 30164, 30195, 30225, 30256, 30286], // 2074 190 | [30317, 30348, 30376, 30407, 30437, 30468, 30498, 30529, 30560, 30590, 30621, 30651], // 2075 191 | [30682, 30713, 30742, 30773, 30803, 30834, 30864, 30895, 30926, 30956, 30987, 31017], // 2076 192 | [31048, 31079, 31107, 31138, 31168, 31199, 31229, 31260, 31291, 31321, 31352, 31382], // 2077 193 | [31413, 31444, 31472, 31503, 31533, 31564, 31594, 31625, 31656, 31686, 31717, 31747], // 2078 194 | [31778, 31809, 31837, 31868, 31898, 31929, 31959, 31990, 32021, 32051, 32082, 32112], // 2079 195 | [32143, 32174, 32203, 32234, 32264, 32295, 32325, 32356, 32387, 32417, 32448, 32478], // 2080 196 | [32509, 32540, 32568, 32599, 32629, 32660, 32690, 32721, 32752, 32782, 32813, 32843], // 2081 197 | [32874, 32905, 32933, 32964, 32994, 33025, 33055, 33086, 33117, 33147, 33178, 33208], // 2082 198 | [33239, 33270, 33298, 33329, 33359, 33390, 33420, 33451, 33482, 33512, 33543, 33573], // 2083 199 | [33604, 33635, 33664, 33695, 33725, 33756, 33786, 33817, 33848, 33878, 33909, 33939], // 2084 200 | [33970, 34001, 34029, 34060, 34090, 34121, 34151, 34182, 34213, 34243, 34274, 34304], // 2085 201 | [34335, 34366, 34394, 34425, 34455, 34486, 34516, 34547, 34578, 34608, 34639, 34669], // 2086 202 | [34700, 34731, 34759, 34790, 34820, 34851, 34881, 34912, 34943, 34973, 35004, 35034], // 2087 203 | [35065, 35096, 35125, 35156, 35186, 35217, 35247, 35278, 35309, 35339, 35370, 35400], // 2088 204 | [35431, 35462, 35490, 35521, 35551, 35582, 35612, 35643, 35674, 35704, 35735, 35765], // 2089 205 | [35796, 35827, 35855, 35886, 35916, 35947, 35977, 36008, 36039, 36069, 36100, 36130], // 2090 206 | [36161, 36192, 36220, 36251, 36281, 36312, 36342, 36373, 36404, 36434, 36465, 36495], // 2091 207 | [36526, 36557, 36586, 36617, 36647, 36678, 36708, 36739, 36770, 36800, 36831, 36861], // 2092 208 | [36892, 36923, 36951, 36982, 37012, 37043, 37073, 37104, 37135, 37165, 37196, 37226], // 2093 209 | [37257, 37288, 37316, 37347, 37377, 37408, 37438, 37469, 37500, 37530, 37561, 37591], // 2094 210 | [37622, 37653, 37681, 37712, 37742, 37773, 37803, 37834, 37865, 37895, 37926, 37956], // 2095 211 | [37987, 38018, 38047, 38078, 38108, 38139, 38169, 38200, 38231, 38261, 38292, 38322], // 2096 212 | [38353, 38384, 38412, 38443, 38473, 38504, 38534, 38565, 38596, 38626, 38657, 38687], // 2097 213 | [38718, 38749, 38777, 38808, 38838, 38869, 38899, 38930, 38961, 38991, 39022, 39052], // 2098 214 | [39083, 39114, 39142, 39173, 39203, 39234, 39264, 39295, 39326, 39356, 39387, 39417], // 2099 215 | [39448, 39479, 39507, 39538, 39568, 39599, 39629, 39660, 39691, 39721, 39752, 39782], // 2100 216 | [39813, 39844, 39872, 39903, 39933, 39964, 39994, 40025, 40056, 40086, 40117, 40147], // 2101 217 | [40178, 40209, 40237, 40268, 40298, 40329, 40359, 40390, 40421, 40451, 40482, 40512], // 2102 218 | [40543, 40574, 40602, 40633, 40663, 40694, 40724, 40755, 40786, 40816, 40847, 40877], // 2103 219 | [40908, 40939, 40968, 40999, 41029, 41060, 41090, 41121, 41152, 41182, 41213, 41243], // 2104 220 | [41274, 41305, 41333, 41364, 41394, 41425, 41455, 41486, 41517, 41547, 41578, 41608], // 2105 221 | [41639, 41670, 41698, 41729, 41759, 41790, 41820, 41851, 41882, 41912, 41943, 41973], // 2106 222 | [42004, 42035, 42063, 42094, 42124, 42155, 42185, 42216, 42247, 42277, 42308, 42338], // 2107 223 | [42369, 42400, 42429, 42460, 42490, 42521, 42551, 42582, 42613, 42643, 42674, 42704], // 2108 224 | [42735, 42766, 42794, 42825, 42855, 42886, 42916, 42947, 42978, 43008, 43039, 43069], // 2109 225 | [43100, 43131, 43159, 43190, 43220, 43251, 43281, 43312, 43343, 43373, 43404, 43434], // 2110 226 | [43465, 43496, 43524, 43555, 43585, 43616, 43646, 43677, 43708, 43738, 43769, 43799], // 2111 227 | [43830, 43861, 43890, 43921, 43951, 43982, 44012, 44043, 44074, 44104, 44135, 44165], // 2112 228 | [44196, 44227, 44255, 44286, 44316, 44347, 44377, 44408, 44439, 44469, 44500, 44530], // 2113 229 | [44561, 44592, 44620, 44651, 44681, 44712, 44742, 44773, 44804, 44834, 44865, 44895], // 2114 230 | [44926, 44957, 44985, 45016, 45046, 45077, 45107, 45138, 45169, 45199, 45230, 45260], // 2115 231 | [45291, 45322, 45351, 45382, 45412, 45443, 45473, 45504, 45535, 45565, 45596, 45626], // 2116 232 | [45657, 45688, 45716, 45747, 45777, 45808, 45838, 45869, 45900, 45930, 45961, 45991], // 2117 233 | [46022, 46053, 46081, 46112, 46142, 46173, 46203, 46234, 46265, 46295, 46326, 46356], // 2118 234 | [46387, 46418, 46446, 46477, 46507, 46538, 46568, 46599, 46630, 46660, 46691, 46721], // 2119 235 | [46752, 46783, 46812, 46843, 46873, 46904, 46934, 46965, 46996, 47026, 47057, 47087], // 2120 236 | [47118, 47149, 47177, 47208, 47238, 47269, 47299, 47330, 47361, 47391, 47422, 47452], // 2121 237 | [47483, 47514, 47542, 47573, 47603, 47634, 47664, 47695, 47726, 47756, 47787, 47817], // 2122 238 | [47848, 47879, 47907, 47938, 47968, 47999, 48029, 48060, 48091, 48121, 48152, 48182], // 2123 239 | [48213, 48244, 48273, 48304, 48334, 48365, 48395, 48426, 48457, 48487, 48518, 48548], // 2124 240 | [48579, 48610, 48638, 48669, 48699, 48730, 48760, 48791, 48822, 48852, 48883, 48913], // 2125 241 | [48944, 48975, 49003, 49034, 49064, 49095, 49125, 49156, 49187, 49217, 49248, 49278], // 2126 242 | [49309, 49340, 49368, 49399, 49429, 49460, 49490, 49521, 49552, 49582, 49613, 49643], // 2127 243 | [49674, 49705, 49734, 49765, 49795, 49826, 49856, 49887, 49918, 49948, 49979, 50009], // 2128 244 | [50040, 50071, 50099, 50130, 50160, 50191, 50221, 50252, 50283, 50313, 50344, 50374], // 2129 245 | [50405, 50436, 50464, 50495, 50525, 50556, 50586, 50617, 50648, 50678, 50709, 50739], // 2130 246 | [50770, 50801, 50829, 50860, 50890, 50921, 50951, 50982, 51013, 51043, 51074, 51104], // 2131 247 | [51135, 51166, 51195, 51226, 51256, 51287, 51317, 51348, 51379, 51409, 51440, 51470], // 2132 248 | [51501, 51532, 51560, 51591, 51621, 51652, 51682, 51713, 51744, 51774, 51805, 51835], // 2133 249 | [51866, 51897, 51925, 51956, 51986, 52017, 52047, 52078, 52109, 52139, 52170, 52200], // 2134 250 | [52231, 52262, 52290, 52321, 52351, 52382, 52412, 52443, 52474, 52504, 52535, 52565], // 2135 251 | [52596, 52627, 52656, 52687, 52717, 52748, 52778, 52809, 52840, 52870, 52901, 52931], // 2136 252 | [52962, 52993, 53021, 53052, 53082, 53113, 53143, 53174, 53205, 53235, 53266, 53296], // 2137 253 | [53327, 53358, 53386, 53417, 53447, 53478, 53508, 53539, 53570, 53600, 53631, 53661], // 2138 254 | [53692, 53723, 53751, 53782, 53812, 53843, 53873, 53904, 53935, 53965, 53996, 54026], // 2139 255 | [54057, 54088, 54117, 54148, 54178, 54209, 54239, 54270, 54301, 54331, 54362, 54392], // 2140 256 | [54423, 54454, 54482, 54513, 54543, 54574, 54604, 54635, 54666, 54696, 54727, 54757], // 2141 257 | [54788, 54819, 54847, 54878, 54908, 54939, 54969, 55000, 55031, 55061, 55092, 55122], // 2142 258 | [55153, 55184, 55212, 55243, 55273, 55304, 55334, 55365, 55396, 55426, 55457, 55487], // 2143 259 | [55518, 55549, 55578, 55609, 55639, 55670, 55700, 55731, 55762, 55792, 55823, 55853], // 2144 260 | [55884, 55915, 55943, 55974, 56004, 56035, 56065, 56096, 56127, 56157, 56188, 56218], // 2145 261 | [56249, 56280, 56308, 56339, 56369, 56400, 56430, 56461, 56492, 56522, 56553, 56583], // 2146 262 | [56614, 56645, 56673, 56704, 56734, 56765, 56795, 56826, 56857, 56887, 56918, 56948], // 2147 263 | [56979, 57010, 57039, 57070, 57100, 57131, 57161, 57192, 57223, 57253, 57284, 57314], // 2148 264 | [57345, 57376, 57404, 57435, 57465, 57496, 57526, 57557, 57588, 57618, 57649, 57679], // 2149 265 | [57710, 57741, 57769, 57800, 57830, 57861, 57891, 57922, 57953, 57983, 58014, 58044], // 2150 266 | [58075, 58106, 58134, 58165, 58195, 58226, 58256, 58287, 58318, 58348, 58379, 58409], // 2151 267 | [58440, 58471, 58500, 58531, 58561, 58592, 58622, 58653, 58684, 58714, 58745, 58775], // 2152 268 | [58806, 58837, 58865, 58896, 58926, 58957, 58987, 59018, 59049, 59079, 59110, 59140], // 2153 269 | [59171, 59202, 59230, 59261, 59291, 59322, 59352, 59383, 59414, 59444, 59475, 59505], // 2154 270 | [59536, 59567, 59595, 59626, 59656, 59687, 59717, 59748, 59779, 59809, 59840, 59870], // 2155 271 | [59901, 59932, 59961, 59992, 60022, 60053, 60083, 60114, 60145, 60175, 60206, 60236], // 2156 272 | [60267, 60298, 60326, 60357, 60387, 60418, 60448, 60479, 60510, 60540, 60571, 60601], // 2157 273 | [60632, 60663, 60691, 60722, 60752, 60783, 60813, 60844, 60875, 60905, 60936, 60966], // 2158 274 | [60997, 61028, 61056, 61087, 61117, 61148, 61178, 61209, 61240, 61270, 61301, 61331], // 2159 275 | [61362, 61393, 61422, 61453, 61483, 61514, 61544, 61575, 61606, 61636, 61667, 61697], // 2160 276 | [61728, 61759, 61787, 61818, 61848, 61879, 61909, 61940, 61971, 62001, 62032, 62062], // 2161 277 | [62093, 62124, 62152, 62183, 62213, 62244, 62274, 62305, 62336, 62366, 62397, 62427], // 2162 278 | [62458, 62489, 62517, 62548, 62578, 62609, 62639, 62670, 62701, 62731, 62762, 62792], // 2163 279 | [62823, 62854, 62883, 62914, 62944, 62975, 63005, 63036, 63067, 63097, 63128, 63158], // 2164 280 | [63189, 63220, 63248, 63279, 63309, 63340, 63370, 63401, 63432, 63462, 63493, 63523], // 2165 281 | [63554, 63585, 63613, 63644, 63674, 63705, 63735, 63766, 63797, 63827, 63858, 63888], // 2166 282 | [63919, 63950, 63978, 64009, 64039, 64070, 64100, 64131, 64162, 64192, 64223, 64253], // 2167 283 | [64284, 64315, 64344, 64375, 64405, 64436, 64466, 64497, 64528, 64558, 64589, 64619], // 2168 284 | [64650, 64681, 64709, 64740, 64770, 64801, 64831, 64862, 64893, 64923, 64954, 64984], // 2169 285 | [65015, 65046, 65074, 65105, 65135, 65166, 65196, 65227, 65258, 65288, 65319, 65349], // 2170 286 | [65380, 65411, 65439, 65470, 65500, 65531, 65561, 65592, 65623, 65653, 65684, 65714], // 2171 287 | [65745, 65776, 65805, 65836, 65866, 65897, 65927, 65958, 65989, 66019, 66050, 66080], // 2172 288 | [66111, 66142, 66170, 66201, 66231, 66262, 66292, 66323, 66354, 66384, 66415, 66445], // 2173 289 | [66476, 66507, 66535, 66566, 66596, 66627, 66657, 66688, 66719, 66749, 66780, 66810], // 2174 290 | [66841, 66872, 66900, 66931, 66961, 66992, 67022, 67053, 67084, 67114, 67145, 67175], // 2175 291 | [67206, 67237, 67266, 67297, 67327, 67358, 67388, 67419, 67450, 67480, 67511, 67541], // 2176 292 | [67572, 67603, 67631, 67662, 67692, 67723, 67753, 67784, 67815, 67845, 67876, 67906], // 2177 293 | [67937, 67968, 67996, 68027, 68057, 68088, 68118, 68149, 68180, 68210, 68241, 68271], // 2178 294 | [68302, 68333, 68361, 68392, 68422, 68453, 68483, 68514, 68545, 68575, 68606, 68636], // 2179 295 | [68667, 68698, 68727, 68758, 68788, 68819, 68849, 68880, 68911, 68941, 68972, 69002], // 2180 296 | [69033, 69064, 69092, 69123, 69153, 69184, 69214, 69245, 69276, 69306, 69337, 69367], // 2181 297 | [69398, 69429, 69457, 69488, 69518, 69549, 69579, 69610, 69641, 69671, 69702, 69732], // 2182 298 | [69763, 69794, 69822, 69853, 69883, 69914, 69944, 69975, 70006, 70036, 70067, 70097], // 2183 299 | [70128, 70159, 70188, 70219, 70249, 70280, 70310, 70341, 70372, 70402, 70433, 70463], // 2184 300 | [70494, 70525, 70553, 70584, 70614, 70645, 70675, 70706, 70737, 70767, 70798, 70828], // 2185 301 | [70859, 70890, 70918, 70949, 70979, 71010, 71040, 71071, 71102, 71132, 71163, 71193], // 2186 302 | [71224, 71255, 71283, 71314, 71344, 71375, 71405, 71436, 71467, 71497, 71528, 71558], // 2187 303 | [71589, 71620, 71649, 71680, 71710, 71741, 71771, 71802, 71833, 71863, 71894, 71924], // 2188 304 | [71955, 71986, 72014, 72045, 72075, 72106, 72136, 72167, 72198, 72228, 72259, 72289], // 2189 305 | [72320, 72351, 72379, 72410, 72440, 72471, 72501, 72532, 72563, 72593, 72624, 72654], // 2190 306 | [72685, 72716, 72744, 72775, 72805, 72836, 72866, 72897, 72928, 72958, 72989, 73019], // 2191 307 | [73050, 73081, 73110, 73141, 73171, 73202, 73232, 73263, 73294, 73324, 73355, 73385], // 2192 308 | [73416, 73447, 73475, 73506, 73536, 73567, 73597, 73628, 73659, 73689, 73720, 73750], // 2193 309 | [73781, 73812, 73840, 73871, 73901, 73932, 73962, 73993, 74024, 74054, 74085, 74115], // 2194 310 | [74146, 74177, 74205, 74236, 74266, 74297, 74327, 74358, 74389, 74419, 74450, 74480], // 2195 311 | [74511, 74542, 74571, 74602, 74632, 74663, 74693, 74724, 74755, 74785, 74816, 74846], // 2196 312 | [74877, 74908, 74936, 74967, 74997, 75028, 75058, 75089, 75120, 75150, 75181, 75211], // 2197 313 | [75242, 75273, 75301, 75332, 75362, 75393, 75423, 75454, 75485, 75515, 75546, 75576], // 2198 314 | [75607, 75638, 75666, 75697, 75727, 75758, 75788, 75819, 75850, 75880, 75911, 75941], // 2199 315 | [75972, 76003, 76031, 76062, 76092, 76123, 76153, 76184, 76215, 76245, 76276, 76306] // 2200 316 | ]; 317 | END_VAR 318 | END_INTERFACE 319 | 320 | 321 | IMPLEMENTATION 322 | // INTERNAL: Returns numeric representation of given DATE. 323 | FUNCTION DATE_TO_UDINT_INTERNAL : UDINT 324 | VAR_INPUT 325 | in : DATE; 326 | END_VAR 327 | 328 | VAR_TEMP 329 | in_array : ARRAY[0..3] OF BYTE; // Array for marshalling 330 | END_VAR 331 | 332 | in_array := ANYTYPE_TO_LITTLEBYTEARRAY(in, 0); 333 | DATE_TO_UDINT_INTERNAL := LITTLEBYTEARRAY_TO_ANYTYPE(in_array, 0); 334 | END_FUNCTION 335 | 336 | // INTERNAL: Returns DATE of given numeric representation. 337 | FUNCTION UDINT_TO_DATE_INTERNAL : DATE 338 | VAR_INPUT 339 | in : UDINT; 340 | END_VAR 341 | 342 | VAR_TEMP 343 | out_array : ARRAY[0..3] OF BYTE; // Array for marshalling 344 | END_VAR 345 | 346 | out_array := ANYTYPE_TO_LITTLEBYTEARRAY(in, 0); 347 | UDINT_TO_DATE_INTERNAL := LITTLEBYTEARRAY_TO_ANYTYPE(out_array, 0); 348 | END_FUNCTION 349 | 350 | 351 | // INTERNAL: Returns numeric representation of given TIME_OF_DAY. 352 | FUNCTION TIME_OF_DAY_TO_UDINT_INTERNAL : UDINT 353 | VAR_INPUT 354 | in : TIME_OF_DAY; 355 | END_VAR 356 | 357 | VAR_TEMP 358 | in_array : ARRAY[0..3] OF BYTE; // Array for marshalling 359 | END_VAR 360 | 361 | in_array := ANYTYPE_TO_LITTLEBYTEARRAY(in, 0); 362 | TIME_OF_DAY_TO_UDINT_INTERNAL := LITTLEBYTEARRAY_TO_ANYTYPE(in_array, 0); 363 | END_FUNCTION 364 | 365 | // INTERNAL: Returns TIME_OF_DAY of given numeric representation. 366 | FUNCTION UDINT_TO_TIME_OF_DAY_INTERNAL : TIME_OF_DAY 367 | VAR_INPUT 368 | in : UDINT; 369 | END_VAR 370 | 371 | VAR_TEMP 372 | out_array : ARRAY[0..3] OF BYTE; // Array for marshalling 373 | END_VAR 374 | 375 | out_array := ANYTYPE_TO_LITTLEBYTEARRAY(in, 0); 376 | UDINT_TO_TIME_OF_DAY_INTERNAL := LITTLEBYTEARRAY_TO_ANYTYPE(out_array, 0); 377 | END_FUNCTION 378 | 379 | 380 | // INTERNAL: Creates a TIME using given values. 381 | FUNCTION CREATE_TIME_INTERNAL : UDINT 382 | VAR_INPUT 383 | day : USINT; // numeric day (e.g. 1) 384 | hour : USINT; // numeric hour (e.g. 21) 385 | minute : USINT; // numeric minute of hour (e.g. 34) 386 | second : USINT; // numeric second of minute (e.g. 56) 387 | millisecond : UINT; // numeric millisecond of second (e.g. 789) 388 | END_VAR 389 | 390 | CREATE_TIME_INTERNAL := (((USINT_TO_UDINT(day) 391 | * 24 + USINT_TO_UDINT(hour)) 392 | * 60 + USINT_TO_UDINT(minute)) 393 | * 60 + USINT_TO_UDINT(second)) 394 | * 1000 + UINT_TO_UDINT(millisecond); 395 | END_FUNCTION 396 | 397 | // Creates a TIME using given values. 398 | FUNCTION CREATE_TIME : TIME 399 | VAR_INPUT 400 | day : USINT; // numeric day (e.g. 1) 401 | hour : USINT; // numeric hour (e.g. 21) 402 | minute : USINT; // numeric minute of hour (e.g. 34) 403 | second : USINT; // numeric second of minute (e.g. 56) 404 | millisecond : UINT; // numeric millisecond of second (e.g. 789) 405 | END_VAR 406 | 407 | CREATE_TIME := UDINT_TO_TIME(CREATE_TIME_INTERNAL(day, hour, minute, second, millisecond)); 408 | END_FUNCTION 409 | 410 | 411 | // INTERNAL: Creates a TIME_OF_DAY using given values. 412 | FUNCTION CREATE_TIME_OF_DAY_INTERNAL : UDINT 413 | VAR_INPUT 414 | hour : USINT; // numeric hour of day (e.g. 21) 415 | minute : USINT; // numeric minute of hour (e.g. 34) 416 | second : USINT; // numeric second of minute (e.g. 56) 417 | millisecond : UINT; // numeric millisecond of second (e.g. 789) 418 | END_VAR 419 | 420 | CREATE_TIME_OF_DAY_INTERNAL := ((USINT_TO_UDINT(hour) 421 | * 60 + USINT_TO_UDINT(minute)) 422 | * 60 + USINT_TO_UDINT(second)) 423 | * 1000 + UINT_TO_UDINT(millisecond); 424 | END_FUNCTION 425 | 426 | // Creates a TIME_OF_DAY using given values. 427 | FUNCTION CREATE_TIME_OF_DAY : TIME_OF_DAY 428 | VAR_INPUT 429 | hour : USINT; // numeric hour of day (e.g. 21) 430 | minute : USINT; // numeric minute of hour (e.g. 34) 431 | second : USINT; // numeric second of minute (e.g. 56) 432 | millisecond : UINT; // numeric millisecond of second (e.g. 789) 433 | END_VAR 434 | 435 | CREATE_TIME_OF_DAY := UDINT_TO_TIME_OF_DAY_INTERNAL(CREATE_TIME_OF_DAY_INTERNAL(hour, minute, second, millisecond)); 436 | END_FUNCTION 437 | 438 | 439 | // INTERNAL: Creates a DATE using given values. 440 | FUNCTION CREATE_DATE_INTERNAL : UDINT 441 | VAR_INPUT 442 | year : UINT; // numeric year (e.g. 2019) 443 | month : USINT; // numeric month of year (e.g. 10) 444 | day : USINT; // numeric day of month (e.g. 31) 445 | END_VAR 446 | 447 | IF year < 1992 THEN 448 | CREATE_DATE_INTERNAL := 0; 449 | ELSE 450 | CREATE_DATE_INTERNAL := Start_Of_Months_Of_Years_Values[year][month] + day - 1; 451 | END_IF; 452 | END_FUNCTION 453 | 454 | // Creates a DATE using given values. 455 | FUNCTION CREATE_DATE : DATE 456 | VAR_INPUT 457 | year : UINT; // numeric year (e.g. 2019) 458 | month : USINT; // numeric month of year (e.g. 10) 459 | day : USINT; // numeric day of month (e.g. 31) 460 | END_VAR 461 | 462 | CREATE_DATE := UDINT_TO_DATE_INTERNAL(CREATE_DATE_INTERNAL(year, month, day)); 463 | END_FUNCTION 464 | 465 | 466 | // Creates a DATE_AND_TIME using given values. 467 | FUNCTION CREATE_DATE_AND_TIME : DATE_AND_TIME 468 | VAR_INPUT 469 | year : UINT; // numeric year (e.g. 2019) 470 | month : USINT; // numeric month of year (e.g. 10) 471 | day : USINT; // numeric day of month (e.g. 31) 472 | hour : USINT; // numeric hour of day (e.g. 21) 473 | minute : USINT; // numeric minute of hour (e.g. 34) 474 | second : USINT; // numeric second of minute (e.g. 56) 475 | millisecond : UINT; // numeric millisecond of second (e.g. 789) 476 | END_VAR 477 | 478 | CREATE_DATE_AND_TIME := CONCAT_DATE_TOD( 479 | CREATE_DATE(year, month, day), 480 | CREATE_TIME_OF_DAY(hour, minute, second, millisecond) 481 | ); 482 | END_FUNCTION 483 | 484 | 485 | // Converts internal TIME type to TIME_DISSECTED structure. 486 | FUNCTION DISSECT_TIME : TIME_DISSECTED 487 | VAR_INPUT 488 | in : TIME; 489 | END_VAR 490 | 491 | VAR_TEMP 492 | milliseconds : UDINT; 493 | seconds : UDINT; 494 | minutes : UDINT; 495 | hours : UDINT; 496 | days : UDINT; 497 | out : TIME_DISSECTED; 498 | END_VAR 499 | 500 | milliseconds := TIME_TO_UDINT(in); 501 | out.millisecond := UDINT_TO_UINT(milliseconds MOD 1000); 502 | 503 | seconds := (milliseconds - out.millisecond) / 1000; 504 | out.second := UDINT_TO_USINT(seconds MOD 60); 505 | 506 | minutes := (seconds - out.second) / 60; 507 | out.minute := UDINT_TO_USINT(minutes MOD 60); 508 | 509 | hours := (minutes - out.minute) / 60; 510 | out.hour := UDINT_TO_USINT(hours MOD 24); 511 | 512 | days := (hours - out.hour) / 24; 513 | out.day := UDINT_TO_USINT(days); 514 | 515 | DISSECT_TIME := out; 516 | END_FUNCTION 517 | 518 | 519 | // Converts internal TIME_OF_DAY type to TIME_OF_DAY_DISSECTED structure. 520 | FUNCTION DISSECT_TIME_OF_DAY : TIME_OF_DAY_DISSECTED 521 | VAR_INPUT 522 | in : TIME_OF_DAY; 523 | END_VAR 524 | 525 | VAR_TEMP 526 | milliseconds : UDINT; 527 | seconds : UDINT; 528 | minutes : UDINT; 529 | hours : UDINT; 530 | out : TIME_OF_DAY_DISSECTED; 531 | END_VAR 532 | 533 | milliseconds := TIME_OF_DAY_TO_UDINT_INTERNAL(in); 534 | out.millisecond := UDINT_TO_UINT(milliseconds MOD 1000); 535 | 536 | seconds := (milliseconds - out.millisecond) / 1000; 537 | out.second := UDINT_TO_USINT(seconds MOD 60); 538 | 539 | minutes := (seconds - out.second) / 60; 540 | out.minute := UDINT_TO_USINT(minutes MOD 60); 541 | 542 | hours := (minutes - out.minute) / 60; 543 | out.hour := UDINT_TO_USINT(hours MOD 24); 544 | 545 | DISSECT_TIME_OF_DAY := out; 546 | END_FUNCTION 547 | 548 | 549 | // INTERNAL: Converts internal DATE type to DATE_DISSECTED structure. 550 | FUNCTION DISSECT_DATE_INTERNAL : DATE_DISSECTED 551 | VAR_INPUT 552 | in : UDINT; 553 | END_VAR 554 | 555 | VAR_TEMP 556 | year_lookup_start : DINT; 557 | year_lookup_end : DINT; 558 | year : DINT; 559 | month : DINT; 560 | out : DATE_DISSECTED; 561 | END_VAR 562 | 563 | IF in = 0 THEN 564 | DISSECT_DATE_INTERNAL := out; 565 | RETURN; 566 | END_IF; 567 | 568 | year_lookup_start := MIN(MAX(1992 + TRUNC((in - 1) / 366), 1992), 2200); 569 | year_lookup_end := MIN(MAX(1992 + TRUNC((in - 1) / 365), 1992), 2200); 570 | 571 | FOR year := year_lookup_start TO year_lookup_end DO 572 | IF year = 2200 THEN 573 | EXIT; 574 | ELSIF Start_Of_Months_Of_Years_Values[year + 1][1] > in THEN 575 | EXIT; 576 | END_IF; 577 | END_FOR; 578 | 579 | FOR month := 1 TO 12 DO 580 | IF month = 12 THEN 581 | EXIT; 582 | ELSIF Start_Of_Months_Of_Years_Values[year][month + 1] > in THEN 583 | EXIT; 584 | END_IF; 585 | END_FOR; 586 | 587 | out.year := DINT_TO_UINT(year); 588 | out.month := DINT_TO_USINT(month); 589 | out.day := UDINT_TO_USINT(in - Start_Of_Months_Of_Years_Values[year][month] + 1); 590 | 591 | DISSECT_DATE_INTERNAL := out; 592 | END_FUNCTION 593 | 594 | // Converts internal DATE type to DATE_DISSECTED structure. 595 | FUNCTION DISSECT_DATE : DATE_DISSECTED 596 | VAR_INPUT 597 | in : DATE; 598 | END_VAR 599 | 600 | DISSECT_DATE := DISSECT_DATE_INTERNAL(DATE_TO_UDINT_INTERNAL(in)); 601 | END_FUNCTION 602 | 603 | 604 | // Converts internal DATE_AND_TIME type to DATE_AND_TIME_DISSECTED structure. 605 | FUNCTION DISSECT_DATE_AND_TIME : DATE_AND_TIME_DISSECTED 606 | VAR_INPUT 607 | in : DATE_AND_TIME; 608 | END_VAR 609 | 610 | VAR_TEMP 611 | dat_DISSECTED : DATE_DISSECTED; 612 | tod_DISSECTED : TIME_OF_DAY_DISSECTED; 613 | out : DATE_AND_TIME_DISSECTED; 614 | END_VAR 615 | 616 | dat_DISSECTED := DISSECT_DATE(DT_TO_DATE(in)); 617 | tod_DISSECTED := DISSECT_TIME_OF_DAY(DT_TO_TOD(in)); 618 | 619 | out.year := dat_DISSECTED.year; 620 | out.month := dat_DISSECTED.month; 621 | out.day := dat_DISSECTED.day; 622 | 623 | out.hour := tod_DISSECTED.hour; 624 | out.minute := tod_DISSECTED.minute; 625 | out.second := tod_DISSECTED.second; 626 | out.millisecond := tod_DISSECTED.millisecond; 627 | 628 | DISSECT_DATE_AND_TIME := out; 629 | END_FUNCTION 630 | 631 | 632 | // INTERNAL: Returns the numeric day of week (from Monday = 1 to Sunday = 7). 633 | FUNCTION GET_DAY_OF_WEEK_INTERNAL : USINT 634 | VAR_INPUT 635 | in : UDINT; 636 | END_VAR 637 | 638 | GET_DAY_OF_WEEK_INTERNAL := UDINT_TO_USINT((in MOD 7 + 1) MOD 7 + 1); 639 | END_FUNCTION 640 | 641 | // Returns the numeric day of week. 642 | FUNCTION GET_DAY_OF_WEEK : USINT 643 | VAR_INPUT 644 | in : DATE; 645 | kind : DAY_OF_WEEK_TYPE; // currently, only ISO 8601 is supported 646 | END_VAR 647 | 648 | GET_DAY_OF_WEEK := GET_DAY_OF_WEEK_INTERNAL(DATE_TO_UDINT_INTERNAL(in)); 649 | END_FUNCTION 650 | 651 | 652 | // INTERNAL: Returns the numeric week of year (from 1st week = 1 to last week = 52 or 53). 653 | FUNCTION GET_WEEK_OF_YEAR_INTERNAL : USINT 654 | VAR_INPUT 655 | in : UDINT; 656 | END_VAR 657 | 658 | VAR_TEMP 659 | anchor_date : DINT; 660 | anchor_date_DISSECTED : DATE_DISSECTED; 661 | anchor_date2 : UDINT; 662 | out : USINT; 663 | END_VAR 664 | 665 | anchor_date := UDINT_TO_DINT(in) - GET_DAY_OF_WEEK_INTERNAL(in) + 4; 666 | 667 | CASE anchor_date OF 668 | // special handling required for some anchor dates before 1992-01-01 669 | -5, -4, -3: out := 52; 670 | -2, -1, 0: out := 1; 671 | // special handling required for some anchor dates before 2200-12-31 672 | 76337, 76336, 76335: out := 1; 673 | ELSE 674 | anchor_date_DISSECTED := DISSECT_DATE_INTERNAL(DINT_TO_UDINT(anchor_date)); 675 | anchor_date2 := CREATE_DATE_INTERNAL(anchor_date_DISSECTED.year, 1, 4); 676 | out := DINT_TO_USINT(( 677 | anchor_date 678 | - UDINT_TO_DINT(anchor_date2) 679 | + USINT_TO_DINT(GET_DAY_OF_WEEK_INTERNAL(anchor_date2)) 680 | - 4 681 | ) / 7 + 1); 682 | END_CASE; 683 | 684 | GET_WEEK_OF_YEAR_INTERNAL := out; 685 | END_FUNCTION 686 | 687 | // Returns the numeric week of year. 688 | FUNCTION GET_WEEK_OF_YEAR : USINT 689 | VAR_INPUT 690 | in : DATE; 691 | kind : WEEK_OF_YEAR_TYPE; // currently, only ISO 8601 is supported 692 | END_VAR 693 | 694 | GET_WEEK_OF_YEAR := GET_WEEK_OF_YEAR_INTERNAL(DATE_TO_UDINT_INTERNAL(in)); 695 | END_FUNCTION 696 | 697 | 698 | // INTERNAL: Returns the numeric day of year (from 1st of January = 1 to 31th of December = 365 or 366). 699 | FUNCTION GET_DAY_OF_YEAR_INTERNAL : UINT 700 | VAR_INPUT 701 | in : UDINT; 702 | END_VAR 703 | 704 | VAR_TEMP 705 | year_lookup_start : DINT; 706 | year_lookup_end : DINT; 707 | year : DINT; 708 | start_of_year : UDINT; 709 | END_VAR 710 | 711 | year_lookup_start := MIN(MAX(1992 + TRUNC((in - 1) / 366), 1992), 2200); 712 | year_lookup_end := MIN(MAX(1992 + TRUNC((in - 1) / 365), 1992), 2200); 713 | 714 | FOR year := year_lookup_start TO year_lookup_end DO 715 | IF Start_Of_Months_Of_Years_Values[year][1] <= in THEN 716 | start_of_year := Start_Of_Months_Of_Years_Values[year][1]; 717 | END_IF; 718 | END_FOR; 719 | 720 | GET_DAY_OF_YEAR_INTERNAL := UDINT_TO_UINT(in - start_of_year + 1); 721 | END_FUNCTION 722 | 723 | // Returns the numeric day of year (from 1st of January = 1 to 31th of December = 365 or 366). 724 | FUNCTION GET_DAY_OF_YEAR : UINT 725 | VAR_INPUT 726 | in : DATE; 727 | END_VAR 728 | 729 | GET_DAY_OF_YEAR := GET_DAY_OF_YEAR_INTERNAL(DATE_TO_UDINT_INTERNAL(in)); 730 | END_FUNCTION 731 | 732 | 733 | // Formats given duration using specified format and returns created string. 734 | FUNCTION FORMAT_TIME_DISSECTED : STRING[18] 735 | VAR_INPUT 736 | in : TIME_DISSECTED; 737 | format : TIME_FORMAT; // currently, only ISO 8601 is supported 738 | END_VAR 739 | 740 | VAR_TEMP 741 | out : STRING[18]; 742 | END_VAR 743 | 744 | out := 'P'; 745 | 746 | IF in.day <> 0 THEN 747 | out := CONCAT3(out, USINT_TO_STRING(in.day), 'D'); 748 | END_IF; 749 | 750 | out := CONCAT(out, 'T'); 751 | 752 | IF in.hour <> 0 THEN 753 | out := CONCAT3(out, USINT_TO_STRING(in.hour), 'H'); 754 | END_IF; 755 | 756 | IF in.minute <> 0 THEN 757 | out := CONCAT3(out, USINT_TO_STRING(in.minute), 'M'); 758 | END_IF; 759 | 760 | IF in.second <> 0 OR in.millisecond <> 0 THEN 761 | out := CONCAT5(out, USINT_TO_STRING(in.second), '.', PAD_STRING_LEFT(UINT_TO_STRING(in.millisecond), 3, '0'), 'S'); 762 | END_IF; 763 | 764 | FORMAT_TIME_DISSECTED := out; 765 | END_FUNCTION 766 | 767 | // Formats given duration using specified format and returns created string. 768 | FUNCTION FORMAT_TIME : STRING[18] 769 | VAR_INPUT 770 | in : TIME; 771 | format : TIME_FORMAT; 772 | END_VAR 773 | 774 | FORMAT_TIME := FORMAT_TIME_DISSECTED(DISSECT_TIME(in),format); 775 | END_FUNCTION 776 | 777 | 778 | // Formats given time using specified format and returns created string. 779 | FUNCTION FORMAT_TIME_OF_DAY_DISSECTED : STRING[12] 780 | VAR_INPUT 781 | in : TIME_OF_DAY_DISSECTED; 782 | format : TIME_OF_DAY_FORMAT; 783 | END_VAR 784 | 785 | CASE format OF 786 | ISO8601: FORMAT_TIME_OF_DAY_DISSECTED := CONCAT7( 787 | PAD_STRING_LEFT(USINT_TO_STRING(in.hour), 2, '0'), 788 | ':', 789 | PAD_STRING_LEFT(USINT_TO_STRING(in.minute), 2, '0'), 790 | ':', 791 | PAD_STRING_LEFT(USINT_TO_STRING(in.second), 2, '0'), 792 | '.', 793 | PAD_STRING_LEFT(UINT_TO_STRING(in.millisecond), 3, '0') 794 | ); 795 | ISO8601_HHMMSS: FORMAT_TIME_OF_DAY_DISSECTED := CONCAT5( 796 | PAD_STRING_LEFT(USINT_TO_STRING(in.hour), 2, '0'), 797 | ':', 798 | PAD_STRING_LEFT(USINT_TO_STRING(in.minute), 2, '0'), 799 | ':', 800 | PAD_STRING_LEFT(USINT_TO_STRING(in.second), 2, '0') 801 | ); 802 | ISO8601_HHMMSS_NODLM: FORMAT_TIME_OF_DAY_DISSECTED := CONCAT3( 803 | PAD_STRING_LEFT(USINT_TO_STRING(in.hour), 2, '0'), 804 | PAD_STRING_LEFT(USINT_TO_STRING(in.minute), 2, '0'), 805 | PAD_STRING_LEFT(USINT_TO_STRING(in.second), 2, '0') 806 | ); 807 | ISO8601_HHMM: FORMAT_TIME_OF_DAY_DISSECTED := CONCAT3( 808 | PAD_STRING_LEFT(USINT_TO_STRING(in.hour), 2, '0'), 809 | ':', 810 | PAD_STRING_LEFT(USINT_TO_STRING(in.minute), 2, '0') 811 | ); 812 | ISO8601_HHMM_NODLM: FORMAT_TIME_OF_DAY_DISSECTED := CONCAT( 813 | PAD_STRING_LEFT(USINT_TO_STRING(in.hour), 2, '0'), 814 | PAD_STRING_LEFT(USINT_TO_STRING(in.minute), 2, '0') 815 | ); 816 | END_CASE; 817 | END_FUNCTION 818 | 819 | // Formats given time using specified format and returns created string. 820 | FUNCTION FORMAT_TIME_OF_DAY : STRING[12] 821 | VAR_INPUT 822 | in : TIME_OF_DAY; 823 | format : TIME_OF_DAY_FORMAT; 824 | END_VAR 825 | 826 | FORMAT_TIME_OF_DAY := FORMAT_TIME_OF_DAY_DISSECTED(DISSECT_TIME_OF_DAY(in),format); 827 | END_FUNCTION 828 | 829 | 830 | // Formats given date using specified format and returns created string. 831 | FUNCTION FORMAT_DATE_DISSECTED : STRING[10] 832 | VAR_INPUT 833 | in : DATE_DISSECTED; 834 | format : DATE_FORMAT; 835 | END_VAR 836 | 837 | CASE format OF 838 | ISO8601: FORMAT_DATE_DISSECTED := CONCAT5( 839 | UINT_TO_STRING(in.year), 840 | '-', 841 | PAD_STRING_LEFT(USINT_TO_STRING(in.month), 2, '0'), 842 | '-', 843 | PAD_STRING_LEFT(USINT_TO_STRING(in.day), 2, '0') 844 | ); 845 | ISO8601_NODLM: FORMAT_DATE_DISSECTED := CONCAT3( 846 | UINT_TO_STRING(in.year), 847 | PAD_STRING_LEFT(USINT_TO_STRING(in.month), 2, '0'), 848 | PAD_STRING_LEFT(USINT_TO_STRING(in.day), 2, '0') 849 | ); 850 | DIN5008: FORMAT_DATE_DISSECTED := CONCAT5( 851 | PAD_STRING_LEFT(USINT_TO_STRING(in.day), 2, '0'), 852 | '.', 853 | PAD_STRING_LEFT(USINT_TO_STRING(in.month), 2, '0'), 854 | '.', 855 | UINT_TO_STRING(in.year) 856 | ); 857 | END_CASE; 858 | END_FUNCTION 859 | 860 | // Formats given date using specified format and returns created string. 861 | FUNCTION FORMAT_DATE : STRING[10] 862 | VAR_INPUT 863 | in : DATE; 864 | format : DATE_FORMAT; 865 | END_VAR 866 | 867 | FORMAT_DATE := FORMAT_DATE_DISSECTED(DISSECT_DATE(in), format); 868 | END_FUNCTION 869 | 870 | 871 | // Formats given date and time using specified format and returns created string. 872 | FUNCTION FORMAT_DATE_AND_TIME_DISSECTED : STRING[23] 873 | VAR_INPUT 874 | in : DATE_AND_TIME_DISSECTED; 875 | format : DATE_AND_TIME_FORMAT; // currently, only ISO 8601 is supported 876 | END_VAR 877 | 878 | FORMAT_DATE_AND_TIME_DISSECTED := CONCAT3( 879 | CONCAT5( 880 | UINT_TO_STRING(in.year), 881 | '-', 882 | PAD_STRING_LEFT(USINT_TO_STRING(in.month), 2, '0'), 883 | '-', 884 | PAD_STRING_LEFT(USINT_TO_STRING(in.day), 2, '0') 885 | ), 886 | 'T', 887 | CONCAT7( 888 | PAD_STRING_LEFT(USINT_TO_STRING(in.hour), 2, '0'), 889 | ':', 890 | PAD_STRING_LEFT(USINT_TO_STRING(in.minute), 2, '0'), 891 | ':', 892 | PAD_STRING_LEFT(USINT_TO_STRING(in.second), 2, '0'), 893 | '.', 894 | PAD_STRING_LEFT(UINT_TO_STRING(in.millisecond), 3, '0') 895 | ) 896 | ); 897 | END_FUNCTION 898 | 899 | // Formats given date and time using specified format and returns created string. 900 | FUNCTION FORMAT_DATE_AND_TIME : STRING[23] 901 | VAR_INPUT 902 | in : DATE_AND_TIME; 903 | format : DATE_AND_TIME_FORMAT; 904 | END_VAR 905 | 906 | FORMAT_DATE_AND_TIME := FORMAT_DATE_AND_TIME_DISSECTED(DISSECT_DATE_AND_TIME(in), format); 907 | END_FUNCTION 908 | END_IMPLEMENTATION 909 | --------------------------------------------------------------------------------