├── .eslintignore ├── .eslintrc ├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── package-lock.json ├── package.json ├── src ├── iec-61131-3.ts ├── iec-resolver.ts ├── iec-type-handler.ts ├── iec-types.ts └── types │ └── types.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | example -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint" 6 | ], 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/eslint-recommended", 10 | "plugin:@typescript-eslint/recommended" 11 | ], 12 | "rules": { 13 | "no-async-promise-executor": "off" 14 | } 15 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | # Ignore files with sensitive environment variables 3 | .env 4 | .env.test 5 | # Next.js output 6 | .next/ 7 | # Parcel cache 8 | .cache/ 9 | # Ignore IDE configuration files 10 | .idea/ 11 | .vscode/ 12 | # Logs 13 | logs 14 | *.log 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | # Ignore built ts files 19 | dist/**/* -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.1.0] - 27.07.2022 8 | ### Added 9 | - New iterators for iterating variables and array elements 10 | - `variableIterator()` 11 | - Iterates each variable recursively in memory order 12 | - Usage: `for(const variable of dataType.variableIterator()) {...}` OR shorthand: `for(const variable of dataType) {...}` 13 | - `elementIterator()` 14 | - Iterates each variable and array element recursively in memory-order 15 | - Usage: `for(const variable of dataType.elementIterator()) {...}` 16 | 17 | ### Changed 18 | - `IecType` interface is now exported for external use 19 | 20 | ## [1.0.0] - 17.08.2021 21 | ### Added 22 | - `fromString()` support for other data types than STRUCT 23 | - ENUM 24 | - ALIAS 25 | - UNION 26 | - New data types 27 | - ENUM 28 | - UNION 29 | - ALIAS (only with `fromString()`) 30 | 31 | ### Updated 32 | - README updated 33 | - `fromString()` updated to work better 34 | 35 | ## [0.2.0] - 10.08.2021 36 | ### Added 37 | - fromString() method to convert IEC types automatically from PLC data type declarations 38 | - All non-complex data types supported (INT, REAL, ...) 39 | - STRING and WSTRING support 40 | - STRUCT support 41 | - ARRAY support (multi-dimensional) 42 | 43 | ### Updated 44 | - Updated multi-dimensional array handling to work properly 45 | - Updated README 46 | 47 | ## [0.1.1] - 07.08.2021 48 | ### Added 49 | - README updates etc. 50 | 51 | ## [0.1.0] - 07.08.2021 52 | ### Added 53 | - First release -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jussi Isotalo 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 | # iec-61131-3 2 | 3 | [![npm version](https://img.shields.io/npm/v/iec-61131-3)](https://www.npmjs.org/package/iec-61131-3) 4 | [![GitHub](https://img.shields.io/badge/View%20on-GitHub-brightgreen)](https://github.com/jisotalo/iec-61131-3) 5 | [![License](https://img.shields.io/github/license/jisotalo/iec-61131-3)](https://choosealicense.com/licenses/mit/) 6 | 7 | IEC 61131-3 PLC data type helper for Node.js. Allows creating PLC data type schemas in Javascript and conversion between Javascript objects and raw binary PLC data. 8 | 9 | Supports automatic conversion from PLC code variable declarations (structs, enums, unions, aliases) to IEC types. 10 | 11 | Inspiration from [iecstruct](https://www.npmjs.com/package/iecstruct) project, however written from scratch. 12 | 13 | 14 | # Project status 15 | 16 | Things to do 17 | - Adding more examples to README 18 | - Updating `fromString()` to support CONSTANTs (for constant sized arrays etc.) 19 | - Updating `fromString()` to support network variable lists (just a helper for Codesys systems) 20 | - Adding checks if Node.js version has BigInt support 21 | - Adding more error checking 22 | - Adding TypeScript type/interface definition generator 23 | 24 | 25 | 26 | # Table of contents 27 | - [Installing](#installing) 28 | - [IMPORTANT NOTE](#important-note) 29 | - [Available data types](#available-data-types) 30 | - [Documentation](#documentation) 31 | - [Defining data types](#defining-data-types) 32 | - [Using `fromString()` automatic method](#using-fromstring-automatic-method) 33 | - [Examples](#examples) 34 | - [Manually creating the data type schema](#manually-creating-the-data-type-schema) 35 | - [Automatic conversion from PLC declaration](#automatic-conversion-from-plc-declaration) 36 | - [Iterating variables](#iterating-variables) 37 | - [Iterating variables](#iterating-variables) 38 | - [Iterating elements (variables and array elements)](#iterating-elements-variables-and-array-elements) 39 | - [License](#license) 40 | 41 | # Installing 42 | Install the [npm package](https://www.npmjs.com/package/iec-61131-3) using npm command: 43 | ```bash 44 | npm i iec-61131-3 45 | ``` 46 | 47 | # IMPORTANT NOTE 48 | The PLC data has to be saved in **pack-mode 1** so there will be no padding bytes. 49 | 50 | In CODESYS based systems (TwinCAT etc.), add the following above `STRUCT` definitions: 51 | 52 | `{attribute 'pack_mode' := '1'}` 53 | 54 | # Available data types 55 | - STRUCT 56 | - UNION 57 | - ARRAY 58 | - ENUM 59 | - STRING 60 | - WSTRING 61 | - BOOL 62 | - USINT 63 | - BYTE 64 | - SINT 65 | - UINT 66 | - WORD 67 | - INT 68 | - DINT 69 | - UDINT 70 | - DWORD 71 | - TIME 72 | - TOD 73 | - TIME_OF_DAY 74 | - DT 75 | - DATE_AND_TIME 76 | - DATE 77 | - REAL 78 | - LREAL 79 | - ULINT 80 | - LWORD 81 | - LINT 82 | 83 | Also, when using `fromString()`, the ALIAS data type is supported. 84 | 85 | # Documentation 86 | 87 | ## Defining data types 88 | 89 | Note: It's much easier to use the `fromString()` way. See next chapter. 90 | 91 | 92 | Including the library with the following: 93 | ```js 94 | const iec = require('iec-61131-3') 95 | ``` 96 | 97 | **STRUCT** 98 | ```js 99 | /* 100 | TYPE ST_Struct : 101 | STRUCT 102 | variable1: INT; 103 | variable2: REAL; 104 | END_STRUCT 105 | END_TYPE 106 | */ 107 | const ST_Struct = iec.STRUCT({ 108 | variable1: iec.INT, 109 | variable2: iec.REAL 110 | }) 111 | ``` 112 | 113 | **UNION** 114 | 115 | All `UNION` members occupy the same memory, so size of the `UNION` = size of the biggest variable data type. 116 | 117 | ```js 118 | /* 119 | TYPE U_Union : 120 | UNION 121 | variable1: INT; 122 | variable2: REAL; 123 | END_UNION 124 | END_TYPE 125 | */ 126 | const U_Union = iec.UNION({ 127 | variable1: iec.INT, 128 | variable2: iec.REAL 129 | }) 130 | ``` 131 | **ARRAY** 132 | 133 | Single-dimensional array 134 | ```js 135 | //singleDimension : ARRAY[0..9] OF INT; 136 | const singleDimension = iec.ARRAY(iec.INT, 10) 137 | ``` 138 | Multi-dimensional array 139 | ```js 140 | //multiDimension : ARRAY[0..1, 0..9] OF INT; 141 | const multiDimension = iec.ARRAY(iec.INT, [2, 10]) 142 | ``` 143 | 144 | **ENUM** 145 | 146 | `ENUM` with default type (`INT`): 147 | ```js 148 | /* 149 | TYPE E_Enum : 150 | ( 151 | member0 := 0, 152 | member1, 153 | member2, 154 | member100 := 100 155 | ); 156 | END_TYPE 157 | */ 158 | const E_Enum = iec.ENUM({ 159 | member0: 0, 160 | member1: 1, 161 | member2: 2, 162 | member100: 100 163 | }) 164 | ``` 165 | 166 | `ENUM` with specific type (like `DWORD`): 167 | ```js 168 | /* 169 | TYPE E_Enum : 170 | ( 171 | member0 := 0 172 | ) DWORD; 173 | END_TYPE 174 | */ 175 | const E_Enum = iec.ENUM({ 176 | member0: 0 177 | }, iec.DWORD) 178 | ``` 179 | 180 | **STRING** 181 | 182 | Default length (80): 183 | ```js 184 | //stringValue : STRING; 185 | const stringValue = iec.STRING() 186 | ``` 187 | 188 | Custom length: 189 | ```js 190 | //stringValue : STRING(200); 191 | const stringValue = iec.STRING(200) 192 | ``` 193 | 194 | WSTRING 195 | Default length (80): 196 | ```js 197 | //stringValue : WSTRING; 198 | const wstringValue = iec.WSTRING() 199 | ``` 200 | 201 | Custom length: 202 | ```js 203 | //stringValue : WSTRING(200); 204 | const wstringValue = iec.WSTRING(200) 205 | ``` 206 | **BOOL** 207 | ```js 208 | const BOOL = iec.BOOL 209 | ``` 210 | **USINT** 211 | ```js 212 | const USINT = iec.USINT 213 | ``` 214 | **BYTE** 215 | ```js 216 | const BYTE = iec.BYTE 217 | ``` 218 | **SINT** 219 | ```js 220 | const SINT = iec.SINT 221 | ``` 222 | **UINT** 223 | ```js 224 | const UINT = iec.UINT 225 | ``` 226 | **WORD** 227 | ```js 228 | const WORD = iec.WORD 229 | ``` 230 | **INT** 231 | ```js 232 | const INT = iec.INT 233 | ``` 234 | **DINT** 235 | ```js 236 | const DINT = iec.DINT 237 | ``` 238 | **UDINT** 239 | ```js 240 | const UDINT = iec.UDINT 241 | ``` 242 | **DWORD** 243 | ```js 244 | const DWORD = iec.DWORD 245 | ``` 246 | **TIME** 247 | ```js 248 | const TIME = iec.TIME 249 | ``` 250 | **TOD, TIME_OF_DAY** 251 | ```js 252 | const TOD = iec.TOD 253 | const TIME_OF_DAY = iec.TIME_OF_DAY 254 | ``` 255 | **DT, DATE_AND_TIME, DATE** 256 | 257 | Epoch timestamp (seconds since 1970) 258 | 259 | **IMPORTANT NOTE:** At the moment, the value is not converted to Javascript `Date` object. This might change in future updates!! 260 | ```js 261 | const DT = iec.DT 262 | const DATE_AND_TIME = iec.DATE_AND_TIME 263 | const DATE = iec.DATE 264 | ``` 265 | **REAL** 266 | ```js 267 | const REAL = iec.REAL 268 | ``` 269 | **LREAL** 270 | ```js 271 | const LREAL = iec.LREAL 272 | ``` 273 | **ULINT** 274 | 275 | NOTE: Requires `BigInt` support from Node.js 276 | ```js 277 | const ULINT = iec.ULINT 278 | ``` 279 | **LWORD** 280 | 281 | NOTE: Requires `BigInt` support from Node.js 282 | ```js 283 | const LWORD = iec.LWORD 284 | ``` 285 | **LINT** 286 | 287 | NOTE: Requires `BigInt` support from Node.js 288 | ```js 289 | const LINT = iec.LINT 290 | ``` 291 | 292 | ## Using `fromString()` automatic method 293 | 294 | The PLC data type declarations (STRUCT, UNION, ENUM, ALIAS) can be automatically converted to Javascript data type schemas. 295 | 296 | Single `STRUCT`: 297 | ```js 298 | const ST_Struct = iec.fromString(` 299 | {attribute 'pack_mode' := '1'} 300 | TYPE ST_Struct: 301 | STRUCT 302 | variable1: INT; 303 | variable2: REAL; 304 | END_STRUCT 305 | END_TYPE 306 | `) 307 | ``` 308 | 309 | Single `ENUM`: 310 | ```js 311 | const E_Enum = iec.fromString(` 312 | {attribute 'qualified_only'} 313 | {attribute 'strict'} 314 | TYPE E_Enum : 315 | ( 316 | member0 := 0, 317 | member1, 318 | member2, 319 | member100 := 100 320 | ); 321 | END_TYPE 322 | `) 323 | ``` 324 | 325 | `STRUCT` that depends on another `STRUCT` and also on `ENUM`: 326 | ```js 327 | const ST_Struct = iec.fromString(` 328 | {attribute 'pack_mode' := '1'} 329 | TYPE ST_Struct : 330 | STRUCT 331 | StructValue: ST_Struct2; 332 | EnumValue: E_Enum; 333 | END_STRUCT 334 | END_TYPE 335 | 336 | {attribute 'pack_mode' := '1'} 337 | TYPE ST_Struct2 : 338 | STRUCT 339 | StringValue: STRING(); 340 | END_STRUCT 341 | END_TYPE 342 | 343 | {attribute 'qualified_only'} 344 | {attribute 'strict'} 345 | TYPE E_Enum : 346 | ( 347 | member0 := 0, 348 | member1, 349 | member2, 350 | member100 := 100 351 | ); 352 | END_TYPE 353 | `, 'ST_Struct') //NOTE 2nd parameter (= top-level data type / desired return value) 354 | ``` 355 | 356 | Providing already defined data types and also using `ALIAS` (`ST_Struct2` is used with alias `MyStruct2Alias`) 357 | ```js 358 | const ST_Struct2 = iec.STRUCT({ 359 | StringValue: iec.STRING() 360 | }) 361 | 362 | const E_Enum = iec.ENUM({ 363 | member0: 0, 364 | member1: 1, 365 | member2: 2, 366 | member100: 100 367 | }) 368 | 369 | const ST_Struct = iec.fromString(` 370 | TYPE MyStruct2Alias : ST_Struct2; END_TYPE 371 | 372 | {attribute 'pack_mode' := '1'} 373 | TYPE ST_Struct : 374 | STRUCT 375 | StructValue: MyStruct2Alias; 376 | EnumValue: E_Enum; 377 | END_STRUCT 378 | END_TYPE`, 379 | 'ST_Struct', //NOTE 2nd parameter (= top-level data type / desired return value) 380 | { ST_Struct2, E_Enum } //NOTE 3rd parameter ( = provided data types) 381 | ) 382 | ``` 383 | 384 | 385 | 386 | 387 | # Examples 388 | 389 | ## Manually creating the data type schema 390 | 391 | Let's assume that the PLC struct definitions are as follows: 392 | 393 | ST_IEC_Example: 394 | ``` 395 | {attribute 'pack_mode' := '1'} 396 | TYPE ST_IEC_Example : 397 | STRUCT 398 | Text : STRING(50) := 'Hello iec-61131-3 helper'; 399 | Decimal : REAL := 3.14159265359; 400 | ArrayData : ARRAY[0..9] OF INT := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 401 | StructData : ST_IEC_Example2; 402 | END_STRUCT 403 | END_TYPE 404 | ``` 405 | ST_IEC_Example2: 406 | ``` 407 | {attribute 'pack_mode' := '1'} 408 | TYPE ST_IEC_Example2 : 409 | STRUCT 410 | Text : STRING(50) := 'Cheers from second struct'; 411 | END_STRUCT 412 | END_TYPE 413 | 414 | ``` 415 | 416 | Then we can define the same schema in Javascript code: 417 | ```js 418 | const iec = require('iec-61131-3') 419 | 420 | //Creating a IEC data type schema matching the PLC datatype ST_Example 421 | const ST_Example = iec.STRUCT({ 422 | Text: iec.STRING(50), 423 | Decimal: iec.REAL, 424 | ArrayData: iec.ARRAY(iec.INT, 10), 425 | StructData: iec.STRUCT({ 426 | Text: iec.STRING(50), 427 | }) 428 | }) 429 | 430 | //Creating a default empty object from it 431 | const obj = ST_Example.getDefault() 432 | console.log(obj) 433 | /* 434 | { 435 | Text: '', 436 | Decimal: 0, 437 | ArrayData: [ 438 | 0, 0, 0, 0, 0, 439 | 0, 0, 0, 0, 0 440 | ], 441 | StructData: { Text: '' } 442 | } 443 | */ 444 | 445 | //Changing some values 446 | obj.Text = 'Whats up?' 447 | obj.ArrayData[5] = 123 448 | obj.StructData.Text = 'Hello' 449 | 450 | console.log(obj) 451 | /* 452 | { 453 | Text: 'Whats up?', 454 | Decimal: 0, 455 | ArrayData: [ 456 | 0, 0, 0, 0, 0, 457 | 123, 0, 0, 0, 0 458 | ], 459 | StructData: { Text: 'Hello' } 460 | } 461 | */ 462 | 463 | //Creating raw binary Buffer data 464 | const buffer = ST_Example.convertToBuffer(obj) 465 | console.log(buffer) 466 | /* 467 | 468 | */ 469 | 470 | //Converting back to Javascript object 471 | const converted = ST_Example.convertFromBuffer(buffer) 472 | console.log(converted) 473 | /* 474 | { 475 | Text: 'Whats up?', 476 | Decimal: 0, 477 | ArrayData: [ 478 | 0, 0, 0, 0, 0, 479 | 123, 0, 0, 0, 0 480 | ], 481 | StructData: { Text: 'Hello' } 482 | } 483 | */ 484 | ``` 485 | 486 | We could also define structs separatelyindependsently: 487 | 488 | ```js 489 | const iec = require('iec-61131-3') 490 | 491 | //ST_Example2 schema 492 | const ST_Example2 = iec.STRUCT({ 493 | Text: iec.STRING(50) 494 | }) 495 | 496 | //ST_Example schema 497 | const ST_Example = iec.STRUCT({ 498 | Text: iec.STRING(50), 499 | Decimal: iec.REAL, 500 | ArrayData: iec.ARRAY(iec.INT, 10), 501 | StructData: ST_Example2 502 | }) 503 | ``` 504 | 505 | ## Automatic conversion from PLC declaration 506 | 507 | **Note:** This example does not care how to data is written or read. It's left out of this example. 508 | 509 | ### Single struct 510 | 511 | Copypaste PLC struct declaration into the `fromString()` method input parameter. 512 | 513 | ```js 514 | const iec = require('iec-61131-3') 515 | 516 | //Copypasted directly from from PLC 517 | //Initial values, comments and pragmas do not matter 518 | const ST_IEC_Example = iec.fromString(` 519 | {attribute 'pack_mode' := '1'} 520 | TYPE ST_IEC_Example : 521 | STRUCT 522 | Text : STRING(50) := 'Hello iec-61131-3 helper'; 523 | Decimal : REAL := 3.14159265359; 524 | ArrayData : ARRAY[0..9] OF INT := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 525 | MultiDimArrayData : ARRAY[0..1, 0..2] OF INT := [1, 2, 3, 4, 5, 6]; 526 | END_STRUCT 527 | END_TYPE 528 | `) 529 | 530 | //IEC data type schema 531 | console.log(ST_IEC_Example) 532 | /* 533 | STRUCT { 534 | type: 'STRUCT', 535 | byteLength: 87, 536 | children: { 537 | Text: STRING { type: 'STRING', byteLength: 51 }, 538 | Decimal: REAL { type: 'REAL', byteLength: 4 }, 539 | ArrayData: ARRAY { 540 | type: 'ARRAY', 541 | byteLength: 20, 542 | dimensions: [Array], 543 | totalSize: 10, 544 | dataType: [INT] 545 | }, 546 | MultiDimArrayData: ARRAY { 547 | type: 'ARRAY', 548 | byteLength: 12, 549 | dimensions: [Array], 550 | totalSize: 6, 551 | dataType: [INT] 552 | } 553 | } 554 | } 555 | */ 556 | 557 | 558 | //Empty object 559 | let obj = ST_IEC_Example.getDefault() 560 | console.log(obj) 561 | /* 562 | { 563 | Text: '', 564 | Decimal: 0, 565 | ArrayData: [ 566 | 0, 0, 0, 0, 0, 567 | 0, 0, 0, 0, 0 568 | ], 569 | MultiDimArrayData: [ [ 0, 0, 0 ], [ 0, 0, 0 ] ] 570 | } 571 | */ 572 | 573 | 574 | //Updating data + converting to bytes 575 | obj.Text = 'Whats up?' 576 | obj.MultiDimArrayData[0][1] = 123 577 | 578 | const buffer = ST_IEC_Example.convertToBuffer(obj) 579 | console.log(buffer) 580 | /* 581 | 582 | */ 583 | 584 | 585 | //Converting back to object 586 | obj = ST_IEC_Example.convertFromBuffer(buffer) 587 | console.log(obj) 588 | /* 589 | { 590 | Text: 'Whats up?', 591 | Decimal: 0, 592 | ArrayData: [ 593 | 0, 0, 0, 0, 0, 594 | 0, 0, 0, 0, 0 595 | ], 596 | MultiDimArrayData: [ [ 0, 123, 0 ], [ 0, 0, 0 ] ] 597 | } 598 | */ 599 | 600 | ``` 601 | 602 | ### Multiple structs - Providing all data types at once 603 | 604 | Copypaste all PLC struct declarations into the `fromString()` method input parameter. The order in the structs appear doesn't matter. 605 | 606 | **NOTE:** As there are multiple `STRUCT` declarations, the top-level data type needs to be given as 2nd parameter. Otherwise the method would throw an error. When there is only one struct declaration, the 2nd parameter can be omitted. 607 | 608 | In this case, we want the schema to the `ST_IEC_Example2` data type, so we give it as 2nd parameter. 609 | 610 | 611 | ```js 612 | const iec = require('iec-61131-3') 613 | 614 | //Copypasted directly from from PLC 615 | //Initial values, comments and pragmas do not matter 616 | 617 | //NOTE: Multiple struct definitions -> order doesn't matter 618 | //NOTE: We need to provide top-level data type as 2nd parameter! 619 | const ST_IEC_Example2 = iec.fromString(` 620 | {attribute 'pack_mode' := '1'} 621 | TYPE ST_IEC_Example : 622 | STRUCT 623 | Text : STRING(50) := 'Hello iec-61131-3 helper'; 624 | Decimal : REAL := 3.14159265359; 625 | ArrayData : ARRAY[0..9] OF INT := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 626 | MultiDimArrayData : ARRAY[0..1, 0..2] OF INT := [1, 2, 3, 4, 5, 6]; 627 | END_STRUCT 628 | END_TYPE 629 | 630 | {attribute 'pack_mode' := '1'} 631 | TYPE ST_IEC_Example2 : 632 | STRUCT 633 | Text : STRING(50) := 'Cheers from second struct'; 634 | StructArray : ARRAY[0..1] OF ST_IEC_Example; 635 | END_STRUCT 636 | END_TYPE 637 | 638 | `, 'ST_IEC_Example2') //Note the 2nd parameter 639 | 640 | //IEC data type schema 641 | console.log(ST_IEC_Example2) 642 | /* 643 | STRUCT { 644 | type: 'STRUCT', 645 | byteLength: 225, 646 | children: { 647 | Text: STRING { type: 'STRING', byteLength: 51 }, 648 | StructArray: ARRAY { 649 | type: 'ARRAY', 650 | byteLength: 174, 651 | dimensions: [Array], 652 | totalSize: 2, 653 | dataType: [STRUCT] 654 | } 655 | } 656 | } 657 | */ 658 | 659 | 660 | //Empty object 661 | let obj = ST_IEC_Example2.getDefault() 662 | console.log(obj) 663 | /* 664 | { 665 | Text: '', 666 | StructArray: [ 667 | { 668 | Text: '', 669 | Decimal: 0, 670 | ArrayData: [Array], 671 | MultiDimArrayData: [Array] 672 | }, 673 | { 674 | Text: '', 675 | Decimal: 0, 676 | ArrayData: [Array], 677 | MultiDimArrayData: [Array] 678 | } 679 | ] 680 | } 681 | */ 682 | 683 | 684 | //Updating data + converting to bytes 685 | obj.Text = 'Whats up?' 686 | obj.StructArray[0].Text = 'Changing value from struct under array' 687 | 688 | const buffer = ST_IEC_Example2.convertToBuffer(obj) 689 | console.log(buffer) 690 | /* 691 | 692 | */ 693 | 694 | 695 | //Converting back to object 696 | obj = ST_IEC_Example2.convertFromBuffer(buffer) 697 | console.log(obj) 698 | /* 699 | { 700 | Text: 'Whats up?', 701 | StructArray: [ 702 | { 703 | Text: 'Changing value from struct under array', 704 | Decimal: 0, 705 | ArrayData: [Array], 706 | MultiDimArrayData: [Array] 707 | }, 708 | { 709 | Text: '', 710 | Decimal: 0, 711 | ArrayData: [Array], 712 | MultiDimArrayData: [Array] 713 | } 714 | ] 715 | } 716 | */ 717 | 718 | ``` 719 | 720 | ### Multiple structs - Providing data types separately 721 | 722 | Create separate IEC data type schema for each `STRUCT` using `fromString()` method. In this case, the order is important as the `ST_IEC_Example2` needs to know the data type `ST_IEC_Example` (so it needs to be created first). 723 | 724 | We provide the `ST_IEC_Example` IEC data type in object that is passed as 3rd parameter for the `fromString()` method. We also need to pass the top-level data type as 2nd parameter even though there is only one struct declaration. 725 | 726 | ```js 727 | const iec = require('iec-61131-3') 728 | 729 | //Copypasted directly from from PLC 730 | //Initial values, comments and pragmas do not matter 731 | const ST_IEC_Example = iec.fromString(` 732 | {attribute 'pack_mode' := '1'} 733 | TYPE ST_IEC_Example : 734 | STRUCT 735 | Text : STRING(50) := 'Hello iec-61131-3 helper'; 736 | Decimal : REAL := 3.14159265359; 737 | ArrayData : ARRAY[0..9] OF INT := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 738 | MultiDimArrayData : ARRAY[0..1, 0..2] OF INT := [1, 2, 3, 4, 5, 6]; 739 | END_STRUCT 740 | END_TYPE 741 | `) 742 | 743 | //NOTE: 2nd and 3rd parameter 744 | const ST_IEC_Example2 = iec.fromString(` 745 | {attribute 'pack_mode' := '1'} 746 | TYPE ST_IEC_Example2 : 747 | STRUCT 748 | Text : STRING(50) := 'Cheers from second struct'; 749 | StructArray : ARRAY[0..1] OF ST_IEC_Example; 750 | END_STRUCT 751 | END_TYPE`, 752 | 'ST_IEC_Example2', 753 | { ST_IEC_Example } //NOTE: Providing schema for ST_IEC_Example 754 | ) 755 | 756 | //The rest works 1:1 the same as previous example 757 | 758 | //IEC data type schema 759 | console.log(ST_IEC_Example2) 760 | /* 761 | STRUCT { 762 | type: 'STRUCT', 763 | byteLength: 225, 764 | children: { 765 | Text: STRING { type: 'STRING', byteLength: 51 }, 766 | StructArray: ARRAY { 767 | type: 'ARRAY', 768 | byteLength: 174, 769 | dimensions: [Array], 770 | totalSize: 2, 771 | dataType: [STRUCT] 772 | } 773 | } 774 | } 775 | */ 776 | 777 | 778 | //Empty object 779 | let obj = ST_IEC_Example2.getDefault() 780 | console.log(obj) 781 | /* 782 | { 783 | Text: '', 784 | StructArray: [ 785 | { 786 | Text: '', 787 | Decimal: 0, 788 | ArrayData: [Array], 789 | MultiDimArrayData: [Array] 790 | }, 791 | { 792 | Text: '', 793 | Decimal: 0, 794 | ArrayData: [Array], 795 | MultiDimArrayData: [Array] 796 | } 797 | ] 798 | } 799 | */ 800 | 801 | 802 | //Updating data + converting to bytes 803 | obj.Text = 'Whats up?' 804 | obj.StructArray[0].Text = 'Changing value from struct under array' 805 | 806 | const buffer = ST_IEC_Example2.convertToBuffer(obj) 807 | console.log(buffer) 808 | /* 809 | 810 | */ 811 | 812 | 813 | //Converting back to object 814 | obj = ST_IEC_Example2.convertFromBuffer(buffer) 815 | console.log(obj) 816 | /* 817 | { 818 | Text: 'Whats up?', 819 | StructArray: [ 820 | { 821 | Text: 'Changing value from struct under array', 822 | Decimal: 0, 823 | ArrayData: [Array], 824 | MultiDimArrayData: [Array] 825 | }, 826 | { 827 | Text: '', 828 | Decimal: 0, 829 | ArrayData: [Array], 830 | MultiDimArrayData: [Array] 831 | } 832 | ] 833 | } 834 | */ 835 | 836 | ``` 837 | 838 | # Iterating variables 839 | Since version 1.1.0 it is possible to iterate through data type variables and elements more easily. 840 | 841 | The iterators return each variable name (if any), starting index in raw data `Buffer` and data type. This info can be used to extract value for each variable from data buffer. 842 | 843 | NOTE: Compare iterating arrays with following examples. 844 | 845 | ## Iterating variables 846 | ```js 847 | const iec = require('iec-61131-3') 848 | 849 | const ST_Iterating_Example = iec.fromString(` 850 | {attribute 'pack_mode' := '1'} 851 | TYPE ST_IEC_Example : 852 | STRUCT 853 | Text : STRING(50) := 'Hello iec-61131-3 helper'; 854 | Decimal : REAL := 3.14159265359; 855 | ArrayData : ARRAY[0..9] OF INT := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 856 | MultiDimArrayData : ARRAY[0..1, 0..2] OF INT := [1, 2, 3, 4, 5, 6]; 857 | END_STRUCT 858 | END_TYPE 859 | 860 | {attribute 'pack_mode' := '1'} 861 | TYPE ST_Iterating_Example : 862 | STRUCT 863 | Text : STRING(50) := 'Cheers'; 864 | Struct : ST_IEC_Example; 865 | END_STRUCT 866 | END_TYPE 867 | `, 'ST_Iterating_Example') 868 | 869 | for (const variable of ST_Iterating_Example) { 870 | //Or you can use for (const variable of ST_IEC_Example2.variableIterator()) {...} 871 | console.log('Variable:', variable); 872 | } 873 | 874 | /* 875 | Console output: 876 | 877 | Variable: { 878 | name: 'Text', 879 | startIndex: 0, 880 | type: STRING { type: 'STRING', byteLength: 51 } 881 | } 882 | Variable: { 883 | name: 'Text', 884 | startIndex: 51, 885 | type: STRING { type: 'STRING', byteLength: 51 } 886 | } 887 | Variable: { 888 | name: 'Decimal', 889 | startIndex: 102, 890 | type: REAL { type: 'REAL', byteLength: 4 } 891 | } 892 | Variable: { 893 | name: 'ArrayData', 894 | startIndex: 106, 895 | type: ARRAY { 896 | type: 'ARRAY', 897 | byteLength: 20, 898 | dimensions: [ 10 ], 899 | totalSize: 10, 900 | dataType: INT { type: 'INT', byteLength: 2 } 901 | } 902 | } 903 | Variable: { 904 | name: 'MultiDimArrayData', 905 | startIndex: 126, 906 | type: ARRAY { 907 | type: 'ARRAY', 908 | byteLength: 12, 909 | dimensions: [ 2, 3 ], 910 | totalSize: 6, 911 | dataType: INT { type: 'INT', byteLength: 2 } 912 | } 913 | } 914 | */ 915 | ``` 916 | 917 | ## Iterating elements (variables and array elements) 918 | 919 | Note: At the moment indexes in names are 0-based (not matching to PLC declaration). 920 | 921 | ```js 922 | const iec = require('iec-61131-3') 923 | const ST_Iterating_Example = iec.fromString(` 924 | {attribute 'pack_mode' := '1'} 925 | TYPE ST_IEC_Example : 926 | STRUCT 927 | Text : STRING(50) := 'Hello iec-61131-3 helper'; 928 | Decimal : REAL := 3.14159265359; 929 | ArrayData : ARRAY[0..9] OF INT := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 930 | MultiDimArrayData : ARRAY[0..1, 0..2] OF INT := [1, 2, 3, 4, 5, 6]; 931 | END_STRUCT 932 | END_TYPE 933 | 934 | {attribute 'pack_mode' := '1'} 935 | TYPE ST_Iterating_Example : 936 | STRUCT 937 | Text : STRING(50) := 'Cheers'; 938 | Struct : ST_IEC_Example; 939 | END_STRUCT 940 | END_TYPE 941 | `, 'ST_Iterating_Example') 942 | 943 | for (const element of ST_Iterating_Example.elementIterator()) { 944 | console.log('Element:', element); 945 | } 946 | /* 947 | Console output: 948 | 949 | Element: { 950 | name: 'Text', 951 | startIndex: 0, 952 | type: STRING { type: 'STRING', byteLength: 51 } 953 | } 954 | Element: { 955 | name: 'Text', 956 | startIndex: 51, 957 | type: STRING { type: 'STRING', byteLength: 51 } 958 | } 959 | Element: { 960 | name: 'Decimal', 961 | startIndex: 102, 962 | type: REAL { type: 'REAL', byteLength: 4 } 963 | } 964 | Element: { 965 | name: 'ArrayData[0]', 966 | startIndex: 106, 967 | type: INT { type: 'INT', byteLength: 2 } 968 | } 969 | Element: { 970 | name: 'ArrayData[1]', 971 | startIndex: 108, 972 | type: INT { type: 'INT', byteLength: 2 } 973 | } 974 | Element: { 975 | name: 'ArrayData[2]', 976 | startIndex: 110, 977 | type: INT { type: 'INT', byteLength: 2 } 978 | } 979 | Element: { 980 | name: 'ArrayData[3]', 981 | startIndex: 112, 982 | type: INT { type: 'INT', byteLength: 2 } 983 | } 984 | Element: { 985 | name: 'ArrayData[4]', 986 | startIndex: 114, 987 | type: INT { type: 'INT', byteLength: 2 } 988 | } 989 | Element: { 990 | name: 'ArrayData[5]', 991 | startIndex: 116, 992 | type: INT { type: 'INT', byteLength: 2 } 993 | } 994 | Element: { 995 | name: 'ArrayData[6]', 996 | startIndex: 118, 997 | type: INT { type: 'INT', byteLength: 2 } 998 | } 999 | Element: { 1000 | name: 'ArrayData[7]', 1001 | startIndex: 120, 1002 | type: INT { type: 'INT', byteLength: 2 } 1003 | } 1004 | Element: { 1005 | name: 'ArrayData[8]', 1006 | startIndex: 122, 1007 | type: INT { type: 'INT', byteLength: 2 } 1008 | } 1009 | Element: { 1010 | name: 'ArrayData[9]', 1011 | startIndex: 124, 1012 | type: INT { type: 'INT', byteLength: 2 } 1013 | } 1014 | Element: { 1015 | name: 'MultiDimArrayData[0]', 1016 | startIndex: 126, 1017 | type: INT { type: 'INT', byteLength: 2 } 1018 | } 1019 | Element: { 1020 | name: 'MultiDimArrayData[1]', 1021 | startIndex: 128, 1022 | type: INT { type: 'INT', byteLength: 2 } 1023 | } 1024 | Element: { 1025 | name: 'MultiDimArrayData[2]', 1026 | startIndex: 130, 1027 | type: INT { type: 'INT', byteLength: 2 } 1028 | } 1029 | Element: { 1030 | name: 'MultiDimArrayData[3]', 1031 | startIndex: 132, 1032 | type: INT { type: 'INT', byteLength: 2 } 1033 | } 1034 | Element: { 1035 | name: 'MultiDimArrayData[4]', 1036 | startIndex: 134, 1037 | type: INT { type: 'INT', byteLength: 2 } 1038 | } 1039 | Element: { 1040 | name: 'MultiDimArrayData[5]', 1041 | startIndex: 136, 1042 | type: INT { type: 'INT', byteLength: 2 } 1043 | } 1044 | */ 1045 | ``` 1046 | # License 1047 | 1048 | Licensed under [MIT License](http://www.opensource.org/licenses/MIT) so commercial use is possible. Please respect the license, linking to this page is also much appreciated. 1049 | 1050 | Copyright (c) Jussi Isotalo <> 1051 | 1052 | Permission is hereby granted, free of charge, to any person obtaining a copy 1053 | of this software and associated documentation files (the "Software"), to deal 1054 | in the Software without restriction, including without limitation the rights 1055 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1056 | copies of the Software, and to permit persons to whom the Software is 1057 | furnished to do so, subject to the following conditions: 1058 | 1059 | The above copyright notice and this permission notice shall be included in all 1060 | copies or substantial portions of the Software. 1061 | 1062 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1063 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1064 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1065 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1066 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1067 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1068 | SOFTWARE. 1069 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iec-61131-3", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.12.11", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", 10 | "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.14.9", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", 19 | "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.14.5", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", 25 | "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.14.5", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "chalk": { 34 | "version": "2.4.2", 35 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 36 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 37 | "dev": true, 38 | "requires": { 39 | "ansi-styles": "^3.2.1", 40 | "escape-string-regexp": "^1.0.5", 41 | "supports-color": "^5.3.0" 42 | } 43 | }, 44 | "escape-string-regexp": { 45 | "version": "1.0.5", 46 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 47 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 48 | "dev": true 49 | } 50 | } 51 | }, 52 | "@eslint/eslintrc": { 53 | "version": "0.4.3", 54 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", 55 | "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", 56 | "dev": true, 57 | "requires": { 58 | "ajv": "^6.12.4", 59 | "debug": "^4.1.1", 60 | "espree": "^7.3.0", 61 | "globals": "^13.9.0", 62 | "ignore": "^4.0.6", 63 | "import-fresh": "^3.2.1", 64 | "js-yaml": "^3.13.1", 65 | "minimatch": "^3.0.4", 66 | "strip-json-comments": "^3.1.1" 67 | }, 68 | "dependencies": { 69 | "ignore": { 70 | "version": "4.0.6", 71 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 72 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 73 | "dev": true 74 | } 75 | } 76 | }, 77 | "@humanwhocodes/config-array": { 78 | "version": "0.5.0", 79 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", 80 | "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", 81 | "dev": true, 82 | "requires": { 83 | "@humanwhocodes/object-schema": "^1.2.0", 84 | "debug": "^4.1.1", 85 | "minimatch": "^3.0.4" 86 | } 87 | }, 88 | "@humanwhocodes/object-schema": { 89 | "version": "1.2.0", 90 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", 91 | "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", 92 | "dev": true 93 | }, 94 | "@nodelib/fs.scandir": { 95 | "version": "2.1.5", 96 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 97 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 98 | "dev": true, 99 | "requires": { 100 | "@nodelib/fs.stat": "2.0.5", 101 | "run-parallel": "^1.1.9" 102 | } 103 | }, 104 | "@nodelib/fs.stat": { 105 | "version": "2.0.5", 106 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 107 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 108 | "dev": true 109 | }, 110 | "@nodelib/fs.walk": { 111 | "version": "1.2.8", 112 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 113 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 114 | "dev": true, 115 | "requires": { 116 | "@nodelib/fs.scandir": "2.1.5", 117 | "fastq": "^1.6.0" 118 | } 119 | }, 120 | "@types/json-schema": { 121 | "version": "7.0.9", 122 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", 123 | "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", 124 | "dev": true 125 | }, 126 | "@types/node": { 127 | "version": "14.17.9", 128 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.9.tgz", 129 | "integrity": "sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g==", 130 | "dev": true 131 | }, 132 | "@typescript-eslint/eslint-plugin": { 133 | "version": "4.29.0", 134 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.0.tgz", 135 | "integrity": "sha512-eiREtqWRZ8aVJcNru7cT/AMVnYd9a2UHsfZT8MR1dW3UUEg6jDv9EQ9Cq4CUPZesyQ58YUpoAADGv71jY8RwgA==", 136 | "dev": true, 137 | "requires": { 138 | "@typescript-eslint/experimental-utils": "4.29.0", 139 | "@typescript-eslint/scope-manager": "4.29.0", 140 | "debug": "^4.3.1", 141 | "functional-red-black-tree": "^1.0.1", 142 | "regexpp": "^3.1.0", 143 | "semver": "^7.3.5", 144 | "tsutils": "^3.21.0" 145 | } 146 | }, 147 | "@typescript-eslint/experimental-utils": { 148 | "version": "4.29.0", 149 | "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.0.tgz", 150 | "integrity": "sha512-FpNVKykfeaIxlArLUP/yQfv/5/3rhl1ov6RWgud4OgbqWLkEq7lqgQU9iiavZRzpzCRQV4XddyFz3wFXdkiX9w==", 151 | "dev": true, 152 | "requires": { 153 | "@types/json-schema": "^7.0.7", 154 | "@typescript-eslint/scope-manager": "4.29.0", 155 | "@typescript-eslint/types": "4.29.0", 156 | "@typescript-eslint/typescript-estree": "4.29.0", 157 | "eslint-scope": "^5.1.1", 158 | "eslint-utils": "^3.0.0" 159 | } 160 | }, 161 | "@typescript-eslint/parser": { 162 | "version": "4.29.0", 163 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.29.0.tgz", 164 | "integrity": "sha512-+92YRNHFdXgq+GhWQPT2bmjX09X7EH36JfgN2/4wmhtwV/HPxozpCNst8jrWcngLtEVd/4zAwA6BKojAlf+YqA==", 165 | "dev": true, 166 | "requires": { 167 | "@typescript-eslint/scope-manager": "4.29.0", 168 | "@typescript-eslint/types": "4.29.0", 169 | "@typescript-eslint/typescript-estree": "4.29.0", 170 | "debug": "^4.3.1" 171 | } 172 | }, 173 | "@typescript-eslint/scope-manager": { 174 | "version": "4.29.0", 175 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.29.0.tgz", 176 | "integrity": "sha512-HPq7XAaDMM3DpmuijxLV9Io8/6pQnliiXMQUcAdjpJJSR+fdmbD/zHCd7hMkjJn04UQtCQBtshgxClzg6NIS2w==", 177 | "dev": true, 178 | "requires": { 179 | "@typescript-eslint/types": "4.29.0", 180 | "@typescript-eslint/visitor-keys": "4.29.0" 181 | } 182 | }, 183 | "@typescript-eslint/types": { 184 | "version": "4.29.0", 185 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.29.0.tgz", 186 | "integrity": "sha512-2YJM6XfWfi8pgU2HRhTp7WgRw78TCRO3dOmSpAvIQ8MOv4B46JD2chnhpNT7Jq8j0APlIbzO1Bach734xxUl4A==", 187 | "dev": true 188 | }, 189 | "@typescript-eslint/typescript-estree": { 190 | "version": "4.29.0", 191 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.0.tgz", 192 | "integrity": "sha512-8ZpNHDIOyqzzgZrQW9+xQ4k5hM62Xy2R4RPO3DQxMc5Rq5QkCdSpk/drka+DL9w6sXNzV5nrdlBmf8+x495QXQ==", 193 | "dev": true, 194 | "requires": { 195 | "@typescript-eslint/types": "4.29.0", 196 | "@typescript-eslint/visitor-keys": "4.29.0", 197 | "debug": "^4.3.1", 198 | "globby": "^11.0.3", 199 | "is-glob": "^4.0.1", 200 | "semver": "^7.3.5", 201 | "tsutils": "^3.21.0" 202 | } 203 | }, 204 | "@typescript-eslint/visitor-keys": { 205 | "version": "4.29.0", 206 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.0.tgz", 207 | "integrity": "sha512-LoaofO1C/jAJYs0uEpYMXfHboGXzOJeV118X4OsZu9f7rG7Pr9B3+4HTU8+err81rADa4xfQmAxnRnPAI2jp+Q==", 208 | "dev": true, 209 | "requires": { 210 | "@typescript-eslint/types": "4.29.0", 211 | "eslint-visitor-keys": "^2.0.0" 212 | } 213 | }, 214 | "acorn": { 215 | "version": "7.4.1", 216 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 217 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 218 | "dev": true 219 | }, 220 | "acorn-jsx": { 221 | "version": "5.3.2", 222 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 223 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 224 | "dev": true 225 | }, 226 | "ajv": { 227 | "version": "6.12.6", 228 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 229 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 230 | "dev": true, 231 | "requires": { 232 | "fast-deep-equal": "^3.1.1", 233 | "fast-json-stable-stringify": "^2.0.0", 234 | "json-schema-traverse": "^0.4.1", 235 | "uri-js": "^4.2.2" 236 | } 237 | }, 238 | "ansi-colors": { 239 | "version": "4.1.1", 240 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 241 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 242 | "dev": true 243 | }, 244 | "ansi-regex": { 245 | "version": "5.0.0", 246 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 247 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 248 | "dev": true 249 | }, 250 | "ansi-styles": { 251 | "version": "3.2.1", 252 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 253 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 254 | "dev": true, 255 | "requires": { 256 | "color-convert": "^1.9.0" 257 | } 258 | }, 259 | "argparse": { 260 | "version": "1.0.10", 261 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 262 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 263 | "dev": true, 264 | "requires": { 265 | "sprintf-js": "~1.0.2" 266 | } 267 | }, 268 | "array-union": { 269 | "version": "2.1.0", 270 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 271 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 272 | "dev": true 273 | }, 274 | "astral-regex": { 275 | "version": "2.0.0", 276 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", 277 | "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", 278 | "dev": true 279 | }, 280 | "balanced-match": { 281 | "version": "1.0.2", 282 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 283 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 284 | "dev": true 285 | }, 286 | "brace-expansion": { 287 | "version": "1.1.11", 288 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 289 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 290 | "dev": true, 291 | "requires": { 292 | "balanced-match": "^1.0.0", 293 | "concat-map": "0.0.1" 294 | } 295 | }, 296 | "braces": { 297 | "version": "3.0.2", 298 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 299 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 300 | "dev": true, 301 | "requires": { 302 | "fill-range": "^7.0.1" 303 | } 304 | }, 305 | "callsites": { 306 | "version": "3.1.0", 307 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 308 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 309 | "dev": true 310 | }, 311 | "chalk": { 312 | "version": "4.1.2", 313 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 314 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 315 | "dev": true, 316 | "requires": { 317 | "ansi-styles": "^4.1.0", 318 | "supports-color": "^7.1.0" 319 | }, 320 | "dependencies": { 321 | "ansi-styles": { 322 | "version": "4.3.0", 323 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 324 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 325 | "dev": true, 326 | "requires": { 327 | "color-convert": "^2.0.1" 328 | } 329 | }, 330 | "color-convert": { 331 | "version": "2.0.1", 332 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 333 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 334 | "dev": true, 335 | "requires": { 336 | "color-name": "~1.1.4" 337 | } 338 | }, 339 | "color-name": { 340 | "version": "1.1.4", 341 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 342 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 343 | "dev": true 344 | }, 345 | "has-flag": { 346 | "version": "4.0.0", 347 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 348 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 349 | "dev": true 350 | }, 351 | "supports-color": { 352 | "version": "7.2.0", 353 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 354 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 355 | "dev": true, 356 | "requires": { 357 | "has-flag": "^4.0.0" 358 | } 359 | } 360 | } 361 | }, 362 | "color-convert": { 363 | "version": "1.9.3", 364 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 365 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 366 | "dev": true, 367 | "requires": { 368 | "color-name": "1.1.3" 369 | } 370 | }, 371 | "color-name": { 372 | "version": "1.1.3", 373 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 374 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 375 | "dev": true 376 | }, 377 | "concat-map": { 378 | "version": "0.0.1", 379 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 380 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 381 | "dev": true 382 | }, 383 | "cross-spawn": { 384 | "version": "7.0.3", 385 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 386 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 387 | "dev": true, 388 | "requires": { 389 | "path-key": "^3.1.0", 390 | "shebang-command": "^2.0.0", 391 | "which": "^2.0.1" 392 | } 393 | }, 394 | "debug": { 395 | "version": "4.3.2", 396 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", 397 | "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", 398 | "dev": true, 399 | "requires": { 400 | "ms": "2.1.2" 401 | } 402 | }, 403 | "deep-is": { 404 | "version": "0.1.3", 405 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 406 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 407 | "dev": true 408 | }, 409 | "dir-glob": { 410 | "version": "3.0.1", 411 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 412 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 413 | "dev": true, 414 | "requires": { 415 | "path-type": "^4.0.0" 416 | } 417 | }, 418 | "doctrine": { 419 | "version": "3.0.0", 420 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 421 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 422 | "dev": true, 423 | "requires": { 424 | "esutils": "^2.0.2" 425 | } 426 | }, 427 | "emoji-regex": { 428 | "version": "8.0.0", 429 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 430 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 431 | "dev": true 432 | }, 433 | "enquirer": { 434 | "version": "2.3.6", 435 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 436 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 437 | "dev": true, 438 | "requires": { 439 | "ansi-colors": "^4.1.1" 440 | } 441 | }, 442 | "escape-string-regexp": { 443 | "version": "4.0.0", 444 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 445 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 446 | "dev": true 447 | }, 448 | "eslint": { 449 | "version": "7.32.0", 450 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", 451 | "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", 452 | "dev": true, 453 | "requires": { 454 | "@babel/code-frame": "7.12.11", 455 | "@eslint/eslintrc": "^0.4.3", 456 | "@humanwhocodes/config-array": "^0.5.0", 457 | "ajv": "^6.10.0", 458 | "chalk": "^4.0.0", 459 | "cross-spawn": "^7.0.2", 460 | "debug": "^4.0.1", 461 | "doctrine": "^3.0.0", 462 | "enquirer": "^2.3.5", 463 | "escape-string-regexp": "^4.0.0", 464 | "eslint-scope": "^5.1.1", 465 | "eslint-utils": "^2.1.0", 466 | "eslint-visitor-keys": "^2.0.0", 467 | "espree": "^7.3.1", 468 | "esquery": "^1.4.0", 469 | "esutils": "^2.0.2", 470 | "fast-deep-equal": "^3.1.3", 471 | "file-entry-cache": "^6.0.1", 472 | "functional-red-black-tree": "^1.0.1", 473 | "glob-parent": "^5.1.2", 474 | "globals": "^13.6.0", 475 | "ignore": "^4.0.6", 476 | "import-fresh": "^3.0.0", 477 | "imurmurhash": "^0.1.4", 478 | "is-glob": "^4.0.0", 479 | "js-yaml": "^3.13.1", 480 | "json-stable-stringify-without-jsonify": "^1.0.1", 481 | "levn": "^0.4.1", 482 | "lodash.merge": "^4.6.2", 483 | "minimatch": "^3.0.4", 484 | "natural-compare": "^1.4.0", 485 | "optionator": "^0.9.1", 486 | "progress": "^2.0.0", 487 | "regexpp": "^3.1.0", 488 | "semver": "^7.2.1", 489 | "strip-ansi": "^6.0.0", 490 | "strip-json-comments": "^3.1.0", 491 | "table": "^6.0.9", 492 | "text-table": "^0.2.0", 493 | "v8-compile-cache": "^2.0.3" 494 | }, 495 | "dependencies": { 496 | "eslint-utils": { 497 | "version": "2.1.0", 498 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 499 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 500 | "dev": true, 501 | "requires": { 502 | "eslint-visitor-keys": "^1.1.0" 503 | }, 504 | "dependencies": { 505 | "eslint-visitor-keys": { 506 | "version": "1.3.0", 507 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 508 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 509 | "dev": true 510 | } 511 | } 512 | }, 513 | "ignore": { 514 | "version": "4.0.6", 515 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 516 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 517 | "dev": true 518 | } 519 | } 520 | }, 521 | "eslint-scope": { 522 | "version": "5.1.1", 523 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 524 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 525 | "dev": true, 526 | "requires": { 527 | "esrecurse": "^4.3.0", 528 | "estraverse": "^4.1.1" 529 | } 530 | }, 531 | "eslint-utils": { 532 | "version": "3.0.0", 533 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", 534 | "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", 535 | "dev": true, 536 | "requires": { 537 | "eslint-visitor-keys": "^2.0.0" 538 | } 539 | }, 540 | "eslint-visitor-keys": { 541 | "version": "2.1.0", 542 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", 543 | "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", 544 | "dev": true 545 | }, 546 | "espree": { 547 | "version": "7.3.1", 548 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", 549 | "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", 550 | "dev": true, 551 | "requires": { 552 | "acorn": "^7.4.0", 553 | "acorn-jsx": "^5.3.1", 554 | "eslint-visitor-keys": "^1.3.0" 555 | }, 556 | "dependencies": { 557 | "eslint-visitor-keys": { 558 | "version": "1.3.0", 559 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 560 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 561 | "dev": true 562 | } 563 | } 564 | }, 565 | "esprima": { 566 | "version": "4.0.1", 567 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 568 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 569 | "dev": true 570 | }, 571 | "esquery": { 572 | "version": "1.4.0", 573 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", 574 | "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", 575 | "dev": true, 576 | "requires": { 577 | "estraverse": "^5.1.0" 578 | }, 579 | "dependencies": { 580 | "estraverse": { 581 | "version": "5.2.0", 582 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 583 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 584 | "dev": true 585 | } 586 | } 587 | }, 588 | "esrecurse": { 589 | "version": "4.3.0", 590 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 591 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 592 | "dev": true, 593 | "requires": { 594 | "estraverse": "^5.2.0" 595 | }, 596 | "dependencies": { 597 | "estraverse": { 598 | "version": "5.2.0", 599 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 600 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 601 | "dev": true 602 | } 603 | } 604 | }, 605 | "estraverse": { 606 | "version": "4.3.0", 607 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 608 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 609 | "dev": true 610 | }, 611 | "esutils": { 612 | "version": "2.0.3", 613 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 614 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 615 | "dev": true 616 | }, 617 | "fast-deep-equal": { 618 | "version": "3.1.3", 619 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 620 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 621 | "dev": true 622 | }, 623 | "fast-glob": { 624 | "version": "3.2.7", 625 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", 626 | "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", 627 | "dev": true, 628 | "requires": { 629 | "@nodelib/fs.stat": "^2.0.2", 630 | "@nodelib/fs.walk": "^1.2.3", 631 | "glob-parent": "^5.1.2", 632 | "merge2": "^1.3.0", 633 | "micromatch": "^4.0.4" 634 | } 635 | }, 636 | "fast-json-stable-stringify": { 637 | "version": "2.1.0", 638 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 639 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 640 | "dev": true 641 | }, 642 | "fast-levenshtein": { 643 | "version": "2.0.6", 644 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 645 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 646 | "dev": true 647 | }, 648 | "fastq": { 649 | "version": "1.11.1", 650 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", 651 | "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", 652 | "dev": true, 653 | "requires": { 654 | "reusify": "^1.0.4" 655 | } 656 | }, 657 | "file-entry-cache": { 658 | "version": "6.0.1", 659 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 660 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 661 | "dev": true, 662 | "requires": { 663 | "flat-cache": "^3.0.4" 664 | } 665 | }, 666 | "fill-range": { 667 | "version": "7.0.1", 668 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 669 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 670 | "dev": true, 671 | "requires": { 672 | "to-regex-range": "^5.0.1" 673 | } 674 | }, 675 | "flat-cache": { 676 | "version": "3.0.4", 677 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", 678 | "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", 679 | "dev": true, 680 | "requires": { 681 | "flatted": "^3.1.0", 682 | "rimraf": "^3.0.2" 683 | } 684 | }, 685 | "flatted": { 686 | "version": "3.2.2", 687 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", 688 | "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", 689 | "dev": true 690 | }, 691 | "fs.realpath": { 692 | "version": "1.0.0", 693 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 694 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 695 | "dev": true 696 | }, 697 | "functional-red-black-tree": { 698 | "version": "1.0.1", 699 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 700 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 701 | "dev": true 702 | }, 703 | "glob": { 704 | "version": "7.1.7", 705 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", 706 | "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", 707 | "dev": true, 708 | "requires": { 709 | "fs.realpath": "^1.0.0", 710 | "inflight": "^1.0.4", 711 | "inherits": "2", 712 | "minimatch": "^3.0.4", 713 | "once": "^1.3.0", 714 | "path-is-absolute": "^1.0.0" 715 | } 716 | }, 717 | "glob-parent": { 718 | "version": "5.1.2", 719 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 720 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 721 | "dev": true, 722 | "requires": { 723 | "is-glob": "^4.0.1" 724 | } 725 | }, 726 | "globals": { 727 | "version": "13.10.0", 728 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", 729 | "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", 730 | "dev": true, 731 | "requires": { 732 | "type-fest": "^0.20.2" 733 | } 734 | }, 735 | "globby": { 736 | "version": "11.0.4", 737 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", 738 | "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", 739 | "dev": true, 740 | "requires": { 741 | "array-union": "^2.1.0", 742 | "dir-glob": "^3.0.1", 743 | "fast-glob": "^3.1.1", 744 | "ignore": "^5.1.4", 745 | "merge2": "^1.3.0", 746 | "slash": "^3.0.0" 747 | } 748 | }, 749 | "has-flag": { 750 | "version": "3.0.0", 751 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 752 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 753 | "dev": true 754 | }, 755 | "iconv-lite": { 756 | "version": "0.5.2", 757 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", 758 | "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", 759 | "requires": { 760 | "safer-buffer": ">= 2.1.2 < 3" 761 | } 762 | }, 763 | "ignore": { 764 | "version": "5.1.8", 765 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", 766 | "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", 767 | "dev": true 768 | }, 769 | "import-fresh": { 770 | "version": "3.3.0", 771 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 772 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 773 | "dev": true, 774 | "requires": { 775 | "parent-module": "^1.0.0", 776 | "resolve-from": "^4.0.0" 777 | } 778 | }, 779 | "imurmurhash": { 780 | "version": "0.1.4", 781 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 782 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 783 | "dev": true 784 | }, 785 | "inflight": { 786 | "version": "1.0.6", 787 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 788 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 789 | "dev": true, 790 | "requires": { 791 | "once": "^1.3.0", 792 | "wrappy": "1" 793 | } 794 | }, 795 | "inherits": { 796 | "version": "2.0.4", 797 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 798 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 799 | "dev": true 800 | }, 801 | "is-extglob": { 802 | "version": "2.1.1", 803 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 804 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 805 | "dev": true 806 | }, 807 | "is-fullwidth-code-point": { 808 | "version": "3.0.0", 809 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 810 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 811 | "dev": true 812 | }, 813 | "is-glob": { 814 | "version": "4.0.1", 815 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 816 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 817 | "dev": true, 818 | "requires": { 819 | "is-extglob": "^2.1.1" 820 | } 821 | }, 822 | "is-number": { 823 | "version": "7.0.0", 824 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 825 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 826 | "dev": true 827 | }, 828 | "isexe": { 829 | "version": "2.0.0", 830 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 831 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 832 | "dev": true 833 | }, 834 | "js-tokens": { 835 | "version": "4.0.0", 836 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 837 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 838 | "dev": true 839 | }, 840 | "js-yaml": { 841 | "version": "3.14.1", 842 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", 843 | "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 844 | "dev": true, 845 | "requires": { 846 | "argparse": "^1.0.7", 847 | "esprima": "^4.0.0" 848 | } 849 | }, 850 | "json-schema-traverse": { 851 | "version": "0.4.1", 852 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 853 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 854 | "dev": true 855 | }, 856 | "json-stable-stringify-without-jsonify": { 857 | "version": "1.0.1", 858 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 859 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 860 | "dev": true 861 | }, 862 | "levn": { 863 | "version": "0.4.1", 864 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 865 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 866 | "dev": true, 867 | "requires": { 868 | "prelude-ls": "^1.2.1", 869 | "type-check": "~0.4.0" 870 | } 871 | }, 872 | "lodash.clonedeep": { 873 | "version": "4.5.0", 874 | "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", 875 | "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", 876 | "dev": true 877 | }, 878 | "lodash.merge": { 879 | "version": "4.6.2", 880 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 881 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 882 | "dev": true 883 | }, 884 | "lodash.truncate": { 885 | "version": "4.4.2", 886 | "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", 887 | "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", 888 | "dev": true 889 | }, 890 | "lru-cache": { 891 | "version": "6.0.0", 892 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 893 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 894 | "dev": true, 895 | "requires": { 896 | "yallist": "^4.0.0" 897 | } 898 | }, 899 | "merge2": { 900 | "version": "1.4.1", 901 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 902 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 903 | "dev": true 904 | }, 905 | "micromatch": { 906 | "version": "4.0.4", 907 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", 908 | "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", 909 | "dev": true, 910 | "requires": { 911 | "braces": "^3.0.1", 912 | "picomatch": "^2.2.3" 913 | } 914 | }, 915 | "minimatch": { 916 | "version": "3.0.4", 917 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 918 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 919 | "dev": true, 920 | "requires": { 921 | "brace-expansion": "^1.1.7" 922 | } 923 | }, 924 | "ms": { 925 | "version": "2.1.2", 926 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 927 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 928 | "dev": true 929 | }, 930 | "natural-compare": { 931 | "version": "1.4.0", 932 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 933 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 934 | "dev": true 935 | }, 936 | "once": { 937 | "version": "1.4.0", 938 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 939 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 940 | "dev": true, 941 | "requires": { 942 | "wrappy": "1" 943 | } 944 | }, 945 | "optionator": { 946 | "version": "0.9.1", 947 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 948 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 949 | "dev": true, 950 | "requires": { 951 | "deep-is": "^0.1.3", 952 | "fast-levenshtein": "^2.0.6", 953 | "levn": "^0.4.1", 954 | "prelude-ls": "^1.2.1", 955 | "type-check": "^0.4.0", 956 | "word-wrap": "^1.2.3" 957 | } 958 | }, 959 | "parent-module": { 960 | "version": "1.0.1", 961 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 962 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 963 | "dev": true, 964 | "requires": { 965 | "callsites": "^3.0.0" 966 | } 967 | }, 968 | "path-is-absolute": { 969 | "version": "1.0.1", 970 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 971 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 972 | "dev": true 973 | }, 974 | "path-key": { 975 | "version": "3.1.1", 976 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 977 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 978 | "dev": true 979 | }, 980 | "path-type": { 981 | "version": "4.0.0", 982 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 983 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 984 | "dev": true 985 | }, 986 | "picomatch": { 987 | "version": "2.3.0", 988 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", 989 | "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", 990 | "dev": true 991 | }, 992 | "prelude-ls": { 993 | "version": "1.2.1", 994 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 995 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 996 | "dev": true 997 | }, 998 | "progress": { 999 | "version": "2.0.3", 1000 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 1001 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 1002 | "dev": true 1003 | }, 1004 | "punycode": { 1005 | "version": "2.1.1", 1006 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1007 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1008 | "dev": true 1009 | }, 1010 | "queue-microtask": { 1011 | "version": "1.2.3", 1012 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1013 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1014 | "dev": true 1015 | }, 1016 | "regexpp": { 1017 | "version": "3.2.0", 1018 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", 1019 | "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", 1020 | "dev": true 1021 | }, 1022 | "require-from-string": { 1023 | "version": "2.0.2", 1024 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 1025 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 1026 | "dev": true 1027 | }, 1028 | "resolve-from": { 1029 | "version": "4.0.0", 1030 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1031 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1032 | "dev": true 1033 | }, 1034 | "reusify": { 1035 | "version": "1.0.4", 1036 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1037 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1038 | "dev": true 1039 | }, 1040 | "rimraf": { 1041 | "version": "3.0.2", 1042 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1043 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1044 | "dev": true, 1045 | "requires": { 1046 | "glob": "^7.1.3" 1047 | } 1048 | }, 1049 | "run-parallel": { 1050 | "version": "1.2.0", 1051 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1052 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1053 | "dev": true, 1054 | "requires": { 1055 | "queue-microtask": "^1.2.2" 1056 | } 1057 | }, 1058 | "safer-buffer": { 1059 | "version": "2.1.2", 1060 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1061 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1062 | }, 1063 | "semver": { 1064 | "version": "7.3.5", 1065 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 1066 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 1067 | "dev": true, 1068 | "requires": { 1069 | "lru-cache": "^6.0.0" 1070 | } 1071 | }, 1072 | "shebang-command": { 1073 | "version": "2.0.0", 1074 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1075 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1076 | "dev": true, 1077 | "requires": { 1078 | "shebang-regex": "^3.0.0" 1079 | } 1080 | }, 1081 | "shebang-regex": { 1082 | "version": "3.0.0", 1083 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1084 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1085 | "dev": true 1086 | }, 1087 | "slash": { 1088 | "version": "3.0.0", 1089 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 1090 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 1091 | "dev": true 1092 | }, 1093 | "slice-ansi": { 1094 | "version": "4.0.0", 1095 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", 1096 | "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", 1097 | "dev": true, 1098 | "requires": { 1099 | "ansi-styles": "^4.0.0", 1100 | "astral-regex": "^2.0.0", 1101 | "is-fullwidth-code-point": "^3.0.0" 1102 | }, 1103 | "dependencies": { 1104 | "ansi-styles": { 1105 | "version": "4.3.0", 1106 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1107 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1108 | "dev": true, 1109 | "requires": { 1110 | "color-convert": "^2.0.1" 1111 | } 1112 | }, 1113 | "color-convert": { 1114 | "version": "2.0.1", 1115 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1116 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1117 | "dev": true, 1118 | "requires": { 1119 | "color-name": "~1.1.4" 1120 | } 1121 | }, 1122 | "color-name": { 1123 | "version": "1.1.4", 1124 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1125 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1126 | "dev": true 1127 | } 1128 | } 1129 | }, 1130 | "sprintf-js": { 1131 | "version": "1.0.3", 1132 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1133 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1134 | "dev": true 1135 | }, 1136 | "string-width": { 1137 | "version": "4.2.2", 1138 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", 1139 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", 1140 | "dev": true, 1141 | "requires": { 1142 | "emoji-regex": "^8.0.0", 1143 | "is-fullwidth-code-point": "^3.0.0", 1144 | "strip-ansi": "^6.0.0" 1145 | } 1146 | }, 1147 | "strip-ansi": { 1148 | "version": "6.0.0", 1149 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1150 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1151 | "dev": true, 1152 | "requires": { 1153 | "ansi-regex": "^5.0.0" 1154 | } 1155 | }, 1156 | "strip-json-comments": { 1157 | "version": "3.1.1", 1158 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1159 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1160 | "dev": true 1161 | }, 1162 | "supports-color": { 1163 | "version": "5.5.0", 1164 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1165 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1166 | "dev": true, 1167 | "requires": { 1168 | "has-flag": "^3.0.0" 1169 | } 1170 | }, 1171 | "table": { 1172 | "version": "6.7.1", 1173 | "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", 1174 | "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", 1175 | "dev": true, 1176 | "requires": { 1177 | "ajv": "^8.0.1", 1178 | "lodash.clonedeep": "^4.5.0", 1179 | "lodash.truncate": "^4.4.2", 1180 | "slice-ansi": "^4.0.0", 1181 | "string-width": "^4.2.0", 1182 | "strip-ansi": "^6.0.0" 1183 | }, 1184 | "dependencies": { 1185 | "ajv": { 1186 | "version": "8.6.2", 1187 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", 1188 | "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", 1189 | "dev": true, 1190 | "requires": { 1191 | "fast-deep-equal": "^3.1.1", 1192 | "json-schema-traverse": "^1.0.0", 1193 | "require-from-string": "^2.0.2", 1194 | "uri-js": "^4.2.2" 1195 | } 1196 | }, 1197 | "json-schema-traverse": { 1198 | "version": "1.0.0", 1199 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 1200 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 1201 | "dev": true 1202 | } 1203 | } 1204 | }, 1205 | "text-table": { 1206 | "version": "0.2.0", 1207 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1208 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1209 | "dev": true 1210 | }, 1211 | "to-regex-range": { 1212 | "version": "5.0.1", 1213 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1214 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1215 | "dev": true, 1216 | "requires": { 1217 | "is-number": "^7.0.0" 1218 | } 1219 | }, 1220 | "tslib": { 1221 | "version": "1.14.1", 1222 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 1223 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 1224 | "dev": true 1225 | }, 1226 | "tsutils": { 1227 | "version": "3.21.0", 1228 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", 1229 | "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", 1230 | "dev": true, 1231 | "requires": { 1232 | "tslib": "^1.8.1" 1233 | } 1234 | }, 1235 | "type-check": { 1236 | "version": "0.4.0", 1237 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1238 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1239 | "dev": true, 1240 | "requires": { 1241 | "prelude-ls": "^1.2.1" 1242 | } 1243 | }, 1244 | "type-fest": { 1245 | "version": "0.20.2", 1246 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 1247 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 1248 | "dev": true 1249 | }, 1250 | "typescript": { 1251 | "version": "4.3.5", 1252 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", 1253 | "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", 1254 | "dev": true 1255 | }, 1256 | "uri-js": { 1257 | "version": "4.4.1", 1258 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1259 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1260 | "dev": true, 1261 | "requires": { 1262 | "punycode": "^2.1.0" 1263 | } 1264 | }, 1265 | "v8-compile-cache": { 1266 | "version": "2.3.0", 1267 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", 1268 | "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", 1269 | "dev": true 1270 | }, 1271 | "which": { 1272 | "version": "2.0.2", 1273 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1274 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1275 | "dev": true, 1276 | "requires": { 1277 | "isexe": "^2.0.0" 1278 | } 1279 | }, 1280 | "word-wrap": { 1281 | "version": "1.2.3", 1282 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 1283 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 1284 | "dev": true 1285 | }, 1286 | "wrappy": { 1287 | "version": "1.0.2", 1288 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1289 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1290 | "dev": true 1291 | }, 1292 | "yallist": { 1293 | "version": "4.0.0", 1294 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1295 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 1296 | "dev": true 1297 | } 1298 | } 1299 | } 1300 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iec-61131-3", 3 | "version": "1.1.0", 4 | "description": "IEC 61131-3 PLC data type helper for Node.js", 5 | "main": "./dist/iec-61131-3.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "tsc", 9 | "lint": "eslint . --ext .ts", 10 | "watch": "tsc -w" 11 | }, 12 | "keywords": [ 13 | "plc", 14 | "iec-61131-3", 15 | "61131-3", 16 | "codesys", 17 | "twincat", 18 | "codesys", 19 | "struct" 20 | ], 21 | "author": "Jussi Isotalo (https://github.com/jisotalo)", 22 | "license": "MIT", 23 | "homepage": "https://github.com/jisotalo/iec-61131-3/", 24 | "bugs": { 25 | "url": "https://github.com/jisotalo/iec-61131-3/issues" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/jisotalo/iec-61131-3.git" 30 | }, 31 | "dependencies": { 32 | "iconv-lite": "^0.5.2" 33 | }, 34 | "devDependencies": { 35 | "@types/node": "^14.14.14", 36 | "@typescript-eslint/eslint-plugin": "^4.11.1", 37 | "@typescript-eslint/parser": "^4.11.1", 38 | "eslint": "^7.17.0", 39 | "typescript": "^4.1.3" 40 | }, 41 | "files": [ 42 | "dist/", 43 | "CHANGELOG.md", 44 | "README.md" 45 | ], 46 | "types": "./dist/iec-61131-3.d.ts" 47 | } 48 | -------------------------------------------------------------------------------- /src/iec-61131-3.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/jisotalo/iec-61131-3 3 | 4 | Copyright (c) 2021 Jussi Isotalo 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | import { resolveIecTypes } from './iec-resolver' 25 | import type { IecType } from './types/types' 26 | 27 | /** 28 | * Exporting IecType interface 29 | */ 30 | export type { IecType } from './types/types' 31 | 32 | /** 33 | * Exporting all IEC data types 34 | */ 35 | export * from './iec-types' 36 | 37 | /** 38 | * Converts given PLC data type declaration(s) to IEC data type. 39 | * - If only one declaration given, it's selected automatically. 40 | * - If multiple given, top-level type needs to be given as 2nd parameter. 41 | * - Additionally, pre-defined IEC data types can be provided as 3rd parameter {name: type} 42 | * 43 | * @param declarations PLC IEC-61131-3 struct type declarations (one or multiple) 44 | * @param topLevelDataType If multiple struct type declarations given, the top-level struct type name needs to be provided (= which struct should be returned as IEC type) 45 | * @param providedTypes Object containing struct data type names and their IEC types if required (if some structs are defined somewhere else) - like {'ST_Example': STRUCT(...)} 46 | * @returns IEC data type object 47 | */ 48 | export const fromString = (declarations: string, topLevelDataType?: string, providedTypes?: Record): IecType => { 49 | return resolveIecTypes(declarations, topLevelDataType, providedTypes) 50 | } 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/iec-resolver.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/jisotalo/iec-61131-3 3 | 4 | Copyright (c) 2021 Jussi Isotalo 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | 26 | 27 | import * as iecTypes from "./iec-types" 28 | 29 | import { 30 | dataTypeUnit, 31 | EnumEntry, 32 | ExtractedEnum, 33 | ExtractedType, 34 | IecType, 35 | ExtractedAlias, 36 | EnumDataType, 37 | ExtractedTypeVariable 38 | } from "./types/types" 39 | 40 | 41 | /** 42 | * Available non-complex IEC types 43 | */ 44 | const nonComplexTypes = [ 45 | iecTypes.BOOL, 46 | iecTypes.USINT, 47 | iecTypes.BYTE, 48 | iecTypes.SINT, 49 | iecTypes.UINT, 50 | iecTypes.WORD, 51 | iecTypes.INT, 52 | iecTypes.DINT, 53 | iecTypes.UDINT, 54 | iecTypes.DWORD, 55 | iecTypes.TIME, 56 | iecTypes.TOD, 57 | iecTypes.TIME_OF_DAY, 58 | iecTypes.DT, 59 | iecTypes.DATE_AND_TIME, 60 | iecTypes.DATE, 61 | iecTypes.REAL, 62 | iecTypes.LREAL, 63 | iecTypes.ULINT, 64 | iecTypes.LWORD, 65 | iecTypes.LINT 66 | ] 67 | 68 | /** 69 | * RegExp pattern for matching data type units DUTs (struct, union, alias, enum) 70 | */ 71 | const typeRegEx = new RegExp(/type\s*(\w*)\s*[:]*\s*(struct|union|\(|:)\s*(.*?)(?:end_struct|end_union|;|\)\s*([^\s]*?)\s*;)\s*end_type/gis) 72 | 73 | /** 74 | * RegExp pattern for matching STRUCT variables (children) 75 | */ 76 | const structVariableRegEx = new RegExp(/(\w+)\s*:\s*([^:;]*)/gis) 77 | 78 | /** 79 | * RegExp pattern for matching ENUM 80 | */ 81 | const enumRegEx = new RegExp(/\s*(.+?)\s*(?::=\s*(.*?)\s*)*(?:,|$)/gis) 82 | 83 | /** 84 | * RegExp pattern for matching STRING or WSTRING types 85 | * STRING (=80) 86 | * WSTRING (=80) 87 | * STRING(123) 88 | * WSTRING(123) 89 | * STRING[123] 90 | * WSTRING[123] 91 | */ 92 | const stringRegEx = new RegExp('^(STRING|WSTRING)([\\[\\(](.*?)[\\)\\]])*$', 'i') 93 | 94 | /** 95 | * RegExp pattern for matching ARRAY types 96 | */ 97 | const arrayRegEx = new RegExp(/array\s*[\[(]+(.*?)[\])]\s*of\s*([^:;]*)/gis) 98 | 99 | /** 100 | * RegExp pattern for matching ARRAY dimensions 101 | * Input: "0..10", "-5..5, 0..2" etc 102 | */ 103 | const arrayDimensionsRegEx = new RegExp(/(?:\s*(?:([^\.,\s]*)\s*\.\.\s*([^,\s]*))\s*)/gis) 104 | 105 | /** 106 | * Extracts struct declarations from given string containing one or multiple TYPE...END_TYPE declarations 107 | * @param declaration Declaration string 108 | * @returns Extracted data types 109 | */ 110 | const extractTypeDeclarations = (declarations: string): ExtractedType[] => { 111 | const extractedTypes: ExtractedType[] = [] 112 | let match: RegExpExecArray | null 113 | const typeMatcher = new RegExp(typeRegEx) 114 | 115 | //Looping until all no more declarations found 116 | //TODO: Add checks if RegExp was successful 117 | while ((match = typeMatcher.exec(declarations)) !== null) { 118 | // This is necessary to avoid infinite loops with zero-width matches 119 | if (match.index === typeMatcher.lastIndex) { 120 | typeMatcher.lastIndex++; 121 | } 122 | 123 | if (match.length < 5) { 124 | throw new Error(`Problem extracting IEC type declaration from given string. RegExp result has less than 4 matches: ${JSON.stringify(match)}`) 125 | } 126 | 127 | const type = { 128 | resolved: undefined 129 | } as ExtractedType 130 | 131 | //Match 1 is the user-defined name 132 | type.name = match[1] 133 | 134 | //Match 2 provides info which type is it 135 | //Match 3 is the content (depends on type) 136 | switch (match[2].toLowerCase()) { 137 | //STRUCT: 138 | case 'struct': 139 | type.type = dataTypeUnit.STRUCT 140 | type.content = extractTypeVariables(match[3]) 141 | break; 142 | 143 | //UNION: 144 | case 'union': 145 | type.type = dataTypeUnit.UNION 146 | type.content = extractTypeVariables(match[3]) 147 | break; 148 | 149 | //ENUM: 150 | case '(': 151 | type.type = dataTypeUnit.ENUM 152 | type.content = extractEnum(match[3], match[4]) 153 | break; 154 | 155 | //ALIAS: 156 | case ':': 157 | type.type = dataTypeUnit.ALIAS 158 | type.content = { 159 | dataType: match[3] 160 | } as ExtractedAlias 161 | break; 162 | 163 | default: 164 | throw new Error(`Problem extracting IEC data type (DUT) from given string. Found match: ${JSON.stringify(match)}`) 165 | } 166 | 167 | extractedTypes.push(type) 168 | } 169 | 170 | return extractedTypes 171 | } 172 | 173 | /** 174 | * Extracts STRUCT/UNION variables (children) from given declaration string 175 | * @param declaration 176 | * @returns 177 | */ 178 | const extractTypeVariables = (declaration: string): ExtractedTypeVariable[] => { 179 | 180 | const extractedVariables: ExtractedTypeVariable[] = [] 181 | 182 | let match: RegExpExecArray | null 183 | const structVariableMatcher = new RegExp(structVariableRegEx) 184 | 185 | //TODO: Add checks if RegExp was successful 186 | while ((match = structVariableMatcher.exec(declaration)) !== null) { 187 | 188 | // This is necessary to avoid infinite loops with zero-width matches 189 | if (match.index === structVariableMatcher.lastIndex) { 190 | structVariableMatcher.lastIndex++ 191 | } 192 | 193 | extractedVariables.push({ 194 | name: match[1], 195 | dataType: match[2].trim() //Removing whitespace here (TODO: with regexp?) 196 | }) 197 | } 198 | 199 | return extractedVariables 200 | } 201 | 202 | /** 203 | * Extracts ENUM from given declaration string 204 | * @param declaration 205 | * @returns 206 | */ 207 | const extractEnum = (declaration: string, dataType?: string): ExtractedEnum => { 208 | 209 | if (dataType === undefined || dataType === '') 210 | dataType = 'INT' 211 | 212 | const extractedEnum = { 213 | dataType, 214 | content: {} 215 | } as ExtractedEnum 216 | 217 | let match: RegExpExecArray | null 218 | const enumMatcher = new RegExp(enumRegEx) 219 | const enumList = [] as EnumEntry[] 220 | 221 | //TODO: Add checks if RegExp was successful 222 | while ((match = enumMatcher.exec(declaration)) !== null) { 223 | 224 | // This is necessary to avoid infinite loops with zero-width matches 225 | if (match.index === enumMatcher.lastIndex) { 226 | enumMatcher.lastIndex++ 227 | } 228 | 229 | let value = match[2] === undefined ? undefined : parseInt(match[2]) 230 | 231 | //If value is undefined, it's not provided in the ENUM 232 | //--> The value is previous value + 1 if available 233 | //Otherwise value is 0 234 | if (value === undefined && enumList.length > 0) { 235 | value = enumList[enumList.length - 1].value + 1 236 | 237 | } else if(value === undefined){ 238 | value = 0 239 | } 240 | 241 | if (value === undefined) 242 | throw new Error(`Problem calculating ENUM entry "${match[1]}". Found match: ${JSON.stringify(match)}`) 243 | 244 | //TODO: When adding support for constants edit this 245 | //We could have ENUM that has value := SOME_CONSTANT 246 | if (isNaN(value)) 247 | throw new Error(`Problem calculating ENUM entry "${match[1]}" value from "${match[2]}". Found match: ${JSON.stringify(match)}`) 248 | 249 | enumList.push({ 250 | name: match[1], 251 | value 252 | }) 253 | 254 | extractedEnum.content[match[1]] = value 255 | } 256 | 257 | return extractedEnum 258 | } 259 | 260 | /** 261 | * Resolves given string PLC type declarations to IEC data types 262 | * @param declarations 263 | * @param topLevelDataType 264 | * @param providedTypes 265 | * @returns 266 | */ 267 | export const resolveIecTypes = (declarations: string, topLevelDataType?: string, providedTypes?: Record): IecType => { 268 | //First extracting basic type definitions from string 269 | const types = extractTypeDeclarations(declarations) 270 | 271 | //If multiple data types found, we need to know which one is the top-level 272 | if (!topLevelDataType && types.length > 1) { 273 | throw new Error('Top level data type name (topLevelDataType) is not given and multiple type declarations found. Not possible to guess.') 274 | 275 | } else if (!topLevelDataType) { 276 | //When only one type found, we know it's the top-level 277 | topLevelDataType = types[0].name 278 | } 279 | 280 | //Resolving types to IEC data types 281 | for (const type of types) { 282 | //If already resolved, skip (happens if some other type already depended on it) 283 | if (type.resolved) 284 | continue 285 | 286 | type.resolved = resolveIecDataTypeUnit(type, types, providedTypes) 287 | } 288 | 289 | //Return the top-level struct 290 | const returnVal = types.find(type => topLevelDataType !== undefined && type.name.toLowerCase() === topLevelDataType.toLowerCase()) 291 | 292 | if (!returnVal) { 293 | throw new Error(`Top-level type ${topLevelDataType} was not found - Is data type declaration provided? Types found: ${types.map(type => type.name).join(', ')}`) 294 | } 295 | 296 | return returnVal.resolved as IecType 297 | } 298 | 299 | /** 300 | * Resolves single IEC data type unit from given parsed ExtractedType object 301 | * 302 | * @param type Type to be resolved 303 | * @param types List of all available types that are/will be resolved 304 | * @param providedTypes List of user-provided types 305 | * @returns Resolved type (IecType object) 306 | */ 307 | const resolveIecDataTypeUnit = (type: ExtractedType, types: ExtractedType[], providedTypes?: Record): IecType => { 308 | 309 | switch (type.type) { 310 | case dataTypeUnit.STRUCT: { 311 | const children = {} as Record 312 | 313 | for (const variable of type.content as ExtractedTypeVariable[]) { 314 | children[variable.name] = resolveIecVariable(variable.dataType, types, providedTypes) 315 | } 316 | return iecTypes.STRUCT(children) 317 | } 318 | 319 | case dataTypeUnit.UNION: { 320 | //Basically 1:1 as struct 321 | const children = {} as Record 322 | 323 | for (const variable of type.content as ExtractedTypeVariable[]) { 324 | children[variable.name] = resolveIecVariable(variable.dataType, types, providedTypes) 325 | } 326 | return iecTypes.UNION(children) 327 | } 328 | 329 | case dataTypeUnit.ENUM: { 330 | const enumeration = type.content as ExtractedEnum 331 | 332 | //TODO: Check if valid ENUM data type 333 | return iecTypes.ENUM(enumeration.content, resolveIecVariable(enumeration.dataType, types, providedTypes) as EnumDataType) 334 | } 335 | 336 | case dataTypeUnit.ALIAS: 337 | return resolveIecVariable((type.content as ExtractedAlias).dataType, types, providedTypes) 338 | 339 | default: 340 | throw new Error(`Problem resolving IEC data type unit (DUT). Unknown type: ${type.type}`) 341 | } 342 | } 343 | 344 | /** 345 | * Resolves a single variable data type (string) to IEC data type 346 | * Calls itself recursively if needed (like array types) 347 | * @param dataType Data type name as string 348 | * @param structs List of all available types that are/will be resolved 349 | * @param providedTypes List of user-provided types 350 | * @returns Resolved type (IecType object) 351 | */ 352 | const resolveIecVariable = (dataType: string, types: ExtractedType[], providedTypes?: Record): IecType => { 353 | //Simple non-complex data type 354 | let type: IecType | undefined = nonComplexTypes.find(type => type.type.toLowerCase() === dataType.toLowerCase()) 355 | 356 | if (type) { 357 | return type 358 | } 359 | 360 | //String or wstring 361 | //TODO: Add checks if RegExp was successful 362 | const stringMatcher = new RegExp(stringRegEx) 363 | const stringMatch = stringMatcher.exec(dataType) 364 | 365 | if (stringMatch) { 366 | if (stringMatch[1].toLowerCase() === 'string') { 367 | return iecTypes.STRING(stringMatch[3] ? parseInt(stringMatch[3]) : 80) 368 | 369 | } else if (stringMatch[1].toLowerCase() === 'wstring') { 370 | return iecTypes.WSTRING(stringMatch[3] ? parseInt(stringMatch[3]) : 80) 371 | 372 | } else { 373 | throw new Error(`Unknown STRING definition: "${stringMatch}"`) 374 | } 375 | } 376 | 377 | //Array 378 | //TODO: Add checks if RegExp was successful 379 | const arrayMatcher = new RegExp(arrayRegEx) 380 | const arrayMatch = arrayMatcher.exec(dataType) 381 | 382 | if (arrayMatch) { 383 | type = resolveIecVariable(arrayMatch[2], types, providedTypes) 384 | 385 | if (!type) { 386 | //This shouldn't happen 387 | throw new Error(`Unknown array data type "${arrayMatch[2]}"`) 388 | } 389 | 390 | //Array dimensions 391 | const dimensions = [] 392 | 393 | let match: RegExpExecArray | null 394 | const arrayDimensionsMatcher = new RegExp(arrayDimensionsRegEx) 395 | 396 | //TODO: Add checks if RegExp was successful 397 | while ((match = arrayDimensionsMatcher.exec(arrayMatch[1])) !== null) { 398 | // This is necessary to avoid infinite loops with zero-width matches 399 | if (match.index === arrayDimensionsMatcher.lastIndex) { 400 | arrayDimensionsMatcher.lastIndex++ 401 | } 402 | 403 | dimensions.push(parseInt(match[2]) - parseInt(match[1]) + 1) 404 | } 405 | return iecTypes.ARRAY(type, dimensions) 406 | } 407 | 408 | //Data type unit (DUT) like struct, enum etc. (or unknown) 409 | const dataTypeUnit = types.find(type => type.name.toLowerCase() == dataType.toLowerCase()) 410 | 411 | if (dataTypeUnit) { 412 | //If type is found but not yet resolved, resolve it now 413 | if (!dataTypeUnit.resolved) { 414 | dataTypeUnit.resolved = resolveIecDataTypeUnit(dataTypeUnit, types, providedTypes) 415 | } 416 | 417 | return dataTypeUnit.resolved as IecType 418 | 419 | } else { 420 | //Data type was unknown. Was it provided already? 421 | if (providedTypes) { 422 | const key = Object.keys(providedTypes).find(key => key.toLowerCase() === dataType.toLowerCase()) 423 | 424 | if (key) { 425 | return providedTypes[key] 426 | } else { 427 | throw new Error(`Unknown data type "${dataType}" found! Types found from declaration: ${types.map(type => type.name).join(', ')}, types provided separately: ${Object.keys(providedTypes).join(',')}`) 428 | } 429 | } else { 430 | throw new Error(`Unknown data type "${dataType}" found! Types found from declaration: ${types.map(type => type.name).join(', ')}`) 431 | } 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /src/iec-type-handler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/jisotalo/iec-61131-3 3 | 4 | Copyright (c) 2021 Jussi Isotalo 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | import iconv from 'iconv-lite' 26 | 27 | import type { 28 | EnumDataType, 29 | EnumEntry, 30 | EnumValue, 31 | IecType, 32 | IteratedIecType 33 | } from './types/types' 34 | 35 | 36 | /** 37 | * Base abstract type 38 | */ 39 | abstract class TypeBase implements Partial { 40 | type = '' 41 | byteLength = 0 42 | 43 | /** 44 | * Shorthand for variableIterator() 45 | * --> for(const variable of dataType) {...} 46 | */ 47 | public *[Symbol.iterator](): IterableIterator { 48 | let startIndex = 0; 49 | 50 | //Helper recursive function 51 | function* iterate(dt: IecType): IterableIterator { 52 | if (dt.children === undefined) { 53 | yield { 54 | name: undefined, 55 | startIndex, 56 | type: dt 57 | } 58 | startIndex += dt.byteLength; 59 | } else { 60 | for (const name in dt.children) { 61 | const type = dt.children[name] 62 | 63 | if (type.children !== undefined) { 64 | //There are children -> go deeper 65 | yield* iterate(type) 66 | } else { 67 | yield { 68 | name, 69 | startIndex, 70 | type 71 | } 72 | startIndex += type.byteLength; 73 | } 74 | } 75 | } 76 | } 77 | 78 | yield * iterate(this as unknown as IecType) 79 | } 80 | 81 | /** 82 | * Iterator for looping through all variables in memory order 83 | * NOTE: Array variable is _one_ variable, see elementIterator() for looping each array element 84 | * 85 | * Usage: for(const variable of dataType.variableIterator()) {...} 86 | * Shorthand for this is: for(const variable of dataType) {...} 87 | */ 88 | public *variableIterator(): IterableIterator { 89 | yield* this[Symbol.iterator](); 90 | } 91 | 92 | /** 93 | * Iterator for looping through all variables (and their array elements) in memory order 94 | * NOTE: Each array element is yield separately unlike with variableIterator() 95 | * 96 | * Usage: for(const variable of dataType.elementIterator()) {...} 97 | * 98 | */ 99 | public *elementIterator(): IterableIterator { 100 | function* iterateArrayLevel(dt: ARRAY, startIndex: number, name?: string): IterableIterator { 101 | for (let i = 0; i < dt.totalSize; i++) { 102 | if (dt.dataType instanceof ARRAY) { 103 | yield* iterateArrayLevel(dt.dataType, startIndex + dt.dataType.byteLength * i, `${name}[${i}]`) 104 | } else { 105 | yield { 106 | name: `${name}[${i}]`, 107 | startIndex: startIndex + dt.dataType.byteLength * i, 108 | type: dt.dataType 109 | } 110 | } 111 | } 112 | } 113 | 114 | for (const variable of this) { 115 | if (variable.type instanceof ARRAY) { 116 | yield* iterateArrayLevel(variable.type, variable.startIndex, variable.name) 117 | } else { 118 | yield variable 119 | } 120 | } 121 | } 122 | } 123 | 124 | /** 125 | * IEC 61131-3 type: STRUCT 126 | */ 127 | export class STRUCT extends TypeBase implements IecType { 128 | type = 'STRUCT' 129 | /** 130 | * STRUCT children data types 131 | */ 132 | children?: Record 133 | 134 | constructor(children?: Record) { 135 | super() 136 | 137 | this.children = children 138 | 139 | //Calculating struct size 140 | for (const key in this.children) { 141 | if (typeof this.children[key] !== 'object' || this.children[key].byteLength === undefined) { 142 | throw new Error(`Struct member ${key} is not valid IEC data type - Did you remember to use () with some data types that require it (example with STRING())?`) 143 | } 144 | 145 | this.byteLength += this.children[key].byteLength 146 | } 147 | } 148 | 149 | convertToBuffer(data: Record): Buffer { 150 | if (!data) 151 | return Buffer.alloc(0) 152 | 153 | const buffer = Buffer.alloc(this.byteLength) 154 | let pos = 0 155 | 156 | for (const key in this.children) { 157 | const converted = this.children[key].convertToBuffer(data[key]) 158 | 159 | converted.copy(buffer, pos) 160 | pos += converted.byteLength 161 | } 162 | 163 | return buffer 164 | } 165 | 166 | convertFromBuffer(data: Buffer): Record { 167 | const obj = {} as Record 168 | let pos = 0 169 | 170 | for (const key in this.children) { 171 | obj[key] = this.children[key].convertFromBuffer(data.slice(pos, pos + this.children[key].byteLength)) 172 | pos += this.children[key].byteLength 173 | } 174 | 175 | return obj 176 | } 177 | 178 | getDefault(): Record { 179 | const obj = {} as Record 180 | 181 | for (const key in this.children) { 182 | obj[key] = this.children[key].getDefault() 183 | } 184 | 185 | return obj 186 | } 187 | } 188 | 189 | 190 | 191 | /** 192 | * IEC 61131-3 type: UNION 193 | */ 194 | export class UNION extends TypeBase implements IecType { 195 | type = 'UNION' 196 | /** 197 | * UNION children data types 198 | */ 199 | children?: Record 200 | 201 | constructor(children?: Record) { 202 | super() 203 | 204 | this.children = children 205 | 206 | //Calculating union size (= biggest child) 207 | for (const key in this.children) { 208 | if (typeof this.children[key] !== 'object' || this.children[key].byteLength === undefined) { 209 | throw new Error(`Struct member ${key} is not valid IEC data type - Did you remember to use () with some data types that require it (example with STRING())?`) 210 | } 211 | 212 | if (this.children[key].byteLength > this.byteLength) 213 | this.byteLength = this.children[key].byteLength 214 | } 215 | } 216 | 217 | 218 | convertToBuffer(data: Record): Buffer { 219 | if (!data) 220 | return Buffer.alloc(0) 221 | 222 | //As UNION type member all are located in same memory, it's not that easy 223 | //For now: Use the last given object key value 224 | 225 | const buffer = Buffer.alloc(this.byteLength) 226 | 227 | for (const key in this.children) { 228 | if (data[key] === undefined) 229 | continue 230 | 231 | //There is only one value allowed so quit after this 232 | const converted = this.children[key].convertToBuffer(data[key]) 233 | converted.copy(buffer, 0) 234 | } 235 | 236 | return buffer 237 | } 238 | 239 | convertFromBuffer(data: Buffer): Record { 240 | const obj = {} as Record 241 | 242 | for (const key in this.children) { 243 | obj[key] = this.children[key].convertFromBuffer(data.slice(0, this.children[key].byteLength)) 244 | } 245 | 246 | return obj 247 | } 248 | 249 | getDefault(): Record { 250 | const obj = {} as Record 251 | 252 | for (const key in this.children) { 253 | obj[key] = this.children[key].getDefault() 254 | } 255 | 256 | return obj 257 | } 258 | } 259 | 260 | 261 | 262 | 263 | /** 264 | * IEC 61131-3 type: ARRAY 265 | * Handles 1..3 dimensional arrays 266 | */ 267 | export class ARRAY extends TypeBase implements IecType { 268 | type = 'ARRAY' 269 | dataType: IecType 270 | dimensions: number[] = [] 271 | totalSize = 0 272 | 273 | /** 274 | * Constructor for array 275 | * @param dataType Data type of the array (example: iec.INT) 276 | * @param dimensions If 1-dimensional array: Array dimension (size) as number. If multi-dimensional array, array dimensions as array (like [1, 10, 5]) 277 | */ 278 | constructor(dataType: IecType, dimensions: number | number[]) { 279 | super() 280 | 281 | this.dataType = dataType 282 | 283 | if (Array.isArray(dimensions)) { 284 | this.dimensions = dimensions 285 | } else { 286 | this.dimensions.push(dimensions) 287 | } 288 | 289 | //Calculating total size 290 | this.totalSize = this.dimensions.reduce((total, size) => total * size, 1) 291 | this.byteLength = this.totalSize * dataType.byteLength 292 | } 293 | 294 | 295 | convertToBuffer(data: unknown[]): Buffer { 296 | if (!data) 297 | return Buffer.alloc(0) 298 | 299 | const buffer = Buffer.alloc(this.byteLength) 300 | let pos = 0 301 | 302 | //Recursive handling of array dimensions 303 | //Loops dimensions until we found the last one and then reads the data 304 | const parseArray = (data: unknown[], arrayDimension: number): void => { 305 | 306 | for (let dimension = 0; dimension < this.dimensions[arrayDimension]; dimension++) { 307 | if (this.dimensions[arrayDimension + 1]) { 308 | //More dimensions available -> go deeper 309 | parseArray(data[dimension] as unknown[], arrayDimension + 1) 310 | 311 | } else { 312 | //This is the final dimension -> we have actual data 313 | const converted = this.dataType.convertToBuffer(data[dimension]) 314 | converted.copy(buffer, pos) 315 | pos += converted.byteLength 316 | } 317 | } 318 | } 319 | 320 | //Start from 1st dimension 321 | parseArray(data, 0) 322 | 323 | return buffer 324 | } 325 | 326 | convertFromBuffer(data: Buffer): unknown[] { 327 | let pos = 0 328 | 329 | //Recursive handling of array dimensions 330 | //Loops dimensions until we found the last one and then fills with data 331 | const parseArray = (arrayDimension: number): unknown[] => { 332 | const result = [] 333 | 334 | for (let dimension = 0; dimension < this.dimensions[arrayDimension]; dimension++) { 335 | if (this.dimensions[arrayDimension + 1]) { 336 | //More dimensions available -> go deeper 337 | result.push(parseArray(arrayDimension + 1)) 338 | 339 | } else { 340 | //This is the final dimension -> we have actual data 341 | result.push(this.dataType.convertFromBuffer(data.slice(pos, pos + this.dataType.byteLength))) 342 | pos += this.dataType.byteLength 343 | } 344 | } 345 | return result 346 | } 347 | 348 | //Start from 1st dimension 349 | return parseArray(0) 350 | } 351 | 352 | getDefault(): unknown[] { 353 | 354 | //Recursive parsing of array dimensions 355 | //Loops dimensions until we found the last one and then fills with data 356 | const parseArray = (arrayDimension: number): unknown[] => { 357 | const result = [] 358 | 359 | for (let dimension = 0; dimension < this.dimensions[arrayDimension]; dimension++) { 360 | if (this.dimensions[arrayDimension + 1]) { 361 | //More dimensions available -> go deeper 362 | result.push(parseArray(arrayDimension + 1)) 363 | 364 | } else { 365 | //This is the final dimension -> we have actual data 366 | result.push(this.dataType.getDefault()) 367 | } 368 | } 369 | return result 370 | } 371 | 372 | //Start from 1st dimension 373 | return parseArray(0) 374 | } 375 | } 376 | 377 | 378 | 379 | /** 380 | * IEC 61131-3 type: ENUM 381 | */ 382 | export class ENUM extends TypeBase implements IecType { 383 | type = 'ENUM' 384 | 385 | definition: Record 386 | dataType: IecType 387 | 388 | constructor(definition: Record, dataType?: EnumDataType) { 389 | super() 390 | 391 | this.definition = definition 392 | 393 | this.dataType = dataType ? dataType : new INT() 394 | this.byteLength = this.dataType.byteLength 395 | } 396 | 397 | 398 | convertToBuffer(data: EnumValue): Buffer { 399 | if (!data) 400 | return Buffer.alloc(0) 401 | 402 | if (typeof data === 'string') { 403 | //Enumeration name given as string 404 | const found = Object.keys(this.definition).find(key => key.toLowerCase() === data.toLowerCase()) 405 | 406 | if (found) { 407 | return this.dataType.convertToBuffer(this.definition[found]) 408 | } 409 | throw new Error('Input parameter is not valid for ENUM') 410 | 411 | } else if (typeof data === 'number') { 412 | //Enumeration value given as number 413 | return this.dataType.convertToBuffer(data) 414 | 415 | } else if (typeof data === 'object' && (data as EnumEntry).value) { 416 | //Object given with value key 417 | return this.dataType.convertToBuffer(data) 418 | 419 | } else if (typeof data === 'object' && (data as EnumEntry).name) { 420 | //Object given with name key 421 | const found = Object.keys(this.definition).find(key => data.name && key.toLowerCase() === data.name.toLowerCase()) 422 | 423 | if (found) { 424 | return this.dataType.convertToBuffer(this.definition[found]) 425 | } 426 | throw new Error('Input parameter is not valid for ENUM') 427 | 428 | } else { 429 | throw new Error('Input parameter is not valid for ENUM (number, string or object with { value })') 430 | } 431 | } 432 | 433 | 434 | convertFromBuffer(data: Buffer): EnumEntry { 435 | //First, converting buffer to number 436 | const value = this.dataType.convertFromBuffer(data) as number 437 | 438 | const entry = this.findEnumEntryByValue(value) 439 | 440 | if (entry) 441 | return entry 442 | 443 | //Not found 444 | return { 445 | name: undefined, 446 | value 447 | } 448 | } 449 | 450 | 451 | 452 | getDefault(): EnumEntry { 453 | //Codeys initializes the value with the first enumeration component 454 | //Use it, unless there are none 455 | const keys = Object.keys(this.definition) 456 | 457 | if (keys.length > 0) { 458 | return { 459 | name: keys[0], 460 | value: this.definition[keys[0]] 461 | } 462 | 463 | } else { 464 | //No entries? Use data type default value 465 | const value = (this.dataType as EnumDataType).getDefault() 466 | 467 | //Do we have enumeration entry for default value? 468 | const entry = this.findEnumEntryByValue(value) 469 | 470 | if (entry) 471 | return entry 472 | 473 | //Not found 474 | return { 475 | name: undefined, 476 | value 477 | } 478 | } 479 | } 480 | 481 | 482 | 483 | findEnumEntryByValue(value: number): EnumEntry | undefined { 484 | for (const key in this.definition) { 485 | if (this.definition[key] === value) { 486 | //Found 487 | return { 488 | name: key, 489 | value: value 490 | } 491 | } 492 | } 493 | 494 | //Not found 495 | return undefined 496 | } 497 | } 498 | 499 | 500 | 501 | /** 502 | * IEC 61131-3 type: STRING 503 | * Default length 80 characters 504 | */ 505 | export class STRING extends TypeBase implements IecType { 506 | type = 'STRING' 507 | byteLength = 80 508 | 509 | /** 510 | * Constructor for string 511 | * @param length Length of the string variable (similar as in the PLC), default is 80 512 | */ 513 | constructor(length = 80) { 514 | super() 515 | 516 | //Adding string end delimeter 517 | this.byteLength = length + 1 518 | } 519 | 520 | convertToBuffer(data: string): Buffer { 521 | const buffer = Buffer.alloc(this.byteLength) 522 | iconv.encode(data, 'cp1252').copy(buffer) 523 | 524 | return buffer 525 | } 526 | 527 | convertFromBuffer(data: Buffer): string { 528 | return trimPlcString(iconv.decode(data, 'cp1252')) 529 | } 530 | 531 | getDefault(): string { 532 | return '' 533 | } 534 | } 535 | 536 | 537 | 538 | 539 | /** 540 | * IEC 61131-3 type: WSTRING 541 | * Default length 80 characters 542 | */ 543 | export class WSTRING extends TypeBase implements IecType { 544 | type = 'WSTRING' 545 | byteLength = 160 546 | 547 | /** 548 | * Constructor for wstring 549 | * @param length Length of the string variable (similar as in the PLC), default is 80 550 | */ 551 | constructor(length = 80) { 552 | super() 553 | 554 | //Adding string end delimeter 555 | this.byteLength = length * 2 + 2 556 | } 557 | 558 | convertToBuffer(data: string): Buffer { 559 | const buffer = Buffer.alloc(this.byteLength) 560 | iconv.encode(data, 'ucs2').copy(buffer) 561 | 562 | return buffer 563 | } 564 | 565 | convertFromBuffer(data: Buffer): string { 566 | return trimPlcString(iconv.decode(data, 'ucs2')) 567 | } 568 | 569 | getDefault(): string { 570 | return '' 571 | } 572 | } 573 | 574 | 575 | 576 | /** 577 | * IEC 61131-3 type: BOOL (1 byte) 578 | */ 579 | export class BOOL extends TypeBase implements IecType { 580 | type = 'BOOL' 581 | byteLength = 1 582 | 583 | convertToBuffer(data: boolean): Buffer { 584 | const buffer = Buffer.alloc(this.byteLength) 585 | buffer.writeUInt8(data ? 1 : 0) 586 | 587 | return buffer 588 | } 589 | 590 | convertFromBuffer(data: Buffer): boolean { 591 | return data.readUInt8() ? true : false 592 | } 593 | 594 | getDefault(): boolean { 595 | return false 596 | } 597 | } 598 | 599 | 600 | 601 | /** 602 | * IEC 61131-3 type: USINT (1 byte) 603 | */ 604 | export class USINT extends TypeBase implements IecType { 605 | type = 'USINT' 606 | byteLength = 1 607 | 608 | convertToBuffer(data: number): Buffer { 609 | const buffer = Buffer.alloc(this.byteLength) 610 | buffer.writeUInt8(data) 611 | 612 | return buffer 613 | } 614 | 615 | convertFromBuffer(data: Buffer): number { 616 | return data.readUInt8() 617 | } 618 | 619 | getDefault(): number { 620 | return 0 621 | } 622 | } 623 | 624 | 625 | /** 626 | * IEC 61131-3 type: BYTE (1 byte) 627 | */ 628 | export class BYTE extends USINT implements IecType { 629 | type = 'BYTE' 630 | } 631 | 632 | 633 | 634 | /** 635 | * IEC 61131-3 type: SINT (1 byte) 636 | */ 637 | export class SINT extends TypeBase implements IecType { 638 | type = 'SINT' 639 | byteLength = 1 640 | 641 | convertToBuffer(data: number): Buffer { 642 | const buffer = Buffer.alloc(this.byteLength) 643 | buffer.writeInt8(data) 644 | 645 | return buffer 646 | } 647 | 648 | convertFromBuffer(data: Buffer): number { 649 | return data.readInt8() 650 | } 651 | 652 | getDefault(): number { 653 | return 0 654 | } 655 | } 656 | 657 | 658 | 659 | 660 | /** 661 | * IEC 61131-3 type: UINT (2 bytes) 662 | */ 663 | export class UINT extends TypeBase implements IecType { 664 | type = 'UINT' 665 | byteLength = 2 666 | 667 | convertToBuffer(data: number): Buffer { 668 | const buffer = Buffer.alloc(this.byteLength) 669 | buffer.writeUInt16LE(data) 670 | 671 | return buffer 672 | } 673 | 674 | convertFromBuffer(data: Buffer): number { 675 | return data.readUInt16LE() 676 | } 677 | 678 | getDefault(): number { 679 | return 0 680 | } 681 | } 682 | 683 | 684 | 685 | /** 686 | * IEC 61131-3 type: WORD (2 bytes) 687 | */ 688 | export class WORD extends UINT implements IecType { 689 | type = 'WORD' 690 | } 691 | 692 | 693 | 694 | /** 695 | * IEC 61131-3 type: INT (2 bytes) 696 | */ 697 | export class INT extends TypeBase implements IecType { 698 | type = 'INT' 699 | byteLength = 2 700 | 701 | convertToBuffer(data: number): Buffer { 702 | const buffer = Buffer.alloc(this.byteLength) 703 | buffer.writeInt16LE(data) 704 | 705 | return buffer 706 | } 707 | 708 | convertFromBuffer(data: Buffer): number { 709 | return data.readInt16LE() 710 | } 711 | 712 | getDefault(): number { 713 | return 0 714 | } 715 | } 716 | 717 | 718 | /** 719 | * IEC 61131-3 type: DINT (4 bytes) 720 | */ 721 | export class DINT extends TypeBase implements IecType { 722 | type = 'DINT' 723 | byteLength = 4 724 | 725 | convertToBuffer(data: number): Buffer { 726 | const buffer = Buffer.alloc(this.byteLength) 727 | buffer.writeInt32LE(data) 728 | 729 | return buffer 730 | } 731 | 732 | convertFromBuffer(data: Buffer): number { 733 | return data.readInt32LE() 734 | } 735 | 736 | getDefault(): number { 737 | return 0 738 | } 739 | } 740 | 741 | 742 | 743 | /** 744 | * IEC 61131-3 type: UDINT (4 bytes) 745 | */ 746 | export class UDINT extends TypeBase implements IecType { 747 | type = 'UDINT' 748 | byteLength = 4 749 | 750 | convertToBuffer(data: number): Buffer { 751 | const buffer = Buffer.alloc(this.byteLength) 752 | buffer.writeUInt32LE(data) 753 | 754 | return buffer 755 | } 756 | 757 | convertFromBuffer(data: Buffer): number { 758 | return data.readUInt32LE() 759 | } 760 | 761 | getDefault(): number { 762 | return 0 763 | } 764 | } 765 | 766 | 767 | 768 | /** 769 | * IEC 61131-3 type: DWORD (4 bytes) 770 | */ 771 | export class DWORD extends UDINT implements IecType { 772 | type = 'DWORD' 773 | } 774 | 775 | 776 | 777 | /** 778 | * IEC 61131-3 type: TIME (4 bytes) 779 | */ 780 | export class TIME extends UDINT implements IecType { 781 | type = 'TIME' 782 | } 783 | 784 | 785 | 786 | /** 787 | * IEC 61131-3 type: TOD (4 bytes) 788 | */ 789 | export class TOD extends UDINT implements IecType { 790 | type = 'TOD' 791 | } 792 | 793 | 794 | 795 | /** 796 | * IEC 61131-3 type: TIME_OF_DAY (4 bytes) 797 | */ 798 | export class TIME_OF_DAY extends TOD implements IecType { 799 | type = 'TIME_OF_DAY' 800 | } 801 | 802 | 803 | 804 | /** 805 | * IEC 61131-3 type: DT (4 bytes) 806 | * TODO: Conversion to Javascript Date object? 807 | */ 808 | export class DT extends UDINT implements IecType { 809 | type = 'DT' 810 | } 811 | 812 | 813 | 814 | /** 815 | * IEC 61131-3 type: DATE_AND_TIME (4 bytes) 816 | * TODO: Conversion to Javascript Date object? 817 | */ 818 | export class DATE_AND_TIME extends DT implements IecType { 819 | type = 'DATE_AND_TIME' 820 | } 821 | 822 | 823 | 824 | /** 825 | * IEC 61131-3 type: DATE (4 bytes) 826 | * TODO: Conversion to Javascript Date object? 827 | */ 828 | export class DATE extends UDINT implements IecType { 829 | type = 'DATE' 830 | } 831 | 832 | 833 | 834 | 835 | /** 836 | * IEC 61131-3 type: REAL (4 bytes) 837 | */ 838 | export class REAL extends TypeBase implements IecType { 839 | type = 'REAL' 840 | byteLength = 4 841 | 842 | convertToBuffer(data: number): Buffer { 843 | const buffer = Buffer.alloc(this.byteLength) 844 | buffer.writeFloatLE(data) 845 | 846 | return buffer 847 | } 848 | 849 | convertFromBuffer(data: Buffer): number { 850 | return data.readFloatLE() 851 | } 852 | 853 | getDefault(): number { 854 | return 0 855 | } 856 | } 857 | 858 | 859 | 860 | 861 | /** 862 | * IEC 61131-3 type: LREAL (4 bytes) 863 | */ 864 | export class LREAL extends TypeBase implements IecType { 865 | type = 'LREAL' 866 | byteLength = 8 867 | 868 | convertToBuffer(data: number): Buffer { 869 | const buffer = Buffer.alloc(this.byteLength) 870 | buffer.writeDoubleLE(data) 871 | 872 | return buffer 873 | } 874 | 875 | convertFromBuffer(data: Buffer): number { 876 | return data.readDoubleLE() 877 | } 878 | 879 | getDefault(): number { 880 | return 0 881 | } 882 | } 883 | 884 | 885 | 886 | 887 | /** 888 | * IEC 61131-3 type: ULINT (8 bytes) 889 | * TODO: Requires Node.js that supports BigInt 890 | */ 891 | export class ULINT extends TypeBase implements IecType { 892 | type = 'ULINT' 893 | byteLength = 8 894 | 895 | convertToBuffer(data: bigint): Buffer { 896 | const buffer = Buffer.alloc(this.byteLength) 897 | buffer.writeBigUInt64LE(data) 898 | 899 | return buffer 900 | } 901 | 902 | convertFromBuffer(data: Buffer): bigint { 903 | return data.readBigUInt64LE() 904 | } 905 | 906 | getDefault(): number { 907 | return 0 908 | } 909 | } 910 | 911 | 912 | 913 | 914 | /** 915 | * IEC 61131-3 type: LWORD (8 bytes) 916 | * TODO: Requires Node.js that supports BigInt 917 | */ 918 | export class LWORD extends ULINT implements IecType { 919 | type = 'LWORD' 920 | } 921 | 922 | 923 | 924 | 925 | /** 926 | * IEC 61131-3 type: LINT (8 bytes) 927 | * TODO: Requires Node.js that supports BigInt 928 | */ 929 | export class LINT extends TypeBase implements IecType { 930 | type = 'LINT' 931 | byteLength = 8 932 | 933 | convertToBuffer(data: bigint): Buffer { 934 | const buffer = Buffer.alloc(this.byteLength) 935 | buffer.writeBigInt64LE(data) 936 | 937 | return buffer 938 | } 939 | 940 | convertFromBuffer(data: Buffer): bigint { 941 | return data.readBigInt64LE() 942 | } 943 | 944 | getDefault(): number { 945 | return 0 946 | } 947 | } 948 | 949 | 950 | 951 | 952 | 953 | /** 954 | * Trims the given PLC string until end mark (\0, 0 byte) is found 955 | * (= removes empty bytes from end of the string) 956 | * @param {string} plcString String to trim 957 | * 958 | * @returns {string} Trimmed string 959 | */ 960 | const trimPlcString = (plcString: string): string => { 961 | let parsedStr = '' 962 | 963 | for (let i = 0; i < plcString.length; i++) { 964 | if (plcString.charCodeAt(i) === 0) break 965 | 966 | parsedStr += plcString[i] 967 | } 968 | 969 | return parsedStr 970 | } 971 | 972 | 973 | -------------------------------------------------------------------------------- /src/iec-types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/jisotalo/iec-61131-3 3 | 4 | Copyright (c) 2021 Jussi Isotalo 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | import type { 26 | EnumDataType, 27 | IecType 28 | } from './types/types' 29 | 30 | import * as handler from './iec-type-handler' 31 | 32 | /** 33 | * IEC 61131-3 type: STRUCT - Handles STRUCT data type, provide struct children as object 34 | * 35 | * @param children Children variables as IEC object, like: `{intVal: INT, boolVal: BOOL, structVal: STRUCT({...})}` 36 | * @returns IecType object 37 | */ 38 | export const STRUCT = (children?: Record): handler.STRUCT => new handler.STRUCT(children) 39 | 40 | /** 41 | * IEC 61131-3 type: UNION - Handles UNION data type, provide union children as object 42 | * 43 | * @param children Children variables as IEC object, like: `{intVal: INT, boolVal: BOOL, structVal: STRUCT({...})}` 44 | * @returns IecType object 45 | */ 46 | export const UNION = (children?: Record): handler.UNION => new handler.UNION(children) 47 | 48 | /** 49 | * IEC 61131-3 type: ARRAY - Handles 1..3 dimensional arrays. 50 | * 51 | * Example with 1-dimensional INT array of 10 values: `ARRAY(INT, 10)` 52 | 53 | * Example with 2-dimensional REAL array of 2*5 values: `ARRAY(REAL, [2, 5])` 54 | * 55 | * @param dataType Data type of the array (example: INT) 56 | * @param dimensions Array dimension as number (if 1-dimensional array). If multi-dimensional, array dimensions as array of numbers (like `[2, 5]`) 57 | */ 58 | export const ARRAY = (dataType: IecType, dimensions: number | number[]): handler.ARRAY => new handler.ARRAY(dataType, dimensions) 59 | 60 | /** 61 | * IEC 61131-3 type: ENUM 62 | * Handles enumeration types with different data types 63 | * 64 | * @param definition Enumeration definition as object (like `{key1: 1, key2: 2}`) 65 | * @param dataType Data type of the ENUM (default is iec.INT) 66 | * @returns 67 | */ 68 | export const ENUM = (definition: Record, dataType?: EnumDataType): handler.ENUM => new handler.ENUM(definition, dataType) 69 | 70 | 71 | /** 72 | * IEC 61131-3 type: STRING - Default length 80 characters 73 | * @param length Length of the string variable (similar as in the PLC), default is 80 74 | */ 75 | export const STRING = (length?: number): handler.STRING => new handler.STRING(length) 76 | 77 | /** 78 | * IEC 61131-3 type: WSTRING - Default length 80 characters 79 | * @param length Length of the string variable (similar as in the PLC), default is 80 80 | */ 81 | export const WSTRING = (length?: number): handler.WSTRING => new handler.WSTRING(length) 82 | 83 | /** 84 | * IEC 61131-3 type: BOOL (1 byte) 85 | */ 86 | export const BOOL = new handler.BOOL() 87 | 88 | /** 89 | * IEC 61131-3 type: USINT (1 byte) 90 | */ 91 | export const USINT = new handler.USINT() 92 | 93 | /** 94 | * IEC 61131-3 type: BYTE (1 byte) 95 | */ 96 | export const BYTE = new handler.BYTE() 97 | 98 | /** 99 | * IEC 61131-3 type: SINT (1 byte) 100 | */ 101 | export const SINT = new handler.SINT() 102 | 103 | 104 | /** 105 | * IEC 61131-3 type: UINT (2 bytes) 106 | */ 107 | export const UINT = new handler.UINT() 108 | 109 | /** 110 | * IEC 61131-3 type: WORD (2 bytes) 111 | */ 112 | export const WORD = new handler.WORD() 113 | 114 | /** 115 | * IEC 61131-3 type: INT (2 bytes) 116 | */ 117 | export const INT = new handler.INT() 118 | 119 | 120 | /** 121 | * IEC 61131-3 type: DINT (4 bytes) 122 | */ 123 | export const DINT = new handler.DINT() 124 | 125 | /** 126 | * IEC 61131-3 type: UDINT (4 bytes) 127 | */ 128 | export const UDINT = new handler.UDINT() 129 | 130 | /** 131 | * IEC 61131-3 type: DWORD (4 bytes) 132 | */ 133 | export const DWORD = new handler.DWORD() 134 | 135 | /** 136 | * IEC 61131-3 type: TIME (4 bytes) 137 | */ 138 | export const TIME = new handler.TIME() 139 | 140 | /** 141 | * IEC 61131-3 type: TOD (4 bytes) 142 | */ 143 | export const TOD = new handler.TOD() 144 | 145 | /** 146 | * IEC 61131-3 type: TIME_OF_DAY (4 bytes) 147 | */ 148 | export const TIME_OF_DAY = new handler.TIME_OF_DAY() 149 | 150 | /** 151 | * IEC 61131-3 type: DT (4 bytes) 152 | * TODO: Conversion to Javascript Date object? 153 | */ 154 | export const DT = new handler.DT() 155 | 156 | /** 157 | * IEC 61131-3 type: DATE_AND_TIME (4 bytes) 158 | * TODO: Conversion to Javascript Date object? 159 | */ 160 | export const DATE_AND_TIME = new handler.DATE_AND_TIME() 161 | 162 | /** 163 | * IEC 61131-3 type: DATE (4 bytes) 164 | * TODO: Conversion to Javascript Date object? 165 | */ 166 | export const DATE = new handler.DATE() 167 | 168 | 169 | /** 170 | * IEC 61131-3 type: REAL (4 bytes) 171 | */ 172 | export const REAL = new handler.REAL() 173 | 174 | /** 175 | * IEC 61131-3 type: LREAL (4 bytes) 176 | */ 177 | export const LREAL = new handler.LREAL() 178 | 179 | 180 | /** 181 | * IEC 61131-3 type: ULINT (8 bytes) 182 | * TODO: Requires Node.js that supports BigInt 183 | */ 184 | export const ULINT = new handler.ULINT() 185 | 186 | /** 187 | * IEC 61131-3 type: LWORD (8 bytes) 188 | * TODO: Requires Node.js that supports BigInt 189 | */ 190 | export const LWORD = new handler.LWORD() 191 | 192 | /** 193 | * IEC 61131-3 type: LINT (8 bytes) 194 | * TODO: Requires Node.js that supports BigInt 195 | */ 196 | export const LINT = new handler.LINT() 197 | 198 | -------------------------------------------------------------------------------- /src/types/types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/jisotalo/iec-61131-3 3 | 4 | Copyright (c) 2021 Jussi Isotalo 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | 26 | import * as types from '../iec-type-handler' 27 | 28 | 29 | /** 30 | * IEC data type interface 31 | */ 32 | export interface IecType extends Iterable { 33 | /** 34 | * Name of the IEC data type as string 35 | */ 36 | type: string, 37 | 38 | /** 39 | * Size of the data in bytes 40 | */ 41 | byteLength: number, 42 | 43 | /** 44 | * Converts given Javascript object to Buffer 45 | */ 46 | /* eslint-disable @typescript-eslint/no-explicit-any*/ 47 | convertToBuffer: (data: any) => Buffer, 48 | 49 | /** 50 | * Converts given Buffer data to Javascript object 51 | */ 52 | convertFromBuffer: (data: Buffer) => Record | unknown, 53 | 54 | /** 55 | * Returns default (empty) Javascript object representing the IEC type 56 | */ 57 | getDefault: () => Record | unknown 58 | 59 | /** 60 | * Children of the data type (if any) 61 | */ 62 | children?: Record 63 | 64 | /** 65 | * Iterator for looping through all variables in memory order 66 | * NOTE: Array variable is _one_ variable, see elementIterator() for looping each array element 67 | * 68 | * Usage: for(const variable of dataType.variableIterator()) {...} 69 | * Shorthand for this is: for(const variable of dataType) {...} 70 | */ 71 | variableIterator: () => IterableIterator 72 | 73 | /** 74 | * Iterator for looping through all variables (and their array elements) in memory order 75 | * NOTE: Each array element is yield separately unlike with variableIterator() 76 | * 77 | * Usage: for(const variable of dataType.elementIterator()) {...} 78 | * 79 | */ 80 | elementIterator: () => IterableIterator 81 | } 82 | 83 | 84 | /** 85 | * Allowed enumeration values 86 | */ 87 | export type EnumValue = EnumEntry | string | number 88 | 89 | /** 90 | * Enumeration value object 91 | */ 92 | export interface EnumEntry { 93 | name: string | undefined, 94 | value: number 95 | } 96 | 97 | 98 | /** 99 | * Possible enumeration IEC data types 100 | */ 101 | export type EnumDataType = 102 | types.USINT 103 | | types.BYTE 104 | | types.SINT 105 | | types.UINT 106 | | types.WORD 107 | | types.INT 108 | | types.DINT 109 | | types.UDINT 110 | | types.DWORD 111 | | types.ULINT 112 | | types.LWORD 113 | | types.LINT 114 | 115 | 116 | /** 117 | * Different data type units (DUT) as enumeration 118 | */ 119 | export enum dataTypeUnit { 120 | STRUCT = 'STRUCT', 121 | UNION = 'UNION', 122 | ENUM = 'ENUM', 123 | ALIAS = 'ALIAS' 124 | } 125 | 126 | /** 127 | * Extracted data type unit (DUT) definition from string declaration 128 | * Contains extracted data for STRUCT, UNION, ENUM or ALIAS 129 | */ 130 | export interface ExtractedType { 131 | /** 132 | * Data type unit type (STRUCT, UNION, ENUM or ALIAS) 133 | */ 134 | type: dataTypeUnit, 135 | 136 | /** 137 | * Name of the type as string (like ST_Example, E_Enum) 138 | */ 139 | name: string, 140 | 141 | /** 142 | * Extracted content of the data type unit 143 | * Depends on type 144 | */ 145 | content: ExtractedTypeVariable[] | ExtractedEnum | ExtractedAlias 146 | 147 | /** 148 | * Resolved IEC data type (undefined if not yet resolved) 149 | */ 150 | resolved?: IecType, 151 | } 152 | 153 | 154 | 155 | 156 | /** 157 | * Extracted STRUCT or UNION child variable definition from string declaration 158 | * Example: In STRUCT "value: BOOL" --> {name: "value", dataType: "BOOL"} 159 | */ 160 | export interface ExtractedTypeVariable { 161 | /** 162 | * Variable name 163 | */ 164 | name: string, 165 | 166 | /** 167 | * Variable data type as string 168 | */ 169 | dataType: string 170 | } 171 | 172 | 173 | /** 174 | * Extracted ENUM definition 175 | */ 176 | export interface ExtractedEnum { 177 | /** 178 | * ENUM data type as string 179 | */ 180 | 181 | dataType: string, 182 | /** 183 | * ENUM members (name and value) 184 | */ 185 | content: Record, 186 | 187 | /** 188 | * Resolved IEC data type (undefined if not yet resolved) 189 | */ 190 | resolved?: IecType, 191 | } 192 | 193 | /** 194 | * Extracted ALIAS definition 195 | */ 196 | export interface ExtractedAlias { 197 | /** 198 | * ALIAS data type as string 199 | */ 200 | dataType: string, 201 | 202 | /** 203 | * Resolved IEC data type (undefined if not yet resolved) 204 | */ 205 | resolved?: IecType, 206 | } 207 | 208 | /** 209 | * Return value when iterating through types 210 | */ 211 | export interface IteratedIecType { 212 | /** 213 | * Variable name 214 | */ 215 | name?: string, 216 | 217 | /** 218 | * Start index 219 | */ 220 | startIndex: number, 221 | 222 | /** 223 | * Data type 224 | */ 225 | type: IecType 226 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ES2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | //"allowJs": false, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 13 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | //"sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./dist", /* Redirect output structure to the directory. */ 18 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true, /* Enable all strict type-checking options. */ 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | 44 | /* Module Resolution Options */ 45 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 49 | // "typeRoots": [], /* List of folders to include type definitions from. */ 50 | // "types": [], /* Type declaration files to be included in compilation. */ 51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 52 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 55 | 56 | /* Source Map Options */ 57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 61 | 62 | /* Experimental Options */ 63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 65 | 66 | /* Advanced Options */ 67 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 69 | } 70 | } 71 | --------------------------------------------------------------------------------