├── .github ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bugs.yaml │ └── features.yaml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ ├── greet-collaborators.yml │ ├── snyk-analysis.yml │ ├── stale.yml │ └── summary.yml ├── .gitignore ├── .gitlab-ci.yml ├── .gitpod.yml ├── .istanbul.yml ├── .jsinspectrc ├── .npmignore ├── .nvmrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── TODO.md ├── _config.yml ├── badges ├── badge-branches.svg ├── badge-functions.svg ├── badge-lines.svg └── badge-statements.svg ├── clean.sh ├── create_certificates.js ├── examples ├── SDM630 │ └── SDM630toOPCUA.json ├── all-in-one.json ├── client │ ├── aso-browse-crawle.json │ ├── browser-listen-group.json │ ├── browser-read.json │ ├── browser-to-listener.json │ ├── browser.json │ ├── crawler.json │ ├── event-listener.json │ ├── flex-connector-jwt.json │ ├── flex-connector.json │ ├── grouped-listener.json │ ├── listener.json │ ├── method-call.json │ ├── read-history.json │ ├── read-structure.json │ ├── read-write.json │ ├── read.json │ ├── response.json │ ├── result-filter.json │ ├── write-read.json │ └── write.json ├── external │ ├── codesys-pi-sl.json │ ├── prosys-simulator.json │ └── ua-cpp-demo-server.json └── server │ ├── program-server-with-context.json │ ├── programmable-server.json │ ├── server-ISA95.json │ ├── server-aso-types.json │ ├── server-demo-users.json │ └── server-demo.json ├── gulpfile.js ├── images ├── icon.png ├── icon.psd ├── listener-example-subv220.png ├── listener-flow-examplev220.png ├── logoISA88blue.png ├── logoISA95blue.png ├── logoISA95blue.xcf ├── logoISA95blue2.png ├── logoISA99blue.png ├── logoRAMI40blue.png ├── method-caller-examplev220.png ├── opcua-icon-small.png ├── opcua-icon-small.xcf ├── opcua-iiot-logo-glass.png ├── opcua-iiot-logo-white.png ├── opcua-iiot-logo.png ├── opcua-iiot-logo64-glass.png ├── opcua-iiot-logo64-white.png ├── opcua-iiot-logo64.png ├── opcua-iiot-logo6464.png ├── opcua.png ├── opcua64.png ├── read-examplev220.png ├── server-aso-type-examplev220.png ├── wiki │ ├── ASOTestVariablesUAExpert.png │ ├── Node-RED-Menu-Examples.png │ ├── browser-flow3.png │ ├── browser-listener-flow3-active.png │ ├── browser-listener-flow3.png │ ├── browserResultMessageJustValue.png │ ├── browserResultMessageToListener.png │ ├── browserResultMessageToRead.png │ ├── certificateTrustedUAExpert.png │ ├── certificateUntrustedUAExpert.png │ ├── crawler.png │ ├── discoveryExampleUAExpert.png │ ├── example-menu31.png │ ├── flex-connector-flow31.png │ ├── flexServerAddressSapceExample.png │ ├── flexServerAddressSapceExamplev3.png │ ├── iiot-nodes-v3.png │ ├── injectMessage.png │ ├── listenerChangedMessage.png │ ├── listenerResultMessageChanged.png │ ├── method-call3-active.png │ ├── read-history3-active.png │ ├── read-write-flow.png │ ├── readResultMessage.png │ ├── server-aso-flow3.png │ ├── write-flow3-active.png │ ├── write-read-flow.png │ ├── write-read-flow2.png │ ├── write-read-flow3.png │ ├── writeInputMessage.png │ └── writeMessage.png └── write-examplev220.png ├── index.js ├── jest.config.js ├── npm-update.sh ├── npm-upgrade.sh ├── package-lock.json ├── package.json ├── scripts └── JestOutputToSummary.js ├── src ├── core │ ├── opcua-iiot-core-browser.ts │ ├── opcua-iiot-core-client.ts │ ├── opcua-iiot-core-connector.ts │ ├── opcua-iiot-core-discovery.ts │ ├── opcua-iiot-core-filter.ts │ ├── opcua-iiot-core-inject.ts │ ├── opcua-iiot-core-listener.ts │ ├── opcua-iiot-core-method.ts │ ├── opcua-iiot-core-response.ts │ ├── opcua-iiot-core-server.ts │ └── opcua-iiot-core.ts ├── helpers │ ├── alarms-and-conditions-demo.ts │ └── isa95_demo_address_space.ts ├── icons │ ├── OPCUA-IIoT-Logo.png │ ├── icon.png │ ├── isa95-icon-blue-1024.png │ ├── isa95-icon-blue-128.png │ ├── isa95-icon-blue-64.png │ ├── isa95-icon-blue.png │ └── opcuaiiot.png ├── locales │ ├── de-DE │ │ ├── messages.json │ │ ├── opcua-iiot-browser.json │ │ ├── opcua-iiot-connector.json │ │ ├── opcua-iiot-event.json │ │ ├── opcua-iiot-flex-server.json │ │ ├── opcua-iiot-inject.json │ │ ├── opcua-iiot-listener.json │ │ ├── opcua-iiot-method-caller.json │ │ ├── opcua-iiot-node.json │ │ ├── opcua-iiot-read.json │ │ ├── opcua-iiot-response.json │ │ ├── opcua-iiot-result-filter.json │ │ ├── opcua-iiot-server-aso.json │ │ ├── opcua-iiot-server-cmd.json │ │ ├── opcua-iiot-server.json │ │ └── opcua-iiot-write.json │ └── en-US │ │ ├── messages.json │ │ ├── opcua-iiot-browser.json │ │ ├── opcua-iiot-connector.json │ │ ├── opcua-iiot-crawler.json │ │ ├── opcua-iiot-discovery.json │ │ ├── opcua-iiot-event.json │ │ ├── opcua-iiot-flex-connector.json │ │ ├── opcua-iiot-flex-server.json │ │ ├── opcua-iiot-inject.json │ │ ├── opcua-iiot-listener.json │ │ ├── opcua-iiot-method-caller.json │ │ ├── opcua-iiot-node.json │ │ ├── opcua-iiot-read.json │ │ ├── opcua-iiot-response.json │ │ ├── opcua-iiot-result-filter.json │ │ ├── opcua-iiot-server-aso.json │ │ ├── opcua-iiot-server-cmd.json │ │ ├── opcua-iiot-server.json │ │ └── opcua-iiot-write.json ├── opcua-iiot-browser.html ├── opcua-iiot-browser.ts ├── opcua-iiot-connector.html ├── opcua-iiot-connector.ts ├── opcua-iiot-crawler.html ├── opcua-iiot-crawler.ts ├── opcua-iiot-discovery.html ├── opcua-iiot-discovery.ts ├── opcua-iiot-event.html ├── opcua-iiot-event.ts ├── opcua-iiot-flex-connector.html ├── opcua-iiot-flex-connector.ts ├── opcua-iiot-flex-server.html ├── opcua-iiot-flex-server.ts ├── opcua-iiot-inject.html ├── opcua-iiot-inject.ts ├── opcua-iiot-listener.html ├── opcua-iiot-listener.ts ├── opcua-iiot-method-caller.html ├── opcua-iiot-method-caller.ts ├── opcua-iiot-node.html ├── opcua-iiot-node.ts ├── opcua-iiot-read.html ├── opcua-iiot-read.ts ├── opcua-iiot-response.html ├── opcua-iiot-response.ts ├── opcua-iiot-result-filter.html ├── opcua-iiot-result-filter.ts ├── opcua-iiot-server-aso.html ├── opcua-iiot-server-aso.ts ├── opcua-iiot-server-cmd.html ├── opcua-iiot-server-cmd.ts ├── opcua-iiot-server.html ├── opcua-iiot-server.ts ├── opcua-iiot-write.html ├── opcua-iiot-write.ts ├── public │ ├── vendor │ │ ├── harting │ │ │ ├── 10_di.xml │ │ │ ├── 20_autoid.xml │ │ │ └── 30_aim.xml │ │ └── opc-foundation │ │ │ ├── binary │ │ │ ├── OPC.ISA95.Types.bsd.xml │ │ │ ├── Opc.Ua.Adi.Types.bsd.xml │ │ │ ├── Opc.Ua.Di.Types.bsd.xml │ │ │ ├── Opc.Ua.Gds.Types.bsd.xml │ │ │ └── Opc.Ua.Types.bsd.xml │ │ │ ├── csv │ │ │ ├── AttributeIds.csv │ │ │ ├── NodeIds.csv │ │ │ ├── Opc.Ua.StatusCodes.csv │ │ │ ├── OpcUaGdsModel.csv │ │ │ ├── ServerCapabilities.csv │ │ │ ├── ServerCapabilityIdentifiers.csv │ │ │ ├── StatusCode.csv │ │ │ └── StatusCodes.csv │ │ │ ├── schema │ │ │ ├── OPC.ISA95.Types.xsd │ │ │ ├── OPCBinarySchema.xsd │ │ │ ├── Opc.Ua.Types.xsd │ │ │ ├── SecuredApplication.xsd │ │ │ └── UANodeSet.xsd │ │ │ └── xml │ │ │ ├── OPC.ISA95.NodeSet2.Nov52013.xml │ │ │ ├── Opc.ISA95.NodeSet2.xml │ │ │ ├── Opc.Ua.Adi.NodeSet2.xml │ │ │ ├── Opc.Ua.Adi.Types.bsd.xml │ │ │ ├── Opc.Ua.Adi.Types.xsd │ │ │ ├── Opc.Ua.Di.NodeSet2.xml │ │ │ ├── Opc.Ua.Di.Types.bsd.xml │ │ │ ├── Opc.Ua.Di.Types.xsd │ │ │ ├── Opc.Ua.Gds.NodeSet.xml │ │ │ ├── Opc.Ua.Gds.NodeSet2.xml │ │ │ ├── Opc.Ua.Gds.Types.bsd.xml │ │ │ ├── Opc.Ua.Gds.Types.xsd │ │ │ ├── Opc.Ua.NodeSet-2.xml │ │ │ ├── Opc.Ua.NodeSet-3.xml │ │ │ ├── Opc.Ua.NodeSet.xml │ │ │ ├── Opc.Ua.NodeSet2.Part10.xml │ │ │ ├── Opc.Ua.NodeSet2.Part11.xml │ │ │ ├── Opc.Ua.NodeSet2.Part12.xml │ │ │ ├── Opc.Ua.NodeSet2.Part13.xml │ │ │ ├── Opc.Ua.NodeSet2.Part3.xml │ │ │ ├── Opc.Ua.NodeSet2.Part4.xml │ │ │ ├── Opc.Ua.NodeSet2.Part5.xml │ │ │ ├── Opc.Ua.NodeSet2.Part8.xml │ │ │ ├── Opc.Ua.NodeSet2.Part9.xml │ │ │ ├── Opc.Ua.NodeSet2.Part999.xml │ │ │ ├── Opc.Ua.NodeSet2.xml │ │ │ └── Opc.Ua.PredefinedNodes.xml │ └── xmlsets.txt └── types │ ├── assertion.ts │ ├── helpers.ts │ ├── payloads.ts │ └── placeholders.ts ├── test ├── .gitignore ├── core │ ├── opcua-iiot-core-browser.test.js │ ├── opcua-iiot-core-client.test.js │ ├── opcua-iiot-core-connector.test.js │ ├── opcua-iiot-core-listener.test.js │ ├── opcua-iiot-core-method.test.js │ ├── opcua-iiot-core-server.test.js │ └── opcua-iiot-core.test.js ├── e2e │ ├── flows │ │ ├── browser-e2e-flows.js │ │ ├── browser-recursive-e2e-flows.js │ │ ├── connector-e2e-flows.js │ │ ├── crawler-e2e-flows.js │ │ ├── event-listener-e2e-flows.js │ │ ├── flex-connector-e2e-flows.js │ │ ├── flex-server-e2e-flows.js │ │ ├── listener-browser-e2e-flows.js │ │ ├── listener-e2e-flows.js │ │ ├── method-caller-e2e-flow.js │ │ ├── node-e2e-flows.js │ │ ├── read-e2e-flows.js │ │ ├── response-e2e-flows.js │ │ ├── result-filter-e2e-flows.js │ │ ├── server-aso-e2e-flows.js │ │ ├── server-cmd-e2e-flows.js │ │ └── write-e2e-flows.js │ ├── opcua-iiot-browser-e2e.test.js │ ├── opcua-iiot-browser-recursive-e2e.test.js │ ├── opcua-iiot-connector-e2e.test.js │ ├── opcua-iiot-crawler-e2e.test.js │ ├── opcua-iiot-event-listener-e2e.test.js │ ├── opcua-iiot-flex-connector-e2e.test.js │ ├── opcua-iiot-flex-server.test.js │ ├── opcua-iiot-listener-browser-e2e.test.js │ ├── opcua-iiot-listener-e2e.test.js │ ├── opcua-iiot-method-caller-e2e.test.js │ ├── opcua-iiot-node-e2e.test.js │ ├── opcua-iiot-read-e2e.test.js │ ├── opcua-iiot-response-e2e.test.js │ ├── opcua-iiot-result-filter-e2e.test.js │ ├── opcua-iiot-server-aso-e2e.test.js │ ├── opcua-iiot-server-cmd-e2e.test.js │ └── opcua-iiot-write-e2e.test.js ├── helper │ ├── receive.js │ └── test-helper-extensions.js └── units │ ├── flows │ ├── browser-flows.js │ ├── connector-flows.js │ ├── crawler-flows.js │ ├── discovery-flows.js │ ├── event-flows.js │ ├── flex-connector-flows.js │ ├── flex-server-flows.js │ ├── inject-flows.js │ ├── listener-flows.js │ ├── method-caller-flows.js │ ├── node-flows.js │ ├── read-flows.js │ ├── response-flows.js │ ├── response-status-flows.js │ ├── result-filter-flows.js │ ├── server-aso-flows.js │ ├── server-cmd-flows.js │ ├── server-flows.js │ └── write-flows.js │ ├── opcua-iiot-browser.test.js │ ├── opcua-iiot-connector.test.js │ ├── opcua-iiot-crawler.test.js │ ├── opcua-iiot-discovery.test.js │ ├── opcua-iiot-event.test.js │ ├── opcua-iiot-flex-connector.test.js │ ├── opcua-iiot-flex-server.test.js │ ├── opcua-iiot-inject.test.js │ ├── opcua-iiot-listener.test.js │ ├── opcua-iiot-method-caller.test.js │ ├── opcua-iiot-node.test.js │ ├── opcua-iiot-read.test.js │ ├── opcua-iiot-reponse-status.test.js │ ├── opcua-iiot-response.test.js │ ├── opcua-iiot-result-filter.test.js │ ├── opcua-iiot-server-aso.test.js │ ├── opcua-iiot-server-cmd.test.js │ ├── opcua-iiot-server.test.js │ └── opcua-iiot-write.test.js └── tsconfig.json /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to node-red-contrib-iiot-opcua 2 | 3 | As in Node-RED we have some guidelines for the OPC UA IIoT contribution package. 4 | We welcome contributions, but request you follow these guidelines. 5 | 6 | - [Coding rules](#coding-rules) 7 | - [Raising issues](#raising-issues) 8 | - [Feature requests](#feature-requests) 9 | - [Pull-Requests](#pull-requests) 10 | 11 | ## How to start 12 | 13 | 1. install node-red global without the IIoT OPC UA package 14 | 2. run the shell script: ./clean.sh 15 | 3. run command: npm run dev-link 16 | 4. start node-red and see the IIoT OPC UA nodes for testing 17 | 5. start your test and develop life cycle 18 | 6. Create a new version via 'npm run release & npm run rewrite-changelog' 19 | 20 | You can unlink via 'npm run dev-unlink' and test via 'npm test'. 21 | Check the coverage state before and after your development via 'npm run coverage'! 22 | 23 | ## Coding rules 24 | 25 | 1. Code for one another, and use tools to perform mechanical optimizations. 26 | 2. Keep it simple; compactness != succinctness. 27 | 3. Just because you can doesn’t mean you should. 28 | 4. Utilize familiar paradigms and patterns. 29 | 5. Consistency is king. 30 | 6. Lay good foundations. Be mindful of evolutionary complexity. 31 | 7. Write tests and check the coverage of your code. 32 | 33 | ## Raising issues 34 | 35 | Please raise any bug reports on the relevant project's issue tracker. 36 | Be sure to search the list to see if your issue has already been raised. 37 | 38 | A good bug report is one that make it easy for us to understand what you were 39 | trying to do and what went wrong. 40 | 41 | Provide as much context as possible so we can try to recreate the issue. 42 | If possible, include the relevant part of your flow. To do this, select the 43 | relevant nodes, press Ctrl-E and copy the flow data from the Export dialog. 44 | 45 | At a minimum, please include: 46 | 47 | - Version of node.js? (should be >= 16) 48 | - Version of Node-RED? (should be >= 3) 49 | - Version of node-red-contrib-iiot-opcua? (should be >= 4) 50 | 51 | - What is your platform? (Linux, macOS, ...) 52 | - What does `DEBUG=opcuaIIoT:* node-red -v` say? (log files are welcome) 53 | 54 | - What is your platform? (Linux, macOS, ...) 55 | - What does `DEBUG=opcuaIIoT:* node-red -v` say? (log files are welcome) 56 | 57 | ## Feature requests 58 | 59 | For feature requests, please raise them on the relevant project's issue tracker. 60 | 61 | ## Pull-Requests 62 | 63 | Please, send your PRs to the **develop** branch! 64 | If you want to raise a pull-request with a new feature, or a refactoring 65 | of existing code, it may well get rejected if you haven't discussed it on the relevant project's issue tracker first. 66 | 67 | ### Coding standards 68 | 69 | Please ensure you follow the coding standards used through-out the existing code base. 70 | 71 | Some basic rules include: 72 | 73 | - all files must have the BSD 3-Clause license in the header. 74 | - indent with 2-spaces, no tabs. No arguments. 75 | - follow ES6 or above coding standards 76 | - follow standard.js coding standards 77 | 78 | Some style suggestions to follow when possible: 79 | 80 | - Arrow functions should be prioritized over the function keyword. 81 | - Assign types as narrowly as possible 82 | - Using the `any` type should be avoided unless a variable's type truly doesn't matter. 83 | - If it is very difficult to deduce an object's type, use the `Todo` type from `src/types/placeholders.ts` instead 84 | of `any`. 85 | - When writing new code, use functional programming principles when possible. 86 | - Modifying objects makes the code more complex and harder to read, so favor creating a new object instead. 87 | - A lot of the existing code doesn't follow this, so use your best judgement. 88 | 89 | ### Update 90 | 91 | ### Upgrade 92 | 93 | ### Return of Code Investment 94 | 95 | Please, make pull requests! 96 | If you are not sure how to do this, then ask for help please! 97 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | custom: ["https://github.com/sponsors/BiancoRoyal", "https://bianco-royal.space/supporter/", "https://opencollective.com/node-red-contrib-modbus", "https://www.noderedplus.de/"] 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bugs.yaml: -------------------------------------------------------------------------------- 1 | name: "Bug Report" 2 | description: Report a bug or unexpected behavior encountered. 3 | labels: 4 | - bug 5 | body: 6 | - type: "input" 7 | id: "version" 8 | attributes: 9 | label: Which node-red-contrib-iiot-opcua version are you using? 10 | description: | 11 | Please check the `package.json` file in your Node-RED data directory (default: `$HOME/.node-red/`). 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: descriptions 16 | attributes: 17 | label: What happened? 18 | description: | 19 | Please provide a clear and thorough description of what happened. 20 | validations: 21 | required: true 22 | - type: dropdown 23 | id: server 24 | attributes: 25 | label: Server 26 | description: | 27 | Which type of OPC UA Server are you connected to? 28 | multiple: false 29 | options: 30 | - OPCUA-IIoT-Server Node 31 | - OPCUA-IIoT-Flex-Server Node (Please attach AddressSpaceScript to the next section) 32 | - Other/External server 33 | - None/This is related to a node that doesn't connect to a server 34 | validations: 35 | required: true 36 | - type: textarea 37 | id: reproduction 38 | attributes: 39 | label: How can this be reproduced? 40 | description: | 41 | Please provide a step-by-step description of how to reproduce this behavior. 42 | Please attach relevant flows as a `.json` file. (In the top-right menu, click `Export`) 43 | validations: 44 | required: true 45 | - type: textarea 46 | id: expected 47 | attributes: 48 | label: What did you expect to happen? 49 | description: | 50 | Describe the expected behavior and how the actual behavior differed. 51 | validations: 52 | required: false 53 | 54 | - type: textarea 55 | id: other 56 | attributes: 57 | label: Other Information 58 | description: | 59 | Please provide any other information you feel is relevant, such as Node-RED or NodeJS versions. 60 | validations: 61 | required: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/features.yaml: -------------------------------------------------------------------------------- 1 | name: "Feature Request" 2 | description: Request a new feature 3 | labels: 4 | - feature 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Describe the requested feature 10 | description: | 11 | Describe the feature you would like to see added. 12 | Feel free to attach diagrams or screenshots. 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: motivation 17 | attributes: 18 | label: Motivation 19 | description: | 20 | Describe why you would like to see this feature or the use-case for the feature. 21 | validations: 22 | required: true 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | **Which issues are addressed by this Pull Request?** 9 | 10 | **What does this Pull Request change?** 11 | 12 | **Does this Pull Request introduce any potentially breaking changes?** 13 | 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | target-branch: "develop" 6 | schedule: 7 | interval: "weekly" 8 | allow: 9 | - dependency-type: "production" 10 | ignore: 11 | - dependency-name: "node-opcua*" -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and publish 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | push: 7 | branches: 8 | - develop 9 | - development 10 | - alpha 11 | - beta 12 | - master 13 | - main 14 | jobs: 15 | build: 16 | runs-on: ubuntu-20.04 17 | strategy: 18 | matrix: 19 | node: [14, 16, 18] 20 | steps: 21 | - uses: actions/checkout@v3 22 | - uses: actions/setup-node@v3 23 | with: 24 | node-version: ${{ matrix.node }} 25 | - run: npm install 26 | - run: npm run build 27 | - run: tar -czvf distrib.tar.gz opcuaIIoT examples package.json create_certificates.js docs index.js README.md CHANGELOG.md 28 | - uses: actions/upload-artifact@v3 29 | with: 30 | name: build-folders 31 | path: distrib.tar.gz 32 | 33 | publish: 34 | runs-on: ubuntu-20.04 35 | needs: build 36 | if: github.ref == 'refs/heads/master' 37 | steps: 38 | - uses: actions/checkout@v3 39 | - uses: actions/setup-node@v3 40 | with: 41 | node-version: 16 42 | - run: npm install 43 | - uses: JS-DevTools/npm-publish@v1 44 | with: 45 | token: ${{ secrets.NPM_TOKEN }} 46 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: 17 | - main 18 | - master 19 | - development 20 | - develop 21 | pull_request: 22 | # The branches below must be a subset of the branches above 23 | branches: 24 | - develop 25 | schedule: 26 | - cron: '39 20 * * 0' 27 | 28 | jobs: 29 | analyze: 30 | name: Analyze 31 | runs-on: ubuntu-latest 32 | permissions: 33 | actions: read 34 | contents: read 35 | security-events: write 36 | 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | language: [ 'javascript' ] 41 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 42 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 43 | 44 | steps: 45 | - name: Checkout repository 46 | uses: actions/checkout@v2 47 | 48 | # Initializes the CodeQL tools for scanning. 49 | - name: Initialize CodeQL 50 | uses: github/codeql-action/init@v2 51 | with: 52 | languages: ${{ matrix.language }} 53 | # If you wish to specify custom queries, you can do so here or in a config file. 54 | # By default, queries listed here will override any specified in a config file. 55 | # Prefix the list here with "+" to use these queries and those in the config file. 56 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 57 | 58 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 59 | # If this step fails, then you should remove it and run the build manually (see below) 60 | - name: Autobuild 61 | uses: github/codeql-action/autobuild@v2 62 | 63 | # ℹ️ Command-line programs to run using the OS shell. 64 | # 📚 https://git.io/JvXDl 65 | 66 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 67 | # and modify them (or add more) to build your code if your project 68 | # uses a compiled language 69 | 70 | #- run: | 71 | # make bootstrap 72 | # make release 73 | 74 | - name: Perform CodeQL Analysis 75 | uses: github/codeql-action/analyze@v2 76 | -------------------------------------------------------------------------------- /.github/workflows/greet-collaborators.yml: -------------------------------------------------------------------------------- 1 | name: "Greet Contributors" 2 | on: 3 | pull_request: 4 | types: [opened, synchronize] 5 | 6 | jobs: 7 | GreetCommitter: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: "Greet Contributor" 11 | uses: ibakshay/greet-contributors-action@v3 12 | env: 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/snyk-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "Snyk Vulnerability Analysis" 2 | on: 3 | push: 4 | branches: 5 | - 6-additional-github-actions 6 | - main 7 | pull_request: 8 | types: 9 | - synchronize 10 | - opened 11 | jobs: 12 | snyk: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Run Snyk vulnerability scan 17 | uses: snyk/actions/node@master 18 | env: 19 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 20 | with: 21 | command: monitor 22 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v4 11 | with: 12 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. It will be closed in 15 days, but can be saved by removing the stale label or commenting.' 13 | days-before-stale: 60 14 | days-before-close: 15 -------------------------------------------------------------------------------- /.github/workflows/summary.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: "Generate Summary" 5 | 6 | on: 7 | - push 8 | - pull_request 9 | 10 | jobs: 11 | tests: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | node-version: 16 | - 12 17 | - 14 18 | - 16 19 | - 18 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | cache: 'npm' 28 | - run: npm install 29 | - run: npm run summary 30 | - run: echo Test results for NodeJS version ${{ matrix.node-version }} >> $GITHUB_STEP_SUMMARY 31 | - run: cat summary.md >> $GITHUB_STEP_SUMMARY 32 | - uses: actions/upload-artifact@v3 33 | with: 34 | name: badges 35 | path: badges 36 | badgeUpdate: 37 | name: "Update badges" 38 | runs-on: ubuntu-latest 39 | needs: tests 40 | if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' 41 | steps: 42 | - uses: actions/checkout@v3 43 | - uses: actions/download-artifact@v3 44 | with: 45 | name: badges 46 | path: badges 47 | - run: | 48 | git config user.name "$(git log -n 1 --pretty=format:%an)" 49 | git config user.email "$(git log -n 1 --pretty=format:%ae)" 50 | - run: git add badges 51 | - run: git commit -m "Automatically update coverage badges" 52 | - run: git push --force 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | scripts/out* 2 | 3 | .idea/ 4 | *.iml 5 | code/ 6 | node_modules/ 7 | opcuaIIoT/ 8 | *leifert* 9 | error*.txt 10 | .DS_Store 11 | maps/ 12 | docs/gen/ 13 | coverage/ 14 | .coverage/ 15 | pki/ 16 | .vscode/ 17 | jcoverage/ 18 | 19 | \.jsinspect-results\.json 20 | 21 | certificates/ 22 | 23 | dist/ 24 | 25 | *.log 26 | *.bak -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: node:14 2 | 3 | before_script: 4 | - npm install 5 | stages: 6 | - test 7 | npm_test: 8 | stage: test 9 | script: 10 | - npm run build 11 | - echo "test TBD" # npm test 12 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | verbose: false 2 | instrumentation: 3 | globals: ['expect'] 4 | root: ./src 5 | default-excludes: true 6 | excludes: ['src/public'] 7 | include-all-sources: true 8 | include-pid: false 9 | es-modules: true 10 | ignoreLeaks: false 11 | ui: bdd 12 | embed-source: false 13 | variable: __coverage__ 14 | compact: true 15 | preserve-comments: false 16 | complete-copy: false 17 | save-baseline: false 18 | baseline-file: ./coverage/coverage-baseline.json 19 | reporting: 20 | print: both 21 | reports: 22 | - lcov 23 | - html 24 | - cobertura 25 | - clover 26 | dir: ./coverage 27 | watermarks: 28 | statements: [50, 80] 29 | lines: [50, 80] 30 | functions: [50, 80] 31 | branches: [50, 80] 32 | hooks: 33 | hook-run-in-context: false 34 | post-require-hook: null 35 | handle-sigint: false 36 | check: 37 | global: 38 | statements: 0 39 | lines: 0 40 | branches: 0 41 | functions: 0 42 | excludes: [] 43 | each: 44 | statements: 0 45 | lines: 0 46 | branches: 0 47 | functions: 0 48 | excludes: [] -------------------------------------------------------------------------------- /.jsinspectrc: -------------------------------------------------------------------------------- 1 | { 2 | "threshold": 50, 3 | "identifiers": true, 4 | "literals": true, 5 | "color": true, 6 | "minInstances": 2, 7 | "ignore": "test/|spec|mock≤|public/", 8 | "reporter": "json", 9 | "truncate": 100 10 | } 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git 2 | .idea 3 | test 4 | code/ 5 | node_modules 6 | .gitignore 7 | *.iml 8 | Example.png 9 | *EXAMPLE.png 10 | *.conf.js 11 | *.log 12 | maps/ 13 | pki/ 14 | .vscode/ 15 | jcoverage/ 16 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.19.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | matrix: 3 | include: 4 | - node_js: "lts/*" 5 | - node_js: 18 6 | - node_js: 16 7 | - node_js: 14 8 | allow_failures: 9 | - node_js: 16 10 | - node_js: 18 11 | install: 12 | - npm install 13 | - npm install --only=dev 14 | script: 15 | - npm run coverage:ci 16 | - npm run coverage:upload 17 | cache: 18 | directories: 19 | - "pki" 20 | branches: 21 | only: 22 | - master 23 | - develop 24 | - LTS 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 4 | Copyright (c) 2016,2017,2018,2019,2020,2021,2022 Klaus Landsdorf (https://bianco-royal.space/) 5 | Copyright (c) 2015, 2016, Mika Karaila, Valmet Automation Inc. (node-red-contrib-opcua) 6 | All rights reserved. 7 | node-red-contrib-iiot-opcua 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | * Neither the name of the copyright holder nor the names of its 20 | contributors may be used to endorse or promote products derived from 21 | this software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## OPC UA IIoT for Node-RED 2 | 3 | ## Known Issues 4 | 5 | * Result Filter does not filter correctly in every case (Will be fixed in v5.x) 6 | * Read after browse in browse example does not work 7 | * No output and no error 8 | * Browse-Listener and grouped-listener example top browse/listen will throw following error 9 | 10 | ``` 11 | Error: encodeObject Error: [Error] Value: { 12 | payload: 'Monitored Item Error', 13 | monitoredItem: ClientMonitoredItemImpl { 14 | _events: { 15 | initialized: [Function (anonymous)], 16 | changed: [Function (anonymous)], 17 | err: [Function (anonymous)], 18 | terminated: [Function (anonymous)] 19 | }, 20 | _eventsCount: 4, 21 | _maxListeners: undefined, 22 | statusCode: ConstantStatusCode { 23 | _value: 2150957056, 24 | _description: 'The attribute is not supported for the specified Node.', 25 | _name: 'BadAttributeIdInvalid' 26 | }, 27 | subscription: ClientSubscriptionImpl { 28 | _events: [Object], 29 | _eventsCount: 4, 30 | _maxListeners: undefined, 31 | monitoredItemGroups: [], 32 | timeoutHint: 14000, 33 | _nextClientHandle: 26, 34 | publishEngine: [ClientSidePublishEngine], 35 | lastSequenceNumber: 10, 36 | publishingInterval: 200, 37 | lifetimeCount: 18000, 38 | maxKeepAliveCount: 60, 39 | maxNotificationsPerPublish: 100, 40 | publishingEnabled: true, 41 | priority: 10, 42 | ``` 43 | Removing the catch node will bring the following error messages 44 | ``` 45 | "{"name":"Error","message":"ns=0;i=2253: BadAttributeIdInvalid (0x80350000)"}" 46 | ``` 47 | Maybe that happens because the browser browses the root object recursively and returns also 48 | nodes of the server that aren't allowed to be listened 49 | * Event Listener monitoring the basic opc ua events will always return an empty array of values 50 | * Discovered following error: Expected data is unavailable for the requested time range due to an 51 | un-mounted volume, an off-line archive or tape, or similar reason for temporary unavailability. 52 | (Will be fixed in v5.x) 53 | 54 | 55 | ### Features 56 | 57 | #### ASO and Servers 58 | 59 | * adding variables 60 | * adding folders/objects 61 | * adding complex structures 62 | * adding arrays 63 | * adding methods 64 | 65 | ---- 66 | 67 | ### Worked before 68 | 69 | #### Listener 70 | 71 | * Events cannot be monitored (error node-opcua maxAge) 72 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-modernist -------------------------------------------------------------------------------- /badges/badge-branches.svg: -------------------------------------------------------------------------------- 1 | Coverage:branches: 32.5%Coverage:branches32.5% -------------------------------------------------------------------------------- /badges/badge-functions.svg: -------------------------------------------------------------------------------- 1 | Coverage:functions: 43.86%Coverage:functions43.86% -------------------------------------------------------------------------------- /badges/badge-lines.svg: -------------------------------------------------------------------------------- 1 | Coverage:lines: 51.69%Coverage:lines51.69% -------------------------------------------------------------------------------- /badges/badge-statements.svg: -------------------------------------------------------------------------------- 1 | Coverage:statements: 51.78%Coverage:statements51.78% -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | node -v 4 | 5 | rm -rf node_modules/ 6 | 7 | rm -rf opcuaIIoT/ 8 | 9 | rm -rf code/ 10 | 11 | rm -rf certificates/ 12 | 13 | rm -rf coverage/ 14 | 15 | rm -rf docs/gen 16 | 17 | rm package-lock.json 18 | 19 | npm cache verify 20 | 21 | npm install 22 | 23 | npm i --only=dev 24 | 25 | npm run test:clearCache 26 | 27 | npm test 28 | 29 | npm run coverage 30 | 31 | npm run build 32 | 33 | npm run rewrite-changelog 34 | 35 | node -v 36 | 37 | npm audit 38 | -------------------------------------------------------------------------------- /create_certificates.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('node-opcua-pki/bin/crypto_create_CA') 3 | -------------------------------------------------------------------------------- /examples/client/event-listener.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "b3974c60.a56ab8", 4 | "type": "tab", 5 | "label": "IIoT OPC UA Events", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "d9bca212.ee7d3", 11 | "type": "debug", 12 | "z": "b3974c60.a56ab8", 13 | "name": "", 14 | "active": true, 15 | "tosidebar": true, 16 | "console": false, 17 | "tostatus": false, 18 | "complete": "true", 19 | "targetType": "full", 20 | "statusVal": "", 21 | "statusType": "auto", 22 | "x": 730, 23 | "y": 120, 24 | "wires": [] 25 | }, 26 | { 27 | "id": "cbc425f4.8b596", 28 | "type": "OPCUA-IIoT-Event", 29 | "z": "b3974c60.a56ab8", 30 | "eventType": "BaseEventType", 31 | "eventTypeLabel": "BaseEventType (i=2041)", 32 | "resultType": "basic", 33 | "queueSize": "1000", 34 | "usingListener": true, 35 | "name": "Base Events", 36 | "showStatusActivities": false, 37 | "showErrors": false, 38 | "x": 420, 39 | "y": 120, 40 | "wires": [ 41 | [ 42 | "d4e7bb7a.8ba54" 43 | ] 44 | ] 45 | }, 46 | { 47 | "id": "99ea348a.48cfd8", 48 | "type": "OPCUA-IIoT-Inject", 49 | "z": "b3974c60.a56ab8", 50 | "injectType": "listen", 51 | "payload": "200", 52 | "payloadType": "num", 53 | "topic": "", 54 | "repeat": "", 55 | "crontab": "", 56 | "once": true, 57 | "startDelay": "", 58 | "name": "listen with 200 ms", 59 | "addressSpaceItems": [ 60 | { 61 | "name": "Tanks", 62 | "nodeId": "ns=1;i=1000", 63 | "datatypeName": "" 64 | }, 65 | { 66 | "name": "Server", 67 | "nodeId": "ns=0;i=2253", 68 | "datatypeName": "" 69 | } 70 | ], 71 | "x": 230, 72 | "y": 120, 73 | "wires": [ 74 | [ 75 | "cbc425f4.8b596" 76 | ] 77 | ] 78 | }, 79 | { 80 | "id": "d4e7bb7a.8ba54", 81 | "type": "OPCUA-IIoT-Listener", 82 | "z": "b3974c60.a56ab8", 83 | "connector": "fe9e7411.128358", 84 | "action": "events", 85 | "queueSize": "100", 86 | "name": "", 87 | "topic": "", 88 | "justValue": true, 89 | "useGroupItems": false, 90 | "showStatusActivities": false, 91 | "showErrors": true, 92 | "x": 580, 93 | "y": 120, 94 | "wires": [ 95 | [ 96 | "d9bca212.ee7d3" 97 | ] 98 | ] 99 | }, 100 | { 101 | "id": "193736fe173d5219", 102 | "type": "comment", 103 | "z": "b3974c60.a56ab8", 104 | "name": "Use Demo Server Example", 105 | "info": "Please use the Demo Server example for this example to work.\nImport that with strg+i -> Examples -> opcuaIIoT -> Server -> Demo Server", 106 | "x": 250, 107 | "y": 62, 108 | "wires": [] 109 | }, 110 | { 111 | "id": "fe9e7411.128358", 112 | "type": "OPCUA-IIoT-Connector", 113 | "z": "b3974c60.a56ab8", 114 | "discoveryUrl": "", 115 | "endpoint": "opc.tcp://localhost:55388/", 116 | "endpointMustExist": false, 117 | "keepSessionAlive": true, 118 | "loginEnabled": false, 119 | "name": "LOCAL EVENT SERVER", 120 | "showErrors": false, 121 | "securityPolicy": "None", 122 | "securityMode": "None", 123 | "individualCerts": false, 124 | "publicCertificateFile": "", 125 | "privateKeyFile": "", 126 | "defaultSecureTokenLifetime": "", 127 | "autoSelectRightEndpoint": false, 128 | "strategyMaxRetry": "", 129 | "strategyInitialDelay": "", 130 | "strategyMaxDelay": "", 131 | "strategyRandomisationFactor": "", 132 | "requestedSessionTimeout": "", 133 | "connectionStartDelay": "", 134 | "reconnectDelay": "", 135 | "maxBadSessionRequests": "" 136 | } 137 | ] -------------------------------------------------------------------------------- /examples/client/read-write.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "f49a0646.6b54a8", 4 | "type": "tab", 5 | "label": "IIoT OPC UA Read Write", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "68b2953e.23fe94", 11 | "type": "OPCUA-IIoT-Write", 12 | "z": "f49a0646.6b54a8", 13 | "connector": "7316e0da.4324d", 14 | "name": "", 15 | "justValue": false, 16 | "showStatusActivities": false, 17 | "showErrors": true, 18 | "x": 550, 19 | "y": 80, 20 | "wires": [ 21 | [ 22 | "13fe0f0e.0ca0e9" 23 | ] 24 | ] 25 | }, 26 | { 27 | "id": "51907704.7ca4d", 28 | "type": "OPCUA-IIoT-Inject", 29 | "z": "f49a0646.6b54a8", 30 | "injectType": "read", 31 | "payload": "", 32 | "payloadType": "date", 33 | "topic": "", 34 | "repeat": "120", 35 | "crontab": "", 36 | "once": true, 37 | "startDelay": "", 38 | "name": "inject", 39 | "addressSpaceItems": [ 40 | { 41 | "name": "TestReadWrite", 42 | "nodeId": "ns=1;s=TestReadWrite", 43 | "datatypeName": "Double" 44 | } 45 | ], 46 | "x": 140, 47 | "y": 80, 48 | "wires": [ 49 | [ 50 | "d4817dc5.a09168" 51 | ] 52 | ] 53 | }, 54 | { 55 | "id": "d4817dc5.a09168", 56 | "type": "OPCUA-IIoT-Read", 57 | "z": "f49a0646.6b54a8", 58 | "attributeId": "13", 59 | "maxAge": 1, 60 | "depth": 1, 61 | "connector": "7316e0da.4324d", 62 | "name": "", 63 | "justValue": true, 64 | "showStatusActivities": false, 65 | "showErrors": true, 66 | "parseStrings": false, 67 | "historyDays": "", 68 | "x": 270, 69 | "y": 80, 70 | "wires": [ 71 | [ 72 | "30116e36.369d22" 73 | ] 74 | ] 75 | }, 76 | { 77 | "id": "13fe0f0e.0ca0e9", 78 | "type": "OPCUA-IIoT-Response", 79 | "z": "f49a0646.6b54a8", 80 | "name": "", 81 | "compressStructure": false, 82 | "showStatusActivities": false, 83 | "showErrors": false, 84 | "activateUnsetFilter": false, 85 | "activateFilters": false, 86 | "negateFilter": false, 87 | "filters": [], 88 | "x": 680, 89 | "y": 80, 90 | "wires": [ 91 | [ 92 | "afa379a1.804f18" 93 | ] 94 | ] 95 | }, 96 | { 97 | "id": "afa379a1.804f18", 98 | "type": "debug", 99 | "z": "f49a0646.6b54a8", 100 | "name": "", 101 | "active": true, 102 | "tosidebar": true, 103 | "console": false, 104 | "tostatus": false, 105 | "complete": "true", 106 | "targetType": "full", 107 | "statusVal": "", 108 | "statusType": "auto", 109 | "x": 810, 110 | "y": 80, 111 | "wires": [] 112 | }, 113 | { 114 | "id": "30116e36.369d22", 115 | "type": "function", 116 | "z": "f49a0646.6b54a8", 117 | "name": "toWriteMsg", 118 | "func": "msg.payload.nodetype = 'inject';\nmsg.payload.injectType = 'write'\nmsg.payload.valuesToWrite = [16.04]\nreturn msg;", 119 | "outputs": 1, 120 | "noerr": 0, 121 | "initialize": "", 122 | "finalize": "", 123 | "libs": [], 124 | "x": 410, 125 | "y": 80, 126 | "wires": [ 127 | [ 128 | "68b2953e.23fe94" 129 | ] 130 | ] 131 | }, 132 | { 133 | "id": "f1ed16b147240f8d", 134 | "type": "comment", 135 | "z": "f49a0646.6b54a8", 136 | "name": "Use Demo Server Example!", 137 | "info": "Import Demo Server example with\nstrg+i -> Example -> opcuaIIoT -> Server -> Demo Server", 138 | "x": 200, 139 | "y": 32, 140 | "wires": [] 141 | }, 142 | { 143 | "id": "7316e0da.4324d", 144 | "type": "OPCUA-IIoT-Connector", 145 | "z": "f49a0646.6b54a8", 146 | "discoveryUrl": "", 147 | "endpoint": "opc.tcp://localhost:55388", 148 | "endpointMustExist": false, 149 | "keepSessionAlive": true, 150 | "loginEnabled": false, 151 | "name": "LOCAL DEMO SERVER", 152 | "showErrors": false, 153 | "securityPolicy": "None", 154 | "securityMode": "None", 155 | "individualCerts": false, 156 | "publicCertificateFile": "", 157 | "privateKeyFile": "", 158 | "defaultSecureTokenLifetime": "60000", 159 | "autoSelectRightEndpoint": false, 160 | "strategyMaxRetry": "", 161 | "strategyInitialDelay": "", 162 | "strategyMaxDelay": "", 163 | "strategyRandomisationFactor": "", 164 | "requestedSessionTimeout": "", 165 | "connectionStartDelay": "", 166 | "reconnectDelay": "", 167 | "maxBadSessionRequests": "" 168 | } 169 | ] -------------------------------------------------------------------------------- /examples/server/server-ISA95.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "ade2bb45.1c9a2", 4 | "type": "tab", 5 | "label": "IIoT OPC UA Server ISA95", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "2b0a9d8f.fda952", 11 | "type": "comment", 12 | "z": "ade2bb45.1c9a2", 13 | "name": "information", 14 | "info": "Clients to browse\n\nUA-Expert: https://www.unified-automation.com/products/development-tools/uaexpert.html\nOPCUA-Commander: https://github.com/node-opcua/opcua-commander\nNode-RED: use the browse examples to start browsing the server via Node-RED\n\nHappy wiring!\nKlaus Landsdorf\nhttp://bianco-royal.de/", 15 | "x": 210, 16 | "y": 60, 17 | "wires": [] 18 | }, 19 | { 20 | "id": "4088e400.34f28c", 21 | "type": "comment", 22 | "z": "ade2bb45.1c9a2", 23 | "name": "ISA95", 24 | "info": "Some links\nhttps://opcfoundation.org/markets-collaboration/isa-95/\n\n\nhttps://opcfoundation.org/developer-tools/specifications-unified-architecture/isa-95-common-object-model/\n\n56 min video by Paul Hunkar\n“OPC UA for ISA-95 Common Object Model Companion Specification”\nhttps://youtu.be/OobhzbQoUnA", 25 | "x": 350, 26 | "y": 60, 27 | "wires": [] 28 | }, 29 | { 30 | "id": "cd0b3a6a.197f18", 31 | "type": "OPCUA-IIoT-Server", 32 | "z": "ade2bb45.1c9a2", 33 | "port": "55395", 34 | "endpoint": "", 35 | "acceptExternalCommands": true, 36 | "maxAllowedSessionNumber": "", 37 | "maxConnectionsPerEndpoint": "", 38 | "maxAllowedSubscriptionNumber": "", 39 | "alternateHostname": "", 40 | "name": "ISA95 Server", 41 | "showStatusActivities": false, 42 | "showErrors": true, 43 | "asoDemo": true, 44 | "allowAnonymous": true, 45 | "individualCerts": false, 46 | "isAuditing": false, 47 | "serverDiscovery": true, 48 | "users": [], 49 | "xmlsets": [ 50 | { 51 | "name": "ISA95", 52 | "path": "public/vendor/opc-foundation/xml/Opc.ISA95.NodeSet2.xml" 53 | } 54 | ], 55 | "publicCertificateFile": "", 56 | "privateCertificateFile": "", 57 | "registerServerMethod": "1", 58 | "discoveryServerEndpointUrl": "", 59 | "capabilitiesForMDNS": "", 60 | "maxNodesPerRead": "", 61 | "maxNodesPerBrowse": "", 62 | "delayToClose": "", 63 | "x": 200, 64 | "y": 100, 65 | "wires": [ 66 | [] 67 | ] 68 | } 69 | ] -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | The BSD 3-Clause License 3 | 4 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 5 | All rights reserved. 6 | node-red-contrib-iiot-opcua 7 | */ 8 | 9 | 'use strict' 10 | 11 | const gulp = require('gulp') 12 | const { series, parallel } = require('gulp') 13 | const htmlmin = require('gulp-htmlmin') 14 | const jsdoc = require('gulp-jsdoc3') 15 | const clean = require('gulp-clean') 16 | const sourcemaps = require('gulp-sourcemaps') 17 | const changelog = require('gulp-conventional-changelog') 18 | 19 | function icons () { 20 | return gulp.src('src/icons/**/*').pipe(gulp.dest('opcuaIIoT/icons')) 21 | } 22 | 23 | function docIcons () { 24 | return gulp.src('src/icons/**/*').pipe(gulp.dest('docs/gen/icons')) 25 | } 26 | 27 | function docImages () { 28 | return gulp.src('images/**/*').pipe(gulp.dest('docs/gen/images')) 29 | } 30 | 31 | function locale () { 32 | return gulp.src('src/locales/**/*').pipe(gulp.dest('opcuaIIoT/locales')) 33 | } 34 | 35 | function publics () { 36 | return gulp.src('src/public/**/*').pipe(gulp.dest('opcuaIIoT/public')) 37 | } 38 | 39 | function maps () { 40 | return gulp.src('maps/**/*').pipe(gulp.dest('opcuaIIoT/maps')) 41 | } 42 | 43 | function wipe () { 44 | return gulp.src(['opcuaIIoT', 'docs/gen', 'maps', 'code', 'coverage', 'jcoverage', 'suite/jcoverage', 'pki', 'suite/pki', 'test/pki'], { allowEmpty: true }) 45 | .pipe(clean({ force: true })) 46 | } 47 | 48 | function changelogUpdate () { 49 | return gulp.src('CHANGELOG.md') 50 | .pipe(changelog({ 51 | // conventional-changelog options go here 52 | preset: 'angular', 53 | releaseCount: 0 54 | }, { 55 | // context goes here 56 | }, { 57 | // git-raw-commits options go here 58 | }, { 59 | // conventional-commits-parser options go here 60 | }, { 61 | // conventional-changelog-writer options go here 62 | })) 63 | .pipe(gulp.dest('./')) 64 | } 65 | 66 | function web () { 67 | return gulp.src('src/*.htm*') 68 | .pipe(htmlmin({ 69 | minifyJS: false, 70 | minifyCSS: true, 71 | minifyURLs: true, 72 | maxLineLength: 120, 73 | preserveLineBreaks: false, 74 | collapseWhitespace: true, 75 | collapseInlineTagWhitespace: true, 76 | conservativeCollapse: true, 77 | processScripts: ['text/x-red'], 78 | quoteCharacter: "'" 79 | })) 80 | .pipe(gulp.dest('opcuaIIoT')) 81 | } 82 | 83 | function ts () { 84 | const ts = require('gulp-typescript') 85 | const tsProject = ts.createProject('tsconfig.json') 86 | return gulp.src('src/**/*.ts') 87 | .pipe(sourcemaps.init({ loadMaps: true })) 88 | .pipe(tsProject()) 89 | .pipe(sourcemaps.write('')) 90 | .pipe(gulp.dest('opcuaIIoT')) 91 | } 92 | 93 | function doc (cb) { 94 | return gulp.src(['README.md', 'src/**/*.ts'], { read: false }) 95 | .pipe(jsdoc(cb)) 96 | } 97 | 98 | // function code () { 99 | // return gulp.src('src/**/*.ts') 100 | // .pipe(babel({ presets: ['@babel/env'] })) 101 | // .pipe(gulp.dest('code')) 102 | // } 103 | 104 | const docs = series(doc, docIcons, docImages) 105 | const build = series(wipe, web, ts, locale, publics, icons) 106 | 107 | exports.docs = docs 108 | exports.clean = wipe 109 | exports.build = build 110 | exports.changelog = changelogUpdate 111 | exports.publish = parallel(build, maps, docs, changelogUpdate) 112 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/icon.png -------------------------------------------------------------------------------- /images/icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/icon.psd -------------------------------------------------------------------------------- /images/listener-example-subv220.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/listener-example-subv220.png -------------------------------------------------------------------------------- /images/listener-flow-examplev220.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/listener-flow-examplev220.png -------------------------------------------------------------------------------- /images/logoISA88blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/logoISA88blue.png -------------------------------------------------------------------------------- /images/logoISA95blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/logoISA95blue.png -------------------------------------------------------------------------------- /images/logoISA95blue.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/logoISA95blue.xcf -------------------------------------------------------------------------------- /images/logoISA95blue2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/logoISA95blue2.png -------------------------------------------------------------------------------- /images/logoISA99blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/logoISA99blue.png -------------------------------------------------------------------------------- /images/logoRAMI40blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/logoRAMI40blue.png -------------------------------------------------------------------------------- /images/method-caller-examplev220.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/method-caller-examplev220.png -------------------------------------------------------------------------------- /images/opcua-icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua-icon-small.png -------------------------------------------------------------------------------- /images/opcua-icon-small.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua-icon-small.xcf -------------------------------------------------------------------------------- /images/opcua-iiot-logo-glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua-iiot-logo-glass.png -------------------------------------------------------------------------------- /images/opcua-iiot-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua-iiot-logo-white.png -------------------------------------------------------------------------------- /images/opcua-iiot-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua-iiot-logo.png -------------------------------------------------------------------------------- /images/opcua-iiot-logo64-glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua-iiot-logo64-glass.png -------------------------------------------------------------------------------- /images/opcua-iiot-logo64-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua-iiot-logo64-white.png -------------------------------------------------------------------------------- /images/opcua-iiot-logo64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua-iiot-logo64.png -------------------------------------------------------------------------------- /images/opcua-iiot-logo6464.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua-iiot-logo6464.png -------------------------------------------------------------------------------- /images/opcua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua.png -------------------------------------------------------------------------------- /images/opcua64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/opcua64.png -------------------------------------------------------------------------------- /images/read-examplev220.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/read-examplev220.png -------------------------------------------------------------------------------- /images/server-aso-type-examplev220.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/server-aso-type-examplev220.png -------------------------------------------------------------------------------- /images/wiki/ASOTestVariablesUAExpert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/ASOTestVariablesUAExpert.png -------------------------------------------------------------------------------- /images/wiki/Node-RED-Menu-Examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/Node-RED-Menu-Examples.png -------------------------------------------------------------------------------- /images/wiki/browser-flow3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/browser-flow3.png -------------------------------------------------------------------------------- /images/wiki/browser-listener-flow3-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/browser-listener-flow3-active.png -------------------------------------------------------------------------------- /images/wiki/browser-listener-flow3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/browser-listener-flow3.png -------------------------------------------------------------------------------- /images/wiki/browserResultMessageJustValue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/browserResultMessageJustValue.png -------------------------------------------------------------------------------- /images/wiki/browserResultMessageToListener.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/browserResultMessageToListener.png -------------------------------------------------------------------------------- /images/wiki/browserResultMessageToRead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/browserResultMessageToRead.png -------------------------------------------------------------------------------- /images/wiki/certificateTrustedUAExpert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/certificateTrustedUAExpert.png -------------------------------------------------------------------------------- /images/wiki/certificateUntrustedUAExpert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/certificateUntrustedUAExpert.png -------------------------------------------------------------------------------- /images/wiki/crawler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/crawler.png -------------------------------------------------------------------------------- /images/wiki/discoveryExampleUAExpert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/discoveryExampleUAExpert.png -------------------------------------------------------------------------------- /images/wiki/example-menu31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/example-menu31.png -------------------------------------------------------------------------------- /images/wiki/flex-connector-flow31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/flex-connector-flow31.png -------------------------------------------------------------------------------- /images/wiki/flexServerAddressSapceExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/flexServerAddressSapceExample.png -------------------------------------------------------------------------------- /images/wiki/flexServerAddressSapceExamplev3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/flexServerAddressSapceExamplev3.png -------------------------------------------------------------------------------- /images/wiki/iiot-nodes-v3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/iiot-nodes-v3.png -------------------------------------------------------------------------------- /images/wiki/injectMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/injectMessage.png -------------------------------------------------------------------------------- /images/wiki/listenerChangedMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/listenerChangedMessage.png -------------------------------------------------------------------------------- /images/wiki/listenerResultMessageChanged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/listenerResultMessageChanged.png -------------------------------------------------------------------------------- /images/wiki/method-call3-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/method-call3-active.png -------------------------------------------------------------------------------- /images/wiki/read-history3-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/read-history3-active.png -------------------------------------------------------------------------------- /images/wiki/read-write-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/read-write-flow.png -------------------------------------------------------------------------------- /images/wiki/readResultMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/readResultMessage.png -------------------------------------------------------------------------------- /images/wiki/server-aso-flow3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/server-aso-flow3.png -------------------------------------------------------------------------------- /images/wiki/write-flow3-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/write-flow3-active.png -------------------------------------------------------------------------------- /images/wiki/write-read-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/write-read-flow.png -------------------------------------------------------------------------------- /images/wiki/write-read-flow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/write-read-flow2.png -------------------------------------------------------------------------------- /images/wiki/write-read-flow3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/write-read-flow3.png -------------------------------------------------------------------------------- /images/wiki/writeInputMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/writeInputMessage.png -------------------------------------------------------------------------------- /images/wiki/writeMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/wiki/writeMessage.png -------------------------------------------------------------------------------- /images/write-examplev220.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/images/write-examplev220.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | The BSD 3-Clause License 3 | 4 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 5 | All rights reserved. 6 | node-red-contrib-iiot-opcua 7 | */ 8 | 'use strict' 9 | -------------------------------------------------------------------------------- /npm-update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | npm cache verify 4 | 5 | npm outdated --depth=0 6 | 7 | npm install 8 | -------------------------------------------------------------------------------- /npm-upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # this sh is to upgrade all package dependencies from NPM 4 | # you need to install before: npm i -g npm-check-updates 5 | 6 | rm package-lock.json 7 | 8 | npm cache verify 9 | 10 | npm outdated --depth=0 11 | 12 | ncu -u 13 | 14 | npm i 15 | 16 | npm i --only=dev 17 | 18 | npm install 19 | 20 | npm test 21 | 22 | npm run build 23 | -------------------------------------------------------------------------------- /scripts/JestOutputToSummary.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | const read = async (outfilepath) => { 5 | const result = new Promise((resolve, reject) => { 6 | fs.readFile(path.join(__dirname, outfilepath || 'out.json'), 'utf8', function (err, data) { 7 | if (err) { 8 | reject(err) 9 | return 10 | } 11 | resolve(JSON.parse(data)) 12 | }) 13 | } 14 | ) 15 | 16 | await result 17 | 18 | if (result instanceof Error) { 19 | console.log('Error reading output file: ' + result.message) 20 | process.exit(1) 21 | } 22 | 23 | return result 24 | } 25 | 26 | const write = async () => { 27 | const results = await read() 28 | 29 | const { 30 | numTotalTestSuites, 31 | numTotalTests, 32 | numPassedTestSuites, 33 | numPassedTests, 34 | success, 35 | testResults 36 | } = results 37 | 38 | if (!success) { 39 | console.log('Jest failed') 40 | process.exit(1) 41 | } 42 | 43 | writeMarkdown({ 44 | 'Test Suite Completion': (numPassedTestSuites / numTotalTestSuites * 100) + '% (' + numPassedTestSuites + ' / ' + numTotalTestSuites + ')', 45 | 'Test Completion': (numPassedTests / numTotalTests * 100) + '% (' + numPassedTests + ' / ' + numTotalTests + ')' 46 | }, 'Overall Results', ['Measure', 'Status']) 47 | 48 | const detailedResults = {} 49 | testResults.forEach((singleResult) => { 50 | const name = singleResult.name.split('/test/')[1] 51 | detailedResults[name] = singleResult.status 52 | }) 53 | 54 | writeMarkdown(detailedResults, 'Detailed Results ', ['Test', 'Status']) 55 | } 56 | 57 | const writeMarkdown = (obj, title, columns) => { 58 | if (title) console.log('### ' + title) 59 | 60 | const entries = (typeof obj[Object.keys(obj)[0]] === 'string' ? 1 : obj[Object.keys(obj)[0]].length || 1) + 1 61 | 62 | const header = [...Array(entries)].map((item, index) => { 63 | if (index <= columns.length) { 64 | return columns[index] 65 | } else { 66 | return ' ' 67 | } 68 | }) 69 | const seperator = [...Array(entries)].map((item) => ':---:') 70 | 71 | console.log() 72 | writeRow(header) 73 | writeRow(seperator) 74 | 75 | Object.keys(obj).forEach((key) => { 76 | if (typeof obj[key] !== 'string' && obj[key].length) { 77 | writeRow(key + obj[key]) 78 | } else { 79 | writeRow([key, obj[key]]) 80 | } 81 | }) 82 | console.log() 83 | } 84 | 85 | const writeRow = (array) => { 86 | const output = array.join(' | ') 87 | console.log('| ' + output + ' | ') 88 | } 89 | 90 | write() 91 | -------------------------------------------------------------------------------- /src/core/opcua-iiot-core-discovery.ts: -------------------------------------------------------------------------------- 1 | /** 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 6 | All rights reserved. 7 | node-red-contrib-iiot-opcua 8 | */ 9 | 'use strict' 10 | // SOURCE-MAP-REQUIRED 11 | 12 | import debug from 'debug' 13 | 14 | const internalDebugLog = debug('opcuaIIoT:discovery') // eslint-disable-line no-use-before-define 15 | const detailDebugLog = debug('opcuaIIoT:discovery:details') // eslint-disable-line no-use-before-define 16 | const DEFAULT_OPCUA_DISCOVERY_PORT = 4840 // eslint-disable-line no-use-before-define 17 | 18 | const coreDiscovery = { 19 | internalDebugLog, 20 | detailDebugLog, 21 | DEFAULT_OPCUA_DISCOVERY_PORT, 22 | } 23 | 24 | export default coreDiscovery 25 | -------------------------------------------------------------------------------- /src/core/opcua-iiot-core-filter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 6 | All rights reserved. 7 | node-red-contrib-iiot-opcua 8 | */ 9 | 'use strict' 10 | // SOURCE-MAP-REQUIRED 11 | 12 | 13 | import debug from 'debug'; 14 | 15 | const coreFilter = { 16 | internalDebugLog: debug('opcuaIIoT:filter:internal'), 17 | } 18 | 19 | export default coreFilter 20 | -------------------------------------------------------------------------------- /src/core/opcua-iiot-core-inject.ts: -------------------------------------------------------------------------------- 1 | /** 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 6 | All rights reserved. 7 | node-red-contrib-iiot-opcua 8 | */ 9 | 'use strict' 10 | // SOURCE-MAP-REQUIRED 11 | 12 | import debug from 'debug'; 13 | 14 | const internalDebugLog = debug('opcuaIIoT:inject') // eslint-disable-line no-use-before-define 15 | const detailDebugLog = debug('opcuaIIoT:inject:details') // eslint-disable-line no-use-before-define 16 | 17 | const coreInject = { 18 | internalDebugLog, 19 | detailDebugLog 20 | } 21 | 22 | export default coreInject 23 | -------------------------------------------------------------------------------- /src/helpers/isa95_demo_address_space.ts: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2022 DATATRONiQ GmbH 5 | Copyright (c) 2017-2022 Klaus Landsdorf (http://node-red.plus/) 6 | Copyright (c) 2014-2017 Etienne Rossignon 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | this software and associated documentation files (the "Software"), to deal in 10 | the Software without restriction, including without limitation the rights to 11 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | the Software, and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 'use strict' 26 | -------------------------------------------------------------------------------- /src/icons/OPCUA-IIoT-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/src/icons/OPCUA-IIoT-Logo.png -------------------------------------------------------------------------------- /src/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/src/icons/icon.png -------------------------------------------------------------------------------- /src/icons/isa95-icon-blue-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/src/icons/isa95-icon-blue-1024.png -------------------------------------------------------------------------------- /src/icons/isa95-icon-blue-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/src/icons/isa95-icon-blue-128.png -------------------------------------------------------------------------------- /src/icons/isa95-icon-blue-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/src/icons/isa95-icon-blue-64.png -------------------------------------------------------------------------------- /src/icons/isa95-icon-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/src/icons/isa95-icon-blue.png -------------------------------------------------------------------------------- /src/icons/opcuaiiot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiancoRoyal/node-red-contrib-iiot-opcua/40cf83b46abbed92b9b8f11ebb6eecfc28d2b6a7/src/icons/opcuaiiot.png -------------------------------------------------------------------------------- /src/locales/de-DE/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "common": { 4 | "label": { 5 | "type": "Typ", 6 | "showActivities": "Zeige Aktivitäten", 7 | "showErrors": "Zeige Fehler" 8 | }, 9 | "response": { 10 | }, 11 | "getter": { 12 | }, 13 | "read": { 14 | }, 15 | "write": { 16 | }, 17 | "server": { 18 | }, 19 | "client": { 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "tabs-label":{ 4 | "settings": "Einstellungen" 5 | }, 6 | "label": { 7 | "connector": "Verbindung", 8 | "justValue": "Sende nur Werte", 9 | "sendNodesToRead": "Ergebnisliste für Read", 10 | "sendNodesToListener": "Ergebnisliste für Listener", 11 | "sendNodesToBrowser": "Ergebnisliste für Browser", 12 | "Multiple Outputs": "Separate Ergebnisse", 13 | "lookupUseHint": "Das Browsen funktioniert via Auswahl und erneutem Klick auf Suche.", 14 | "showActivities": "Zeige Aktivitäten", 15 | "showErrors": "Zeige Fehler" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-connector.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "type": "Typ", 5 | "connector": "Verbindung", 6 | "credentials": "Verwende Login", 7 | "user": "Benutzer", 8 | "password": "Kennwort", 9 | "keepSessionAlive": "Halte Session aktiv", 10 | "publicCertificateFile": "Öffentlich", 11 | "privateKeyFile": "Privat", 12 | "certificateFiles": "Zertifikatdateien", 13 | "endpointMustExist": "Endpoint muss exakt sein", 14 | "autoSelectRightEndpoint": "Automatische Endpoint-Auswahl", 15 | "showErrors": "Zeige Fehler", 16 | "individualCerts": "Nutze individuelle Zertifikate" 17 | }, 18 | "tabs-label": { 19 | "settings": "Einstellungen", 20 | "security": "Sicherheit", 21 | "strategy": "Strategie" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "eventType": "Event Typ", 5 | "queueSize": "Queue Umfang", 6 | "showActivities": "Zeige Aktivitäten", 7 | "showErrors": "Zeige Fehler", 8 | "parseStrings": "Parse Daten", 9 | "lookupUseHint": "Die Eventauswahl funktioniert via Auswahl und erneutem Klick auf Suche." 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-flex-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "showActivities": "Zeige Aktivitäten", 5 | "showErrors": "Zeige Fehler", 6 | "stateLogEnabled": "Log Aktiv", 7 | "addButton": "Neu", 8 | "removeButton": "Entfernen", 9 | "maxConnectionsPerEndpoint": "Max Verbindungen/Endpoint", 10 | "alternateHostname": "Alternativer Hostname", 11 | "allowAnonymous": "Erlaube Anonymous", 12 | "publicCertificateFile": "Öffentlich", 13 | "privateCertificateFile": "Privat", 14 | "certificateFiles": "Zertifikatdateien", 15 | "users": "Benutzer", 16 | "individualCerts": "Nutze individuelle Zertifikate" 17 | }, 18 | "tabs-label": { 19 | "users": "Benutzer & Sets", 20 | "settings": "Einstellungen", 21 | "security": "Sicherheit" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-inject.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcuaiiotinject": { 3 | "opcuaiiotinject": "inject", 4 | "inject": "inject", 5 | "injectType": "Typ", 6 | "repeat": "repeat = __repeat__", 7 | "crontab": "crontab = __crontab__", 8 | "stopped": "stopped", 9 | "failed": "Inject failed: __error__", 10 | "label": { 11 | "repeat": "Wiederholung" 12 | }, 13 | "timestamp": "Zeitstempel", 14 | "none": "keine", 15 | "interval": "Interval", 16 | "interval-time": "Zeitinterval", 17 | "time": "bestimmter Zeitpunkt", 18 | "seconds": "Sekunden", 19 | "minutes": "Minuten", 20 | "hours": "Stunden", 21 | "between": "zwischen", 22 | "previous": "previous value", 23 | "at": "an", 24 | "and": "und", 25 | "every": "jede", 26 | "days": [ 27 | "Montag", 28 | "Dienstag", 29 | "Mittwoch", 30 | "Donnerstag", 31 | "Freitag", 32 | "Samstag", 33 | "Sonntag" 34 | ], 35 | "on": "um", 36 | "onstart": "Inject beim Start?", 37 | "tip": "Note: \"interval between times\" and \"at a specific time\" will use cron.
See info box for details.", 38 | "success": "Erfolgreich injected: __label__", 39 | "errors": { 40 | "failed": "inject failed, see log for details" 41 | }, 42 | "startDelay": "Delay beim Start", 43 | "startDelayAddon": "Sek. und dann" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-listener.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "connector": "Verbindung", 5 | "showActivities": "Zeige Aktivitäten", 6 | "showErrors": "Zeige Fehler", 7 | "actiontype": "Actionstyp", 8 | "attributeId": "Attribut Id", 9 | "queueSize": "Queue Umfang", 10 | "justValue": "Sende nur Werte", 11 | "useGroupItems": "Nutze Gruppierung" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-method-caller.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "tabs-label":{ 4 | "settings": "Einstellungen", 5 | "argument": "Argumente" 6 | }, 7 | "label": { 8 | "connector": "Verbindung", 9 | "methodType": "Methodentyp", 10 | "value": "Wert", 11 | "showActivities": "Zeige Aktivitäten", 12 | "showErrors": "Zeige Fehler", 13 | "addButton": "Neu", 14 | "removeButton": "Entfernen", 15 | "justValue": "Sende nur Werte" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-node.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "nodeId": "Node-Id", 5 | "datatype": "Datentyp", 6 | "value": "Wert", 7 | "injectType": "Typ", 8 | "showActivities": "Zeige Aktivitäten", 9 | "showErrors": "Zeige Fehler" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-read.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "connector": "Verbindung", 5 | "showActivities": "Zeige Aktivitäten", 6 | "showErrors": "Zeige Fehler", 7 | "attributeId": "Attribut Id", 8 | "maxAge": "Max. Alter", 9 | "depth": "Tiefe", 10 | "justValue": "Sende nur Werte", 11 | "parseStrings": "Parse Daten" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "compressStructure": "Vereinfache Daten", 5 | "showActivities": "Zeige Aktivitäten", 6 | "showErrors": "Zeige Fehler" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-result-filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "tabs-label":{ 4 | "settings": "Einstellungen", 5 | "convert": "Umwandlung" 6 | }, 7 | "label": { 8 | "datatype": "Datentyp", 9 | "withPrecision": "Max Stellen", 10 | "precision": "Stellen", 11 | "fixedValue": "Nachkommastellen", 12 | "fixPoint": "Anzahl", 13 | "entry": "Array Eintrag", 14 | "justValue": "Sende nur Werte", 15 | "withValueCheck": "Wertprüfung", 16 | "minvalue": "Min Wert", 17 | "maxvalue": "Max Wert", 18 | "defaultvalue": "Default Wert", 19 | "showActivities": "Zeige Aktivitäten", 20 | "showErrors": "Zeige Fehler" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-server-aso.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "objecttype": "Objekttyp", 5 | "referencetype": "Referenztyp", 6 | "referenceNodeId": "Referenz-Id", 7 | "datatype": "Datentyp", 8 | "value": "Wert", 9 | "showActivities": "Zeige Aktivitäten", 10 | "showErrors": "Zeige Fehler", 11 | "displayname": "Anzeigename", 12 | "browsename": "Browse-Name" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-server-cmd.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "commandtype": "Kommandotype", 5 | "showActivities": "Zeige Aktivitäten", 6 | "showErrors": "Zeige Fehler" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "showActivities": "Zeige Aktivitäten", 5 | "showErrors": "Zeige Fehler", 6 | "stateLogEnabled": "Log Aktiv", 7 | "addButton": "Neu", 8 | "removeButton": "Entfernen", 9 | "maxConnectionsPerEndpoint": "Max Verbindungen/Endpoint", 10 | "alternateHostname": "Alternativer Hostname", 11 | "allowAnonymous": "Erlaube Anonymous", 12 | "publicCertificateFile": "Öffentlich", 13 | "privateCertificateFile": "Privat", 14 | "certificateFiles": "Zertifikatdateien", 15 | "users": "Benutzer", 16 | "individualCerts": "Nutze individuelle Zertifikate" 17 | }, 18 | "tabs-label": { 19 | "users": "Benutzer & Sets", 20 | "settings": "Einstellungen", 21 | "security": "Sicherheit" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/locales/de-DE/opcua-iiot-write.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "connector": "Verbindung", 5 | "justValue": "Sende nur Werte", 6 | "showActivities": "Zeige Aktivitäten", 7 | "showErrors": "Zeige Fehler" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/locales/en-US/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": { 3 | "label": { 4 | "payload": "Payload", 5 | "topic": "Topic", 6 | "name": "Name", 7 | "username": "Username", 8 | "password": "Password" 9 | }, 10 | "status": { 11 | "connected": "connected", 12 | "not-connected": "not connected", 13 | "disconnected": "disconnected", 14 | "connecting": "connecting", 15 | "error": "error", 16 | "ok": "OK" 17 | }, 18 | "notification": { 19 | "error": "Error: __message__", 20 | "errors": { 21 | "not-deployed": "node not deployed", 22 | "no-response": "no response from server", 23 | "unexpected": "unexpected error (__status__) __message__" 24 | } 25 | }, 26 | "errors": { 27 | "nooverride": "Warning: msg properties can no longer override set node properties. See bit.ly/nr-override-msg-props" 28 | } 29 | }, 30 | "opcua-iiot-contrib": { 31 | "common": { 32 | "label": { 33 | "type": "Type", 34 | "server": "Server", 35 | "timeout": "Timeout", 36 | "name": "Name", 37 | "endpoint": "Endpoint", 38 | "connector": "Connector", 39 | "topic": "Topic", 40 | "user": "User", 41 | "password": "Password", 42 | "showActivities": "Show Activities", 43 | "showErrors": "Show Errors" 44 | }, 45 | "response": { 46 | }, 47 | "getter": { 48 | }, 49 | "read": { 50 | }, 51 | "write": { 52 | }, 53 | "server": { 54 | }, 55 | "client": { 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "tabs-label":{ 4 | "settings": "Settings", 5 | "browser": "Browser" 6 | }, 7 | "label": { 8 | "connector": "Connector", 9 | "browseAll": "Browse All", 10 | "justValue": "Send Just Values", 11 | "sendNodesToRead": "Results To Read", 12 | "sendNodesToListener": "Results To Listener", 13 | "sendNodesToBrowser": "Results To Browser", 14 | "multipleOutputs": "Multiple Outputs", 15 | "showActivities": "Show Activities", 16 | "showErrors": "Show Errors", 17 | "topic": "Topic", 18 | "nodeId": "Node-Id", 19 | "lookupUseHint": "Start browsing with the lookup button! Select and lookup again to go deeper in hierarchy. Restart at root by deleting the input content.", 20 | "recursiveBrowse": "Recursive", 21 | "recursiveDepth": "Depth", 22 | "delayPerMessage": "Delay msg" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-connector.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "discoveryUrl": "Discovery", 5 | "endpoint": "Endpoint", 6 | "connector": "Connector", 7 | "showErrors": "Show Errors", 8 | "type": "Type", 9 | "host": "Host", 10 | "port": "Port", 11 | "securityPolicy": "Security Policy", 12 | "securityMode": "Security Mode", 13 | "user": "User", 14 | "password": "Password", 15 | "credentials": "Use Login", 16 | "keepSessionAlive": "Keep Session Alive", 17 | "publicCertificateFile": "Public", 18 | "privateKeyFile": "Private", 19 | "defaultSecureTokenLifetime": "Secure TLT", 20 | "certificateFiles": "Certificate Files", 21 | "endpointMustExist": "Endpoint Must Exist", 22 | "autoSelectRightEndpoint": "Auto Select Endpoint", 23 | "strategyMaxRetry": "Max. Retry", 24 | "strategyInitialDelay": "Initial Delay", 25 | "strategyMaxDelay": "Max. Delay", 26 | "strategyRandomisationFactor": "Random Factor", 27 | "requestedSessionTimeout": "Requested Timeout", 28 | "connectionStartDelay": "Start", 29 | "reconnectDelay": "Reconnect", 30 | "connectionStopDelay": "Stop", 31 | "serverConnectionDelays": "Server Connection Delays", 32 | "maxBadSessionRequests": "Max. BadSession Requests", 33 | "individualCerts": "Use individual Certificate Files" 34 | }, 35 | "tabs-label": { 36 | "settings": "Settings", 37 | "security": "Security", 38 | "strategy": "Strategy" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-crawler.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "tabs-label":{ 4 | "settings": "Settings", 5 | "filter": "Filter" 6 | }, 7 | "label": { 8 | "activateUnsetFilter": "Unset filtering", 9 | "activateFilters": "Activate filters", 10 | "negateFilter": "Negate filter", 11 | "filters": "Filter List", 12 | "connector": "Connector", 13 | "justValue": "Remove References", 14 | "singleResult": "Single Request", 15 | "showActivities": "Show Activities", 16 | "showErrors": "Show Errors", 17 | "topic": "Topic", 18 | "nodeId": "Node-Id", 19 | "lookupUseHint": "You can crawl with the lookup by select and lookup agian.", 20 | "delayPerMessage": "Delay Between Messages", 21 | "timeout": "Crawler Timeout" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-discovery.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "showActivities": "Show Activities", 5 | "showErrors": "Show Errors" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "eventType": "Event Type", 5 | "resultType": "Result Type", 6 | "queueSize": "Queue Size", 7 | "usingListener": "Listener", 8 | "showActivities": "Show Activities", 9 | "showErrors": "Show Errors", 10 | "parseStrings": "Parse Output", 11 | "lookupUseHint": "You can use the lookup or give a NodeId of your event." 12 | }, 13 | "resulttype": { 14 | "label": { 15 | "basic": "basic", 16 | "condition": "condition", 17 | "state": "state", 18 | "all": "all" 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-flex-connector.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "connector": "Connector", 5 | "showActivities": "Show Activities", 6 | "showErrors": "Show Errors" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-flex-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "port": "Port", 5 | "endpoint": "Endpoint", 6 | "showActivities": "Show Activities", 7 | "showErrors": "Show Errors", 8 | "stateLogEnabled": "Log State", 9 | "statusLog": "Log Status", 10 | "securityPolicy": "Security Policies", 11 | "securityMode": "Security Modes", 12 | "addButton": "Add", 13 | "removeButton": "Remove", 14 | "maxAllowedSessionNumber": "Max Sessions", 15 | "maxConnectionsPerEndpoint": "Max Connections Per Endpoint", 16 | "maxAllowedSubscriptionNumber": "Max Subscriptions", 17 | "alternateHostname": "Alternate Hostname", 18 | "allowAnonymous": "Allow Anonymous", 19 | "isAuditing": "Auditing", 20 | "function": "Address Space Script", 21 | "publicCertificateFile": "Public", 22 | "privateCertificateFile": "Private", 23 | "certificateFiles": "Certificate Files", 24 | "users": "Users", 25 | "xmlsets": "XML-Sets", 26 | "serverDiscovery": "Discovery", 27 | "maxNodesPerRead": "Max per Read", 28 | "maxNodesPerBrowse": "Max per Browse", 29 | "registerServerMethod": "Register Server Method", 30 | "discoveryServerEndpointUrl": "Endpoint Url", 31 | "capabilitiesForMDNS": "MDNS capabilities", 32 | "delayToClose": "Delay On Close", 33 | "individualCerts": "Use individual Certificate Files" 34 | }, 35 | "tabs-label": { 36 | "users": "Users & Sets", 37 | "settings": "Settings", 38 | "security": "Security", 39 | "ass": "Address Space", 40 | "discovery": "Discovery", 41 | "limits": "Limits" 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-inject.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcuaiiotinject": { 3 | "injectTypeListener": "Listener Inject", 4 | "inject": "inject", 5 | "opcuaiiotinject": "inject", 6 | "injectType": "Type", 7 | "repeat": "repeat = __repeat__", 8 | "crontab": "crontab = __crontab__", 9 | "stopped": "stopped", 10 | "failed": "Inject failed: __error__", 11 | "label": { 12 | "payload": "Payload", 13 | "interval": "Interval", 14 | "repeat": "Repeat", 15 | "datatype": "Data Type", 16 | "nodeid": "Node-Id", 17 | "injectType": "Type" 18 | }, 19 | "timestamp": "timestamp", 20 | "none": "none", 21 | "interval": "interval", 22 | "interval-time": "interval between times", 23 | "time": "at a specific time", 24 | "milliseconds": "milliseconds", 25 | "seconds": "seconds", 26 | "minutes": "minutes", 27 | "hours": "hours", 28 | "between": "between", 29 | "previous": "previous value", 30 | "at": "at", 31 | "and": "and", 32 | "every": "every", 33 | "days": [ 34 | "Monday", 35 | "Tuesday", 36 | "Wednesday", 37 | "Thursday", 38 | "Friday", 39 | "Saturday", 40 | "Sunday" 41 | ], 42 | "on": "on", 43 | "onstart": "Delay before starting", 44 | "tip": "Note: \"interval between times\" and \"at a specific time\" will use cron.
See info box for details.", 45 | "success": "Successfully injected: __label__", 46 | "errors": { 47 | "failed": "inject failed, see log for details" 48 | }, 49 | "startDelay": "Delay at start", 50 | "startDelayAddon": "sec. then", 51 | "addButton": "Add", 52 | "tabs-label": { 53 | "addressSpaceItems": "OPC UA Nodes", 54 | "settings": "Settings", 55 | "security": "Security" 56 | }, 57 | "type-label":{ 58 | "inject": "inject", 59 | "read": "read", 60 | "write": "write", 61 | "listen": "listen" 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-listener.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "connector": "Connector", 5 | "actiontype": "Action Type", 6 | "interval": "Interval", 7 | "showActivities": "Show Activities", 8 | "showErrors": "Show Errors", 9 | "attributeId": "Attribute Id", 10 | "queueSize": "Queue Size", 11 | "justValue": "Send Just Values", 12 | "useGroupItems": "Group Items" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-method-caller.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "tabs-label":{ 4 | "settings": "Settings", 5 | "argument": "Input-Arguments" 6 | }, 7 | "label": { 8 | "connector": "Connector", 9 | "objectId": "Object-Id", 10 | "methodId": "Method-Id", 11 | "methodType": "Method Type", 12 | "value": "Value", 13 | "showActivities": "Show Activities", 14 | "showErrors": "Show Errors", 15 | "addButton": "Add", 16 | "removeButton": "Remove", 17 | "justValue": "Send Just Values" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-node.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "nodeId": "Node-Id", 5 | "datatype": "Data Type", 6 | "value": "Value", 7 | "topic": "Topic", 8 | "injectType": "Type", 9 | "showActivities": "Show Activities", 10 | "showErrors": "Show Errors" 11 | }, 12 | "type-label":{ 13 | "inject": "inject", 14 | "read": "read", 15 | "write": "write", 16 | "listen": "listen" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-read.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "connector": "Connector", 5 | "showActivities": "Show Activities", 6 | "showErrors": "Show Errors", 7 | "attributeId": "Attribute Id", 8 | "maxAge": "Max. Age", 9 | "depth": "Depth", 10 | "justValue": "Send Just Values", 11 | "parseStrings": "Parse Result", 12 | "historyDays": "History Days" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "tabs-label":{ 4 | "settings": "Settings", 5 | "filter": "Filter" 6 | }, 7 | "label": { 8 | "activateUnsetFilter": "Unset filtering", 9 | "negateFilter": "Negate filter", 10 | "activateFilters": "Activate filters", 11 | "compressStructure": "Simplify results", 12 | "showActivities": "Show Activities", 13 | "showErrors": "Show Errors" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-result-filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "tabs-label":{ 4 | "settings": "Settings", 5 | "convert": "Converts" 6 | }, 7 | "label": { 8 | "nodeId": "Node-Id", 9 | "datatype": "Data Type", 10 | "withPrecision": "With Precision", 11 | "precision": "Precision", 12 | "fixedValue": "Fixed Value", 13 | "fixPoint": "Fix", 14 | "entry": "Array Entry", 15 | "justValue": "Send Just Values", 16 | "withValueCheck": "With Value Check", 17 | "minvalue": "Min Value", 18 | "maxvalue": "Max Value", 19 | "defaultvalue": "Default Value", 20 | "usingListener": "Listener", 21 | "showActivities": "Show Activities", 22 | "showErrors": "Show Errors" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-server-aso.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "nodeId": "Node-Id", 5 | "objecttype": "Object Type", 6 | "referencetype": "Reference Type", 7 | "referenceNodeId": "Reference Id", 8 | "datatype": "Data Type", 9 | "value": "Value", 10 | "showActivities": "Show Activities", 11 | "showErrors": "Show Errors", 12 | "displayname": "Display Name", 13 | "browsename": "Browse Name" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-server-cmd.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "nodeId": "Node-Id", 5 | "commandtype": "Command Type", 6 | "showActivities": "Show Activities", 7 | "showErrors": "Show Errors" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "port": "Port", 5 | "endpoint": "Endpoint", 6 | "showActivities": "Show Activities", 7 | "showErrors": "Show Errors", 8 | "stateLogEnabled": "Log State", 9 | "statusLog": "Log Status", 10 | "securityPolicy": "Security Policies", 11 | "securityMode": "Security Modes", 12 | "addButton": "Add", 13 | "removeButton": "Remove", 14 | "maxAllowedSessionNumber": "Max Sessions", 15 | "maxConnectionsPerEndpoint": "Max Connections Per Endpoint", 16 | "maxAllowedSubscriptionNumber": "Max Subscriptions", 17 | "alternateHostname": "Alternate Hostname", 18 | "allowAnonymous": "Allow Anonymous", 19 | "isAuditing": "Auditing", 20 | "publicCertificateFile": "Public", 21 | "privateCertificateFile": "Private", 22 | "certificateFiles": "Certificate Files", 23 | "xmlsets": "XML-Sets", 24 | "asoDemo": "ASO Demo", 25 | "serverDiscovery": "Discovery", 26 | "maxNodesPerRead": "Max per Read", 27 | "maxNodesPerBrowse": "Max per Browse", 28 | "registerServerMethod": "Register Server Method", 29 | "discoveryServerEndpointUrl": "Endpoint Url", 30 | "capabilitiesForMDNS": "MDNS capabilities", 31 | "delayToClose": "Delay On Close", 32 | "individualCerts": "Use individual Certificate Files" 33 | }, 34 | "tabs-label": { 35 | "users": "Users & Sets", 36 | "settings": "Settings", 37 | "security": "Security", 38 | "discovery": "Discovery", 39 | "limits": "Limits" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/locales/en-US/opcua-iiot-write.json: -------------------------------------------------------------------------------- 1 | { 2 | "opcua-iiot-contrib": { 3 | "label": { 4 | "connector": "Connector", 5 | "justValue": "Send Just Values", 6 | "showActivities": "Show Activities", 7 | "showErrors": "Show Errors" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/opcua-iiot-discovery.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 31 | 32 | 42 | 43 | 53 | -------------------------------------------------------------------------------- /src/opcua-iiot-flex-connector.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | 35 | 36 | 57 | 58 | 104 | -------------------------------------------------------------------------------- /src/opcua-iiot-flex-connector.ts: -------------------------------------------------------------------------------- 1 | /* 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 6 | All rights reserved. 7 | node-red-contrib-iiot-opcua 8 | */ 9 | 'use strict' 10 | import * as nodered from "node-red"; 11 | import {NodeMessage, NodeStatus} from "node-red"; 12 | import {TodoTypeAny} from "./types/placeholders"; 13 | import coreConnector from "./core/opcua-iiot-core-connector"; 14 | import {deregisterToConnector, registerToConnector, resetIiotNode} from "./core/opcua-iiot-core"; 15 | import {NodeMessageInFlow} from "@node-red/registry"; 16 | 17 | export interface OPCUAIIoTFlexConnector extends nodered.Node { 18 | showStatusActivities: boolean 19 | showErrors: boolean 20 | connector: any 21 | iiot?: TodoTypeAny 22 | } 23 | 24 | interface OPCUAIIoTFlexConnectorConfigurationDef extends nodered.NodeDef { 25 | showStatusActivities: boolean 26 | showErrors: boolean 27 | connector: any 28 | } 29 | 30 | /** 31 | * Event Node-RED node. 32 | * 33 | * @param RED 34 | */ 35 | module.exports = function (RED: nodered.NodeAPI) { 36 | // SOURCE-MAP-REQUIRED 37 | 38 | function OPCUAIIoTFlexConnector(this: OPCUAIIoTFlexConnector, config: OPCUAIIoTFlexConnectorConfigurationDef) { 39 | RED.nodes.createNode(this, config) 40 | this.name = config.name 41 | this.showStatusActivities = config.showStatusActivities 42 | this.showErrors = config.showErrors 43 | this.connector = RED.nodes.getNode(config.connector) 44 | 45 | let self = this; 46 | self.iiot = {} 47 | 48 | this.status({fill: 'blue', shape: 'ring', text: 'new'}) 49 | 50 | this.on('input', (msg: NodeMessageInFlow) => { 51 | coreConnector.internalDebugLog('connector change request input') 52 | 53 | const payload: TodoTypeAny = msg.payload 54 | 55 | if (self.connector) { 56 | if (payload.endpoint && payload.endpoint.includes('opc.tcp:')) { 57 | coreConnector.internalDebugLog('connector change possible') 58 | coreConnector.internalDebugLog(payload) 59 | self.connector.functions.restartWithNewSettings(payload, () => { 60 | coreConnector.internalDebugLog('connector change injected') 61 | this.send(msg) 62 | }) 63 | } else { 64 | coreConnector.internalDebugLog('Connector Change Not Possible - Wrong Endpoint') 65 | this.error(new Error('Connector Change Not Possible - Wrong Endpoint'), msg) 66 | } 67 | } else { 68 | coreConnector.internalDebugLog('Connector Change Not Possible - No Connector') 69 | this.error(new Error('Connector Change Not Possible - No Connector'), msg) 70 | } 71 | }) 72 | 73 | const statusHandler = (status: string | NodeStatus) => { 74 | this.status(status) 75 | } 76 | 77 | const errorHandler = (err: Error, msg: NodeMessage) => { 78 | this.error(err, msg) 79 | } 80 | 81 | const onAlias = (event: string, callback: () => void) => { 82 | // @ts-ignore 83 | this.on(event, callback) 84 | } 85 | 86 | registerToConnector(this, statusHandler, onAlias, errorHandler) 87 | 88 | this.on('close', (done: () => void) => { 89 | self.removeAllListeners() 90 | 91 | deregisterToConnector(this as TodoTypeAny, () => { 92 | resetIiotNode(this) 93 | done() 94 | }) 95 | }) 96 | } 97 | 98 | RED.nodes.registerType('OPCUA-IIoT-Flex-Connector', OPCUAIIoTFlexConnector) 99 | } 100 | -------------------------------------------------------------------------------- /src/opcua-iiot-node.ts: -------------------------------------------------------------------------------- 1 | /* 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 6 | Copyright 2015,2016 - Mika Karaila, Valmet Automation Inc. (node-red-contrib-opcua) 7 | All rights reserved. 8 | node-red-contrib-iiot-opcua 9 | */ 10 | 'use strict' 11 | 12 | import * as nodered from "node-red"; 13 | import {TodoTypeAny} from "./types/placeholders"; 14 | import {NodeMessageInFlow} from "@node-red/registry"; 15 | import {convertDataValueByDataType} from "./core/opcua-iiot-core"; 16 | import {logger} from "./core/opcua-iiot-core-connector"; 17 | import _ from "underscore"; 18 | 19 | interface OPCUAIIoTNode extends nodered.Node { 20 | nodeId: string 21 | datatype: string 22 | value: string 23 | topic: string 24 | name: string 25 | injectType: string 26 | showErrors: string 27 | } 28 | 29 | interface OPCUAIIoTNodeDef extends nodered.NodeDef { 30 | nodeId: string 31 | datatype: string 32 | value: string 33 | topic: string 34 | name: string 35 | injectType: string 36 | showErrors: string 37 | } 38 | 39 | /** 40 | * OPC UA node representation for Node-RED OPC UA IIoT nodes. 41 | * 42 | * @param RED 43 | */ 44 | module.exports = (RED: nodered.NodeAPI) => { 45 | // SOURCE-MAP-REQUIRED 46 | 47 | function OPCUAIIoTNode(this: OPCUAIIoTNode, config: OPCUAIIoTNodeDef) { 48 | RED.nodes.createNode(this, config) 49 | this.nodeId = config.nodeId 50 | this.datatype = config.datatype 51 | this.value = config.value 52 | this.topic = config.topic 53 | this.name = config.name 54 | this.injectType = config.injectType 55 | this.showErrors = config.showErrors 56 | 57 | let self: TodoTypeAny = this 58 | self.iiot = {} 59 | 60 | self.iiot.subscribed = false 61 | self.status({fill: 'blue', shape: 'ring', text: 'new'}) 62 | 63 | self.toggleNodeStatusSymbol = () => { 64 | self.iiot.subscribed = !self.iiot.subscribed 65 | 66 | if (self.injectType === 'listen') { 67 | if (self.iiot.subscribed) { 68 | self.status({fill: 'blue', shape: 'dot', text: 'subscribed'}) 69 | } else { 70 | self.status({fill: 'blue', shape: 'ring', text: 'not subscribed'}) 71 | } 72 | } else { 73 | self.status({fill: 'blue', shape: 'dot', text: 'injected'}) 74 | } 75 | } 76 | 77 | this.on('input', (msg: NodeMessageInFlow) => { 78 | 79 | self.toggleNodeStatusSymbol(); 80 | 81 | const topic = msg.topic || self.topic 82 | const payload = msg.payload as TodoTypeAny 83 | const value: TodoTypeAny = payload?.value ? payload.value : msg.payload; 84 | const valuesToWrite = payload.valuesToWrite || [] 85 | const addressSpaceItems = payload.addressSpaceItems || [] 86 | 87 | if (self.injectType === 'write') { 88 | addressSpaceItems.push({name: self.name, nodeId: self.nodeId, datatypeName: self.datatype}) 89 | try { 90 | if(typeof self.value !== "string"){ 91 | self.value = self.value.toString() 92 | } 93 | valuesToWrite.push(convertDataValueByDataType( (_.isEmpty(self.value)) ? value : self.value, self.datatype)) 94 | } catch (err) { 95 | logger.internalDebugLog(err) 96 | if (self.showErrors) { 97 | this.error(err, msg) 98 | } 99 | } 100 | } else { 101 | addressSpaceItems.push({name: self.name, nodeId: self.nodeId, datatypeName: self.datatype}) 102 | } 103 | 104 | const outputPayload = { 105 | nodetype: "node", 106 | injectType: self.injectType || payload.injectType, 107 | addressSpaceItems, 108 | valuesToWrite, 109 | value, 110 | } 111 | 112 | const outputMessage = { 113 | payload: outputPayload, 114 | topic, 115 | _msgid: msg._msgid 116 | } 117 | logger.internalDebugLog('node msg stringified: ' + JSON.stringify(msg)) 118 | this.send(outputMessage) 119 | }) 120 | 121 | } 122 | 123 | RED.nodes.registerType('OPCUA-IIoT-Node', OPCUAIIoTNode) 124 | } 125 | -------------------------------------------------------------------------------- /src/opcua-iiot-server-aso.ts: -------------------------------------------------------------------------------- 1 | /* 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 6 | All rights reserved. 7 | node-red-contrib-iiot-opcua 8 | */ 9 | 'use strict' 10 | 11 | import * as nodered from "node-red"; 12 | import {TodoTypeAny} from "./types/placeholders"; 13 | import {NodeMessageInFlow} from "node-red"; 14 | import {OBJECTS_ROOT, resetIiotNode} from "./core/opcua-iiot-core"; 15 | import {ReferenceTypeIds} from "node-opcua"; 16 | import {logger} from "./core/opcua-iiot-core-connector"; 17 | import internalDebugLog = logger.internalDebugLog; 18 | 19 | interface OPCUAIIoTASO extends nodered.Node { 20 | nodeId: string 21 | browsename: string 22 | displayname: string 23 | objecttype: string 24 | referencetype: string 25 | referenceNodeId: string 26 | datatype: string 27 | value: string 28 | name: string 29 | 30 | } 31 | 32 | interface OPCUAIIoTCMDASO extends nodered.NodeDef { 33 | nodeId: string 34 | browsename: string 35 | displayname: string 36 | objecttype: string 37 | referencetype: string 38 | referenceNodeId: string 39 | datatype: string 40 | value: string 41 | name: string 42 | } 43 | 44 | /** 45 | * Address space object Node-RED node. 46 | * 47 | * @param RED 48 | */ 49 | module.exports = (RED: nodered.NodeAPI) => { 50 | // SOURCE-MAP-REQUIRED 51 | 52 | function OPCUAIIoTASO(this: OPCUAIIoTASO, config: OPCUAIIoTCMDASO) { 53 | RED.nodes.createNode(this, config) 54 | this.nodeId = config.nodeId 55 | this.browsename = config.browsename 56 | this.displayname = config.displayname 57 | this.objecttype = config.objecttype 58 | this.referencetype = config.referencetype 59 | this.referenceNodeId = config.referenceNodeId 60 | this.datatype = config.datatype 61 | this.value = config.value 62 | this.name = config.name 63 | 64 | let self = this 65 | internalDebugLog('Open ASO Node') 66 | 67 | this.on('input', (msg: NodeMessageInFlow | TodoTypeAny) => { 68 | if (msg.payload.nodetype === 'inject') { 69 | self.nodeId = msg.payload.topic || self.nodeId 70 | self.datatype = msg.payload.datatype || self.datatype 71 | self.value = msg.payload.payload || self.value 72 | } 73 | const value = self.value || msg.payload.value; 74 | msg = {payload: {}} // clean message 75 | msg.topic = 'ServerAddressSpaceObject' 76 | msg.payload.nodetype = 'inject' 77 | msg.payload.injectType = 'ASO' 78 | 79 | if (self.nodeId.includes('i=') || self.nodeId.includes('s=') || self.nodeId.includes('b=')) { 80 | msg.payload.nodeId = self.nodeId 81 | msg.payload.browsename = self.browsename 82 | msg.payload.displayname = self.displayname 83 | msg.payload.objecttype = self.objecttype 84 | msg.payload.datatype = self.datatype 85 | msg.payload.value = value 86 | 87 | msg.payload.referenceNodeId = self.referenceNodeId || OBJECTS_ROOT 88 | msg.payload.referencetype = self.referencetype || ReferenceTypeIds.Organizes 89 | 90 | internalDebugLog('node msg stringified: ' + JSON.stringify(msg)) 91 | this.send(msg) 92 | } else { 93 | /* istanbul ignore next */ 94 | this.error(new Error('ASO NodeId Is Not Valid'), msg) 95 | } 96 | }) 97 | 98 | this.on('close', (done: () => void) => { 99 | internalDebugLog('Close ASO Node') 100 | resetIiotNode(self) 101 | done() 102 | }) 103 | } 104 | 105 | RED.nodes.registerType('OPCUA-IIoT-Server-ASO', OPCUAIIoTASO) 106 | } 107 | -------------------------------------------------------------------------------- /src/opcua-iiot-server-cmd.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | 45 | 46 | 64 | 65 | 70 | -------------------------------------------------------------------------------- /src/opcua-iiot-server-cmd.ts: -------------------------------------------------------------------------------- 1 | /* 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 6 | All rights reserved. 7 | node-red-contrib-iiot-opcua 8 | */ 9 | 'use strict' 10 | 11 | import * as nodered from "node-red"; 12 | import {NodeMessageInFlow} from "node-red"; 13 | import {TodoTypeAny} from "./types/placeholders"; 14 | import {logger} from "./core/opcua-iiot-core-connector"; 15 | import {resetIiotNode} from "./core/opcua-iiot-core"; 16 | import internalDebugLog = logger.internalDebugLog; 17 | 18 | interface OPCUAIIoTCMD extends nodered.Node { 19 | commandtype: string 20 | nodeId: string 21 | name: string 22 | 23 | } 24 | 25 | interface OPCUAIIoTCMDDef extends nodered.NodeDef { 26 | commandtype: string 27 | nodeId: string 28 | name: string 29 | } 30 | 31 | /** 32 | * Address space object Node-RED node. 33 | * 34 | * @param RED 35 | */ 36 | 37 | module.exports = (RED: nodered.NodeAPI) => { 38 | // SOURCE-MAP-REQUIRED 39 | 40 | function OPCUAIIoTCMD(this: OPCUAIIoTCMD, config: OPCUAIIoTCMDDef) { 41 | RED.nodes.createNode(this, config) 42 | this.commandtype = config.commandtype 43 | this.nodeId = config.nodeId 44 | this.name = config.name 45 | 46 | let self = this 47 | 48 | this.on('input', (msg: NodeMessageInFlow | TodoTypeAny) => { 49 | let returnPayload: TodoTypeAny = {}; 50 | 51 | returnPayload.nodetype = 'inject' 52 | returnPayload.injectType = 'CMD' 53 | returnPayload.commandType = self.commandtype 54 | 55 | if (msg.payload.addressSpaceItems && msg.payload.addressSpaceItems.length > 0) { 56 | let addressSpaceItem 57 | for (addressSpaceItem of msg.payload.addressSpaceItems) { 58 | returnPayload.nodeId = addressSpaceItem.nodeId 59 | } 60 | if (returnPayload.nodeId) { 61 | this.send({ 62 | ...msg, 63 | payload: returnPayload 64 | }) 65 | } 66 | } else { 67 | if (self.nodeId) { 68 | returnPayload.nodeId = self.nodeId 69 | } 70 | this.send({...msg, payload: returnPayload}) 71 | } 72 | }) 73 | 74 | this.on('close', (done: () => void) => { 75 | internalDebugLog('Close CMD Node') 76 | resetIiotNode(self) 77 | done() 78 | }) 79 | } 80 | 81 | RED.nodes.registerType('OPCUA-IIoT-Server-Command', OPCUAIIoTCMD) 82 | } 83 | -------------------------------------------------------------------------------- /src/opcua-iiot-write.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 34 | 35 | 62 | 63 | 107 | -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/binary/Opc.Ua.Adi.Types.bsd.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Structure defining the information for auxiliary axis for array type variables. 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Identify on which type of axis the data shall be displayed. 53 | 54 | 55 | 56 | 57 | 58 | 59 | Structure defining XY value like a list of peaks. 60 | 61 | 62 | 63 | 64 | 65 | Structure defining double IEEE 32 bits complex value 66 | 67 | 68 | 69 | 70 | 71 | Structure defining double IEEE 64 bits complex value 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/binary/Opc.Ua.Di.Types.bsd.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/binary/Opc.Ua.Gds.Types.bsd.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/csv/AttributeIds.csv: -------------------------------------------------------------------------------- 1 | NodeId,1 2 | NodeClass,2 3 | BrowseName,3 4 | DisplayName,4 5 | Description,5 6 | WriteMask,6 7 | UserWriteMask,7 8 | IsAbstract,8 9 | Symmetric,9 10 | InverseName,10 11 | ContainsNoLoops,11 12 | EventNotifier,12 13 | Value,13 14 | DataType,14 15 | ValueRank,15 16 | ArrayDimensions,16 17 | AccessLevel,17 18 | UserAccessLevel,18 19 | MinimumSamplingInterval,19 20 | Historizing,20 21 | Executable,21 22 | UserExecutable,22 23 | -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/csv/ServerCapabilities.csv: -------------------------------------------------------------------------------- 1 | NA, No capability information is available. Cannot be used in combination with any other capability. 2 | DA, Provides current data. 3 | HD, Provides historical data. 4 | AC, Provides alarms and conditions that may require operator interaction. 5 | HE, Provides historical alarms and events. 6 | GDS, Supports the Global Discovery Server information model. 7 | LDS, Only supports the Discovery Services. Cannot be used in combination with any other capability. 8 | DI, Supports the Device Integration (DI) information model. 9 | ADI, Supports the Analyser Device Integration (ADI) information model. 10 | FDI, Supports the Field Device Integration (FDI) information model. 11 | FDIC, Supports the Field Device Integration (FDI) Communication Server information model. 12 | PLC, Supports the PLCopen information model. 13 | S95, Supports the ISA95 information model. -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/csv/ServerCapabilityIdentifiers.csv: -------------------------------------------------------------------------------- 1 | NA, No capability information is available. Cannot be used in combination with any other capability. 2 | DA, Provides current data. 3 | HD, Provides historical data. 4 | AC, Provides alarms and conditions that may require operator interaction. 5 | HE, Provides historical alarms and events. 6 | GDS, Supports the Global Discovery Server information model. 7 | LDS, Only supports the Discovery Services. Cannot be used in combination with any other capability. 8 | DI, Supports the Device Integration (DI) information model. 9 | ADI, Supports the Analyser Device Integration (ADI) information model. 10 | FDI, Supports the Field Device Integration (FDI) information model. 11 | FDIC, Supports the Field Device Integration (FDI) Communication Server information model. 12 | PLC, Supports the PLCopen information model. 13 | S95, Supports the ISA95 information model. -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/xml/Opc.Ua.Adi.Types.bsd.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Structure defining the information for auxiliary axis for array type variables. 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Identify on which type of axis the data shall be displayed. 53 | 54 | 55 | 56 | 57 | 58 | 59 | Structure defining XY value like a list of peaks. 60 | 61 | 62 | 63 | 64 | 65 | Structure defining double IEEE 32 bits complex value 66 | 67 | 68 | 69 | 70 | 71 | Structure defining double IEEE 64 bits complex value 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/xml/Opc.Ua.Di.Types.bsd.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/xml/Opc.Ua.Di.Types.xsd: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/xml/Opc.Ua.Gds.Types.bsd.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/public/vendor/opc-foundation/xml/Opc.Ua.Gds.Types.xsd: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/public/xmlsets.txt: -------------------------------------------------------------------------------- 1 | public/vendor/opc-foundation/xml/Opc.Ua.NodeSet2.xml (Standard) 2 | 3 | 4 | (Extras) 5 | 6 | public/vendor/opc-foundation/xml/Opc.ISA95.NodeSet2.xml 7 | 8 | public/vendor/opc-foundation/xml/Opc.Ua.Adi.NodeSet2.xml 9 | 10 | public/vendor/opc-foundation/xml/Opc.Ua.Gds.NodeSet2.xml 11 | 12 | public/vendor/opc-foundation/xml/Opc.Ua.Di.NodeSet2.xml 13 | 14 | (Harting Mica) 15 | 16 | public/vendor/harting/10_di.xml 17 | public/vendor/harting/20_autoid.xml 18 | public/vendor/harting/30_aim.xml -------------------------------------------------------------------------------- /src/types/assertion.ts: -------------------------------------------------------------------------------- 1 | /** 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | All rights reserved. 6 | node-red-contrib-iiot-opcua 7 | */ 8 | 9 | 10 | /** 11 | * Asserts that the object is undefined or null 12 | */ 13 | export const isNotDefined = (object: any): object is undefined => { 14 | return typeof object === 'undefined' || object === null; 15 | } 16 | 17 | /** 18 | * Asserts that the given object is an array of type . 19 | */ 20 | export const isArray = (object: any): object is Array => { 21 | return Array.isArray(object) 22 | } 23 | -------------------------------------------------------------------------------- /src/types/helpers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | All rights reserved. 6 | node-red-contrib-iiot-opcua 7 | */ 8 | 9 | import {TodoTypeAny} from "./placeholders"; 10 | import {AttributeIds, DataType, DataValue, NodeId, NodeIdType, StatusCode} from "node-opcua"; 11 | import {NodeMessageInFlow} from "node-red"; 12 | 13 | export type TimeUnits = 14 | 'ms' | 15 | 's' | 16 | 'm' | 17 | 'h'; 18 | 19 | export type TimeUnitNames = 20 | 'msec.' | 21 | 'sec.' | 22 | 'min.' | 23 | 'h.' | 24 | ''; 25 | 26 | export type WriteMessage = { 27 | addressSpaceItems: AddressSpaceItem[] 28 | payload: { 29 | nodesToWrite: NodeToWrite[], 30 | } 31 | valuesToWrite?: TodoTypeAny[] 32 | } 33 | 34 | export type AddressSpaceItem = { 35 | nodeId: string 36 | browseName: string 37 | displayName: string 38 | nodeClass: string 39 | datatypeName: string 40 | } 41 | 42 | export type BrowseMessage = { 43 | 'topic': string 44 | 'nodeId': string 45 | 'browseName': string 46 | 'nodeClassType': string 47 | 'typeDefinition': string 48 | 'payload': '' 49 | } 50 | 51 | export type NodeToWrite = { 52 | nodeId: NodeId 53 | attributeId: AttributeIds.Value 54 | indexRange: null 55 | value: DataValue 56 | } 57 | 58 | export type DataTypeInput = DataType | string; 59 | 60 | export type NodeIdentifier = NodeIdentifierNumeric | NodeIdentifierString; 61 | 62 | type NodeIdentifierNumeric = { 63 | identifier: number 64 | type: NodeIdType.NUMERIC 65 | } 66 | 67 | type NodeIdentifierString = { 68 | identifier: string 69 | type: Exclude 70 | } 71 | 72 | /** 73 | * Creates a copy of type , except that all keys are optional. 74 | */ 75 | export type Like = { 76 | [key in keyof T]?: T[key] 77 | } 78 | 79 | /** 80 | * Get a list of enum keys from an enum 81 | */ 82 | export const getEnumKeys = (obj: O): K[] => { 83 | return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[]; 84 | } 85 | 86 | /** 87 | * Give a type to NodeMessageInFlow to voerwrite the unknown type 88 | */ 89 | export type TypedNodeMessage = NodeMessageInFlow & { 90 | payload: T 91 | } 92 | 93 | /** 94 | * A type used to represent anything that might have status codes 95 | */ 96 | export type StatusInput = { 97 | statusCodes?: StatusCode[] 98 | statusCode?: StatusCode 99 | }; -------------------------------------------------------------------------------- /src/types/payloads.ts: -------------------------------------------------------------------------------- 1 | /** 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | All rights reserved. 6 | node-red-contrib-iiot-opcua 7 | */ 8 | 9 | import {CrawlerPayload} from "../opcua-iiot-crawler"; 10 | import {BrowserPayload} from "../opcua-iiot-browser"; 11 | import {ListenPayload} from "../opcua-iiot-listener"; 12 | import {ReadPayload} from "../opcua-iiot-read"; 13 | import {MethodPayload} from "../opcua-iiot-method-caller"; 14 | import {WritePayload} from "../opcua-iiot-write"; 15 | 16 | export type AnyPayload = CrawlerPayload | 17 | BrowserPayload | 18 | ReadPayload | 19 | WritePayload | 20 | ListenPayload | 21 | MethodPayload; 22 | -------------------------------------------------------------------------------- /src/types/placeholders.ts: -------------------------------------------------------------------------------- 1 | /** 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | All rights reserved. 6 | node-red-contrib-iiot-opcua 7 | */ 8 | 9 | export type TodoTypeAny = any; 10 | export type TodoVoidFunction = (...args: any) => void; 11 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | *.log 3 | *.zip -------------------------------------------------------------------------------- /test/core/opcua-iiot-core-browser.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 6 | All rights reserved. 7 | node-red-contrib-iiot-opcua 8 | */ 9 | 'use strict' 10 | 11 | // jest.setTimeout(30000) 12 | 13 | describe('OPC UA Core Browser', function () { 14 | let { default: coreBrowser } = require('../../src/core/opcua-iiot-core-browser') 15 | let { OBJECTS_ROOT } = require('../../src/core/opcua-iiot-core') 16 | const events = require('events') 17 | 18 | let testingOpcUaPort = 0 19 | 20 | beforeAll(() => { 21 | testingOpcUaPort = 50220 22 | }) 23 | 24 | describe('Core Browser unit test', function () { 25 | 26 | it('should return the objects root nodeId', function (done) { 27 | expect(coreBrowser.browseToRoot()).toBe(OBJECTS_ROOT) 28 | done() 29 | }) 30 | 31 | it('should return the default objects nodeId without root in payload request', function (done) { 32 | expect(coreBrowser.extractNodeIdFromTopic({}, {})).toBe(null) 33 | done() 34 | }) 35 | 36 | it('should return the default objects nodeId with empty root in payload request', function (done) { 37 | expect(coreBrowser.extractNodeIdFromTopic({ actiontype: 'browse', root: {} }, {})).toBe(OBJECTS_ROOT) 38 | done() 39 | }) 40 | 41 | it('should return the nodeId from root in payload request', function (done) { 42 | expect(coreBrowser.extractNodeIdFromTopic({ 43 | actiontype: 'browse', 44 | root: { nodeId: 'ns=1;s=MyDemo' } 45 | }, {})).toBe('ns=1;s=MyDemo') 46 | done() 47 | }) 48 | 49 | it('should handle browse error', function (done) { 50 | let statusText = 'idle' 51 | let node = { 52 | showErrors: true, 53 | showStatusActivities: true, 54 | statusText, 55 | status: (state) => { statusText = state.text }, 56 | error: (err, msg) => { coreBrowser.internalDebugLog(err.message) } 57 | } 58 | const statusHandler = (status) => { 59 | node.statusText = status.text || status 60 | } 61 | coreBrowser.browseErrorHandling(node, new Error('Error'), { payload: {} }, [], (err, msg) => {return}, statusHandler, 'idle') 62 | expect(node.statusText).toBe('error') 63 | done() 64 | }) 65 | 66 | it('should return JSON from transformToEntry call', function (done) { 67 | expect(coreBrowser.transformToEntry({})).toBeDefined() 68 | done() 69 | }) 70 | 71 | it('should return reference strings from transformToEntry call', function (done) { 72 | expect(coreBrowser.transformToEntry({ 73 | referenceTypeId: { toString: () => { return '1234' } }, 74 | isForward: true, 75 | nodeId: { toString: () => { return 'ns=1;s=MyDemo' } }, 76 | browseName: { toString: () => { return '1:MyDemo' } }, 77 | displayName: { toString: () => { return 'MyDemo' } }, 78 | nodeClass: { toString: () => { return 'Object' } }, 79 | typeDefinition: { toString: () => { return 'ns=1;i=68' } } 80 | })).toEqual({ 81 | referenceTypeId: '1234', 82 | isForward: true, 83 | nodeId: 'ns=1;s=MyDemo', 84 | browseName: '1:MyDemo', 85 | displayName: 'MyDemo', 86 | nodeClass: 'Object', 87 | typeDefinition: 'ns=1;i=68' 88 | }) 89 | done() 90 | }) 91 | }) 92 | }) 93 | -------------------------------------------------------------------------------- /test/core/opcua-iiot-core-server.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | The BSD 3-Clause License 3 | 4 | Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) 5 | Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 6 | All rights reserved. 7 | node-red-contrib-iiot-opcua 8 | */ 9 | 'use strict' 10 | 11 | // jest.setTimeout(30000) 12 | 13 | let { default: coreServer } = require('../../src/core/opcua-iiot-core-server') 14 | const { OPCUAServer } = require('node-opcua') 15 | const portHelper = require('../helper/test-helper-extensions') 16 | 17 | let opcuaServer = null 18 | 19 | describe('OPC UA Core Server', function () { 20 | 21 | let testingOpcUaPort = 0 22 | 23 | beforeAll(() => { 24 | testingOpcUaPort = 51220 25 | }) 26 | 27 | beforeEach(function (done) { 28 | opcuaServer = null 29 | 30 | testingOpcUaPort = portHelper.getPort(testingOpcUaPort) 31 | const port = testingOpcUaPort 32 | 33 | opcuaServer = new OPCUAServer({ 34 | port, 35 | resourcePath: '/UA/MyLittleTestServer', 36 | buildInfo: { 37 | productName: 'MyTestServer1', 38 | buildNumber: '7658', 39 | buildDate: new Date() 40 | } 41 | }) 42 | done() 43 | }) 44 | 45 | afterEach(function (done) { 46 | opcuaServer.shutdown(function () { 47 | coreServer.destructAddressSpace(done) 48 | }) 49 | }) 50 | 51 | afterAll(function (done) { 52 | opcuaServer = null 53 | coreServer = null 54 | done() 55 | }) 56 | 57 | describe('core server functions', function () { 58 | it('should work on server initialize callback', function (done) { 59 | const run = async () => { 60 | await opcuaServer.initialize() 61 | coreServer.constructAddressSpace(opcuaServer, true).then(() => { 62 | done() 63 | }) 64 | } 65 | run() 66 | }) 67 | 68 | it('should reset count server timeInterval on maxTimeInterval', function (done) { 69 | opcuaServer.initialize(function () { 70 | const run = async () => { 71 | coreServer.constructAddressSpace(opcuaServer, true).then(function () { 72 | coreServer.timeInterval = coreServer.maxTimeInterval 73 | coreServer.simulateVariation({}) 74 | expect(coreServer.timeInterval).toBe(500000) 75 | done() 76 | }) 77 | } 78 | run() 79 | }) 80 | }) 81 | 82 | it('should catch error on start with empty server', function (done) { 83 | coreServer.start(null, null).then().catch(function (err) { 84 | if (err) { 85 | expect(err.message).toBe('Server Not Valid To Start') 86 | done() 87 | } 88 | }) 89 | }) 90 | 91 | it('should catch error on start with empty node', function (done) { 92 | opcuaServer.initialize(function () { 93 | coreServer.constructAddressSpace(opcuaServer, true).then(function () { 94 | coreServer.start(opcuaServer, null).then().catch(function (err) { 95 | if (err) { 96 | expect(err.message).toBe('Node Not Valid To Start') 97 | done() 98 | } 99 | }) 100 | }) 101 | }) 102 | }) 103 | 104 | it('should work on server start callback', function (done) { 105 | opcuaServer.initialize(function () { 106 | coreServer.constructAddressSpace(opcuaServer, true).then(function () { 107 | let node = { iiot: { initialized: false } } 108 | coreServer.start(opcuaServer, node).then(function () { 109 | expect(node.iiot.initialized).toBe(true) 110 | done() 111 | }) 112 | }) 113 | }) 114 | }) 115 | }) 116 | }) 117 | -------------------------------------------------------------------------------- /test/e2e/opcua-iiot-browser-recursive-e2e.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var injectNode = require('../../src/opcua-iiot-inject') 17 | var connectorNode = require('../../src/opcua-iiot-connector') 18 | var inputNode = require('../../src/opcua-iiot-browser') 19 | var listenerNode = require('../../src/opcua-iiot-listener') 20 | var asoNode = require('../../src/opcua-iiot-server-aso') 21 | var serverNode = require('../../src/opcua-iiot-server') 22 | var responseNode = require('../../src/opcua-iiot-response') 23 | var resultFilterNode = require('../../src/opcua-iiot-result-filter') 24 | 25 | var helper = require('node-red-node-test-helper') 26 | var portHelper = require('./../helper/test-helper-extensions') 27 | helper.init(require.resolve('node-red')) 28 | 29 | var browseRecursiveNodesToLoad = [injectNode, asoNode, listenerNode, connectorNode, resultFilterNode, inputNode, serverNode, responseNode] 30 | 31 | var testFlows = require('./flows/browser-recursive-e2e-flows') 32 | 33 | let testingOpcUaPort = 0 34 | 35 | describe('OPC UA Browser recursive with ASO nodes e2e Testing', function () { 36 | 37 | beforeAll(() => { 38 | testingOpcUaPort = 52300 39 | }) 40 | 41 | beforeEach(function (done) { 42 | helper.startServer(function () { 43 | done() 44 | }) 45 | }) 46 | 47 | afterEach(function (done) { 48 | helper.unload().then(function () { 49 | helper.stopServer(function () { 50 | done() 51 | }) 52 | }).catch(function () { 53 | helper.stopServer(function () { 54 | done() 55 | }) 56 | }) 57 | }) 58 | 59 | describe('Browser Recursive node', function () { 60 | it('should verify browser items as result of a recursive browse', function (done) { 61 | const flow = Array.from(testFlows.testBrowseRecursiveASOFlow) 62 | testingOpcUaPort = portHelper.getPort(testingOpcUaPort) 63 | const port = testingOpcUaPort 64 | flow[3].port = port 65 | flow[24].endpoint = 'opc.tcp://localhost:' + port 66 | helper.load(browseRecursiveNodesToLoad, flow, function () { 67 | let n1 = helper.getNode('helperNode') 68 | n1.on('input', function (msg) { 69 | expect(msg.payload.browserResults).toBeDefined() 70 | expect(msg.payload.browserResults).toBeInstanceOf(Array) 71 | expect(msg.payload.browserResults.length).toBe(11) 72 | done() 73 | }) 74 | }) 75 | }) 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /test/e2e/opcua-iiot-flex-server.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var injectNode = require('@node-red/nodes/core/common/20-inject') 17 | var connectorNode = require('../../src/opcua-iiot-connector') 18 | var readNode = require('../../src/opcua-iiot-read') 19 | var serverNode = require('../../src/opcua-iiot-flex-server') 20 | var nodeNode = require('../../src/opcua-iiot-node') 21 | var writeNode = require('../../src/opcua-iiot-write') 22 | 23 | var nodesToLoad = [injectNode, connectorNode, readNode, serverNode, nodeNode, writeNode] 24 | 25 | var helper = require('node-red-node-test-helper') 26 | var portHelper = require('./../helper/test-helper-extensions') 27 | helper.init(require.resolve('node-red')) 28 | 29 | const testFlows = require("./flows/flex-server-e2e-flows") 30 | 31 | let testingOpcUaPort 32 | 33 | describe("OPC UA Flex Server Node E2E testing", () => { 34 | beforeAll(() => { 35 | testingOpcUaPort = 56600 36 | }) 37 | 38 | beforeEach(function (done) { 39 | helper.startServer(function () { 40 | done() 41 | }) 42 | }) 43 | 44 | afterEach(function (done) { 45 | helper.unload().then(function () { 46 | helper.stopServer(function () { 47 | done() 48 | }) 49 | }).catch(function () { 50 | helper.stopServer(function () { 51 | done() 52 | }) 53 | }) 54 | }) 55 | 56 | describe("Flex Server Node", () => { 57 | it("should write 1 then 0 and read 0", (done) => { 58 | const flow = Array.from(testFlows.testFlexServerWriteZeroFlow) 59 | const port = portHelper.getPort(testingOpcUaPort) 60 | flow[1].port = port 61 | flow[11].endpoint = "opc.tcp://localhost:" + port 62 | 63 | helper.load(nodesToLoad, flow, () => { 64 | const helperWrite = helper.getNode("fe3c32681ca3545d") 65 | const helperRead = helper.getNode("f82a189319618699") 66 | let writeCounter = 0; 67 | helperWrite.on('input', (msg) => { 68 | writeCounter++ 69 | expect(msg.payload).toBeDefined() 70 | expect(msg.payload.injectType).toBeDefined() 71 | expect(msg.payload.value).toBeDefined() 72 | expect(msg.payload.valuesToWrite).toBeDefined() 73 | expect(msg.payload.addressSpaceItems).toBeDefined() 74 | expect(msg.payload.value.statusCodes).toBeDefined() 75 | expect(msg.payload.injectType).toBe("write") 76 | expect(msg.payload.value.statusCodes[0].value).toBe(0) 77 | expect(msg.payload.valuesToWrite.length).toBe(1) 78 | 79 | if(writeCounter === 1){ 80 | expect(msg.payload.valuesToWrite[0]).toBe(1) 81 | } else if(writeCounter === 2) { 82 | expect(msg.payload.valuesToWrite[0]).toBe(0) 83 | } 84 | }) 85 | helperRead.on('input', (msg) => { 86 | expect(msg.payload).toBeDefined() 87 | expect(msg.payload.injectType).toBeDefined() 88 | expect(msg.payload.value).toBeDefined() 89 | expect(typeof(msg.payload.value)).toBe(typeof []) 90 | expect(msg.payload.addressSpaceItems).toBeDefined() 91 | expect(msg.payload.value[0].statusCode).toBeDefined() 92 | expect(msg.payload.value[0].statusCode.value).toBe(0) 93 | expect(msg.payload.value[0].value?.value).toBeDefined() 94 | expect(msg.payload.value[0].value?.value).toBe(0) 95 | expect(msg.payload.value[0].value?.dataType).toBe("Double") 96 | expect(msg.payload.injectType).toBe("read") 97 | done() 98 | }) 99 | }) 100 | }) 101 | }) 102 | }) 103 | -------------------------------------------------------------------------------- /test/e2e/opcua-iiot-node-e2e.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var injectNode = require('@node-red/nodes/core/common/20-inject') 17 | 18 | // iiot opc ua nodes 19 | 20 | var serverNode = require('../../src/opcua-iiot-server') 21 | var responseNode = require('../../src/opcua-iiot-response') 22 | var connectorNode = require('../../src/opcua-iiot-connector') 23 | var listenerNode = require('../../src/opcua-iiot-listener') 24 | var inputNode = require('../../src/opcua-iiot-node') 25 | var resultFilterNode = require('../../src/opcua-iiot-result-filter') 26 | 27 | var helper = require('node-red-node-test-helper') 28 | var portHelper = require('./../helper/test-helper-extensions') 29 | helper.init(require.resolve('node-red')) 30 | 31 | var nodeNodesToLoad = [injectNode, inputNode, connectorNode, listenerNode, responseNode, serverNode, resultFilterNode] 32 | 33 | var testFlows = require('./flows/node-e2e-flows') 34 | 35 | let testingOpcUaPort = 0 36 | 37 | describe('OPC UA Node node e2e Testing', function () { 38 | 39 | beforeAll(() => { 40 | testingOpcUaPort = 54700 41 | }) 42 | 43 | beforeEach(function (done) { 44 | helper.startServer(function () { 45 | done() 46 | }) 47 | }) 48 | 49 | afterEach(function (done) { 50 | helper.unload().then(function () { 51 | helper.stopServer(function () { 52 | done() 53 | }) 54 | }).catch(function () { 55 | helper.stopServer(function () { 56 | done() 57 | }) 58 | }) 59 | }) 60 | 61 | describe('Node node', function () { 62 | let msgCounter = 0 63 | 64 | it('should get two messages with payload and value after inject on subscribe with listener', function (done) { 65 | const flow = Array.from(testFlows.testNodeFlow) 66 | testingOpcUaPort = portHelper.getPort(testingOpcUaPort) 67 | const port = testingOpcUaPort 68 | flow[9].port = port 69 | flow[16].endpoint = 'opc.tcp://localhost:' + port 70 | 71 | helper.load(nodeNodesToLoad, flow, function () { 72 | msgCounter = 0 73 | let n2 = helper.getNode('9902563b094d0417') 74 | // let n2 = helper.getNode('53aa4e70.57ae7') 75 | n2.on('input', function (msg) { 76 | msgCounter++ 77 | if (msgCounter === 1) { 78 | expect(msg.payload).toBeDefined() 79 | expect(msg.payload.value).toBeDefined() 80 | } 81 | if (msgCounter === 2) { 82 | expect(msg.payload).toBeDefined() 83 | expect(msg.payload.value).toBeDefined() 84 | done() 85 | } 86 | }) 87 | }) 88 | }) 89 | 90 | it('should get two messages with payload and a value after inject with local value on write', function (done) { 91 | const flow = Array.from(testFlows.testWithValueFromNode) 92 | 93 | helper.load(nodeNodesToLoad, flow, function () { 94 | msgCounter = 0 95 | let n2 = helper.getNode('91ffe73f63f8e590') 96 | n2.on('input', function (msg) { 97 | msgCounter++ 98 | if (msgCounter === 1) { 99 | expect(msg.payload).toBeDefined() 100 | expect(msg.payload.value).toBeDefined() 101 | expect(msg.payload.valuesToWrite).toBeDefined() 102 | expect(msg.payload.valuesToWrite.length).toBe(1) 103 | expect(msg.payload.valuesToWrite[0]).toBe(39) 104 | } 105 | if (msgCounter === 2) { 106 | expect(msg.payload).toBeDefined() 107 | expect(msg.payload.value).toBeDefined() 108 | expect(msg.payload.valuesToWrite).toBeDefined() 109 | expect(msg.payload.valuesToWrite.length).toBe(1) 110 | expect(msg.payload.valuesToWrite[0]).toBe(39) 111 | done() 112 | } 113 | }) 114 | }) 115 | }) 116 | }) 117 | }) 118 | -------------------------------------------------------------------------------- /test/helper/receive.js: -------------------------------------------------------------------------------- 1 | const receive = (node) => { 2 | node.receive({ payload: { value: 'defaultPayload' } }) 3 | } 4 | 5 | module.exports = receive -------------------------------------------------------------------------------- /test/helper/test-helper-extensions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 Klaus Landsdorf (http://node-red.plus/) 6 | * All rights reserved. 7 | * node-red-contrib-iiot-opcua 8 | * 9 | **/ 10 | 11 | 'use strict' 12 | 13 | const net = require('net') 14 | 15 | const isPortTaken = (port) => { 16 | const server = net.createServer() 17 | let result 18 | server.on('error', (err) => { 19 | result = true 20 | }).on('listening', () => { 21 | result = false 22 | server.close() 23 | }) 24 | 25 | server.listen(port) 26 | 27 | return result 28 | } 29 | 30 | module.exports = { 31 | 32 | cleanNodePositionData: (item) => { 33 | let newObject = JSON.parse(JSON.stringify(item)) 34 | 35 | if (newObject.type === 'helper') { 36 | newObject = { 'id': newObject.id, 'type': 'helper', wires: newObject.wires } 37 | } else { 38 | delete newObject['x'] 39 | delete newObject['y'] 40 | delete newObject['z'] 41 | } 42 | 43 | return newObject 44 | }, 45 | 46 | cleanFlowPositionData: (jsonFlow) => { 47 | let cleanFlow = [] 48 | // flow is an array of JSON objects with x,y,z from the Node-RED export 49 | jsonFlow.forEach((item, index, array) => { 50 | let newObject = JSON.parse(JSON.stringify(item)) 51 | if (newObject.type === 'helper') { 52 | cleanFlow.push({ 'id': newObject.id, 'type': 'helper', wires: newObject.wires }) 53 | } else { 54 | delete newObject['x'] 55 | delete newObject['y'] 56 | delete newObject['z'] 57 | cleanFlow.push(newObject) 58 | } 59 | }) 60 | 61 | return cleanFlow 62 | }, 63 | 64 | getPort: (portOffset) => { 65 | let testPort = portOffset 66 | 67 | do { 68 | testPort++ 69 | } while (isPortTaken(testPort)) 70 | 71 | return testPort 72 | } 73 | } -------------------------------------------------------------------------------- /test/units/flows/browser-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitBrowseFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "e41e66b2c57b1657", 9 | "type": "tab", 10 | "label": "Test Unit Browse Flow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "4ac0b7c8.bebe18", 17 | "type": "OPCUA-IIoT-Browser", 18 | "z": "e41e66b2c57b1657", 19 | "connector": "", 20 | "nodeId": "ns=1;i=1234", 21 | "name": "TestNameBrowser", 22 | "justValue": true, 23 | "sendNodesToRead": false, 24 | "sendNodesToListener": false, 25 | "sendNodesToBrowser": false, 26 | "multipleOutputs": false, 27 | "recursiveBrowse": false, 28 | "recursiveDepth": "", 29 | "delayPerMessage": "", 30 | "showStatusActivities": false, 31 | "showErrors": false, 32 | "x": 190, 33 | "y": 140, 34 | "wires": [ 35 | [ 36 | "059bc7271eb02b9d" 37 | ] 38 | ] 39 | }, 40 | { 41 | "id": "059bc7271eb02b9d", 42 | "type": "helper", 43 | "z": "e41e66b2c57b1657", 44 | "name": "helper 1", 45 | "active": true, 46 | "tosidebar": true, 47 | "console": false, 48 | "tostatus": false, 49 | "complete": "payload", 50 | "targetType": "msg", 51 | "statusVal": "", 52 | "statusType": "auto", 53 | "x": 440, 54 | "y": 140, 55 | "wires": [] 56 | } 57 | ]) 58 | } -------------------------------------------------------------------------------- /test/units/flows/connector-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitConnectorFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "b79fdf68790c5ed2", 9 | "type": "tab", 10 | "label": "testUnitConnectorFlow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "n4", 17 | "type": "OPCUA-IIoT-Connector", 18 | "z": "b79fdf68790c5ed2", 19 | "discoveryUrl": "", 20 | "endpoint": "", 21 | "keepSessionAlive": false, 22 | "loginEnabled": false, 23 | "securityPolicy": "None", 24 | "securityMode": "None", 25 | "name": "TESTSERVER", 26 | "showErrors": false, 27 | "individualCerts": false, 28 | "publicCertificateFile": "", 29 | "privateKeyFile": "", 30 | "defaultSecureTokenLifetime": "60000", 31 | "endpointMustExist": false, 32 | "autoSelectRightEndpoint": false, 33 | "strategyMaxRetry": "", 34 | "strategyInitialDelay": "", 35 | "strategyMaxDelay": "", 36 | "strategyRandomisationFactor": "", 37 | "requestedSessionTimeout": "", 38 | "connectionStartDelay": "", 39 | "reconnectDelay": "", 40 | "maxBadSessionRequests": "" 41 | } 42 | ]), 43 | 44 | "testUnitConnectorDefaultsFlow": helperExtensions.cleanFlowPositionData([ 45 | { 46 | "id": "b79fdf68790c5ed2", 47 | "type": "tab", 48 | "label": "testUnitConnectorDefaultsFlow", 49 | "disabled": false, 50 | "info": "", 51 | "env": [] 52 | }, 53 | { 54 | "id": "n4", 55 | "type": "OPCUA-IIoT-Connector", 56 | "z": "b79fdf68790c5ed2", 57 | "discoveryUrl": "", 58 | "endpoint": "opc.tcp://localhost:55388/", 59 | "keepSessionAlive": true, 60 | "loginEnabled": false, 61 | "securityPolicy": "None", 62 | "securityMode": "None", 63 | "name": "LOCAL SERVER", 64 | "showErrors": false, 65 | "individualCerts": false, 66 | "publicCertificateFile": "", 67 | "privateKeyFile": "", 68 | "defaultSecureTokenLifetime": "", 69 | "endpointMustExist": false, 70 | "autoSelectRightEndpoint": false, 71 | "strategyMaxRetry": "", 72 | "strategyInitialDelay": "", 73 | "strategyMaxDelay": "", 74 | "strategyRandomisationFactor": "", 75 | "requestedSessionTimeout": "", 76 | "connectionStartDelay": "", 77 | "connectionStopDelay": "", 78 | "reconnectDelay": "", 79 | "maxBadSessionRequests": 10 80 | } 81 | ]), 82 | 83 | "testUnitConnectorGeneratedDefaultsFlow": helperExtensions.cleanFlowPositionData([ 84 | { 85 | "id": "b79fdf68790c5ed2", 86 | "type": "tab", 87 | "label": "testUnitConnectorDefaultFlowFlow", 88 | "disabled": false, 89 | "info": "", 90 | "env": [] 91 | }, 92 | { 93 | "id": "7291451d5224efde", 94 | "type": "OPCUA-IIoT-Read", 95 | "z": "b79fdf68790c5ed2", 96 | "attributeId": 0, 97 | "maxAge": 1, 98 | "depth": 1, 99 | "connector": "594b2860fa40bda5", 100 | "name": "", 101 | "justValue": true, 102 | "showStatusActivities": false, 103 | "showErrors": false, 104 | "parseStrings": false, 105 | "historyDays": 1, 106 | "x": 440, 107 | "y": 250, 108 | "wires": [ 109 | [] 110 | ] 111 | }, 112 | { 113 | "id": "594b2860fa40bda5", 114 | "type": "OPCUA-IIoT-Connector", 115 | "discoveryUrl": "", 116 | "endpoint": "opc.tcp://localhost:44840/", 117 | "keepSessionAlive": true, 118 | "loginEnabled": false, 119 | "securityPolicy": "None", 120 | "securityMode": "None", 121 | "name": "LOCAL SERVER", 122 | "showErrors": false, 123 | "individualCerts": false, 124 | "publicCertificateFile": "", 125 | "privateKeyFile": "", 126 | "defaultSecureTokenLifetime": "", 127 | "endpointMustExist": false, 128 | "autoSelectRightEndpoint": false, 129 | "strategyMaxRetry": "", 130 | "strategyInitialDelay": "", 131 | "strategyMaxDelay": "", 132 | "strategyRandomisationFactor": "", 133 | "requestedSessionTimeout": "", 134 | "connectionStartDelay": "", 135 | "reconnectDelay": "", 136 | "maxBadSessionRequests": 10 137 | } 138 | ]) 139 | } -------------------------------------------------------------------------------- /test/units/flows/crawler-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitCrawlerFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "4d6ae11544766677", 9 | "type": "tab", 10 | "label": "Test Crawler Flow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "13e5e190.e34516", 17 | "type": "OPCUA-IIoT-Crawler", 18 | "z": "4d6ae11544766677", 19 | "connector": "", 20 | "name": "TestNameCrawler", 21 | "justValue": true, 22 | "singleResult": false, 23 | "showStatusActivities": false, 24 | "showErrors": false, 25 | "activateUnsetFilter": true, 26 | "activateFilters": false, 27 | "negateFilter": false, 28 | "filters": [ 29 | { 30 | "name": "Organizes", 31 | "value": "" 32 | }, 33 | { 34 | "name": "GeneratesEvent", 35 | "value": "" 36 | }, 37 | { 38 | "name": "References", 39 | "value": "" 40 | } 41 | ], 42 | "delayPerMessage": "", 43 | "timeout": "", 44 | "x": 370, 45 | "y": 160, 46 | "wires": [ 47 | [] 48 | ] 49 | } 50 | ]), 51 | "testUnitDefaultCrawlerFlow" : helperExtensions.cleanFlowPositionData([ 52 | { 53 | "id": "4d6ae11544766677", 54 | "type": "tab", 55 | "label": "Test Unit Default Crawler Flow", 56 | "disabled": false, 57 | "info": "", 58 | "env": [] 59 | }, 60 | { 61 | "id": "4bf9f1cbd3fe98bc", 62 | "type": "OPCUA-IIoT-Crawler", 63 | "z": "4d6ae11544766677", 64 | "connector": "", 65 | "name": "", 66 | "justValue": true, 67 | "singleResult": false, 68 | "showStatusActivities": false, 69 | "showErrors": false, 70 | "activateUnsetFilter": false, 71 | "activateFilters": false, 72 | "negateFilter": false, 73 | "filters": [], 74 | "delayPerMessage": 0.2, 75 | "timeout": 30, 76 | "x": 220, 77 | "y": 180, 78 | "wires": [ 79 | [ 80 | "647418b9eaf63928" 81 | ] 82 | ] 83 | }, 84 | { 85 | "id": "647418b9eaf63928", 86 | "type": "helper", 87 | "z": "4d6ae11544766677", 88 | "name": "helper 1", 89 | "active": true, 90 | "tosidebar": true, 91 | "console": false, 92 | "tostatus": false, 93 | "complete": "false", 94 | "statusVal": "", 95 | "statusType": "auto", 96 | "x": 420, 97 | "y": 180, 98 | "wires": [] 99 | } 100 | ]) 101 | } -------------------------------------------------------------------------------- /test/units/flows/discovery-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitDiscoveryFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "86a4cc825a400180", 9 | "type": "tab", 10 | "label": "testUnitDiscoveryFlow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "d9db5e281fac4a0c", 17 | "type": "OPCUA-IIoT-Discovery", 18 | "z": "86a4cc825a400180", 19 | "name": "TestName", 20 | "discoveryPort": "", 21 | "x": 360, 22 | "y": 220, 23 | "wires": [ 24 | [ 25 | "7d54598b282a3ed9" 26 | ] 27 | ] 28 | }, 29 | { 30 | "id": "7d54598b282a3ed9", 31 | "type": "helper", 32 | "z": "86a4cc825a400180", 33 | "name": "", 34 | "tosidebar": true, 35 | "console": false, 36 | "tostatus": false, 37 | "complete": "payload", 38 | "targetType": "msg", 39 | "statusVal": "", 40 | "statusType": "auto", 41 | "x": 380, 42 | "y": 340, 43 | "wires": [] 44 | } 45 | ]), 46 | "testUnitDiscoveryNullPortFlow": helperExtensions.cleanFlowPositionData([ 47 | { 48 | "id": "b79fdf68790c5ed2", 49 | "type": "tab", 50 | "label": "testUnitDiscoveryNullPortFlow", 51 | "disabled": false, 52 | "info": "", 53 | "env": [] 54 | }, 55 | { 56 | "id": "n1dsf2", 57 | "type": "OPCUA-IIoT-Discovery", 58 | "z": "b79fdf68790c5ed2", 59 | "name": "TestName", 60 | "discoveryPort": "", 61 | "x": 370, 62 | "y": 210, 63 | "wires": [ 64 | [ 65 | "n2dsf1" 66 | ] 67 | ] 68 | }, 69 | { 70 | "id": "n2dsf1", 71 | "type": "helper", 72 | "z": "b79fdf68790c5ed2", 73 | "name": "", 74 | "tosidebar": true, 75 | "console": false, 76 | "tostatus": false, 77 | "complete": "payload", 78 | "targetType": "msg", 79 | "statusVal": "", 80 | "statusType": "auto", 81 | "x": 390, 82 | "y": 160, 83 | "wires": [] 84 | } 85 | ]), 86 | "testUnitDiscoveryNullPortAndInjectFlow": helperExtensions.cleanFlowPositionData([ 87 | { 88 | "id": "b79fdf68790c5ed2", 89 | "type": "tab", 90 | "label": "testUnitDiscoveryNullPortAndInjectFlow", 91 | "disabled": false, 92 | "info": "", 93 | "env": [] 94 | }, 95 | { 96 | "id": "n1edf1", 97 | "type": "inject", 98 | "z": "b79fdf68790c5ed2", 99 | "name": "", 100 | "props": [ 101 | { 102 | "p": "payload" 103 | }, 104 | { 105 | "p": "topic", 106 | "vt": "str" 107 | } 108 | ], 109 | "repeat": "", 110 | "crontab": "", 111 | "once": true, 112 | "onceDelay": "4", 113 | "topic": "TestTopic", 114 | "payload": "", 115 | "payloadType": "date", 116 | "x": 290, 117 | "y": 320, 118 | "wires": [ 119 | [ 120 | "n2edf1", 121 | "n3edf1" 122 | ] 123 | ] 124 | }, 125 | { 126 | "id": "n2edf1", 127 | "type": "helper", 128 | "z": "b79fdf68790c5ed2", 129 | "name": "", 130 | "tosidebar": true, 131 | "console": false, 132 | "tostatus": false, 133 | "complete": "payload", 134 | "targetType": "msg", 135 | "statusVal": "", 136 | "statusType": "auto", 137 | "x": 450, 138 | "y": 440, 139 | "wires": [] 140 | }, 141 | { 142 | "id": "n3edf1", 143 | "type": "OPCUA-IIoT-Discovery", 144 | "z": "b79fdf68790c5ed2", 145 | "name": "TestName", 146 | "discoveryPort": "", 147 | "x": 180, 148 | "y": 170, 149 | "wires": [ 150 | [ 151 | "n4edf1" 152 | ] 153 | ] 154 | }, 155 | { 156 | "id": "n4edf1", 157 | "type": "helper", 158 | "z": "b79fdf68790c5ed2", 159 | "name": "", 160 | "tosidebar": true, 161 | "console": false, 162 | "tostatus": false, 163 | "complete": "payload", 164 | "targetType": "msg", 165 | "statusVal": "", 166 | "statusType": "auto", 167 | "x": 390, 168 | "y": 100, 169 | "wires": [] 170 | } 171 | ]) 172 | } -------------------------------------------------------------------------------- /test/units/flows/event-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitEventFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "b79fdf68790c5ed2", 9 | "type": "tab", 10 | "label": "testUnitEventFlow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "n1evf1", 17 | "type": "inject", 18 | "z": "b79fdf68790c5ed2", 19 | "name": "", 20 | "props": [ 21 | { 22 | "p": "payload" 23 | }, 24 | { 25 | "p": "topic", 26 | "vt": "str" 27 | } 28 | ], 29 | "repeat": "", 30 | "crontab": "", 31 | "once": true, 32 | "onceDelay": "4", 33 | "topic": "TestTopic", 34 | "payload": "{\"value\":1000}", 35 | "payloadType": "json", 36 | "x": 470, 37 | "y": 400, 38 | "wires": [ 39 | [ 40 | "n2evf1", 41 | "n3evf1" 42 | ] 43 | ] 44 | }, 45 | { 46 | "id": "n2evf1", 47 | "type": "helper", 48 | "z": "b79fdf68790c5ed2", 49 | "name": "", 50 | "tosidebar": true, 51 | "console": false, 52 | "tostatus": false, 53 | "complete": "payload", 54 | "targetType": "msg", 55 | "statusVal": "", 56 | "statusType": "auto", 57 | "x": 700, 58 | "y": 660, 59 | "wires": [] 60 | }, 61 | { 62 | "id": "n3evf1", 63 | "type": "OPCUA-IIoT-Event", 64 | "z": "b79fdf68790c5ed2", 65 | "eventType": "i=2041", 66 | "eventTypeLabel": "BaseTypeEvent", 67 | "queueSize": 10, 68 | "usingListener": true, 69 | "name": "TestName", 70 | "showStatusActivities": false, 71 | "showErrors": false, 72 | "x": 830, 73 | "y": 300, 74 | "wires": [ 75 | [ 76 | "n4evf1" 77 | ] 78 | ] 79 | }, 80 | { 81 | "id": "n4evf1", 82 | "type": "helper", 83 | "z": "b79fdf68790c5ed2", 84 | "name": "", 85 | "tosidebar": true, 86 | "console": false, 87 | "tostatus": false, 88 | "complete": "payload", 89 | "targetType": "msg", 90 | "statusVal": "", 91 | "statusType": "auto", 92 | "x": 480, 93 | "y": 180, 94 | "wires": [] 95 | } 96 | ]) 97 | } -------------------------------------------------------------------------------- /test/units/flows/flex-connector-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitFlexConnectorFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "b79fdf68790c5ed2", 9 | "type": "tab", 10 | "label": "testUnitFlexConnectorFlow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "14d54403.f94f04", 17 | "type": "OPCUA-IIoT-Flex-Connector", 18 | "z": "b79fdf68790c5ed2", 19 | "name": "TestFlexConnector", 20 | "showStatusActivities": false, 21 | "showErrors": false, 22 | "connector": "", 23 | "x": 510, 24 | "y": 260, 25 | "wires": [ 26 | [] 27 | ] 28 | } 29 | ]), 30 | 31 | "testUnitFlexConnectorShowActivitiesFlow": helperExtensions.cleanFlowPositionData([ 32 | { 33 | "id": "b79fdf68790c5ed2", 34 | "type": "tab", 35 | "label": "testUnitFlexConnectorShowActivitiesFlow", 36 | "disabled": false, 37 | "info": "", 38 | "env": [] 39 | }, 40 | { 41 | "id": "14d54403.f94f05", 42 | "type": "OPCUA-IIoT-Flex-Connector", 43 | "z": "b79fdf68790c5ed2", 44 | "name": "TestFlexConnector2", 45 | "showStatusActivities": true, 46 | "showErrors": false, 47 | "connector": "", 48 | "x": 440, 49 | "y": 260, 50 | "wires": [ 51 | [] 52 | ] 53 | } 54 | ]) 55 | } -------------------------------------------------------------------------------- /test/units/flows/listener-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitListenerFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "b79fdf68790c5ed2", 9 | "type": "tab", 10 | "label": "testUnitListenerFlow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "bee3e3b0.ca1a08", 17 | "type": "OPCUA-IIoT-Listener", 18 | "z": "b79fdf68790c5ed2", 19 | "connector": "", 20 | "action": "subscribe", 21 | "queueSize": 10, 22 | "name": "TestListener", 23 | "topic": "", 24 | "justValue": true, 25 | "useGroupItems": false, 26 | "showStatusActivities": false, 27 | "showErrors": false, 28 | "x": 470, 29 | "y": 240, 30 | "wires": [ 31 | [] 32 | ] 33 | } 34 | ]) 35 | } -------------------------------------------------------------------------------- /test/units/flows/method-caller-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitMethodCallerFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "b79fdf68790c5ed2", 9 | "type": "tab", 10 | "label": "testUnitMethodCallerFlow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "706d43c1.90baac", 17 | "type": "OPCUA-IIoT-Method-Caller", 18 | "z": "b79fdf68790c5ed2", 19 | "connector": "", 20 | "objectId": "ns=1;i=1234", 21 | "methodId": "ns=1;i=12345", 22 | "methodType": "basic", 23 | "value": "", 24 | "justValue": false, 25 | "name": "TestName", 26 | "showStatusActivities": false, 27 | "showErrors": true, 28 | "inputArguments": [ 29 | { 30 | "name": "barks", 31 | "dataType": "UInt32", 32 | "value": "3" 33 | }, 34 | { 35 | "name": "volume", 36 | "dataType": "UInt32", 37 | "value": "6" 38 | } 39 | ], 40 | "x": 480, 41 | "y": 230, 42 | "wires": [ 43 | [] 44 | ] 45 | } 46 | ]), 47 | 48 | "testUnitMethodCallerNotConfiguredFlow": helperExtensions.cleanFlowPositionData([ 49 | { 50 | "id": "b79fdf68790c5ed2", 51 | "type": "tab", 52 | "label": "testUnitMethodCallerNotConfiguredFlow", 53 | "disabled": false, 54 | "info": "", 55 | "env": [] 56 | }, 57 | { 58 | "id": "706d43c1.90babc", 59 | "type": "OPCUA-IIoT-Method-Caller", 60 | "z": "b79fdf68790c5ed2", 61 | "connector": "", 62 | "objectId": "", 63 | "methodId": "", 64 | "methodType": "basic", 65 | "value": "", 66 | "justValue": false, 67 | "name": "TestName", 68 | "showStatusActivities": false, 69 | "showErrors": true, 70 | "inputArguments": [], 71 | "x": 440, 72 | "y": 250, 73 | "wires": [ 74 | [] 75 | ] 76 | } 77 | ]) 78 | } -------------------------------------------------------------------------------- /test/units/flows/read-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitReadFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "b79fdf68790c5ed2", 9 | "type": "tab", 10 | "label": "testUnitReadFlow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "41cb29d.1ab50d8", 17 | "type": "OPCUA-IIoT-Read", 18 | "z": "b79fdf68790c5ed2", 19 | "attributeId": 0, 20 | "maxAge": 1, 21 | "depth": 1, 22 | "connector": "", 23 | "name": "ReadAll", 24 | "justValue": true, 25 | "showStatusActivities": false, 26 | "showErrors": false, 27 | "parseStrings": false, 28 | "historyDays": "", 29 | "x": 400, 30 | "y": 170, 31 | "wires": [ 32 | [ 33 | "508385cc4a0a910b" 34 | ] 35 | ] 36 | }, 37 | { 38 | "id": "508385cc4a0a910b", 39 | "type": "helper", 40 | "z": "b79fdf68790c5ed2", 41 | "name": "helper 1", 42 | "active": true, 43 | "tosidebar": true, 44 | "console": false, 45 | "tostatus": false, 46 | "complete": "false", 47 | "statusVal": "", 48 | "statusType": "auto", 49 | "x": 600, 50 | "y": 170, 51 | "wires": [] 52 | } 53 | ]) 54 | } -------------------------------------------------------------------------------- /test/units/flows/server-aso-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitServerASOFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "628990c93a2db9aa", 9 | "type": "tab", 10 | "label": "Test Unit Server ASO Flow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "7cb85115.7635", 17 | "type": "OPCUA-IIoT-Server-ASO", 18 | "z": "628990c93a2db9aa", 19 | "nodeId": "ns=1;s=TestVariables", 20 | "browsename": "TestVariables", 21 | "displayname": "Test Variables", 22 | "objecttype": "FolderType", 23 | "datatype": "Double", 24 | "value": "1.0", 25 | "referenceNodeId": "ns=0;i=85", 26 | "referencetype": "Organizes", 27 | "name": "Folder", 28 | "x": 210, 29 | "y": 280, 30 | "wires": [ 31 | [ 32 | "a51de437b1ecc2f5" 33 | ] 34 | ] 35 | }, 36 | { 37 | "id": "a51de437b1ecc2f5", 38 | "type": "helper", 39 | "z": "628990c93a2db9aa", 40 | "name": "helper 1", 41 | "active": true, 42 | "tosidebar": true, 43 | "console": false, 44 | "tostatus": false, 45 | "complete": "payload", 46 | "targetType": "msg", 47 | "statusVal": "", 48 | "statusType": "auto", 49 | "x": 380, 50 | "y": 280, 51 | "wires": [] 52 | } 53 | ]) 54 | } -------------------------------------------------------------------------------- /test/units/flows/server-cmd-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitServerCommandFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "b79fdf68790c5ed2", 9 | "type": "tab", 10 | "label": "testUnitServerCommandFlow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "n3cmdf1", 17 | "type": "OPCUA-IIoT-Server-Command", 18 | "z": "b79fdf68790c5ed2", 19 | "commandtype": "restart", 20 | "nodeId": "", 21 | "name": "TestName", 22 | "x": 130, 23 | "y": 200, 24 | "wires": [ 25 | [] 26 | ] 27 | } 28 | ]) 29 | } -------------------------------------------------------------------------------- /test/units/flows/server-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitServerWithDemoFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "b233bdeab126bfd4", 9 | "type": "tab", 10 | "label": "Test Server Demo Flow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "6ec4ef50.86dc1", 17 | "type": "OPCUA-IIoT-Server", 18 | "z": "b233bdeab126bfd4", 19 | "port": "51250", 20 | "endpoint": "", 21 | "acceptExternalCommands": true, 22 | "maxAllowedSessionNumber": "", 23 | "maxConnectionsPerEndpoint": "", 24 | "maxAllowedSubscriptionNumber": "", 25 | "alternateHostname": "", 26 | "name": "DEMOSERVER", 27 | "showStatusActivities": false, 28 | "showErrors": false, 29 | "asoDemo": true, 30 | "allowAnonymous": true, 31 | "individualCerts": false, 32 | "isAuditing": false, 33 | "serverDiscovery": false, 34 | "users": [], 35 | "xmlsets": [], 36 | "publicCertificateFile": "", 37 | "privateCertificateFile": "", 38 | "registerServerMethod": 1, 39 | "discoveryServerEndpointUrl": "", 40 | "capabilitiesForMDNS": "", 41 | "maxNodesPerRead": 1000, 42 | "maxNodesPerBrowse": 2000, 43 | "delayToClose": 200, 44 | "x": 380, 45 | "y": 200, 46 | "wires": [ 47 | [] 48 | ] 49 | } 50 | ]), 51 | 52 | "testUnitServerWithoutDemoFlow": helperExtensions.cleanFlowPositionData([ 53 | { 54 | "id": "4a4f0ffea30b6dcb", 55 | "type": "tab", 56 | "label": "Test Server Flow", 57 | "disabled": false, 58 | "info": "", 59 | "env": [] 60 | }, 61 | { 62 | "id": "6ec4ef50.86dc2", 63 | "type": "OPCUA-IIoT-Server", 64 | "z": "4a4f0ffea30b6dcb", 65 | "port": "51201", 66 | "endpoint": "", 67 | "acceptExternalCommands": true, 68 | "maxAllowedSessionNumber": "", 69 | "maxConnectionsPerEndpoint": "", 70 | "maxAllowedSubscriptionNumber": "", 71 | "alternateHostname": "", 72 | "name": "DEMOSERVER", 73 | "showStatusActivities": false, 74 | "showErrors": false, 75 | "asoDemo": false, 76 | "allowAnonymous": true, 77 | "individualCerts": false, 78 | "isAuditing": false, 79 | "serverDiscovery": true, 80 | "users": [], 81 | "xmlsets": [], 82 | "publicCertificateFile": "", 83 | "privateCertificateFile": "", 84 | "registerServerMethod": 1, 85 | "discoveryServerEndpointUrl": "", 86 | "capabilitiesForMDNS": "", 87 | "maxNodesPerRead": 1000, 88 | "maxNodesPerBrowse": 2000, 89 | "delayToClose": "", 90 | "x": 460, 91 | "y": 140, 92 | "wires": [ 93 | [] 94 | ] 95 | } 96 | ]) 97 | } -------------------------------------------------------------------------------- /test/units/flows/write-flows.js: -------------------------------------------------------------------------------- 1 | 2 | const helperExtensions = require('../../helper/test-helper-extensions') 3 | 4 | module.exports = { 5 | 6 | "testUnitWriteFlow": helperExtensions.cleanFlowPositionData([ 7 | { 8 | "id": "b79fdf68790c5ed2", 9 | "type": "tab", 10 | "label": "testUnitWriteFlow", 11 | "disabled": false, 12 | "info": "", 13 | "env": [] 14 | }, 15 | { 16 | "id": "34d2c6bc.43275b", 17 | "type": "OPCUA-IIoT-Write", 18 | "z": "b79fdf68790c5ed2", 19 | "connector": "", 20 | "name": "TestWrite", 21 | "justValue": false, 22 | "showStatusActivities": false, 23 | "showErrors": true, 24 | "x": 220, 25 | "y": 170, 26 | "wires": [ 27 | [] 28 | ] 29 | } 30 | ]) 31 | } -------------------------------------------------------------------------------- /test/units/opcua-iiot-browser.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var functionNode = require('@node-red/nodes/core/function/10-function') 17 | var injectNode = require('@node-red/nodes/core/common/20-inject') 18 | var browserNode = require('../../src/opcua-iiot-browser') 19 | 20 | var helper = require('node-red-node-test-helper') 21 | var portHelper = require('./../helper/test-helper-extensions') 22 | helper.init(require.resolve('node-red')) 23 | 24 | var browseNodesToLoad = [injectNode, functionNode, browserNode] 25 | 26 | var testFlows = require('./flows/browser-flows') 27 | 28 | let testingOpcUaPort = 0 29 | 30 | describe('OPC UA Browser node Unit Testing', function () { 31 | 32 | beforeAll(() => { 33 | testingOpcUaPort = 57050 34 | }) 35 | 36 | beforeEach(function (done) { 37 | helper.startServer(function () { 38 | done() 39 | }) 40 | }) 41 | 42 | afterEach(function (done) { 43 | helper.unload().then(function () { 44 | helper.stopServer(function () { 45 | done() 46 | }) 47 | }).catch(function () { 48 | helper.stopServer(function () { 49 | done() 50 | }) 51 | }) 52 | }) 53 | 54 | describe('Browser node', function () { 55 | it('should be loaded with correct defaults', function (done) { 56 | const flow = Array.from(testFlows.testUnitBrowseFlow) 57 | 58 | helper.load(browseNodesToLoad, flow, 59 | function () { 60 | let nodeUnderTest = helper.getNode('4ac0b7c8.bebe18') 61 | expect(nodeUnderTest.nodeId).toBe('ns=1;i=1234') 62 | expect(nodeUnderTest.name).toBe('TestNameBrowser') 63 | expect(nodeUnderTest.justValue).toBe(true) 64 | expect(nodeUnderTest.sendNodesToRead).toBe(false) 65 | expect(nodeUnderTest.sendNodesToListener).toBe(false) 66 | expect(nodeUnderTest.sendNodesToBrowser).toBe(false) 67 | expect(nodeUnderTest.multipleOutputs).toBe(false) 68 | expect(nodeUnderTest.recursiveBrowse).toBe(false) 69 | expect(nodeUnderTest.recursiveDepth).toBe(1) 70 | expect(nodeUnderTest.delayPerMessage).toBe(0.2) 71 | expect(nodeUnderTest.showStatusActivities).toBe(false) 72 | expect(nodeUnderTest.showErrors).toBe(false) 73 | setTimeout(done, 3000) 74 | }) 75 | }) 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /test/units/opcua-iiot-crawler.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var injectNode = require('@node-red/nodes/core/common/20-inject') 17 | var functionNode = require('@node-red/nodes/core/function/10-function') 18 | var inputNode = require('../../src/opcua-iiot-crawler') 19 | 20 | var helper = require('node-red-node-test-helper') 21 | var portHelper = require('./../helper/test-helper-extensions') 22 | helper.init(require.resolve('node-red')) 23 | 24 | var crawlerNodesToLoad = [injectNode, functionNode, inputNode] 25 | 26 | var testFlows = require('./flows/crawler-flows') 27 | 28 | let testingOpcUaPort = 0 29 | 30 | describe('OPC UA Crawler node Unit Testing', function () { 31 | 32 | beforeAll(() => { 33 | testingOpcUaPort = 57250 34 | }) 35 | 36 | beforeEach(function (done) { 37 | helper.startServer(function () { 38 | done() 39 | }) 40 | }) 41 | 42 | afterEach(function (done) { 43 | helper.unload().then(function () { 44 | helper.stopServer(function () { 45 | done() 46 | }) 47 | }).catch(function () { 48 | helper.stopServer(function () { 49 | done() 50 | }) 51 | }) 52 | }) 53 | 54 | describe('Crawler node', function () { 55 | it('should be loaded', function (done) { 56 | helper.load(crawlerNodesToLoad, testFlows.testUnitCrawlerFlow, 57 | function () { 58 | let nodeUnderTest = helper.getNode('13e5e190.e34516') 59 | expect(nodeUnderTest.name).toBe('TestNameCrawler') 60 | expect(nodeUnderTest.justValue).toBe(true) 61 | expect(nodeUnderTest.singleResult).toBe(false) 62 | expect(nodeUnderTest.showStatusActivities).toBe(false) 63 | expect(nodeUnderTest.showErrors).toBe(false) 64 | done() 65 | }) 66 | }) 67 | 68 | it('should be loaded with default settings', function (done) { 69 | helper.load(crawlerNodesToLoad, testFlows.testUnitDefaultCrawlerFlow, 70 | function () { 71 | let nodeUnderTest = helper.getNode('4bf9f1cbd3fe98bc') 72 | expect(nodeUnderTest.name).toBe('') 73 | expect(nodeUnderTest.justValue).toBe(true) 74 | expect(nodeUnderTest.singleResult).toBe(false) 75 | expect(nodeUnderTest.showStatusActivities).toBe(false) 76 | expect(nodeUnderTest.showErrors).toBe(false) 77 | expect(nodeUnderTest.activateUnsetFilter).toBe(false) 78 | expect(nodeUnderTest.activateFilters).toBe(false) 79 | expect(nodeUnderTest.negateFilter).toBe(false) 80 | expect(nodeUnderTest.filters).toBeInstanceOf(Array) 81 | expect(nodeUnderTest.filters.length).toBe(0) 82 | expect(nodeUnderTest.delayPerMessage).toBe(0.2) 83 | expect(nodeUnderTest.timeout).toBe(30) 84 | done() 85 | }) 86 | }) 87 | 88 | }) 89 | }) 90 | -------------------------------------------------------------------------------- /test/units/opcua-iiot-discovery.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var injectNode = require('@node-red/nodes/core/common/20-inject') 17 | var inputNode = require('../../src/opcua-iiot-discovery') 18 | 19 | var helper = require('node-red-node-test-helper') 20 | var portHelper = require('./../helper/test-helper-extensions') 21 | helper.init(require.resolve('node-red')) 22 | 23 | var testFlows = require('./flows/discovery-flows') 24 | 25 | let testingOpcUaPort = 0 26 | 27 | describe('OPC UA Discovery node Unit Testing', function () { 28 | 29 | beforeAll(() => { 30 | testingOpcUaPort = 57350 31 | }) 32 | 33 | beforeEach(function (done) { 34 | helper.startServer(function () { 35 | done() 36 | }) 37 | }) 38 | 39 | afterEach(function (done) { 40 | helper.unload().then(function () { 41 | helper.stopServer(function () { 42 | done() 43 | }) 44 | }).catch(function () { 45 | helper.stopServer(function () { 46 | done() 47 | }) 48 | }) 49 | }) 50 | 51 | describe('Discovery node', function () { 52 | it('should be loaded', function (done) { 53 | helper.load( 54 | [inputNode], testFlows.testUnitDiscoveryFlow, 55 | () => { 56 | let nodeUnderTest = helper.getNode('d9db5e281fac4a0c') 57 | expect(nodeUnderTest.type).toBe('OPCUA-IIoT-Discovery') 58 | expect(nodeUnderTest.name).toBe('TestName') 59 | setTimeout(done, 2000) 60 | }) 61 | }) 62 | 63 | it('should be loaded with default port', function (done) { 64 | helper.load( 65 | [inputNode], testFlows.testUnitDiscoveryNullPortFlow, 66 | function () { 67 | let nodeUnderTest = helper.getNode('n1dsf2') 68 | expect(nodeUnderTest.type).toBe('OPCUA-IIoT-Discovery') 69 | expect(nodeUnderTest.name).toBe('TestName') 70 | setTimeout(done, 2000) 71 | }) 72 | }) 73 | 74 | it('should be loaded with default port and send msg after inject', function (done) { 75 | helper.load( 76 | [injectNode, inputNode], testFlows.testUnitDiscoveryNullPortAndInjectFlow, 77 | function () { 78 | let nodeUnderTest = helper.getNode('n4edf1') 79 | nodeUnderTest.on('input', (msg) => { 80 | expect(msg.payload.discoveryUrls).toBeDefined() 81 | expect(msg.payload.endpoints).toBeDefined() 82 | setTimeout(done, 2000) 83 | }) 84 | }) 85 | }) 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /test/units/opcua-iiot-event.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var injectNode = require('@node-red/nodes/core/common/20-inject') 17 | var inputNode = require('../../src/opcua-iiot-event') 18 | 19 | var helper = require('node-red-node-test-helper') 20 | var portHelper = require('./../helper/test-helper-extensions') 21 | helper.init(require.resolve('node-red')) 22 | 23 | var testFlows = require('./flows/event-flows') 24 | 25 | let testingOpcUaPort = 0 26 | 27 | describe('OPC UA Event node Unit Testing', function () { 28 | 29 | beforeAll(() => { 30 | testingOpcUaPort = 57450 31 | }) 32 | 33 | beforeEach(function (done) { 34 | helper.startServer(function () { 35 | done() 36 | }) 37 | }) 38 | 39 | afterEach(function (done) { 40 | helper.unload().then(function () { 41 | helper.stopServer(function () { 42 | done() 43 | }) 44 | }).catch(function () { 45 | helper.stopServer(function () { 46 | done() 47 | }) 48 | }) 49 | }) 50 | 51 | describe('Event node', function () { 52 | it('should load with basic settings', function (done) { 53 | helper.load( 54 | [injectNode, inputNode], 55 | testFlows.testUnitEventFlow, 56 | function () { 57 | let nodeUnderTest = helper.getNode('n3evf1') 58 | expect(nodeUnderTest.name).toBe('TestName') 59 | expect(nodeUnderTest.eventTypeLabel).toBe('BaseTypeEvent') 60 | expect(nodeUnderTest.eventType).toBe('i=2041') 61 | expect(nodeUnderTest.usingListener).toBe(true) 62 | expect(nodeUnderTest.queueSize).toBe(10) 63 | done() 64 | }) 65 | }) 66 | 67 | it('should get a message with payload', function (done) { 68 | helper.load([injectNode, inputNode], testFlows.testUnitEventFlow, function () { 69 | let n2 = helper.getNode('n2evf1') 70 | n2.on('input', function (msg) { 71 | expect(msg.payload).toBeDefined() 72 | done() 73 | }) 74 | }) 75 | }) 76 | 77 | it('should verify a message for event parameters', function (done) { 78 | helper.load([injectNode, inputNode], testFlows.testUnitEventFlow, function () { 79 | let n4 = helper.getNode('n4evf1') 80 | n4.on('input', function (msg) { 81 | expect(msg.payload.eventType).toBeDefined() 82 | expect(msg.payload.uaEventFilter).toBeDefined() 83 | expect(msg.payload.uaEventFields).toBeDefined() 84 | expect(msg.payload.queueSize).toBe(10) 85 | expect(msg.payload.interval).toBe(1000) 86 | /* payload: { eventType: 'i=2041', 87 | eventFilter: EventFilter { selectClauses: [Array], whereClause: [ContentFilter] }, eventFields: [ 'EventId', 'SourceName', 'Message', 'ReceiveTime' ], 88 | queueSize: 10, 89 | interval: 1000 }) */ 90 | done() 91 | }) 92 | }) 93 | }) 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /test/units/opcua-iiot-flex-connector.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var inputNode = require('../../src/opcua-iiot-flex-connector') 17 | 18 | var helper = require('node-red-node-test-helper') 19 | var portHelper = require('./../helper/test-helper-extensions') 20 | helper.init(require.resolve('node-red')) 21 | 22 | var testFlows = require('./flows/flex-connector-flows') 23 | 24 | let testingOpcUaPort = 0 25 | 26 | describe('OPC UA Flex Connector node Unit Testing', function () { 27 | 28 | beforeAll(() => { 29 | testingOpcUaPort = 57550 30 | }) 31 | 32 | beforeEach(function (done) { 33 | helper.startServer(function () { 34 | done() 35 | }) 36 | }) 37 | 38 | afterEach(function (done) { 39 | helper.unload().then(function () { 40 | helper.stopServer(function () { 41 | done() 42 | }) 43 | }).catch(function () { 44 | helper.stopServer(function () { 45 | done() 46 | }) 47 | }) 48 | }) 49 | 50 | describe('Flex Connector node', function () { 51 | it('should be loaded without connector', function (done) { 52 | helper.load([inputNode], testFlows.testUnitFlexConnectorFlow, 53 | function () { 54 | let nodeUnderTest = helper.getNode('14d54403.f94f04') 55 | expect(nodeUnderTest).toBeDefined() 56 | expect(nodeUnderTest.name).toBe('TestFlexConnector') 57 | done() 58 | }) 59 | }) 60 | 61 | it('should be loaded without connector and showing activities', function (done) { 62 | helper.load([inputNode], testFlows.testUnitFlexConnectorShowActivitiesFlow, 63 | function () { 64 | let nodeUnderTest = helper.getNode('14d54403.f94f05') 65 | expect(nodeUnderTest).toBeDefined() 66 | expect(nodeUnderTest.name).toBe('TestFlexConnector2') 67 | done() 68 | }) 69 | }) 70 | }) 71 | }) 72 | -------------------------------------------------------------------------------- /test/units/opcua-iiot-flex-server.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var injectNode = require('@node-red/nodes/core/common/20-inject') 17 | var functionNode = require('@node-red/nodes/core/function/10-function') 18 | var flexServerNode = require('../../src/opcua-iiot-flex-server') 19 | 20 | var flexServerFlowNodes = [injectNode, functionNode, flexServerNode] 21 | 22 | var helper = require('node-red-node-test-helper') 23 | var portHelper = require('./../helper/test-helper-extensions') 24 | helper.init(require.resolve('node-red')) 25 | 26 | var testFlows = require('./flows/flex-server-flows') 27 | 28 | let testingOpcUaPort = 0 29 | 30 | describe('OPC UA Flex Server node Unit Testing', function () { 31 | 32 | beforeAll(() => { 33 | testingOpcUaPort = 57650 34 | }) 35 | 36 | beforeEach(function (done) { 37 | helper.startServer(function () { 38 | done() 39 | }) 40 | }) 41 | 42 | afterEach(function (done) { 43 | helper.unload().then(function () { 44 | helper.stopServer(function () { 45 | done() 46 | }) 47 | }).catch(function () { 48 | helper.stopServer(function () { 49 | done() 50 | }) 51 | }) 52 | }) 53 | 54 | describe('Flex Server node', function () { 55 | it('should be loaded', function (done) { 56 | helper.load(flexServerFlowNodes, testFlows.testUnitFlexServerFlow, 57 | () => { 58 | let nodeUnderTest = helper.getNode('85d5edb0.385143') 59 | // expect(nodeUnderTest).toBeDefined() 60 | nodeUnderTest.on('server_running', () => { 61 | expect(nodeUnderTest.name).toBe('DEMOSERVER') 62 | expect(nodeUnderTest.maxAllowedSessionNumber).toBe(10) 63 | expect(nodeUnderTest.maxNodesPerRead).toBe(1000) 64 | expect(nodeUnderTest.maxNodesPerBrowse).toBe(2000) 65 | setTimeout(done, 4000) 66 | }) 67 | } 68 | ) 69 | }) 70 | 71 | it('should be loaded with user and ISA95 NodeSet XML', function (done) { 72 | helper.load(flexServerFlowNodes, testFlows.testUnitFlexServerWithUserAndISA95Flow, 73 | () => { 74 | let nodeUnderTest = helper.getNode('16093040.5dab23') 75 | expect(nodeUnderTest).toBeDefined() 76 | nodeUnderTest.on('server_running', () => { 77 | expect(nodeUnderTest.name).toBe('DEMOSERVERWITHUSERANDISA95') 78 | expect(nodeUnderTest.maxAllowedSessionNumber).toBe(10) 79 | expect(nodeUnderTest.maxNodesPerRead).toBe(1000) 80 | expect(nodeUnderTest.maxNodesPerBrowse).toBe(2000) 81 | setTimeout(done, 4000) 82 | }) 83 | } 84 | ) 85 | }) 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /test/units/opcua-iiot-server-aso.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var injectNode = require('@node-red/nodes/core/common/20-inject') 17 | var functionNode = require('@node-red/nodes/core/function/10-function') 18 | var serverAsoNode = require('../../src/opcua-iiot-server-aso') 19 | 20 | var serverAsoFlowNodes = [injectNode, functionNode, serverAsoNode] 21 | 22 | var testFlows = require('./flows/server-aso-flows') 23 | 24 | var helper = require('node-red-node-test-helper') 25 | var portHelper = require('./../helper/test-helper-extensions') 26 | helper.init(require.resolve('node-red')) 27 | 28 | let testingOpcUaPort = 0 29 | 30 | describe('OPC UA Server ASO node Unit Testing', function () { 31 | 32 | beforeAll(() => { 33 | testingOpcUaPort = 58350 34 | }) 35 | 36 | beforeEach(function (done) { 37 | helper.startServer(function () { 38 | done() 39 | }) 40 | }) 41 | 42 | afterEach(function (done) { 43 | helper.unload().then(function () { 44 | helper.stopServer(function () { 45 | done() 46 | }) 47 | }).catch(function () { 48 | helper.stopServer(function () { 49 | done() 50 | }) 51 | }) 52 | }) 53 | 54 | describe('Address Space Operation node Unit Testing', function () { 55 | it('should be loaded', function (done) { 56 | helper.load(serverAsoFlowNodes, testFlows.testUnitServerASOFlow, 57 | function () { 58 | let nodeUnderTest = helper.getNode('7cb85115.7635') 59 | expect(nodeUnderTest.name).toBe('Folder') 60 | expect(nodeUnderTest.nodeId.toString()).toBe('ns=1;s=TestVariables') 61 | expect(nodeUnderTest.datatype).toBe('Double') 62 | expect(nodeUnderTest.value).toBe('1.0') 63 | expect(nodeUnderTest.browsename).toBe('TestVariables') 64 | expect(nodeUnderTest.displayname).toBe('Test Variables') 65 | done() 66 | }) 67 | }) 68 | }) 69 | }) 70 | -------------------------------------------------------------------------------- /test/units/opcua-iiot-server-cmd.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var injectNode = require('@node-red/nodes/core/common/20-inject') 17 | var functionNode = require('@node-red/nodes/core/function/10-function') 18 | var serverCmdNode = require('../../src/opcua-iiot-server-cmd') 19 | 20 | var serverCmdNodes = [injectNode, functionNode, serverCmdNode] 21 | 22 | var helper = require('node-red-node-test-helper') 23 | var portHelper = require('./../helper/test-helper-extensions') 24 | helper.init(require.resolve('node-red')) 25 | 26 | var testFlows = require('./flows/server-cmd-flows') 27 | 28 | let testingOpcUaPort = 0 29 | 30 | describe('OPC UA Server Command node Unit Testing', function () { 31 | 32 | beforeAll(() => { 33 | testingOpcUaPort = 58350 34 | }) 35 | 36 | beforeEach(function (done) { 37 | helper.startServer(function () { 38 | done() 39 | }) 40 | }) 41 | 42 | afterEach(function (done) { 43 | helper.unload().then(function () { 44 | helper.stopServer(function () { 45 | done() 46 | }) 47 | }).catch(function () { 48 | helper.stopServer(function () { 49 | done() 50 | }) 51 | }) 52 | }) 53 | 54 | describe('Command node', function () { 55 | it('should be loaded', function (done) { 56 | helper.load(serverCmdNodes, testFlows.testUnitServerCommandFlow, 57 | function () { 58 | let nodeUnderTest = helper.getNode('n3cmdf1') 59 | expect(nodeUnderTest.name).toBe('TestName') 60 | expect(nodeUnderTest.commandtype).toBe('restart') 61 | expect(nodeUnderTest.nodeId).toBe('') 62 | done() 63 | }) 64 | }) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /test/units/opcua-iiot-server.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | // jest.setTimeout(30000) 15 | 16 | var serverNode = require('../../src/opcua-iiot-server') 17 | 18 | var helper = require('node-red-node-test-helper') 19 | var portHelper = require('./../helper/test-helper-extensions') 20 | helper.init(require.resolve('node-red')) 21 | 22 | var testFlows = require('./flows/server-flows') 23 | 24 | let testingOpcUaPort = 0 25 | 26 | describe('OPC UA Server node Unit Testing', function () { 27 | 28 | beforeAll(() => { 29 | testingOpcUaPort = 58250 30 | }) 31 | 32 | beforeEach(function (done) { 33 | helper.startServer(function () { 34 | done() 35 | }) 36 | }) 37 | 38 | afterEach(function (done) { 39 | helper.unload().then(function () { 40 | helper.stopServer(function () { 41 | done() 42 | }) 43 | }).catch(function () { 44 | helper.stopServer(function () { 45 | done() 46 | }) 47 | }) 48 | }) 49 | 50 | describe('Server node', function () { 51 | it('should be loaded with demo address-space', function (done) { 52 | const flow = Array.from(testFlows.testUnitServerWithDemoFlow) 53 | testingOpcUaPort = portHelper.getPort(testingOpcUaPort) 54 | const port = testingOpcUaPort 55 | flow[1].port = port 56 | helper.load(serverNode, flow, 57 | function () { 58 | let nodeUnderTest = helper.getNode('6ec4ef50.86dc1') 59 | // expect(nodeUnderTest).toBeDefined() 60 | nodeUnderTest.on('server_running', () => { 61 | expect(nodeUnderTest.name).toBe('DEMOSERVER') 62 | expect(nodeUnderTest.maxAllowedSessionNumber).toBe(10) 63 | expect(nodeUnderTest.maxNodesPerRead).toBe(1000) 64 | expect(nodeUnderTest.maxNodesPerBrowse).toBe(2000) 65 | setTimeout(done, 3000) 66 | }) 67 | // done() 68 | }) 69 | }) 70 | 71 | it('should be loaded with discovery and without demo address-space', function (done) { 72 | const flow = Array.from(testFlows.testUnitServerWithoutDemoFlow) 73 | testingOpcUaPort = portHelper.getPort(testingOpcUaPort) 74 | const port = testingOpcUaPort 75 | flow[1].port = port 76 | helper.load(serverNode, flow, 77 | function () { 78 | let nodeUnderTest = helper.getNode('6ec4ef50.86dc2') 79 | expect(nodeUnderTest).toBeDefined() 80 | nodeUnderTest.on('server_running', () => { 81 | expect(nodeUnderTest.name).toBe('DEMOSERVER') 82 | expect(nodeUnderTest.maxAllowedSessionNumber).toBe(10) 83 | expect(nodeUnderTest.maxNodesPerRead).toBe(1000) 84 | expect(nodeUnderTest.maxNodesPerBrowse).toBe(2000) 85 | setTimeout(done, 3000) 86 | }) 87 | done() 88 | }) 89 | }) 90 | }) 91 | }) 92 | -------------------------------------------------------------------------------- /test/units/opcua-iiot-write.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original Work Copyright 2014 IBM Corp. 3 | * node-red 4 | * 5 | * Copyright (c) 2022 DATATRONiQ GmbH (https://datatroniq.com) 6 | * Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) 7 | * All rights reserved. 8 | * node-red-contrib-iiot-opcua 9 | * 10 | **/ 11 | 12 | 'use strict' 13 | 14 | process.env.TEST = 'true' 15 | 16 | // jest.setTimeout(30000) 17 | 18 | var injectNodeRedNode = require('@node-red/nodes/core/common/20-inject') 19 | var functionNodeRedNode = require('@node-red/nodes/core/function/10-function') 20 | 21 | // iiot opcua 22 | var inputNode = require('../../src/opcua-iiot-write') 23 | 24 | var helper = require('node-red-node-test-helper') 25 | var portHelper = require('./../helper/test-helper-extensions') 26 | helper.init(require.resolve('node-red')) 27 | 28 | var writeNodesToLoad = [injectNodeRedNode, functionNodeRedNode, inputNode] 29 | 30 | var testFlows = require('./flows/write-flows') 31 | 32 | let testingOpcUaPort = 0 33 | 34 | describe('OPC UA Write node Unit Testing', function () { 35 | 36 | beforeAll(() => { 37 | testingOpcUaPort = 58350 38 | }) 39 | 40 | beforeEach(function (done) { 41 | helper.startServer(function () { 42 | done() 43 | }) 44 | }) 45 | 46 | afterEach(function (done) { 47 | helper.unload().then(function () { 48 | helper.stopServer(function () { 49 | done() 50 | }) 51 | }).catch(function () { 52 | helper.stopServer(function () { 53 | done() 54 | }) 55 | }) 56 | }) 57 | 58 | describe('Write node', function () { 59 | it('should be loaded', function (done) { 60 | helper.load(writeNodesToLoad, testFlows.testUnitWriteFlow, 61 | function () { 62 | let nodeUnderTest = helper.getNode('34d2c6bc.43275b') 63 | expect(nodeUnderTest.name).toBe('TestWrite') 64 | expect(nodeUnderTest.showErrors).toBe(true) 65 | expect(nodeUnderTest.justValue).toBe(false) 66 | done() 67 | }) 68 | }) 69 | 70 | it('should be loaded and handle error', function (done) { 71 | helper.load(writeNodesToLoad, testFlows.testUnitWriteFlow, () => { 72 | let n1 = helper.getNode('34d2c6bc.43275b') 73 | if (n1) { 74 | n1.functions.handleWriteError(new Error('Testing Error To Handle'), { payload: {} }) 75 | done() 76 | } 77 | }) 78 | }) 79 | }) 80 | }) 81 | --------------------------------------------------------------------------------