diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b3c253f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# `.gitattributes` reclassify `.st` files as iec-st: +** linguist-vendored +# *.ppjs linguist-language=ST \ No newline at end of file diff --git a/.github/workflows/generate_assesment.yml b/.github/workflows/generate_assesment.yml new file mode 100644 index 0000000..8196abd --- /dev/null +++ b/.github/workflows/generate_assesment.yml @@ -0,0 +1,315 @@ + +# This GitHub Actions workflow generates an assessment Excel file for SIMATIC AX projects +# and automatically creates an issue with download links and notification emails. +# +# Workflow Structure: +# 1. collect-info: Gathers project information and creates info.yml artifact +# 2. fill-excel: Downloads info, generates Excel file, and uploads as artifact +# 3. notify: Creates GitHub issue with download links and email notification +# +# The workflow is triggered manually via workflow_dispatch with two required inputs: +# - EXACT_Ticket_ID: The EX Classification Ticket number +# - Approver: The business approver (Promotor BL) selected from predefined options +# +# Artifacts generated: +# - info-yml: Contains collected project information +# - assessment-excel: The generated questionnaire Excel file +# +# The final issue includes direct links to the workflow run for artifact download +# and a mailto link for email notification to the actor. +name: Start Assessment +on: + workflow_dispatch: + inputs: + EXACT_Ticket_ID: + description: 'Your EX Classification Ticket' + required: true + default: '0000' + Approver: + description: 'Promotor BL (Approver from Business Side to release)' + required: true + type: string + MainContribution: + description: 'Main Contribution of the Project' + required: true + type: choice + options: + - 'Documentation' + - 'ST-Code' + - 'SCL-Code' + - 'Other programming languages' + default: 'Documentation' + +jobs: + # Job 1: Collect project information and create info.yml artifact + # Uses simatic-ax internal action to gather project details, ticket info, and approver data + collect-info: + runs-on: ubuntu-latest + steps: + # Checkout the repository to access project files + - name: Checkout repository + uses: actions/checkout@v4 + + # Set up Python environment for the collection script + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + # Install required Python dependencies for info collection + - name: Install Python dependencies + run: | + python3 -m pip install requests pyyaml openpyxl + + # Collect project information using internal Simatic AX action + # This creates an info.yml file with project details, ticket ID, and approver + - name: Collect Info + uses: simatic-ax/internal-actions/collect-assessment-information@main + with: + project_name: ${{ github.repository }} + username: ${{ github.actor }} + token: ${{ secrets.READ_USER_TOKEN }} + ticket_id: ${{ inputs.EXACT_Ticket_ID }} + approver: ${{ inputs.Approver }} + + # ✅ FIX YAML formatting for multi-line fields + # This ensures detailed_description and other multi-line fields are properly formatted + - name: Fix YAML formatting for multi-line fields + run: | + python3 << 'EOF' + import yaml + import re + + # Read the generated info.yml + with open('info.yml', 'r') as f: + raw_content = f.read() + + # Use regex to identify and fix multi-line fields that lack proper YAML syntax + lines = raw_content.split('\n') + fixed_lines = [] + i = 0 + + while i < len(lines): + line = lines[i] + + # Check if this is detailed_description field + if line.startswith('detailed_description:') and not '|' in line and not '>' in line: + # Extract the field name and first line of content + match = re.match(r'(detailed_description:)\s+(.*)', line) + if match: + field_name = match.group(1) + first_content = match.group(2) + + # Start building the multi-line field with | + fixed_lines.append(f"{field_name} |") + + # Add the first line of content with proper indentation + if first_content.strip(): + fixed_lines.append(f" {first_content}") + + # Collect all following lines that belong to this field + i += 1 + while i < len(lines): + next_line = lines[i] + + # Check if this line starts a new YAML key + if next_line and not next_line[0].isspace() and ':' in next_line: + i -= 1 + break + + # Add this line as part of the multi-line content + if next_line.strip(): + fixed_lines.append(f" {next_line}") + else: + fixed_lines.append("") + + i += 1 + else: + fixed_lines.append(line) + + i += 1 + + fixed_content = '\n'.join(fixed_lines) + + # Validate the fixed YAML by parsing it + try: + parsed = yaml.safe_load(fixed_content) + print("✅ YAML validation successful!") + print(f"✅ Fields parsed: {', '.join(parsed.keys())}") + except yaml.YAMLError as e: + print(f"❌ YAML validation failed: {e}") + exit(1) + + # Write the fixed content back + with open('info.yml', 'w') as f: + f.write(fixed_content) + + print("✅ YAML formatting fixed and validated!") + EOF + + # Validate the YAML file format + - name: Validate info.yml syntax + run: | + python3 << 'EOF' + import yaml + + try: + with open('info.yml', 'r') as f: + data = yaml.safe_load(f) + + print("✅ info.yml is valid YAML") + print(f"✅ Organization: {data.get('organization', 'N/A')}") + print(f"✅ Project: {data.get('project_name', 'N/A')}") + print(f"✅ Ticket ID: {data.get('ticket_id', 'N/A')}") + print(f"✅ Approver: {data.get('approver', 'N/A')}") + + # Check critical fields + required_fields = ['organization', 'project_name', 'ticket_id', 'approver'] + missing = [f for f in required_fields if f not in data] + + if missing: + print(f"⚠️ Missing fields: {', '.join(missing)}") + exit(1) + + print("✅ All required fields present!") + + except yaml.YAMLError as e: + print(f"❌ YAML parsing error: {e}") + exit(1) + except Exception as e: + print(f"❌ Error: {e}") + exit(1) + EOF + + # Upload the fixed info.yml as an artifact for use in subsequent jobs + - name: Upload info.yml artifact + uses: actions/upload-artifact@v3 + with: + name: info-yml + path: info.yml + + # Debug: Display info.yml content for verification + - name: Display info.yml content (Debug) + run: | + echo "=== Generated info.yml ===" + cat info.yml + echo "=== End of info.yml ===" + + # Job 2: Generate the assessment Excel file using the collected information + # Downloads info.yml artifact and creates the questionnaire Excel file + fill-excel: + runs-on: ubuntu-latest + needs: collect-info # Wait for info collection to complete + steps: + # Checkout repository for Excel generation scripts + - name: Checkout repository + uses: actions/checkout@v4 + + # Set up Python environment for Excel generation + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + # Install Python dependencies required for Excel file creation + - name: Install Python dependencies + run: | + python3 -m pip install requests pyyaml openpyxl + + # Download the info.yml artifact created in the previous job + - name: Download info.yml + uses: actions/download-artifact@v3 + with: + name: info-yml + + # Verify the downloaded info.yml is valid + - name: Verify downloaded info.yml + run: | + python3 << 'EOF' + import yaml + + try: + with open('info.yml', 'r') as f: + data = yaml.safe_load(f) + print("✅ Downloaded info.yml is valid!") + print(f"✅ Ticket ID: {data.get('ticket_id')}") + except Exception as e: + print(f"❌ Invalid info.yml: {e}") + exit(1) + EOF + + # Generate the assessment Excel file using internal Simatic AX action + # This creates a Questionnaire_Contribution_*.xlsx file + - name: Create Assessment Excel-File + uses: simatic-ax/internal-actions/create-assessment-excel@main + + # Upload the generated Excel file as an artifact for download + - name: Upload Excel artifact + uses: actions/upload-artifact@v3 + with: + name: assessment-excel + path: Questionnaire_Contribution_*.xlsx + + # Print workflow run information for reference + - name: Print Run ID and artifact locations + run: | + echo "✅ Assessment workflow completed successfully!" + echo "" + echo "📊 Workflow Information:" + echo " Run ID: ${{ github.run_id }}" + echo " Repository: ${{ github.repository }}" + echo " Actor: ${{ github.actor }}" + echo "" + echo "📁 Artifacts available at:" + echo " https://github.siemens.cloud/${{ github.repository }}/actions/runs/${{ github.run_id }}" + echo "" + echo "⏱️ Artifacts will be available for 90 days" + + # Job 3: Trigger notification workflow with direct artifact links + trigger-notify: + runs-on: ubuntu-latest + needs: fill-excel + if: success() + steps: + # Download info.yml to extract ticket information for notification + - name: Download info.yml + uses: actions/download-artifact@v3 + with: + name: info-yml + + # Extract ticket ID and email for notification content + - name: Extract notification info + id: extract-info + run: | + python3 << 'PYEOF' + import yaml + + with open('info.yml', 'r') as f: + data = yaml.safe_load(f) + + scd_mail = data.get('scd_mail', 'unknown@siemens.com') + ticket_id = data.get('ticket_id', 'UNKNOWN') + + with open('${{ github.output }}', 'a') as f: + f.write(f"scd_mail={scd_mail}\n") + f.write(f"ticket_id={ticket_id}\n") + PYEOF + + # Trigger the separate notification workflow with direct artifact links + - name: Trigger notification workflow + run: | + curl -X POST \ + -H "Authorization: token ${{ secrets.GH_ISSUE_CREATOR_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://github.siemens.cloud/api/v3/repos/${{ github.repository }}/actions/workflows/notify-assessment.yml/dispatches" \ + -d "{ + \"ref\": \"main\", + \"inputs\": { + \"run_id\": \"${{ github.run_id }}\", + \"repository\": \"${{ github.repository }}\", + \"actor\": \"${{ github.actor }}\", + \"ticket_id\": \"${{ steps.extract-info.outputs.ticket_id }}\", + \"scd_mail\": \"${{ steps.extract-info.outputs.scd_mail }}\" + } + }" + continue-on-error: true diff --git a/.github/workflows/notify-assessment-completion.yml b/.github/workflows/notify-assessment-completion.yml new file mode 100644 index 0000000..cd5f54e --- /dev/null +++ b/.github/workflows/notify-assessment-completion.yml @@ -0,0 +1,72 @@ +# Notification workflow for assessment Excel file completion +# This workflow is triggered by the generate_assessment workflow to create +# GitHub issues with direct artifact download links after workflow completion. +# +# The workflow fetches artifacts from the specified run ID and creates +# direct download links for each artifact in the GitHub issue. + +name: 'Create Assessment Notification Issue' +# description: 'Creates a GitHub issue with artifact download links for completed assessment workflows' + +on: + workflow_dispatch: + inputs: + run_id: + description: 'Run ID of the completed assessment workflow' + required: true + type: string + repository: + description: 'Repository name in format owner/repo' + required: true + type: string + actor: + description: 'GitHub username of the person who triggered the original workflow' + required: false + type: string + +jobs: + create-notification-issue: + runs-on: ubuntu-latest + steps: + # Download info.yml artifact from the specified workflow run + - name: Download info.yml artifact + run: | + # Get artifacts from the specified run + ARTIFACTS_JSON=$(curl -s -H "Authorization: token ${{ secrets.GH_ISSUE_CREATOR_TOKEN }}" \ + "https://github.siemens.cloud/api/v3/repos/${{ inputs.repository }}/actions/runs/${{ inputs.run_id }}/artifacts") + + # Find info-yml artifact + INFO_ARTIFACT_ID=$(echo "$ARTIFACTS_JSON" | jq -r '.artifacts[] | select(.name == "info-yml") | .id') + + if [ -n "$INFO_ARTIFACT_ID" ] && [ "$INFO_ARTIFACT_ID" != "null" ]; then + echo "Downloading info-yml artifact (ID: $INFO_ARTIFACT_ID)" + + # Download the artifact + curl -L -H "Authorization: token ${{ secrets.GH_ISSUE_CREATOR_TOKEN }}" \ + "https://github.siemens.cloud/api/v3/repos/${{ inputs.repository }}/actions/artifacts/$INFO_ARTIFACT_ID/zip" \ + -o info-yml.zip + + # Extract the zip file + unzip -q info-yml.zip + + if [ -f "info.yml" ]; then + echo "Successfully downloaded and extracted info.yml" + cat info.yml + else + echo "Warning: info.yml not found in artifact" + touch info.yml # Create empty file as fallback + fi + else + echo "Warning: info-yml artifact not found, creating empty info.yml" + touch info.yml + fi + + # Create GitHub issue using the new comprehensive action + - name: Create Assessment Notification Issue + uses: simatic-ax/internal-actions/create-assessment-issue@v1 + with: + run_id: ${{ inputs.run_id }} + info_file: info.yml + github_token: ${{ secrets.GH_ISSUE_CREATOR_TOKEN }} + repository: ${{ inputs.repository }} + actor: ${{ inputs.actor }} \ No newline at end of file diff --git a/.github/workflows/package-development-workflow.yml b/.github/workflows/package-development.workflow.yml similarity index 74% rename from .github/workflows/package-development-workflow.yml rename to .github/workflows/package-development.workflow.yml index 846f2bb..73d2ebf 100644 --- a/.github/workflows/package-development-workflow.yml +++ b/.github/workflows/package-development.workflow.yml @@ -1,13 +1,14 @@ # This workflow is going to be used during the development phase of the project -# The workflow builds and tests the the sources on the following triggers: -# - once a change is pushed to the main branch or any of its sub-branches -name: Library development workflow +name: Package development workflow on: push: branches: - 'main' # runs the workflow, once new changes have been integrated to main pull_request: + branches: + - 'main' # run workflow in the scope of pull requests towards main + - 'release/*' # run workflow in the scope of pull requests towards release branches workflow_call: secrets: APAX_TOKEN: @@ -30,7 +31,7 @@ jobs: name: Build and Test runs-on: ubuntu-24.04 container: - image: ghcr.io/simatic-ax/ci-images/apax-ci-image:4.2.0 + image: ghcr.io/simatic-ax/ci-images/apax-ci-image:4.3.0 credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} @@ -38,18 +39,17 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 with: - # either check out a provided reference, or use the reference that triggered this workflow, e.g. a push, PR or a release ref: ${{ inputs.ref != '' && inputs.ref || github.ref }} - name: Login to required registries - uses: simatic-ax/actions/apax-login@v4 + uses: simatic-ax/actions/apax-login@v4 with: apax-token: ${{ secrets.APAX_TOKEN }} registries: | https://npm.pkg.github.com/,${{ secrets.GITHUB_TOKEN }} - name: Install dependencies - uses: simatic-ax/actions/apax-install@v4 + uses: simatic-ax/actions/apax-install@v4 with: immutable: true @@ -70,27 +70,16 @@ jobs: uses: simatic-ax/actions/apax-version@v4 with: version: ${{ steps.determine_version.outputs.version }} - + - name: Build source code uses: simatic-ax/actions/apax-build@v4 - with: - apax-build-targets: | - llvm - 1500 - apax-build-args: | - --debug - --log Debug - name: Test source code uses: simatic-ax/actions/apax-test@v4 - with: - coverage: true - loglevel: debug - name: Check links uses: gaurav-nelson/github-action-markdown-link-check@v1 with: - ignoreFiles: '["./actions-test/**"]' check-modified-files-only: 'yes' base-branch: 'main' @@ -99,8 +88,9 @@ jobs: uses: actions/upload-artifact@v6 with: name: build-artifacts - path: | - ./bin/1500 - ./bin/llvm + path: | + bin/1500 + bin/s7 + bin/llvm retention-days: 90 if-no-files-found: error \ No newline at end of file diff --git a/.github/workflows/package-release-workflow.yml b/.github/workflows/package-release-workflow.yml index 20fd218..dbc9032 100644 --- a/.github/workflows/package-release-workflow.yml +++ b/.github/workflows/package-release-workflow.yml @@ -1,5 +1,4 @@ # This workflow is triggered when a release is published via the UI -# The workflow is only executed if the release is a tag and the target_commitish is a release branch name: Release workflow # Start the workflow as soon as a release has been published via the UI @@ -8,10 +7,9 @@ on: types: [published] permissions: - contents: write # required for checkout + contents: read # required for checkout packages: write # required for pulling the container - actions: write # required for artifact downloading - pull-requests: write # Für PR-Erstellung und Management + actions: write # required for artifact uploading jobs: call-development: @@ -20,7 +18,6 @@ jobs: secrets: APAX_TOKEN: ${{ secrets.APAX_TOKEN }} with: - # checks out the branch that has been selected during the release process ref: ${{ github.event.release.target_commitish }} version: ${{ github.event.release.tag_name }} @@ -29,7 +26,7 @@ jobs: needs: call-development runs-on: ubuntu-24.04 container: - image: ghcr.io/simatic-ax/ci-images/apax-ci-image:4.2.0 + image: ghcr.io/simatic-ax/ci-images/apax-ci-image:4.3.0 credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} @@ -37,13 +34,18 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 with: + # checks out the branch that has been selected during the release process ref: ${{ github.event.release.target_commitish }} fetch-depth: 0 + - name: Create bin folder + run: mkdir -p bin + - name: Download build artifacts uses: actions/download-artifact@v7 with: name: build-artifacts + path: bin - name: Version package uses: simatic-ax/actions/apax-version@v4 @@ -66,26 +68,4 @@ jobs: uses: simatic-ax/actions/apax-publish@v4 with: registries: | - https://npm.pkg.github.com - tag: latest - - - name: Update major version tag - if: ${{ success() }} - run: | - git config --global --add safe.directory "$GITHUB_WORKSPACE" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - VERSION=${{ github.event.release.tag_name }} - if echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then - MAJOR_VERSION="v$(echo $VERSION | cut -d. -f1)" - echo "Updating major version tag: $MAJOR_VERSION" - git push origin :refs/tags/$MAJOR_VERSION || true - git tag -f $MAJOR_VERSION - git push origin $MAJOR_VERSION --force - echo "✅ Major version tag updated successfully" - else - echo "❌ Error: Invalid version format: '$VERSION'" - echo "Expected format: X.Y.Z (e.g., 1.2.3)" - exit 1 - fi \ No newline at end of file + https://npm.pkg.github.com \ No newline at end of file diff --git a/.gitignore b/.gitignore index b506e6b..34e41b2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ obj testresult scl *.tgz +*.zip lstream-tiax \ No newline at end of file diff --git a/README.md b/README.md index 5d3252f..698fde6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This version of the library is usable in TIA Portal as a global library and in S Enter: ```iec-st -apax add @simatic-ax/LStream-JSON-XML +apax add @simatic-ax/lstream ``` > to install this package you need to login into the GitHub registry. You'll find more information [here](https://github.com/simatic-ax/.github/blob/main/docs/personalaccesstoken.md) @@ -210,9 +210,10 @@ By leveraging the TIAX workflow, your library blocks are know-how protected when Before you begin, ensure you have: - TIA Portal installed on your Windows machine (see [Compatibility](https://docs.industrial-operations-x.siemens.cloud/r/en-us/ax/ax2tia-docs/7.0.16/converting-simatic-ax-libraries-to-tia-portal-libraries/compatibility) section for required versions) -- AX SDK V3.0.12 or higher -- AX STC (System Toolchain): Version 4.4.116 or higher -- A library built for one of the supported targets: 1500, vplc, or swcpu. LLVM-only libraries cannot be converted. +- AX SDK V2510.17.0 or higher +- A library built for the following supported targets: 1500, vplc, or swcpu. LLVM-only libraries cannot be converted. + +To use the Library in TIA Portal, the PLC needs to have Firmware version V2.9 or higher. ### Configuration diff --git a/apax-lock.json b/apax-lock.json index 9ac8180..7aa51b8 100644 --- a/apax-lock.json +++ b/apax-lock.json @@ -1,109 +1,121 @@ { "name": "@simatic-ax/lstream", - "version": "1.0.0", + "version": "1.1.0", "lockFileVersion": "2", "installStrategy": "strict", "root": { "name": "@simatic-ax/lstream", - "version": "1.0.0", + "version": "1.1.0", "dependencies": { - "@ax/system-timer": "^10.2.7", - "@ax/system-serde": "^10.2.7" + "@ax/system-timer": "^10.4.66", + "@ax/system-serde": "^10.4.66" }, "devDependencies": { - "@ax/ax2tia": "^12.0.29", - "@ax/sdk": "^2510.0.0" + "@ax/ax2tia": "^12.3.10", + "@ax/sdk": "^2510.17.0", + "@simatic-ax/mocks": "^4.3.3" }, "catalogs": { - "@ax/simatic-ax": "^2510.0.0" + "@ax/simatic-ax": "^2510.17.0" } }, "packages": { "@ax/apax-build": { "name": "@ax/apax-build", - "version": "2.1.79", - "integrity": "sha512-hXMWYIik1V/fppSGS6HYDYoDePjylH5NSCKk4esMWy/o2IWoiNWB5QHA1niXQblnS6o/Xlmk2DwEGaigCWBAEw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/apax-build/-/apax-build-2.1.79.tgz", + "version": "2.2.60", + "integrity": "sha512-cY5kzKCx4irIbg+gRp0JEjqArqDPpQhCiljmCIEtkfs/CxQouBw+Pk2TK3Tf+WfvtkM2Zke1pMBsfDjaJBcIYQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/apax-build/-/apax-build-2.2.60.tgz", "dependencies": {} }, "@ax/ax2tia": { "name": "@ax/ax2tia", - "version": "12.0.29", - "integrity": "sha512-OG9PKNffwiUSz8sAsYlBT+VNz8gQkO4jyPW+wWSEC5zSitdPULxFSwp3uZC6E6PaOhEfvdNT7yzAhU1gA8G8YQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/ax2tia/-/ax2tia-12.0.29.tgz", + "version": "12.3.10", + "integrity": "sha512-sQG/UTQ5AqjPVpIzdes/KnU8Tm+wpgsSbkynEAV3KLYceVh+XyNHtyPd1tIVd8O/qX/u+yZ867yK9x38xiiS4Q==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/ax2tia/-/ax2tia-12.3.10.tgz", "cpu": [ "x64" ], "dependencies": { - "@ax/ax2tia-docs": "12.0.29", - "@ax/ax2tia-stc-plugin": "12.0.29" + "@ax/ax2tia-docs": "12.3.10", + "@ax/ax2tia-stc-plugin": "12.3.10" } }, "@ax/ax2tia-docs": { "name": "@ax/ax2tia-docs", - "version": "12.0.29", - "integrity": "sha512-c1msUxqK5fm/cPOz5OiiSBL4x/jGLW+V8/Fk4AIL5TR6MMszZYpmyii0vyQFyEPhCLQayL24Q/O3eXd18Rteeg==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/ax2tia-docs/-/ax2tia-docs-12.0.29.tgz", + "version": "12.3.10", + "integrity": "sha512-THYyxTUZPiHCdP93dkJduRCTpI7lZVW41FUYkbB9hBnNq5IdLC3ImIKveKFRQXnC80ymb67dDVDd9gNnbXEUig==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/ax2tia-docs/-/ax2tia-docs-12.3.10.tgz", "dependencies": {} }, "@ax/ax2tia-stc-plugin": { "name": "@ax/ax2tia-stc-plugin", - "version": "12.0.29", - "integrity": "sha512-G2m27I1OTohHVJHG4b8bbxIyb9N4ifGgD5LzculxyjFyLGQiF17u1gijXsM4pxytaChFJ9rUQbkq3MyNgivaWA==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/ax2tia-stc-plugin/-/ax2tia-stc-plugin-12.0.29.tgz", + "version": "12.3.10", + "integrity": "sha512-Zfw23gaVxiO1Vu10YQvv5dMx/ZtGM+2pfiZkUH5GLyNMU+G+IC0PkiDvQjWonqW9u9c3zw0OueUt2eFIxjRXWQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/ax2tia-stc-plugin/-/ax2tia-stc-plugin-12.3.10.tgz", "dependencies": {} }, + "@ax/axunit-mocking": { + "name": "@ax/axunit-mocking", + "version": "9.6.6", + "integrity": "sha512-XYw0kEt2j/L/7xwPg5wYykG7jV/4hH302gMX/pMXviXfBgz/hgZpgNLJnjkOoRZn1N090xKRdU2IOOlzXbsU3A==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunit-mocking/-/axunit-mocking-9.6.6.tgz", + "dependencies": { + "@ax/mocking-library": "9.6.6", + "@ax/mocking-ls-contrib": "9.6.6", + "@ax/target-mocking": "9.6.6" + } + }, "@ax/axunitst": { "name": "@ax/axunitst", - "version": "8.4.20", - "integrity": "sha512-G1muGl6B0zIgx+GinlhRbmtWOaRpjpeAhgqgAJO5CAkrlzzwWgaKoBWZ9R6goCVcT/C4fxPWPCy5NMNBvDp3mQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst/-/axunitst-8.4.20.tgz", + "version": "9.6.6", + "integrity": "sha512-CIwNNXM0QOzh6szI/9CkWKxX0xTe+mHpgnFJF4vVbusVWv/nqFem9CiRiSw/pglfUHm8c+mvmAF6H2nnCdaSgA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst/-/axunitst-9.6.6.tgz", "dependencies": { - "@ax/axunitst-analyzer.stc-plugin": "8.4.20", - "@ax/axunitst-library": "8.4.20", - "@ax/axunitst-ls-contrib": "8.4.20", - "@ax/axunitst-test-director": "8.4.20", - "@ax/build-native": "^16.0.3" + "@ax/axunitst-library": "9.6.6", + "@ax/axunitst-test-director": "9.6.6" } }, "@ax/axunitst-analyzer.stc-plugin": { "name": "@ax/axunitst-analyzer.stc-plugin", - "version": "8.4.20", - "integrity": "sha512-EZFh6N5eWTZHiN4tvvXKa2cwp5ALYCQ6gpW3aZo0N7VaTlvqLtWg1rJ8jSUwPR9Ax9uxPd8E/t3z+wwQB89lCw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-analyzer.stc-plugin/-/axunitst-analyzer.stc-plugin-8.4.20.tgz", + "version": "9.6.6", + "integrity": "sha512-YYBE5VhlqLNz0btvXmsioeKrasm0Fua0HzMmzCvxghPKP1iWBTC9Beofagb8JuOBSlyIgYAWSrtTha8qX+jRRQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-analyzer.stc-plugin/-/axunitst-analyzer.stc-plugin-9.6.6.tgz", "dependencies": {} }, "@ax/axunitst-library": { "name": "@ax/axunitst-library", - "version": "8.4.20", - "integrity": "sha512-salG//0xqEyMjhzTamQPAKXgIUMs13raGvOMxJIEg7JSS4uzVVFU3H23iZbVbXpdrvECA34hbueP74yS15WP2w==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-library/-/axunitst-library-8.4.20.tgz", + "version": "9.6.6", + "integrity": "sha512-9IbrBfoia+cjgFiPzJy2H4fBv4GSM/Znapsxp1RcTOE4DSIy9qHh9QbLQptjDeSC1vjKpblGGqIa0y54gkov/w==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-library/-/axunitst-library-9.6.6.tgz", "dependencies": { "@ax/system-strings": "^10.0.24" } }, "@ax/axunitst-ls-contrib": { "name": "@ax/axunitst-ls-contrib", - "version": "8.4.20", - "integrity": "sha512-J83/C9YUJWHngxupozW1+uMHTBvxAdsQy7yDI0kWGHHlryMtZgmgTg8/w1VZCX1hYGl08Pcdi/yFvoKqXOHY5A==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-ls-contrib/-/axunitst-ls-contrib-8.4.20.tgz", + "version": "9.6.6", + "integrity": "sha512-qtsIDjzxzV+uDVUcl08a9MfBphZ0xQoa5inbmc4ML/ofTrcL7wZR84N86cfn7VAk5MKCKPEU0gO2MRxfL91S/Q==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-ls-contrib/-/axunitst-ls-contrib-9.6.6.tgz", "dependencies": {} }, "@ax/axunitst-test-director": { "name": "@ax/axunitst-test-director", - "version": "8.4.20", - "integrity": "sha512-ma8RI8GWM2tVFOdCcns1Eeb7q+6X4xgpWruQfBozFvDj67Pa85CXRp23BOt0+gaGVIJhPhpcC0QDkHGLcEv/WQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-test-director/-/axunitst-test-director-8.4.20.tgz", + "version": "9.6.6", + "integrity": "sha512-0hY54z0Reslq+LWmQduIkgG+mWtjl7Mf1JIOxjgllPFniRpF2f0yME4Zf3rQbaaCBJAIaLB2rRzmLUhf6BxGLQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-test-director/-/axunitst-test-director-9.6.6.tgz", "dependencies": { - "@ax/axunitst-test-director-linux-x64": "8.4.20", - "@ax/axunitst-test-director-win-x64": "8.4.20" + "@ax/axunitst-analyzer.stc-plugin": "9.6.6", + "@ax/axunitst-ls-contrib": "9.6.6", + "@ax/build-native": "^16.0.3", + "@ax/axunitst-test-director-linux-x64": "9.6.6", + "@ax/axunitst-test-director-win-x64": "9.6.6" } }, "@ax/axunitst-test-director-linux-x64": { "name": "@ax/axunitst-test-director-linux-x64", - "version": "8.4.20", - "integrity": "sha512-1zrGKG860Y9w4or4YPtXI/uEW0z9w7gnEZMJ3LUT8avU29ZzWYDt9uuvZgHCPc1fS8lxHi/lBo6xCCVUHUXmCQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-test-director-linux-x64/-/axunitst-test-director-linux-x64-8.4.20.tgz", + "version": "9.6.6", + "integrity": "sha512-coyu/oFM8n6MSf+6y097o+qaGD35qSPZTz8NT8/Nt70XXDUKd+nRtgfU0wXNqaI9G34Aa12v0tx2Nfs95wxPAQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-test-director-linux-x64/-/axunitst-test-director-linux-x64-9.6.6.tgz", "os": [ "linux" ], @@ -114,9 +126,9 @@ }, "@ax/axunitst-test-director-win-x64": { "name": "@ax/axunitst-test-director-win-x64", - "version": "8.4.20", - "integrity": "sha512-xPmW1YhdJ8VYvvCo3iw0e28Q6yIogjgBg9dNg6kgS2bPxzj4DtWb5iu7La51NSukK9riT5URyw2XzqyNSZZi6g==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-test-director-win-x64/-/axunitst-test-director-win-x64-8.4.20.tgz", + "version": "9.6.6", + "integrity": "sha512-4W9usK7cIRy5c0HFy2+DPcnS2dOrK6f1q61r2+w677CV+b5Misg1fSRCOmWeXslZkylING5e9snDKQ6vIr/Q4A==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/axunitst-test-director-win-x64/-/axunitst-test-director-win-x64-9.6.6.tgz", "os": [ "win32" ], @@ -163,11 +175,20 @@ }, "@ax/certificate-management": { "name": "@ax/certificate-management", - "version": "2.0.0", - "integrity": "sha512-cyXdwqPYxFX6oBtwyzqvzl8o3NlXj1kP/t3/LPFWjwHvutRZF0v6ypFna1rZdMBnrk3OjJyUj95GTFCFJsbAtg==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/certificate-management/-/certificate-management-2.0.0.tgz", + "version": "2.0.1", + "integrity": "sha512-MsS2M4jGNOAWiZWV+Na3kVsImv42W9lp0Y9ThO+k8tiqrCyuZoE+owHzecU7GSzg3DWo3dUf+QDNlLJOkxgsZQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/certificate-management/-/certificate-management-2.0.1.tgz", + "dependencies": { + "@ax/certificate-management-linux-x64": "2.0.1", + "@ax/certificate-management-win-x64": "2.0.1" + } + }, + "@ax/certificate-management-linux-x64": { + "name": "@ax/certificate-management-linux-x64", + "version": "2.0.1", + "integrity": "sha512-25UHqYKq5fkyZHwp/uk5lXw+pQX4+vz4DJbuwTyqRDeSD59cQLlKG3gvAQP9J68+3RXa47BYBZ2Nvg5NaSRgWQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/certificate-management-linux-x64/-/certificate-management-linux-x64-2.0.1.tgz", "os": [ - "win32", "linux" ], "cpu": [ @@ -175,21 +196,41 @@ ], "dependencies": {} }, + "@ax/certificate-management-win-x64": { + "name": "@ax/certificate-management-win-x64", + "version": "2.0.1", + "integrity": "sha512-N4NLue9MIIZHwH7SFhHHkH6bkcxTOsTveQqc1CF5ARI6laoyjunnbGPL4kM/F/f1QY/AJU5wrDYd6t+sfvWPFA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/certificate-management-win-x64/-/certificate-management-win-x64-2.0.1.tgz", + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "dependencies": {} + }, + "@ax/debug-st-ls-plugin": { + "name": "@ax/debug-st-ls-plugin", + "version": "1.1.11", + "integrity": "sha512-Mn0bV/xTgZp4gL3GCuTh+83oEomcZMyWynZ5lLc/pM4nf3IljZTi8dfWB95FYcxWIlp4KKQMkOvD2K55A8gb+g==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/debug-st-ls-plugin/-/debug-st-ls-plugin-1.1.11.tgz", + "dependencies": {} + }, "@ax/diagnostic-buffer": { "name": "@ax/diagnostic-buffer", - "version": "2.0.0", - "integrity": "sha512-eiS/iQv/nLJX394roFTcl3HtkAKrt96TEsFXVYLqNCrpviKDorA/Z9qSuTRsqlxWaq3WTLT+sLIW3OOrWUIRxA==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/diagnostic-buffer/-/diagnostic-buffer-2.0.0.tgz", + "version": "2.2.0", + "integrity": "sha512-tWuRfaXwHNpaNhLBlNa1E7sEhYSMRa456+8EDHGZGUn2PjGGtwtwNDN9CS9RNz9cahcONWr8vrZ6fFi0g/08jw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/diagnostic-buffer/-/diagnostic-buffer-2.2.0.tgz", "dependencies": { - "@ax/diagnostic-buffer-linux-x64": "2.0.0", - "@ax/diagnostic-buffer-win-x64": "2.0.0" + "@ax/diagnostic-buffer-linux-x64": "2.2.0", + "@ax/diagnostic-buffer-win-x64": "2.2.0" } }, "@ax/diagnostic-buffer-linux-x64": { "name": "@ax/diagnostic-buffer-linux-x64", - "version": "2.0.0", - "integrity": "sha512-e5G9Ub2xI2Tm6joQhSVcL99/VjIIRyFPyd/vVap9tNYF7cRHLq9w7w+OHXldzhHbwERBQmbWKKTvdmyTWeuE8Q==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/diagnostic-buffer-linux-x64/-/diagnostic-buffer-linux-x64-2.0.0.tgz", + "version": "2.2.0", + "integrity": "sha512-bFPwC6wlHUXW7bS6k7EQp4KC6U1qkxd9JK1JERI0mYG1Mrcia1fC9pZ2k583+gVkP7ILZYsAWo8GHGiT+u/UkQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/diagnostic-buffer-linux-x64/-/diagnostic-buffer-linux-x64-2.2.0.tgz", "os": [ "linux" ], @@ -200,9 +241,9 @@ }, "@ax/diagnostic-buffer-win-x64": { "name": "@ax/diagnostic-buffer-win-x64", - "version": "2.0.0", - "integrity": "sha512-cYc8k99TJHFMqqyueVadQKp7yv/VgNzXVQwuBFyUuN3mJ+Z9/F2oweRA8ls9+cdg4TU5HJud6ld00ILpsiKlEw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/diagnostic-buffer-win-x64/-/diagnostic-buffer-win-x64-2.0.0.tgz", + "version": "2.2.0", + "integrity": "sha512-SY/M1wlZdSFzCJ4DzO63WUT/oVMrl+gXXc//xhv3whnzs/q0sc4ldqhalVdWE0+sC7z+lHLINL5xD54CPD+hSg==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/diagnostic-buffer-win-x64/-/diagnostic-buffer-win-x64-2.2.0.tgz", "os": [ "win32" ], @@ -213,35 +254,35 @@ }, "@ax/hw-s7-1500": { "name": "@ax/hw-s7-1500", - "version": "4.0.0", - "integrity": "sha512-gk/d99XH1AVe4jhJbufg7TMH0tEXEHAFze6Lr0swBPoo5IVR3PG4UvUTESmw+VVA+Ad0YLLmvyrBu2RyX8sNzg==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/hw-s7-1500/-/hw-s7-1500-4.0.0.tgz", + "version": "4.6.0", + "integrity": "sha512-H3VkQjRNAiUC2KgT+Q3w9dnIylfJXDrVvVLSM5oauQ9ADg8r2yS3uQ48UvaiK0olhEaDaziXPcOsdadex5mozQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/hw-s7-1500/-/hw-s7-1500-4.6.0.tgz", "dependencies": { - "@ax/hw-shared-s7-1500": "4.0.0" + "@ax/hw-shared-s7-1500": "4.6.0" } }, "@ax/hw-shared-s7-1500": { "name": "@ax/hw-shared-s7-1500", - "version": "4.0.0", - "integrity": "sha512-w2FgclYo+VTM2+fXTSGEJKXOcQXQunOrLCFMTxDyAUoS63B9HJv3vuKPYWCqGB4vTDu1/nZwuLXGlF+njXmBFg==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/hw-shared-s7-1500/-/hw-shared-s7-1500-4.0.0.tgz", + "version": "4.6.0", + "integrity": "sha512-o7fXq5DURbxL0voAmDwLZo1cbq62ps4jX4gJ/TfaByvlIny+rVEEGVCTwb7QOpIk42KmlP0aVJisIYYq/ahMbA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/hw-shared-s7-1500/-/hw-shared-s7-1500-4.6.0.tgz", "dependencies": {} }, "@ax/hwc": { "name": "@ax/hwc", - "version": "4.0.0", - "integrity": "sha512-nDkkVO43F2uBSvbJtMsTxNENvg+U8FIUl6Ud3WR6RwCrTaY5q8EZe6WO25SWRECgU5ODXU4Z/w9dsLfT95HZ2w==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/hwc/-/hwc-4.0.0.tgz", + "version": "4.6.0", + "integrity": "sha512-muvhbwWLtPbh1tI5OUGPGMeqly5NLVk8P8HY26ChNW9U5T1DEfm+zg+PHyFjjDJ7lC3QoaT2lsNvAjs6HzdEqw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/hwc/-/hwc-4.6.0.tgz", "dependencies": { - "@ax/hwc-linux-x64": "4.0.0", - "@ax/hwc-win-x64": "4.0.0" + "@ax/hwc-linux-x64": "4.6.0", + "@ax/hwc-win-x64": "4.6.0" } }, "@ax/hwc-linux-x64": { "name": "@ax/hwc-linux-x64", - "version": "4.0.0", - "integrity": "sha512-YNTz7KWUhVnSSYE9juJrOrYsfOj80ahv3lXDhvXPP6oxJxahSseQxnVvQYUuMlGCnVVOS/m3oM9PBYZyMVNq/g==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/hwc-linux-x64/-/hwc-linux-x64-4.0.0.tgz", + "version": "4.6.0", + "integrity": "sha512-9vXILyo79UcN/jP5at6hICI9FQ1NCEjJEt0eA9ru28fj+mZ3CeIKp8FkvIr76Wi3ULGYfUo+Y6Ra1tWtSRQTEw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/hwc-linux-x64/-/hwc-linux-x64-4.6.0.tgz", "os": [ "linux" ], @@ -252,9 +293,9 @@ }, "@ax/hwc-win-x64": { "name": "@ax/hwc-win-x64", - "version": "4.0.0", - "integrity": "sha512-cxVhMEmqmtSQfIqLempYu5FgN8IyTRUc6KRikueVOOE1seRKjCSwEy5OE0vv5YR+dv2c2YGSyuUw5rzjHNkt/g==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/hwc-win-x64/-/hwc-win-x64-4.0.0.tgz", + "version": "4.6.0", + "integrity": "sha512-w0yoFwun/g0wqvHsERZnNJBofY/J8HKaqmYuPYOC/+0TFCwiVnPyfQUNNz0iXIcOhP0SVEGGED7JefZepZjtEQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/hwc-win-x64/-/hwc-win-x64-4.6.0.tgz", "os": [ "win32" ], @@ -265,29 +306,50 @@ }, "@ax/hwld": { "name": "@ax/hwld", - "version": "3.2.0", - "integrity": "sha512-WhzpnKpaN0sRA4X9SQPI2y+kqgBWJE8URvvMvWPs7llppeqazQwLCFzjoP4vUX1FgH5gVXxfD6SYMIy477WmMw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/hwld/-/hwld-3.2.0.tgz", + "version": "3.6.0", + "integrity": "sha512-BbH3qRa95i2vDeVqih59yOelKKh3KwcL6epchMdk2HlkCiqEs9rJKU5UM0eYlQOYt9I9aOZ6s5rhECDdernonA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/hwld/-/hwld-3.6.0.tgz", "cpu": [ "x64" ], "dependencies": {} }, + "@ax/iec-docs": { + "name": "@ax/iec-docs", + "version": "10.4.66", + "integrity": "sha512-UmP0mzQuOZ0/Vm7YKXQYVLifLrfmV3x4RqBoU4fueSlIy6W3q6AtCkjKvhF4xewF1JQeaK5j0NvIcAY4y/9ihQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/iec-docs/-/iec-docs-10.4.66.tgz", + "dependencies": {} + }, + "@ax/mocking-library": { + "name": "@ax/mocking-library", + "version": "9.6.6", + "integrity": "sha512-SSFOa9Wk1j4WDH+G+0h+bYA2plLhe2gzVp1UHo+uciW3qivTchPg9XOkv8+6COom87pJqUilOi5mzo7e2R5BIw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/mocking-library/-/mocking-library-9.6.6.tgz", + "dependencies": {} + }, + "@ax/mocking-ls-contrib": { + "name": "@ax/mocking-ls-contrib", + "version": "9.6.6", + "integrity": "sha512-nIwUBspCtHwzjOkKIJW4wkzOWRkK44X0J8K/tESDZuP7biDXnp28qrY6tmRq6BgddRKAO+KvhqQJiPOiG3avvg==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/mocking-ls-contrib/-/mocking-ls-contrib-9.6.6.tgz", + "dependencies": {} + }, "@ax/mod": { "name": "@ax/mod", - "version": "1.9.4", - "integrity": "sha512-TwYKjYLqOpKprB4CcgwRyJhbyE6bSVWOK0KHNoOxKS8Q//jkBOYr/rXh/xBFgIfT1opAtcY3DaAceOnio+AA8Q==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/mod/-/mod-1.9.4.tgz", + "version": "1.15.85", + "integrity": "sha512-6aerKTTsFw+hnvxPyIux10EZSE69AR998mgw6WR7op/vSx7CBDDqFUUUaC6mw3fwU+TgFl4tSDuMJcTBpbaimg==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/mod/-/mod-1.15.85.tgz", "dependencies": { - "@ax/mod-linux-x64": "1.9.4", - "@ax/mod-win-x64": "1.9.4" + "@ax/mod-linux-x64": "1.15.85", + "@ax/mod-win-x64": "1.15.85" } }, "@ax/mod-linux-x64": { "name": "@ax/mod-linux-x64", - "version": "1.9.4", - "integrity": "sha512-LuAmBhj1Rrwq9By+UKRkBGeAWrJGJ4SpKYdj3GfJ+CXX+xtTSYcxEGB3IBrGiGQe50FffxBaXgqnhXJFSrBysw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/mod-linux-x64/-/mod-linux-x64-1.9.4.tgz", + "version": "1.15.85", + "integrity": "sha512-6FGIcUNJCHJ4NfQnQ9MpbJJDJomx4/f+74W2Lpa7pEr2ARISwZS/rh4cg9/lAB2QhJ8NJ5AKPSItaYhSBlwEwA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/mod-linux-x64/-/mod-linux-x64-1.15.85.tgz", "os": [ "linux" ], @@ -298,9 +360,9 @@ }, "@ax/mod-win-x64": { "name": "@ax/mod-win-x64", - "version": "1.9.4", - "integrity": "sha512-P93MeCzFrTeXdUpbNUjXpDIuTK3uVLzIItxIfmj7UyAHjIfZHuxT5gUtvFiSgYSgLsTbL1Dqd3qhQLG5EQAcuQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/mod-win-x64/-/mod-win-x64-1.9.4.tgz", + "version": "1.15.85", + "integrity": "sha512-um6vZ06QBwLl/eNG3x3ABmulgrPBc1EjOP/rLF2hi4jfu+8yltY/W4oDQJsqbqtRsmGnr7f3WWUyM6w7a3xjnw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/mod-win-x64/-/mod-win-x64-1.15.85.tgz", "os": [ "win32" ], @@ -311,19 +373,19 @@ }, "@ax/mon": { "name": "@ax/mon", - "version": "1.9.4", - "integrity": "sha512-OU+5D73ym40AWNlNU7uM+X8Vt1gy4Iw/SriqeXiJgN3BHgTNFY3o2adt7ybtF5t8ez5a/ng/Z47CAql+Mc27aQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/mon/-/mon-1.9.4.tgz", + "version": "1.15.85", + "integrity": "sha512-ERqXGSXElP8+m3kA+QozyQz+rbDGQyu7iYfoGX1gy9yXB6PPAQkp0HuB+/KcyahlOKPFs2/SRqB6V1mnoFbAUg==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/mon/-/mon-1.15.85.tgz", "dependencies": { - "@ax/mon-linux-x64": "1.9.4", - "@ax/mon-win-x64": "1.9.4" + "@ax/mon-linux-x64": "1.15.85", + "@ax/mon-win-x64": "1.15.85" } }, "@ax/mon-linux-x64": { "name": "@ax/mon-linux-x64", - "version": "1.9.4", - "integrity": "sha512-8FPGgd8GT9S47WA80bggWOTw6GE6C9EpXmmPQhkcgsxUKSE+Ko/+lIuv2f6DePSJMRB3I6RuM9FiUWGOoW9DQQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/mon-linux-x64/-/mon-linux-x64-1.9.4.tgz", + "version": "1.15.85", + "integrity": "sha512-VbCxoCSzPYtTnZOLgKsRWBm3dZlpII1BcNQWRCu/vOhDxtQiep3PjByI9t0nh9s/ONi1VyxAONxgoDn6sgW13g==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/mon-linux-x64/-/mon-linux-x64-1.15.85.tgz", "os": [ "linux" ], @@ -334,9 +396,9 @@ }, "@ax/mon-win-x64": { "name": "@ax/mon-win-x64", - "version": "1.9.4", - "integrity": "sha512-gT3vQ5a/QXqbeq/SOUG6jeTl2tAZl/2zlvziCOV87AgVb4dgSSNF8OA/XBuyq47MG1dO9Ag7j7FZvRGo5Hknaw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/mon-win-x64/-/mon-win-x64-1.9.4.tgz", + "version": "1.15.85", + "integrity": "sha512-ue+rOC6wsC50oeP/nLsa3QaDG9oJQgEwuD0ndWVVly+xKzwNwmGySlFxGOJ+UCpja/ZQHLWmMEJxVJZFF+OCYA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/mon-win-x64/-/mon-win-x64-1.15.85.tgz", "os": [ "win32" ], @@ -347,9 +409,9 @@ }, "@ax/plc-control": { "name": "@ax/plc-control", - "version": "1.4.3", - "integrity": "sha512-LSZIoWmgDVzyCiQsgakNcd54qaRF9xHPS7k0YWng8fsHjlcMzoEPbbj+pFTo3MNP5Y+ElBOcGSkNTm1Os0+QWw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/plc-control/-/plc-control-1.4.3.tgz", + "version": "1.7.22", + "integrity": "sha512-Q8SmhANO6ssBN4WLXG4+HT085pTjaMfxJNK2HoBuFkgENY/EOSXNFIIc3mLWXGRg00R+j8RZY5hSq/DuCvd+FQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/plc-control/-/plc-control-1.7.22.tgz", "cpu": [ "x64" ], @@ -357,9 +419,9 @@ }, "@ax/plc-info": { "name": "@ax/plc-info", - "version": "4.0.0", - "integrity": "sha512-k2DNlSawgjYuMdfWAYQ0dkyfmeYZRRzPhrk+SL1wI+Up//kTjl20pOFijaydp8nRDKy4k0SotaUUcB3fnUgohA==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/plc-info/-/plc-info-4.0.0.tgz", + "version": "4.2.0", + "integrity": "sha512-8vqd8mXm2hQX+LauqmoCKv3MiCF1a8EmNR+SAYKVL4AypFHvppvwDPCJ+JZXIKh6j7oodrNMVcyagERfl2bGDQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/plc-info/-/plc-info-4.2.0.tgz", "os": [ "win32", "linux" @@ -371,19 +433,20 @@ }, "@ax/sdb": { "name": "@ax/sdb", - "version": "1.9.4", - "integrity": "sha512-IEcVzPVpyteVsjc/3qyeUOK26FDkwnaXvmk6SubGx5m83gm6tWLroQDTkTI+OKO+xiOfrf4nKU/aEgjllgV1eg==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/sdb/-/sdb-1.9.4.tgz", + "version": "1.15.85", + "integrity": "sha512-2q6+8cf7zBONAPJkcC/1kU/O1BzpgXsMAXIaLWOo2y4RLFlvwrncGjMCXDHmkwjwZRga1rrkOdwE7oSsnhcDBA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/sdb/-/sdb-1.15.85.tgz", "dependencies": { - "@ax/sdb-linux-x64": "1.9.4", - "@ax/sdb-win-x64": "1.9.4" + "@ax/debug-st-ls-plugin": "^1.0.0", + "@ax/sdb-linux-x64": "1.15.85", + "@ax/sdb-win-x64": "1.15.85" } }, "@ax/sdb-linux-x64": { "name": "@ax/sdb-linux-x64", - "version": "1.9.4", - "integrity": "sha512-K7iXXGjwAcKxzvSp4Arw3f4JGzCCTBnVQa1ARho32OKB2+0zvyaCfZ9qO0u1A3P1BtzQ3l+0vAhpojkaiFOTCw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/sdb-linux-x64/-/sdb-linux-x64-1.9.4.tgz", + "version": "1.15.85", + "integrity": "sha512-41O4+MgHqoz/ps5F4WkYCNFqKGHiJ3Y5zCRQ/MehHpfXx0d3vNxyap0TnWK2I11M5yPlj91cqVYXkiADNFQNng==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/sdb-linux-x64/-/sdb-linux-x64-1.15.85.tgz", "os": [ "linux" ], @@ -394,9 +457,9 @@ }, "@ax/sdb-win-x64": { "name": "@ax/sdb-win-x64", - "version": "1.9.4", - "integrity": "sha512-t2nFtyTQM9PyzITa3twr1LI7EJoZ46B0uv6Qyz55DlXsJHdE0/ql6e2hVXFx0qi8kXgh4NFk55xXp/wELNhi5Q==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/sdb-win-x64/-/sdb-win-x64-1.9.4.tgz", + "version": "1.15.85", + "integrity": "sha512-4a1Z7rEEYMax9zGCjBs8VodZX0zuCje0yJkjRjLRxQb/ndGpzqidqf2pCz5+CzwIOLsKrcncApdOGqo6UAGYiA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/sdb-win-x64/-/sdb-win-x64-1.15.85.tgz", "os": [ "win32" ], @@ -407,38 +470,40 @@ }, "@ax/sdk": { "name": "@ax/sdk", - "version": "2510.0.0", - "integrity": "sha512-myt8Cps6m0jLcl06s8p1ShKaXIULLbVB3/X1eYgGc2Ir5pDqUALHXjEpDVvlUVCjrmMspyPH3/CDWLDoa4U0Vw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/sdk/-/sdk-2510.0.0.tgz", + "version": "2510.17.0", + "integrity": "sha512-o/bR6C/hL2EWICpTv4ETpOWA7BUGDG4E92HeNAwdbPea7wBBSFXNRqWFpQXwDPIacpL+gmtZvH2jikYbivwd6A==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/sdk/-/sdk-2510.17.0.tgz", "dependencies": { - "@ax/apax-build": "2.1.79", - "@ax/axunitst": "8.4.20", - "@ax/certificate-management": "2.0.0", - "@ax/diagnostic-buffer": "2.0.0", - "@ax/hw-s7-1500": "4.0.0", - "@ax/hwc": "4.0.0", - "@ax/hwld": "3.2.0", - "@ax/mod": "1.9.4", - "@ax/mon": "1.9.4", - "@ax/plc-control": "1.4.3", - "@ax/plc-info": "4.0.0", - "@ax/sdb": "1.9.4", - "@ax/simatic-package-tool": "2.0.17", - "@ax/sld": "3.5.5", - "@ax/st-ls": "11.0.142", - "@ax/st-opcua.stc-plugin": "2.0.0", - "@ax/st-resources.stc-plugin": "4.0.3", - "@ax/stc": "11.0.142", - "@ax/target-llvm": "11.0.142", - "@ax/target-mc7plus": "11.0.142", - "@ax/trace": "3.1.0" + "@ax/apax-build": "2.2.60", + "@ax/axunitst": "9.6.6", + "@ax/certificate-management": "2.0.1", + "@ax/diagnostic-buffer": "2.2.0", + "@ax/hw-s7-1500": "4.6.0", + "@ax/hwc": "4.6.0", + "@ax/hwld": "3.6.0", + "@ax/mod": "1.15.85", + "@ax/mon": "1.15.85", + "@ax/plc-control": "1.7.22", + "@ax/plc-info": "4.2.0", + "@ax/sdb": "1.15.85", + "@ax/simatic-package-tool": "2.0.20", + "@ax/sld": "3.8.3", + "@ax/st-lang-contrib-xlad": "1.3.1", + "@ax/st-ls": "11.6.37", + "@ax/st-opcua.stc-plugin": "2.1.0", + "@ax/st-resources.stc-plugin": "4.0.4", + "@ax/stc": "11.6.37", + "@ax/target-llvm": "11.6.37", + "@ax/target-mc7plus": "11.6.37", + "@ax/trace": "3.4.0", + "@ax/xlad-service": "1.3.0" } }, "@ax/simatic-package-tool": { "name": "@ax/simatic-package-tool", - "version": "2.0.17", - "integrity": "sha512-r4pCNr+6pa/cBcyQzRvMFAw0T+p1Twkz3bks1Tk4CEb37yGkWhIqnUFBgpl0fbqCubAsqdXszEMeAM8tHODSDQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/simatic-package-tool/-/simatic-package-tool-2.0.17.tgz", + "version": "2.0.20", + "integrity": "sha512-3b0GdCYeRBmghGUj6hiyv3S6DXNcpu47KqJ3tcKbaow1NFd/DEphELnkddATBkxpoRBouVav6yxggYanM/8CNw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/simatic-package-tool/-/simatic-package-tool-2.0.20.tgz", "cpu": [ "x64" ], @@ -446,9 +511,9 @@ }, "@ax/sld": { "name": "@ax/sld", - "version": "3.5.5", - "integrity": "sha512-tS/Q1n6O63k9dm40bzL0FRJ/9lo3iAfDvXfzp+aJ2Pf8CqBUrt4EGdAOioiV0dSyWcPtOfmhqAeaxlzLm/3tOw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/sld/-/sld-3.5.5.tgz", + "version": "3.8.3", + "integrity": "sha512-6RkX3cM1SLdBskq59qXOLxpxtA6B+o7xfKHsxMXmAYQdJoId/UJ5Pva+RxX0nN9JCx8O9Q04Vs1leYGbQoqMog==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/sld/-/sld-3.8.3.tgz", "cpu": [ "x64" ], @@ -456,26 +521,33 @@ }, "@ax/st-docs": { "name": "@ax/st-docs", - "version": "11.0.142", - "integrity": "sha512-/EAHrlHoOr+mjt5vIZQA5AEHXY1Nv6U+kU0qGwvmvgBzVY6tN6lECvR8rDEXF3jaVeWr36KxFjwt+e6tqJ6tQg==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-docs/-/st-docs-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-nf/Zs2LOt4la+PZGCZaoqp/vEvI6WLDCs8giaSEHZWMAGQUkEF6Z4sQpP5MRHZPoDcLhdMyiuxKWqVEtfbsWKA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-docs/-/st-docs-11.6.37.tgz", + "dependencies": {} + }, + "@ax/st-lang-contrib-xlad": { + "name": "@ax/st-lang-contrib-xlad", + "version": "1.3.1", + "integrity": "sha512-3ECd0WFcpbG2NldPedGTx6ki6Si/HeIRFwlry3CMbKEXKlzyOCOjE+byZ4euygxNWaetTdrCmQ5poRj6NAV/JQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-lang-contrib-xlad/-/st-lang-contrib-xlad-1.3.1.tgz", "dependencies": {} }, "@ax/st-ls": { "name": "@ax/st-ls", - "version": "11.0.142", - "integrity": "sha512-CM/HOPYW7oiVdKAabgxe0YKDvWSfF3d4/zlggCacC1t4OjJPacbzXl4/m5QHDraViSimF9Xk53a+rw9/1mdmnA==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-ls/-/st-ls-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-c+SFj0soqdhQzisfQBIGPaTP/fmHG8+1S1L0E0Zm0wKTGzHkfSD9hLIOMYQgVIko/UrwR0cmavQwv+rUK0L7yQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-ls/-/st-ls-11.6.37.tgz", "dependencies": { - "@ax/st-ls-linux-x64": "11.0.142", - "@ax/st-ls-win-x64": "11.0.142" + "@ax/st-ls-linux-x64": "11.6.37", + "@ax/st-ls-win-x64": "11.6.37" } }, "@ax/st-ls-linux-x64": { "name": "@ax/st-ls-linux-x64", - "version": "11.0.142", - "integrity": "sha512-+S+ZVR+1B5lBaCGH+kPK/s7gp31+Q8Ec2MopOE8snntXYujuE0eCL+vzn4LGgX6dA6MLl2/HJcTG63JF0kJURQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-ls-linux-x64/-/st-ls-linux-x64-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-gfsB0r3yrNHHdPBC72ghzTTP2WqYlmTh5KJAs8XQcgjoFfAT3bgnMK/Ctwc4QePeQdloxdcsIrLHfGUY3zFpXQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-ls-linux-x64/-/st-ls-linux-x64-11.6.37.tgz", "os": [ "linux" ], @@ -486,9 +558,9 @@ }, "@ax/st-ls-win-x64": { "name": "@ax/st-ls-win-x64", - "version": "11.0.142", - "integrity": "sha512-U+m9jTmfpU25MKVwyo7TI23z9NE7wNvVas8QhnW2DJFcLn8uw94BkWLoc3rLZ8VElvQEJ3XYOG0IW2Kuq72zxg==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-ls-win-x64/-/st-ls-win-x64-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-pWPGqVWBNe3st3OYS/VGBR+xQRBX4WqTjxlJ1GtsEbb99IVgQ/74bdXnh4JPP14lF1YteEe7DrpYW3YGbackqQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-ls-win-x64/-/st-ls-win-x64-11.6.37.tgz", "os": [ "win32" ], @@ -499,33 +571,33 @@ }, "@ax/st-opcua.stc-plugin": { "name": "@ax/st-opcua.stc-plugin", - "version": "2.0.0", - "integrity": "sha512-afCvK6L/haPiKnhmomA/1QjNKwRq+8GBTzscgznNKCXNqVU+ZTmTLpZkSjZAdzAQUaQ2RtqhQ10tVsEZSLWA6w==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-opcua.stc-plugin/-/st-opcua.stc-plugin-2.0.0.tgz", + "version": "2.1.0", + "integrity": "sha512-f5exlEf0IHVuVAqdOZI8MvQpZofUdVidYTCyIwcXNCvzr1K8ReoeHmv6mnjXIJEz2HeFA++e8cgb0w7WcytaFA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-opcua.stc-plugin/-/st-opcua.stc-plugin-2.1.0.tgz", "dependencies": {} }, "@ax/st-resources.stc-plugin": { "name": "@ax/st-resources.stc-plugin", - "version": "4.0.3", - "integrity": "sha512-lE/77sEF/dwaZfj5csiUtJ4+es8PDaysXLtmjiVLucE6s+qfk0VVNt9APE38gfSGgrHclARMR37AkO273iumSQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-resources.stc-plugin/-/st-resources.stc-plugin-4.0.3.tgz", + "version": "4.0.4", + "integrity": "sha512-ZX2Y5U9nOeGeBBRltBiv96gi4YHCPrm08N79lghJUUSRMqq1WFpCjvKg6RSZfnJ8zw5ZDNrycZOKFV/wa8Tu3g==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/st-resources.stc-plugin/-/st-resources.stc-plugin-4.0.4.tgz", "dependencies": {} }, "@ax/stc": { "name": "@ax/stc", - "version": "11.0.142", - "integrity": "sha512-oinyp6RSquEVUAec6F5NlZLjZQ2b3MsiYjujCCvkKMJSUJrx9U+mCc9CU5iPg3g4mX5WX7loB6LR1oxf4XjzkQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/stc/-/stc-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-X1ATVX35M7NWKQnn6S7FKmXNgj2AN2+mRA3S71YKfV77rMMElrTLvYUVGGoTX4DtlRRuuAjfu9aRqOMyOqehfw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/stc/-/stc-11.6.37.tgz", "dependencies": { - "@ax/stc-linux-x64": "11.0.142", - "@ax/stc-win-x64": "11.0.142" + "@ax/stc-linux-x64": "11.6.37", + "@ax/stc-win-x64": "11.6.37" } }, "@ax/stc-linux-x64": { "name": "@ax/stc-linux-x64", - "version": "11.0.142", - "integrity": "sha512-TWeu2zlUNn9BvX62TfdcggAmPzVWqLP3h7/112gGGQ86ww9Q7aqX7h1nMNaaXmIc3Fv1CLJ5Q8i6/zLYRBk5Fg==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/stc-linux-x64/-/stc-linux-x64-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-0OArw6v8ryZyifHNgVHdNaZ/zWX+hd8bL0Cz9v7f9ieJNZpWL6pYrBfAxhd9bNgwKYeCWNZ3UulPDrT/LVW4QQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/stc-linux-x64/-/stc-linux-x64-11.6.37.tgz", "os": [ "linux" ], @@ -533,14 +605,14 @@ "x64" ], "dependencies": { - "@ax/st-docs": "11.0.142" + "@ax/st-docs": "11.6.37" } }, "@ax/stc-win-x64": { "name": "@ax/stc-win-x64", - "version": "11.0.142", - "integrity": "sha512-c5v0s3rD9tbRNVl1RrLyBpfu+qx3ZC/gOJ3DThqR2FK0DMpBZoe94iNmJVX0wPEjKfzFXtsm6oZWy0kw0CJ0CA==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/stc-win-x64/-/stc-win-x64-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-Y/lUoW2FVDEpWToznZC/EsMqkBQx6I5NcAOz5NbZctOWVBTrIQs9vvsnPRIbIZiceU/pT9vwDBTPfVmbjSqUTw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/stc-win-x64/-/stc-win-x64-11.6.37.tgz", "os": [ "win32" ], @@ -548,72 +620,143 @@ "x64" ], "dependencies": { - "@ax/st-docs": "11.0.142" + "@ax/st-docs": "11.6.37" } }, + "@ax/system": { + "name": "@ax/system", + "version": "10.4.66", + "integrity": "sha512-3Zb+czlfibCBTXjTOUFyCiDxCxsXczZpOoHqBNnlBg6DUnrNsvNjGsIvgQioRRuHWBKoLVtbf4OmTdeFaqtKEg==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system/-/system-10.4.66.tgz", + "dependencies": { + "@ax/iec-docs": "^10.4.66", + "@ax/system-bistable": "^10.4.66", + "@ax/system-bitaccess": "^10.4.66", + "@ax/system-conversion": "^10.4.66", + "@ax/system-counters": "^10.4.66", + "@ax/system-data": "^10.4.66", + "@ax/system-datetime": "^10.4.66", + "@ax/system-edgedetection": "^10.4.66", + "@ax/system-fastmath": "^10.4.66", + "@ax/system-math": "^10.4.66", + "@ax/system-selection": "^10.4.66", + "@ax/system-serde": "^10.4.66", + "@ax/system-strings": "^10.4.66", + "@ax/system-timer": "^10.4.66" + } + }, + "@ax/system-bistable": { + "name": "@ax/system-bistable", + "version": "10.4.66", + "integrity": "sha512-3j9gboKIVONVkqHWwLwRXNk4dMi0GfQptg+4Qab6JCCqfkNfOppMtxD857jhxMQzU1+p/GjZxo/BtgCyXMuEIw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-bistable/-/system-bistable-10.4.66.tgz", + "dependencies": {} + }, + "@ax/system-bitaccess": { + "name": "@ax/system-bitaccess", + "version": "10.4.66", + "integrity": "sha512-QNNHn1fKc97XhuZAyUbsHaiBPxUuU8f4OqAOM/+3fu0CAWhc5GVCiuaN7TxykCiAVuPB0wnMgAHkcOYn8GEb7A==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-bitaccess/-/system-bitaccess-10.4.66.tgz", + "dependencies": {} + }, "@ax/system-conversion": { "name": "@ax/system-conversion", - "version": "10.2.7", - "integrity": "sha512-7pwc0Mp434tQxNdMQNreLMDV9CN291MeEwSEpLU+z7wsIJLwIqfE5V6HiJ0IOVkj91zZHa23zxEBd37nm0c66g==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-conversion/-/system-conversion-10.2.7.tgz", + "version": "10.4.66", + "integrity": "sha512-giiPq/Ye4P5q93l+VdijKHizoAjzaiKby0X+j4Hz+PIzZ50yhpL4mITw2xDRMwPF51VWH5yc9/+pdhqm9GqpqA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-conversion/-/system-conversion-10.4.66.tgz", + "dependencies": {} + }, + "@ax/system-counters": { + "name": "@ax/system-counters", + "version": "10.4.66", + "integrity": "sha512-/xVKb9FELdlJ2+SSlA3xeX4ZuDTGxWxyopeqGyjBClYpH9ADqJPpqsQKp7Tj82hnCOUVC4FcRd6ZEj5MgzX+FA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-counters/-/system-counters-10.4.66.tgz", + "dependencies": {} + }, + "@ax/system-data": { + "name": "@ax/system-data", + "version": "10.4.66", + "integrity": "sha512-SdQgLHluHZtJamrfQBPvNEztxULcuj4v4oiG/KIaXxrpF2D1KBasg4kruJcFGSGDkF2KRpFsBWGNJ83gV410Tw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-data/-/system-data-10.4.66.tgz", "dependencies": {} }, "@ax/system-datetime": { "name": "@ax/system-datetime", - "version": "10.2.7", - "integrity": "sha512-9YEXCa2vUo5ZrhhFNHQmGUlmjLkIid/SNlodDAcmLyYZuNLL+352qat2KX6P7PoSDsQ5J+LBF4vRPSR1lmZFeQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-datetime/-/system-datetime-10.2.7.tgz", + "version": "10.4.66", + "integrity": "sha512-Jilix9GRivWDxxCxQDQxxlYE/+xHTwdJgO9TLvoBRo1kTTeM484zj6i08mFPF6lLFeMYL+HfYlp1tnx99HrkMA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-datetime/-/system-datetime-10.4.66.tgz", + "dependencies": {} + }, + "@ax/system-edgedetection": { + "name": "@ax/system-edgedetection", + "version": "10.4.66", + "integrity": "sha512-9WVFljtjHHplK7v8wRE99GW9X6jQjycihGu56LiT35dwXI6fREt2vnKbi9jB9kPUtOiWurNdoslmOFGw63UGyA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-edgedetection/-/system-edgedetection-10.4.66.tgz", + "dependencies": {} + }, + "@ax/system-fastmath": { + "name": "@ax/system-fastmath", + "version": "10.4.66", + "integrity": "sha512-3JPMmkuva7J+yj46JUxcqLSHbPpNkyDvk8yPLfhM0A9y/HtNAUrH1bdgiycZbirhdIX1R51cI/nPyx5v+XSTLA==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-fastmath/-/system-fastmath-10.4.66.tgz", "dependencies": {} }, "@ax/system-math": { "name": "@ax/system-math", - "version": "10.2.7", - "integrity": "sha512-hDjaLGPeNr0IzZfhIwgqoTcQjihCMjVGnsm4wgQBb7CNCOTMebLwKrumxu7wP3L0/AIBFqWbfoiWGsj3B2KmIA==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-math/-/system-math-10.2.7.tgz", + "version": "10.4.66", + "integrity": "sha512-H7zYfCgjxFxSX1xArphN+qjPbpDEX4YAhNpMVSNlOSaYJcID+GpMENLZa/jgzPFshLaax1RFrXSvSII3v89LFQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-math/-/system-math-10.4.66.tgz", + "dependencies": {} + }, + "@ax/system-selection": { + "name": "@ax/system-selection", + "version": "10.4.66", + "integrity": "sha512-t1xujT5DqME+0xCPBpv5nGgkHYV6yOuhysfazit1QkErjU0BcXkjALXbhNHdaWO8Lvh3b9VotOOgCeWCiWcimw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-selection/-/system-selection-10.4.66.tgz", "dependencies": {} }, "@ax/system-serde": { "name": "@ax/system-serde", - "version": "10.2.7", - "integrity": "sha512-v5NYdmNQhrlLdjvuKUo8APcqMUqlDwWVFjoJNM9x9fAPnX2VaOdkKtplQ9usy9/pE4rZGM9hrtevBwQlcpZsaw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-serde/-/system-serde-10.2.7.tgz", + "version": "10.4.66", + "integrity": "sha512-aw24fcdShSXEyMfwq0OtgsFzOov6mLqsVxwuNy3Y7bO2WhYaUvGRRVKCLubVs3ngVwkifTNmDYfWq473sEaYXw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-serde/-/system-serde-10.4.66.tgz", "dependencies": { - "@ax/system-strings": "^10.2.7" + "@ax/system-strings": "^10.4.66" } }, "@ax/system-strings": { "name": "@ax/system-strings", - "version": "10.2.7", - "integrity": "sha512-HIicTu7S5i/Fh/YBEQY93JpUZ5ARNSoNNx1v1FY3syRqu2WQdMTsk9fCFAIfTmCYN9S/cPvqveOgGieiiuCg4A==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-strings/-/system-strings-10.2.7.tgz", + "version": "10.4.66", + "integrity": "sha512-Qs+TPsg1Il+cvD6aN09xivm+N29oAWeqsnf5kqmOEs+2FL7Y6rEkEyKjv48++QplmsswymzzWw23mU9Cjuf/lg==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-strings/-/system-strings-10.4.66.tgz", "dependencies": { - "@ax/system-math": "^10.2.7", - "@ax/system-datetime": "^10.2.7", - "@ax/system-conversion": "^10.2.7" + "@ax/system-conversion": "^10.4.66", + "@ax/system-datetime": "^10.4.66", + "@ax/system-math": "^10.4.66" } }, "@ax/system-timer": { "name": "@ax/system-timer", - "version": "10.2.7", - "integrity": "sha512-wTOcskC9SOaDz29fBngIIKyJWm7bFpVNYqWrLNqL8FqFwQ6Lv24nv4e0d6buoZ7WdB37qbfe/ZS360P/t4SyHQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-timer/-/system-timer-10.2.7.tgz", + "version": "10.4.66", + "integrity": "sha512-Hst6y2tz8/SRhy/h47xRW650RVGGjplWf+V4tNCapKIR00hlUO91kIEFYM3OjyNtfa+6uUYtKoUmuG0cMos4ww==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/system-timer/-/system-timer-10.4.66.tgz", "dependencies": {} }, "@ax/target-llvm": { "name": "@ax/target-llvm", - "version": "11.0.142", - "integrity": "sha512-utPcEt1TbuTrKN2CiWXP1s3EG7sWmHDM69ooF7xcq6jk+3WXScEG98lzy+lE7wyb5GPGItYrwSclOqKEOdepMA==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-llvm/-/target-llvm-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-GorTqZSSNBx87lMRAKTznI74T0IgxdMnwiL+YKZ4rEh12RCkttUGLxjQ1gcnORcqQoFDB9Ssu1+a4kx0YeD9Hw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-llvm/-/target-llvm-11.6.37.tgz", "dependencies": { - "@ax/target-llvm-linux-x64": "11.0.142", - "@ax/target-llvm-win-x64": "11.0.142" + "@ax/target-llvm-linux-x64": "11.6.37", + "@ax/target-llvm-win-x64": "11.6.37" } }, "@ax/target-llvm-linux-x64": { "name": "@ax/target-llvm-linux-x64", - "version": "11.0.142", - "integrity": "sha512-1T6kzdRctglYqBlmcWJ+NBiWILP5RZmnOdwRf87ykFeBzq//QeLly3koCjoX+clmmZKgoYKxoD/spybYiwSxGw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-llvm-linux-x64/-/target-llvm-linux-x64-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-qIH2VU7dNWAyAWnDqc3rhSh67wRWBOdbAP6aJoFz3yg0y5bF88t7DBuIomzuTHEOF6EukeaH4hMYclmdfpNU4Q==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-llvm-linux-x64/-/target-llvm-linux-x64-11.6.37.tgz", "os": [ "linux" ], @@ -624,9 +767,9 @@ }, "@ax/target-llvm-win-x64": { "name": "@ax/target-llvm-win-x64", - "version": "11.0.142", - "integrity": "sha512-eQHmJ5U9xwtBziE44NXuPtrZaJLDlXdtJ9G3dysFnDnROJimrOMyisW7GiU1MBCKI9UZUNNU5PjQqf6KlCLS9w==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-llvm-win-x64/-/target-llvm-win-x64-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-jUYsDorzKlpLEdfOi5/STZs5w1YQ+vG60Lgise67PXR+sPYEGegMQb0N8V9uKQLzDPm8NBDmrkTKxFQcB6jhNg==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-llvm-win-x64/-/target-llvm-win-x64-11.6.37.tgz", "os": [ "win32" ], @@ -637,19 +780,19 @@ }, "@ax/target-mc7plus": { "name": "@ax/target-mc7plus", - "version": "11.0.142", - "integrity": "sha512-O59HEd+rqjOmjSzibM3aFm6xCkTcbmuAprGHLXav9paDGr8h00Sf4iGHCKzXokSiCJ7gH5+/QGClpS1zaRdq1w==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-mc7plus/-/target-mc7plus-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-3v+Kv27AcGqXUJK1puFpYMmYppC32kkv9lpGJOm8L3KgeFNwaR2wzezf1gIz0fd/fvHXhf/WVnmxtnGgbLzVVQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-mc7plus/-/target-mc7plus-11.6.37.tgz", "dependencies": { - "@ax/target-mc7plus-linux-x64": "11.0.142", - "@ax/target-mc7plus-win-x64": "11.0.142" + "@ax/target-mc7plus-linux-x64": "11.6.37", + "@ax/target-mc7plus-win-x64": "11.6.37" } }, "@ax/target-mc7plus-linux-x64": { "name": "@ax/target-mc7plus-linux-x64", - "version": "11.0.142", - "integrity": "sha512-kKyQMHd8RkGXrwIkZtZHJPlqBaK9HDekLAUEW4QN1g9g7xg1cywSuD5523Rlqzuaa4RShu5p7PLxtpUv4uqubg==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-mc7plus-linux-x64/-/target-mc7plus-linux-x64-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-etUamY7XSr9Xyz+z6ImUxq0lwAD8LgRhh4V/5fnZl0KI2Rf/wzGnmF9dw9OfsR3sTv+VXUk43hK6KM1ugH/2xw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-mc7plus-linux-x64/-/target-mc7plus-linux-x64-11.6.37.tgz", "os": [ "linux" ], @@ -660,9 +803,45 @@ }, "@ax/target-mc7plus-win-x64": { "name": "@ax/target-mc7plus-win-x64", - "version": "11.0.142", - "integrity": "sha512-YmDtqcdZCvHxdYMIJQtapu+H6nxoGGoHUnH9Yg2UQVC2ZBYl92uFLQnuT10zYENBnw6qH00Yj9/0MhFkC2Plaw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-mc7plus-win-x64/-/target-mc7plus-win-x64-11.0.142.tgz", + "version": "11.6.37", + "integrity": "sha512-NmmNKMf4nB7aCoNP8gslFosKym7RA38g+66NAfYUxV+WPXEWU51nZ1h0qIwDCiy9LrKiDShTH6+f/g8vb1bXHQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-mc7plus-win-x64/-/target-mc7plus-win-x64-11.6.37.tgz", + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "dependencies": {} + }, + "@ax/target-mocking": { + "name": "@ax/target-mocking", + "version": "9.6.6", + "integrity": "sha512-fdb/ufRKoKvMvQ8jE5G01oHcbq0l+IBYPnpMh+gBIE9/YqwdD+FRgrm0mfxiNosNUObOjwAsK//uxZM06BxKtg==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-mocking/-/target-mocking-9.6.6.tgz", + "dependencies": { + "@ax/target-mocking-linux-x64": "9.6.6", + "@ax/target-mocking-win-x64": "9.6.6" + } + }, + "@ax/target-mocking-linux-x64": { + "name": "@ax/target-mocking-linux-x64", + "version": "9.6.6", + "integrity": "sha512-XeuEajF8UuJ56vSbeZRPl/zu2KaHsIkp0qozrpuLtVb067Dxr7f+9VZrzdByBZwhXrP6/q/tey5VFJR/V8gn4Q==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-mocking-linux-x64/-/target-mocking-linux-x64-9.6.6.tgz", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "dependencies": {} + }, + "@ax/target-mocking-win-x64": { + "name": "@ax/target-mocking-win-x64", + "version": "9.6.6", + "integrity": "sha512-M+LnIaHJXh3ZmY607BGVhtqNbhS19mKoNeXmbYtlO+X1h+JN9NlxVjWMuE/tw94HzuXgfd1CSZvaUTpm9KVihQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/target-mocking-win-x64/-/target-mocking-win-x64-9.6.6.tgz", "os": [ "win32" ], @@ -673,19 +852,19 @@ }, "@ax/trace": { "name": "@ax/trace", - "version": "3.1.0", - "integrity": "sha512-B6i5/SfjMBMsACGsu27lAXaTibENIBeSdzwabBJ0qnKD/byGrW/RDYarOMWiDf6O1JFGy2nrAGeA1Ix4Mz1NSw==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/trace/-/trace-3.1.0.tgz", + "version": "3.4.0", + "integrity": "sha512-5Xhn4v9YS2SH60yxp0+nEIIikkqal+CUH7bjabUZAWEn+PWjOtdalkkmf9hJM3J68aVKBIaYvNTI4NowGdrLfw==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/trace/-/trace-3.4.0.tgz", "dependencies": { - "@ax/trace-linux-x64": "3.1.0", - "@ax/trace-win-x64": "3.1.0" + "@ax/trace-linux-x64": "3.4.0", + "@ax/trace-win-x64": "3.4.0" } }, "@ax/trace-linux-x64": { "name": "@ax/trace-linux-x64", - "version": "3.1.0", - "integrity": "sha512-hiU+vDQWRtrh9zW23AdCzj3rEs9M7bW6sW65FThpMQYZqgXg/R7IyiPr5v0mVFvHzMtxi4/bZQ0bgtqUZcdPXQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/trace-linux-x64/-/trace-linux-x64-3.1.0.tgz", + "version": "3.4.0", + "integrity": "sha512-abAKmsUI78UxTsqJ8FvuYz+8V7Ld5zMP69DYZRVi2NBSKZL8imQcmlhgk8ZUWUI1wswbMyhnAnj/CHKORu3/ng==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/trace-linux-x64/-/trace-linux-x64-3.4.0.tgz", "os": [ "linux" ], @@ -696,9 +875,9 @@ }, "@ax/trace-win-x64": { "name": "@ax/trace-win-x64", - "version": "3.1.0", - "integrity": "sha512-8fmYsQ0tBMs5eWN2XEcijn2fyQHd/AWa2xfqvz/vz+aG7v149TdkYLNq4kwAUj0TRF9/2Tw7FKIfWRxifEzo8g==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/trace-win-x64/-/trace-win-x64-3.1.0.tgz", + "version": "3.4.0", + "integrity": "sha512-9VailyiZ+l9Dw9WrOcxDqeGhThgfOvSANddnHd14/ILLTF9+1rMS2yjmtO3ysUF6oTR5/kdBHkzUzH92Z8nyQQ==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/trace-win-x64/-/trace-win-x64-3.4.0.tgz", "os": [ "win32" ], @@ -706,86 +885,113 @@ "x64" ], "dependencies": {} + }, + "@ax/xlad-service": { + "name": "@ax/xlad-service", + "version": "1.3.0", + "integrity": "sha512-NaO2X8omrUpxvDu16mYWkgd4tCYWch9R+183DHM9u8uqJG8t7oK+2JDHEdfGE02wokzWQ5RGPaVXGDoq2gG0kg==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/xlad-service/-/xlad-service-1.3.0.tgz", + "dependencies": {} + }, + "@simatic-ax/mocks": { + "name": "@simatic-ax/mocks", + "version": "4.3.3", + "integrity": "sha512-/qftODeW9g/1zs8gNKgl74pP69Mduist19pW0pMcbdnaT9timY83/Pwho2XTCXWckatfJkHj+JLNT+ArYS/Rxw==", + "resolved": "https://npm.pkg.github.com/download/@simatic-ax/mocks/4.3.3/837f28a2452b389b16fcd982d5129da08d2aede8", + "dependencies": { + "@ax/axunit-mocking": "^9.0.0", + "@ax/system": "^10.0.0", + "@ax/system-timer": "^10.0.0" + } } }, "workspaces": {}, "catalogs": { "@ax/simatic-ax": { "name": "@ax/simatic-ax", - "version": "2510.0.0", - "integrity": "sha512-S5u9DDjdThQNZg0neo1Q4aebQj/0ezUUA+SJD73/+bTSTCbhEObtXm53nlBD5lr4Vjmv61l0B/Qzz8Asj7TLmQ==", - "resolved": "https://registry.simatic-ax.siemens.io/@ax/simatic-ax/-/simatic-ax-2510.0.0.tgz", + "version": "2510.17.0", + "integrity": "sha512-PMNd/ch+bb6B5/S4F/dlCQqHQ9lZ/96sdCDpFhMnd4/D9f0yVaX97IuPOrlEHb8xXYi7TEGqJo60spGRCe1C+Q==", + "resolved": "https://registry.simatic-ax.siemens.io/@ax/simatic-ax/-/simatic-ax-2510.17.0.tgz", "dependencies": {}, "catalogDependencies": { - "@ax/apax-build": "2.1.79", - "@ax/ax2tia": "12.0.29", - "@ax/axunit-mocking": "8.4.20", - "@ax/axunitst": "8.4.20", - "@ax/axunitst-library": "8.4.20", + "@ax/apax-build": "2.2.60", + "@ax/ax2tia": "12.3.10", + "@ax/axunit-mocking": "9.6.6", + "@ax/axunitst": "9.6.6", + "@ax/axunitst-library": "9.6.6", "@ax/build-native": "16.1.51", - "@ax/certificate-management": "2.0.0", - "@ax/dcp-utility": "1.2.0", - "@ax/diagnostic-buffer": "2.0.0", - "@ax/hardware-diagnostics": "1.0.0", - "@ax/hw-et200sp": "4.0.0", - "@ax/hw-s7-1500": "4.0.0", - "@ax/hwc": "4.0.0", - "@ax/hwld": "3.2.0", - "@ax/mod": "1.9.4", - "@ax/mon": "1.9.4", - "@ax/plc-control": "1.4.3", - "@ax/plc-info": "4.0.0", - "@ax/plc-web-app-manager": "1.1.0", - "@ax/sdb": "1.9.4", - "@ax/simatic-alarming": "5.0.1", - "@ax/simatic-clocks": "11.0.14", - "@ax/simatic-communication": "11.0.1", - "@ax/simatic-crypto": "4.0.15", - "@ax/simatic-diagnostics": "5.0.14", + "@ax/certificate-management": "2.0.1", + "@ax/dcp-utility": "1.2.1", + "@ax/debug-st-ls-plugin": "1.1.11", + "@ax/diagnostic-buffer": "2.2.0", + "@ax/hardware-diagnostics": "1.2.0", + "@ax/hw-et200sp": "4.6.0", + "@ax/hw-s7-1200g2": "4.6.0", + "@ax/hw-s7-1500": "4.6.0", + "@ax/hwc": "4.6.0", + "@ax/hwld": "3.6.0", + "@ax/mod": "1.15.85", + "@ax/mon": "1.15.85", + "@ax/plc-control": "1.7.22", + "@ax/plc-info": "4.2.0", + "@ax/plc-web-app-manager": "1.3.0", + "@ax/sdb": "1.15.85", + "@ax/simatic-alarming": "5.2.0", + "@ax/simatic-clocks": "11.0.116", + "@ax/simatic-communication": "11.0.2", + "@ax/simatic-crypto": "4.0.40", + "@ax/simatic-diagnostics": "5.1.34", "@ax/simatic-diagnostics-hardware": "11.0.0", - "@ax/simatic-distributedio": "11.0.14", - "@ax/simatic-fileaccess": "10.0.8", - "@ax/simatic-hardware-utilities": "6.0.19", - "@ax/simatic-memoryaccess": "6.0.19", - "@ax/simatic-modbusrtu": "4.0.6", - "@ax/simatic-motioncontrol-native-v5": "10.0.9", - "@ax/simatic-motioncontrol-native-v6": "10.0.9", - "@ax/simatic-motioncontrol-native-v7": "10.0.9", - "@ax/simatic-motioncontrol-native-v8": "10.0.9", - "@ax/simatic-motioncontrol-native-v9": "10.0.9", - "@ax/simatic-motioncontrol-v7": "10.0.9", - "@ax/simatic-motioncontrol-v7-mocking": "10.0.9", - "@ax/simatic-motioncontrol-v8": "10.0.9", - "@ax/simatic-motioncontrol-v8-mocking": "10.0.9", - "@ax/simatic-motioncontrol-v9": "10.0.9", - "@ax/simatic-motioncontrol-v9-mocking": "10.0.9", - "@ax/simatic-package-tool": "2.0.17", - "@ax/simatic-pointtopoint": "4.0.6", + "@ax/simatic-distributedio": "11.0.44", + "@ax/simatic-fileaccess": "10.0.66", + "@ax/simatic-hardware-utilities": "6.0.66", + "@ax/simatic-memoryaccess": "6.0.66", + "@ax/simatic-modbusrtu": "4.0.59", + "@ax/simatic-modbustcp": "1.0.166", + "@ax/simatic-motioncontrol-native-v10": "10.1.8", + "@ax/simatic-motioncontrol-native-v5": "10.1.8", + "@ax/simatic-motioncontrol-native-v6": "10.1.8", + "@ax/simatic-motioncontrol-native-v7": "10.1.8", + "@ax/simatic-motioncontrol-native-v8": "10.1.8", + "@ax/simatic-motioncontrol-native-v9": "10.1.8", + "@ax/simatic-motioncontrol-v10": "10.1.8", + "@ax/simatic-motioncontrol-v10-mocking": "10.1.8", + "@ax/simatic-motioncontrol-v7": "10.1.8", + "@ax/simatic-motioncontrol-v7-mocking": "10.1.8", + "@ax/simatic-motioncontrol-v8": "10.1.8", + "@ax/simatic-motioncontrol-v8-mocking": "10.1.8", + "@ax/simatic-motioncontrol-v9": "10.1.8", + "@ax/simatic-motioncontrol-v9-mocking": "10.1.8", + "@ax/simatic-package-tool": "2.0.20", + "@ax/simatic-pointtopoint": "4.0.54", "@ax/simatic-tasks": "11.0.2", - "@ax/simatic-technology-objects": "4.0.8", - "@ax/sld": "3.5.5", - "@ax/st-ls": "11.0.142", - "@ax/st-opcua.stc-plugin": "2.0.0", - "@ax/st-resources.stc-plugin": "4.0.3", - "@ax/stc": "11.0.142", - "@ax/system": "10.2.7", - "@ax/system-bitaccess": "10.2.7", - "@ax/system-conversion": "10.2.7", - "@ax/system-counters": "10.2.7", - "@ax/system-data": "10.2.7", - "@ax/system-datetime": "10.2.7", - "@ax/system-edgedetection": "10.2.7", - "@ax/system-fastmath": "10.2.7", - "@ax/system-math": "10.2.7", - "@ax/system-selection": "10.2.7", - "@ax/system-serde": "10.2.7", - "@ax/system-strings": "10.2.7", - "@ax/system-timer": "10.2.7", - "@ax/target-llvm": "11.0.142", - "@ax/target-mc7plus": "11.0.142", - "@ax/tia2st": "4.0.10", - "@ax/trace": "3.1.0", - "@ax/sdk": "2510.0.0" + "@ax/simatic-technology-objects": "4.0.43", + "@ax/sld": "3.8.3", + "@ax/st-lang-contrib-xlad": "1.3.1", + "@ax/st-ls": "11.6.37", + "@ax/st-opcua.stc-plugin": "2.1.0", + "@ax/st-resources.stc-plugin": "4.0.4", + "@ax/stc": "11.6.37", + "@ax/system": "10.4.66", + "@ax/system-bistable": "10.4.66", + "@ax/system-bitaccess": "10.4.66", + "@ax/system-conversion": "10.4.66", + "@ax/system-counters": "10.4.66", + "@ax/system-data": "10.4.66", + "@ax/system-datetime": "10.4.66", + "@ax/system-edgedetection": "10.4.66", + "@ax/system-fastmath": "10.4.66", + "@ax/system-math": "10.4.66", + "@ax/system-selection": "10.4.66", + "@ax/system-serde": "10.4.66", + "@ax/system-strings": "10.4.66", + "@ax/system-timer": "10.4.66", + "@ax/target-llvm": "11.6.37", + "@ax/target-mc7plus": "11.6.37", + "@ax/tia2st": "4.3.8", + "@ax/trace": "3.4.0", + "@ax/xlad-service": "1.3.0", + "@ax/sdk": "2510.17.0" } } } diff --git a/apax.yml b/apax.yml index 6c91baa..a66beb3 100644 --- a/apax.yml +++ b/apax.yml @@ -1,6 +1,6 @@ # General information name: "@simatic-ax/lstream" -version: 1.0.0 +version: 0.0.0-placeholder type: lib keywords: - library @@ -13,14 +13,21 @@ repository: type: "git" url: "https://github.com/simatic-ax/LStream-JSON-XML" targets: - - "1500" + - "s7" - "llvm" # Install settings -apaxVersion: 4.0.0 +apaxVersion: ^4.3.0 # Dependencies devDependencies: - "@ax/ax2tia": ^12.0.29 - "@ax/sdk": ^2510.0.0 + "@ax/ax2tia": ^12.3.10 + "@ax/sdk": ^2510.17.0 + "@simatic-ax/mocks": ^4.3.3 +dependencies: + "@ax/system-timer": ^10.4.66 + "@ax/system-serde": ^10.4.66 +catalogs: + "@ax/simatic-ax": ^2510.17.0 + variables: # an arbitrary directory that can be chosen freely PATH_NAME: "./bin/handover-folder" @@ -34,8 +41,8 @@ variables: - --generate-runtime-checks # Apax scripts scripts: - # transfer a AX Library to handover library documents for a TIA Libray - export-tia-handover-documents: ax2tia -i ./bin/1500/*.lib -o "$PATH_NAME" + # transfer a AX Library to handover library documents for a TIA Library + export-tia-handover-documents: ax2tia -i ./bin/s7/*.lib -o "$PATH_NAME" # convert library handover documents into a global library for the TIA Portal. import-handover-documents-to-tia: '"$TIA_INSTALL_PATH\\bin\\Siemens.Simatic.Lang.Library.Importer.exe" -i "$PATH_NAME" -o "$TIA_GLOBAL_LIB_PATH" -u' create-tialib: @@ -47,8 +54,6 @@ scripts: files: - ./src -dependencies: - "@ax/system-timer": ^10.2.7 - "@ax/system-serde": ^10.2.7 -catalogs: - "@ax/simatic-ax": ^2510.0.0 +# Registries +registries: + '@simatic-ax': 'https://npm.pkg.github.com/' diff --git a/src/LStream/LStream_JsonDeserializer.st b/src/LStream/LStream_JsonDeserializer.st index 78abb5e..9937844 100644 --- a/src/LStream/LStream_JsonDeserializer.st +++ b/src/LStream/LStream_JsonDeserializer.st @@ -1,10 +1,10 @@ -//REGION BLOCK INFO HEADER +//REGION BLOCK INFO HEADER //=============================================================================== // SIEMENS AG / (c)Copyright 2021 //------------------------------------------------------------------------------- // Title: LStream_JsonDeserializer - // Comment/Function: parses a JSON string provided in the raw paramter - // and rebuilds the coinatend bytes into a tree structure + // Comment/Function: parses a JSON string provided in the raw parameter + // and rebuilds the contained bytes into a tree structure // Library/Family: LStream // Author: DI FA S SUP E&C // Tested with: S7-1500 V2.8 @@ -20,150 +20,245 @@ // 01.6.02 | 2023-07-28 | DI FA S SUP E&C | Raw byte and tree array start index bug fixing //=============================================================================== //END_REGION -USING Simatic.Ax.Timer; USING System.Timer; USING Simatic.Ax.System.Strings; USING Simatic.Ax.LStream.Utilities; USING Simatic.Ax.LStream.Models; NAMESPACE Simatic.Ax.LStream + /// Parses a JSON string provided in the raw parameter and rebuilds the contained bytes into a tree structure FUNCTION_BLOCK LStream_JsonDeserializer - /// LStream_JsonDeserializer + // LStream_JsonDeserializer // Author : DI_FA_S_SUP_EuC // Family : LStream // Version : 1.0 - //parses a JSON string provided in the raw paramter and rebuilds the coinatend bytes into a tree structure VAR_INPUT - execute : Bool; // Rising edge starts action once - search : Bool; // TRUE: Search option is acticve + /// Rising edge starts action once + execute : Bool; + /// TRUE: Search option is active + search : Bool; END_VAR - VAR_OUTPUT - done : Bool; // TRUE: Commanded functionality has been completed successfully - busy : Bool; // TRUE: FB is not finished and new output values can be expected - error : Bool; // TRUE: An error occurred during the execution of the FB - status : Word := STATUS_NO_CALL; // 16#0000 - 16#7FFF: Status of the FB, 16#8000 - 16#FFFF: Error identification - resultCount : Int; // Count of parsed JSON elements + /// TRUE: Commanded functionality has been completed successfully + done : Bool; + /// TRUE: FB is not finished and new output values can be expected + busy : Bool; + /// TRUE: An error occurred during the execution of the FB + error : Bool; + /// 16#0000 - 16#7FFF: Status of the FB, 16#8000 - 16#FFFF: Error identification + status : Word := STATUS_NO_CALL; + /// Count of parsed JSON elements + resultCount : Int; END_VAR - VAR_IN_OUT - tree : Array[*] of LStream_typeElement; // JSON tree containing the stored data - raw : Array[*] of Byte; // raw JSON data to parse in byte format + /// JSON tree containing the stored data + tree : Array[*] of LStream_typeElement; + /// raw JSON data to parse in byte format + raw : Array[*] of Byte; END_VAR VAR PUBLIC - TimeoutTimer : ITimerFunctions; + TimeoutTimer : System.Timer.OnDelay; END_VAR VAR - statExecuteOld : Bool; // Old value of 'execute' input for edge detection - statDone : Bool; // Static value for output 'done' - statBusy : Bool; // Static value for output 'busy' - statError : Bool; // Static value for output 'error' - statStatus : Word := STATUS_NO_CALL; // Static value for output 'status' - statFBState : DInt := FB_STATE_NO_PROCESSING; // State in the state machine of the FB - statCountOfTree : Int; // Static count of tree fields - statResultCount : Int; // Static value of result count - statRawIndex : Int; // Static value of raw index - statSearch : Bool; // TRUE: Only keys provided in tree will be parsed - statKeyValueFound : Bool; // TRUE: Key exisits in raw data - statTreeIndex : Int; // Static value of tree index - statSearchIndex : DInt; // Static search - statLastDepth : SInt := SINT#-1; // Static value of the last element JSON depth - statDepth : SInt := SINT#-1; // Static value of JSON depth - statOpen : Bool; // TRUE: Key or Value is opened - statInfoStartIndex : Int; // Start index of current info in raw key or value - statIgnorableCharacter : Bool; // Next character to be ignored - statInfoLen : UInt; // Current information length key or value - statKey : String; // Static key string - statValue : String; // Static value string - statKeyNotFoundCount : Int; // Count of provided keys that weren't found - statArrayOpen : Bool; // TRUE: New JSON array is open - statObjectArray : Bool; // TRUE: Is type array of obejct - statType : SInt; // Current type of element - statDefineValueType : Bool; // TRUE: Value Type can be defined - statArrayName : String; // Name of array - statLastChar : Int; // Value of the last input character - statOpenFormat : Bool; // Boolean for compressed file format; FALSE= file format compressed - statFirstElement : Bool; // Element is first element of an array; TRUE= first element - statIsMultiarray : Bool; // Flag bit for multi dimensional arrays + /// Old value of 'execute' input for edge detection + statExecuteOld : Bool; + /// Static value for output 'done' + statDone : Bool; + /// Static value for output 'busy' + statBusy : Bool; + /// Static value for output 'error' + statError : Bool; + /// Static value for output 'status' + statStatus : Word := STATUS_NO_CALL; + /// State in the state machine of the FB + statFBState : DInt := FB_STATE_NO_PROCESSING; + /// Static count of tree fields + statCountOfTree : Int; + /// Static value of result count + statResultCount : Int; + /// Static value of raw index + statRawIndex : Int; + /// TRUE: Only keys provided in tree will be parsed + statSearch : Bool; + /// TRUE: Key exists in raw data + statKeyValueFound : Bool; + /// Static value of tree index + statTreeIndex : Int; + /// Static search + statSearchIndex : DInt; + /// Static value of the last element JSON depth + statLastDepth : SInt := SINT#-1; + /// Static value of JSON depth + statDepth : SInt := SINT#-1; + /// TRUE: Key or Value is opened + statOpen : Bool; + /// Start index of current info in raw key or value + statInfoStartIndex : Int; + /// Next character to be ignored + statIgnorableCharacter : Bool; + /// Current information length key or value + statInfoLen : UInt; + /// Static key string + statKey : String; + /// Static value string + statValue : String; + /// Count of provided keys that weren't found + statKeyNotFoundCount : Int; + /// TRUE: New JSON array is open + statArrayOpen : Bool; + /// TRUE: Is type array of object + statObjectArray : Bool; + /// Current type of element + statType : SInt; + /// TRUE: Value Type can be defined + statDefineValueType : Bool; + /// Name of array + statArrayName : String; + /// Value of the last input character + statLastChar : Int; + /// Boolean for compressed file format; FALSE= file format compressed + statOpenFormat : Bool; + /// Element is first element of an array; TRUE= first element + statFirstElement : Bool; + /// Flag bit for multi dimensional arrays + statIsMultiArray : Bool; + /// Empty element for clearing statEmptyTree : LStream_typeElement; - statRawStartIndex : Int; // Static start index number of the RAW array - statRawEndIndex : Int; // Static end index number of the RAW array - statTreeStartIndex : Int; // Static start index number of the Tree array - instWatchDog : TimerFunctionsImpl; + /// Static start index number of the RAW array + statRawStartIndex : Int; + /// Static end index number of the RAW array + statRawEndIndex : Int; + /// Static start index number of the Tree array + statTreeStartIndex : Int; END_VAR VAR_TEMP - tempExecute : Bool; // Temporary value for input 'execute' - tempRawIndex : Int; // Index of raw field - tempKeyToFind : String; // Provided key to which the value should be found - tempSearchResult : DInt; // Temp value of search result - tempMultiIndex : Int; // Temp Index value for Multi dimensional array check - tempIteral : Int; // Temp index for clearing the raw - tempTreeIndex : Int; // Temp index for clearing the tree + /// Temporary value for input 'execute' + tempExecute : Bool; + /// Index of raw field + tempRawIndex : Int; + /// Provided key to which the value should be found + tempKeyToFind : String; + /// Temp value of search result + tempSearchResult : DInt; + /// Temp Index value for Multi dimensional array check + tempMultiIndex : Int; + /// Temp index for clearing the raw + tempIteral : Int; + /// Temp index for clearing the tree + tempTreeIndex : Int; END_VAR VAR CONSTANT - FB_STATE_NO_PROCESSING : DInt := 0; // FB state: No processing - FB_STATE_PARSE : DInt := 1; // FB state: Processing Parsing - FB_STATE_CLEAR_BYTE_ARRAY : DInt := 2; // FB state clearing the tree array - FB_STATE_FILE_FORMAT : DInt := 3; // FB state: Checking the JSON file format - STATUS_EXECUTION_FINISHED : Word := WORD#16#0000; // Execution finished without errors - STATUS_NO_CALL : Word := WORD#16#7000; // No job being currently processed - STATUS_FIRST_CALL : Word := WORD#16#7001; // First call after incoming new job (rising edge 'execute') - STATUS_SUBSEQUENT_CALL : Word := WORD#16#7002; // Subsequent call during active processing without further details - ERR_TREE_ARRAY_TOO_SMALL : Word := WORD#16#8201; // Error: provided array to too small - ERR_EMPTY_RAW_DATA : Word := WORD#16#8401; // Error: no raw data provided - ERR_UNDEFINED_STATE : Word := WORD#16#8600; // Error: due to an undefined state in state machine - JSON_QUTATIONMARK : Byte := BYTE#16#22; // ASCII code for '"' - JSON_COLON : Byte := BYTE#16#3A; // ASCII code for ':' - JSON_COMMA : Byte := BYTE#16#2C; // ASCII code for ',' - JSON_BRACES_OPEN : Byte := BYTE#16#7B; // ASCII code for '{' - JSON_BRACES_CLOSE : Byte := BYTE#16#7D; // ASCII code for '}' - JSON_BRACKETS_OPEN : Byte := BYTE#16#5B; // ASCII code for '[' - JSON_BRACKETS_CLOSE : Byte := BYTE#16#5D; // ASCII code for ] - JSON_PROTECTION_KEY : Byte := BYTE#16#5C; // ASCII coder for \ indicates closing - JSON_CLOSE_KEY : Byte := BYTE#16#2F; // ASCII coder for / indicates closing - LINE_FEED : Byte := BYTE#16#0A; // ASCII code for line feed - TAB : Byte := BYTE#16#09; // ASCII code for TAB - CARRIAGE_RETURN : Byte := BYTE#16#0D; // ASCII code for carriage return - SPACE : Byte := BYTE#16#20; // ASCII code for blank space - FIRST_DIM : USInt := USINT#1; // Constant for first array dimension - MAX_LOOP_TIME : Time := T#3ms; // Max duration of loop, will be continued in next cycle - INVALID_SEARCH : DInt := -1; // Return value if Search in LParse_FindStringInCharrArrayAdv was not successfull - KEY_NOT_FOUND : String := 'Key not found'; // Value string in case key wasn't found - INCREMENT_BY_ONE : USInt := USINT#1; // Constant to increment by one - DECREMENT_BY_ONE : SInt := SINT#1; // Constant to decrement by one - DECREMENT_BY_TWO : SInt := SINT#2; // Constant to decrement by two - OBJECT : SInt := SINT#0; // Constant for value typ object - ARRAYCONST : SInt := SINT#1; // Constant for value typ array - STRINGCONST : SInt := SINT#2; // Constant for value type string - NUMBER : SInt := SINT#3; // Constant for value type number - BOOLEAN : SInt := SINT#4; // Constant for value type boolean - DEPTH_NOT_DEFINED : Int := -1; // Constant indicating depth wasn't yet defined - MULTI_ARRAY : String := 'Multi@$$@y'; // Constant key value for identify multidimensional arrays - NUL : Byte := BYTE#16#0; // Constant empty byte + /// FB state: No processing + FB_STATE_NO_PROCESSING : DInt := 0; + /// FB state: Processing Parsing + FB_STATE_PARSE : DInt := 1; + /// FB state clearing the tree array + FB_STATE_CLEAR_BYTE_ARRAY : DInt := 2; + /// FB state: Checking the JSON file format + FB_STATE_FILE_FORMAT : DInt := 3; + /// Execution finished without errors + STATUS_EXECUTION_FINISHED : Word := WORD#16#0000; + /// No job being currently processed + STATUS_NO_CALL : Word := WORD#16#7000; + /// First call after incoming new job (rising edge 'execute') + STATUS_FIRST_CALL : Word := WORD#16#7001; + /// Subsequent call during active processing without further details + STATUS_SUBSEQUENT_CALL : Word := WORD#16#7002; + /// Error: provided array to too small + ERR_TREE_ARRAY_TOO_SMALL : Word := WORD#16#8201; + /// Error: no raw data provided + ERR_EMPTY_RAW_DATA : Word := WORD#16#8401; + /// Error: due to an undefined state in state machine + ERR_UNDEFINED_STATE : Word := WORD#16#8600; + /// ASCII code for '"' + JSON_QUOTATION_MARK : Byte := BYTE#16#22; + /// ASCII code for ':' + JSON_COLON : Byte := BYTE#16#3A; + /// ASCII code for ',' + JSON_COMMA : Byte := BYTE#16#2C; + /// ASCII code for '{' + JSON_BRACES_OPEN : Byte := BYTE#16#7B; + /// ASCII code for '}' + JSON_BRACES_CLOSE : Byte := BYTE#16#7D; + /// ASCII code for '[' + JSON_BRACKETS_OPEN : Byte := BYTE#16#5B; + /// ASCII code for ] + JSON_BRACKETS_CLOSE : Byte := BYTE#16#5D; + /// ASCII coder for \ indicates closing + JSON_PROTECTION_KEY : Byte := BYTE#16#5C; + /// ASCII coder for / indicates closing + JSON_CLOSE_KEY : Byte := BYTE#16#2F; + /// ASCII code for line feed + LINE_FEED : Byte := BYTE#16#0A; + /// ASCII code for TAB + TAB : Byte := BYTE#16#09; + /// ASCII code for carriage return + CARRIAGE_RETURN : Byte := BYTE#16#0D; + /// ASCII code for blank space + SPACE : Byte := BYTE#16#20; + /// Constant for first array dimension + FIRST_DIM : USInt := USINT#1; + /// Max duration of loop, will be continued in next cycle + MAX_LOOP_TIME : Time := T#3ms; + /// Return value if Search in LStream_FindStringInByteCharArrayAdv was not successful + INVALID_SEARCH : DInt := -1; + /// Value string in case key wasn't found + KEY_NOT_FOUND : String := 'Key not found'; + /// Constant to increment by one + INCREMENT_BY_ONE : USInt := USINT#1; + /// Constant to decrement by one + DECREMENT_BY_ONE : SInt := SINT#1; + /// Constant to decrement by two + DECREMENT_BY_TWO : SInt := SINT#2; + /// Constant for value typ object + OBJECT : SInt := SINT#0; + /// Constant for value typ array + ARRAY_CONST : SInt := SINT#1; + /// Constant for value type string + STRING_CONST : SInt := SINT#2; + /// Constant for value type number + NUMBER : SInt := SINT#3; + /// Constant for value type boolean + BOOLEAN : SInt := SINT#4; + /// Constant indicating depth wasn't yet defined + DEPTH_NOT_DEFINED : Int := -1; + /// Constant key value for identify multidimensional arrays + MULTI_ARRAY : String := 'MultiArray'; + /// Constant empty byte + NUL : Byte := BYTE#16#0; - JSON_QUTATIONMARK_INT : INT := 34; // ASCII code for '"' - JSON_COLON_INT : INT := 58; // ASCII code for ':' - JSON_COMMA_INT : INT := 44; // ASCII code for ',' - JSON_BRACES_OPEN_INT : INT := 123; // ASCII code for '{' - JSON_BRACES_CLOSE_INT : INT := 125; // ASCII code for '}' - JSON_BRACKETS_OPEN_INT : INT := 91; // ASCII code for '[' - JSON_BRACKETS_CLOSE_INT : INT := 93; // ASCII code for ']' - JSON_PROTECTION_KEY_INT : INT := 92; // ASCII code for '\' indicates closing - JSON_CLOSE_KEY_INT : INT := 47; // ASCII code for '/' indicates closing - LINE_FEED_INT : INT := 10; // ASCII code for line feed - TAB_INT : INT := 9; // ASCII code for TAB - CARRIAGE_RETURN_INT : INT := 13; // ASCII code for carriage return - SPACE_INT : INT := 32; // ASCII code for blank space - NUL_INT : INT := 0; // Constant empty byte + /// ASCII code for '"' + JSON_QUOTATION_MARK_INT : INT := 34; + /// ASCII code for ':' + JSON_COLON_INT : INT := 58; + /// ASCII code for ',' + JSON_COMMA_INT : INT := 44; + /// ASCII code for '{' + JSON_BRACES_OPEN_INT : INT := 123; + /// ASCII code for '}' + JSON_BRACES_CLOSE_INT : INT := 125; + /// ASCII code for '[' + JSON_BRACKETS_OPEN_INT : INT := 91; + /// ASCII code for ']' + JSON_BRACKETS_CLOSE_INT : INT := 93; + /// ASCII code for '\' indicates closing + JSON_PROTECTION_KEY_INT : INT := 92; + /// ASCII code for '/' indicates closing + JSON_CLOSE_KEY_INT : INT := 47; + /// ASCII code for line feed + LINE_FEED_INT : INT := 10; + /// ASCII code for TAB + TAB_INT : INT := 9; + /// ASCII code for carriage return + CARRIAGE_RETURN_INT : INT := 13; + /// ASCII code for blank space + SPACE_INT : INT := 32; + /// Constant empty byte + NUL_INT : INT := 0; END_VAR - - IF TimeoutTimer = NULL THEN - TimeoutTimer := instWatchDog; - END_IF; - tempExecute := execute; // Work with temporary value / create process image //REGION TRIGGERING @@ -186,7 +281,7 @@ NAMESPACE Simatic.Ax.LStream statOpen := FALSE; statDepth := SINT#-1; statRawIndex := statRawStartIndex; - statIsMultiarray := false; + statIsMultiArray := false; statTreeIndex := statTreeStartIndex; statInfoLen := UINT#0; statSearchIndex := 0; @@ -237,15 +332,22 @@ NAMESPACE Simatic.Ax.LStream statLastChar := tempRawIndex; END_IF; END_FOR; - statRawIndex := statRawStartIndex; - tempRawIndex := statRawStartIndex; - // if there is a special format character before the closeing bracket the JSON file is not compressed - IF raw[statLastChar - 1] = LINE_FEED OR raw[statLastChar - 1] = CARRIAGE_RETURN OR raw[statLastChar - 1] = SPACE OR raw[statLastChar - 1] = TAB THEN - statOpenFormat := true; + // Cancel in case raw data is empty + IF raw[statLastChar] = NUL THEN + statStatus := ERR_EMPTY_RAW_DATA; ELSE - statOpenFormat := false; + // Continue processing if input not empty + statRawIndex := statRawStartIndex; + tempRawIndex := statRawStartIndex; + // if there is a special format character before the closing bracket the JSON file is not compressed + IF raw[statLastChar - 1] = LINE_FEED OR raw[statLastChar - 1] = CARRIAGE_RETURN OR raw[statLastChar - 1] = SPACE OR raw[statLastChar - 1] = TAB THEN + statOpenFormat := true; + ELSE + statOpenFormat := false; + END_IF; + statFBState := FB_STATE_PARSE; END_IF; - statFBState := FB_STATE_PARSE; + //END_REGION FILE FORMAT @@ -254,10 +356,10 @@ NAMESPACE Simatic.Ax.LStream //REGION PARSING //rest watchdog - TimeoutTimer.TimerFunction(signal := FALSE, + TimeoutTimer(signal := FALSE, duration := MAX_LOOP_TIME); //start watchdog - TimeoutTimer.TimerFunction(signal := TRUE, + TimeoutTimer(signal := TRUE, duration := MAX_LOOP_TIME); FOR tempRawIndex := statRawIndex TO statRawEndIndex DO @@ -301,11 +403,11 @@ NAMESPACE Simatic.Ax.LStream statDepth := statDepth - DECREMENT_BY_ONE; //last element closed, and there is still info in the buffer, then write value to last key IF (statDepth = -1 AND statInfoLen > UINT#0) THEN - //in case of 0 length, Chars_To_Strig returns the maxium possible char combination + //in case of 0 length, Chars_To_String returns the maximum possible char combination CharsToString(chars := raw, startPosition := statInfoStartIndex - statRawStartIndex, length := statInfoLen, result => statValue); statInfoLen := UINT#0; - //value Type wasn't yet definied must be either number or boolean + //value Type wasn't yet defined must be either number or boolean IF (statDefineValueType) THEN statDefineValueType := FALSE; @@ -319,7 +421,7 @@ NAMESPACE Simatic.Ax.LStream IF (statSearch) THEN IF (statTreeIndex > 0) THEN - //in case of 0 length, Chars_To_Strig returns the maxium possible char combination + //in case of 0 length, Chars_To_String returns the maximum possible char combination IF (statKey = tree[statTreeIndex - DECREMENT_BY_ONE].key) THEN //only write into tree, if key matches, otherwise continue with search for matching key tree[statTreeIndex - DECREMENT_BY_ONE].value := statValue; @@ -353,14 +455,14 @@ NAMESPACE Simatic.Ax.LStream //checking for multidimensional array tempMultiIndex := tempRawIndex; // check that is it a multidimensional array structure or not - WHILE ((NOT statIsMultiarray) AND tempMultiIndex > statRawStartIndex) DO + WHILE ((NOT statIsMultiArray) AND tempMultiIndex > statRawStartIndex) DO CASE TO_INT(raw[tempMultiIndex - 1]) OF TAB_INT, LINE_FEED_INT, CARRIAGE_RETURN_INT, SPACE_INT: tempMultiIndex := tempMultiIndex - DECREMENT_BY_ONE; JSON_BRACKETS_OPEN_INT: - statIsMultiarray := true; + statIsMultiArray := true; ELSE tempMultiIndex := 0; @@ -368,13 +470,13 @@ NAMESPACE Simatic.Ax.LStream END_WHILE; // if it is a simple array - IF (NOT statIsMultiarray) THEN + IF (NOT statIsMultiArray) THEN IF (statTreeIndex > statTreeStartIndex) THEN//simple array //in multi dimensional array do not write over the existing format IF tree[statTreeIndex - DECREMENT_BY_ONE].key <> MULTI_ARRAY THEN //set the element type to array - tree[statTreeIndex - DECREMENT_BY_ONE].types := ARRAYCONST; + tree[statTreeIndex - DECREMENT_BY_ONE].types := ARRAY_CONST; ELSE// in multidimensional array keep the type ; END_IF; @@ -382,24 +484,24 @@ NAMESPACE Simatic.Ax.LStream statArrayName := tree[statTreeIndex - DECREMENT_BY_ONE].key; statTreeIndex := statTreeIndex + INCREMENT_BY_ONE; ELSE // first element, is of type array - tree[statTreeIndex].typeS := ARRAYCONST; + tree[statTreeIndex].typeS := ARRAY_CONST; END_IF; ELSE //multidimensional array //set the element type of the inside array - tree[statTreeIndex - DECREMENT_BY_ONE].types := ARRAYCONST; + tree[statTreeIndex - DECREMENT_BY_ONE].types := ARRAY_CONST; tree[statTreeIndex - DECREMENT_BY_ONE].key := MULTI_ARRAY; tree[statTreeIndex - DECREMENT_BY_ONE].depth := statDepth - DECREMENT_BY_ONE; //save arrayName statArrayName := MULTI_ARRAY; statTreeIndex := INCREMENT_BY_ONE; - tree[statTreeIndex].types := ARRAYCONST; + tree[statTreeIndex].types := ARRAY_CONST; statInfoStartIndex := statInfoStartIndex + INCREMENT_BY_ONE; END_IF; statArrayOpen := TRUE; statObjectArray := FALSE; - statIsMultiarray := false; + statIsMultiArray := false; //END_REGION ARRAY OPEN @@ -407,14 +509,23 @@ NAMESPACE Simatic.Ax.LStream //REGION ARRAY CLOSE statDepth := statDepth - DECREMENT_BY_ONE; statArrayOpen := FALSE; - statFirstElement := false; - //last element of an array->set closing element flag to true - tree[statTreeIndex - 1].closingElement := true; + // Empty array: undo the tree index reservation made by '[' + IF (statFirstElement = TRUE) THEN + statTreeIndex := statTreeIndex - DECREMENT_BY_ONE; // undo the slot reservation + statValue := 'NULL'; + statInfoLen := UINT#0; + statType := ARRAY_CONST; // preserve array type through COMMA handler + ELSE + //last element of an array->set closing element flag to true + tree[statTreeIndex - 1].closingElement := true; + END_IF; + + statFirstElement := false; //END_REGION ARRAY CLOSE - - JSON_QUTATIONMARK_INT: - //REGION QUTATIONMARK + + JSON_QUOTATION_MARK_INT: + //REGION QUOTATION_MARK IF (statOpen) THEN IF (NOT (raw[tempRawIndex - DECREMENT_BY_ONE] = JSON_PROTECTION_KEY)) THEN // previous sign \ then allowed in string adjust @@ -426,31 +537,32 @@ NAMESPACE Simatic.Ax.LStream statInfoStartIndex := tempRawIndex + INCREMENT_BY_ONE; //set reader to next sign statOpen := TRUE; statIgnorableCharacter := FALSE; - //Qutation mark after colomn indicates value is of type string + //Quotation mark after column indicates value is of type string IF statDefineValueType THEN - statType := STRINGCONST; + statType := STRING_CONST; tree[statTreeIndex - DECREMENT_BY_ONE].types := statType; statDefineValueType := FALSE; END_IF; END_IF; - //END_REGION QUTATIONMARK - + //END_REGION QUOTATION_MARK + JSON_COLON_INT: //REGION COLON - //array is open, and a sperate key is found indication for array of objects + //array is open, and a separate key is found indication for array of objects // IF statArrayOpen AND NOT statObjectArray THEN statObjectArray := TRUE; statTreeIndex := statTreeIndex - DECREMENT_BY_ONE; + statFirstElement := FALSE; // array has at least one object element END_IF; //error handling IF (statTreeIndex > statCountOfTree) THEN //data does not fit in the provided tree statStatus := ERR_TREE_ARRAY_TOO_SMALL; - statDone := TRUE; + EXIT; END_IF; @@ -460,7 +572,7 @@ NAMESPACE Simatic.Ax.LStream statInfoLen := UINT#0; - //After key was written, type of value can be definied, value can be object, array, string, number, or boolean + //After key was written, type of value can be defined, value can be object, array, string, number, or boolean statDefineValueType := TRUE; IF (statSearch) THEN @@ -470,7 +582,7 @@ NAMESPACE Simatic.Ax.LStream tree[statTreeIndex].depth := statDepth; statTreeIndex := statTreeIndex + INCREMENT_BY_ONE; END_IF; - + ELSE// if search option isn't active (parse all) the add key to the current tree index // IF statTreeIndex < statTreeStartIndex THEN @@ -499,16 +611,15 @@ NAMESPACE Simatic.Ax.LStream IF (statTreeIndex > statCountOfTree + INCREMENT_BY_ONE) THEN //data does not fit in the provided tree statStatus := ERR_TREE_ARRAY_TOO_SMALL; - statDone := TRUE; EXIT; END_IF; IF (NOT statOpen) THEN //write value comma isn't part of string IF (statInfoLen > UINT#0) THEN - //in case of 0 length, Chars_To_Strig returns the maxium possible char combination + //in case of 0 length, Chars_To_String returns the maximum possible char combination - //If it's compressed file or multiarray etc., bracket can be the first element->need shit the startindex by one + //If it's compressed file or multi array etc., bracket can be the first element->need shit the start index by one IF raw[statInfoStartIndex] = JSON_BRACKETS_OPEN THEN CharsToString(chars := raw, startPosition := statInfoStartIndex - statRawStartIndex + 1, @@ -525,10 +636,10 @@ NAMESPACE Simatic.Ax.LStream ELSE statValue := 'NULL'; statDefineValueType := FALSE; - //no value Type deafult value type NULL/Default is correct + //no value Type default value type NULL/Default is correct END_IF; - //value Type wasn't yet definied must be either number or boolean + //value Type wasn't yet defined must be either number or boolean IF (statDefineValueType) THEN statDefineValueType := FALSE; @@ -544,7 +655,7 @@ NAMESPACE Simatic.Ax.LStream IF (statSearch) THEN IF (statTreeIndex > statTreeStartIndex) THEN - //in case of 0 length, Chars_To_Strig returns the maxium possible char combination + //in case of 0 length, Chars_To_String returns the maximum possible char combination IF (statKey = tree[statTreeIndex - DECREMENT_BY_ONE].key) THEN //only write into tree, if key matches, otherwise continue with search for matching key tree[statTreeIndex - DECREMENT_BY_ONE].value := statValue; @@ -585,9 +696,9 @@ NAMESPACE Simatic.Ax.LStream ELSE//case ; included in string statInfoLen := statInfoLen + INCREMENT_BY_ONE; END_IF; - - //END_REGION + //END_REGION + TAB_INT, LINE_FEED_INT, CARRIAGE_RETURN_INT, SPACE_INT: //REGION READ TAB LINE FEED CARRIAGE RETURN IF (NOT statOpen) THEN @@ -597,9 +708,9 @@ NAMESPACE Simatic.Ax.LStream statInfoLen := statInfoLen + INCREMENT_BY_ONE; END_IF; - //END_REGION READ TAB LINE FEED CARRIAGE RETURN - - + //END_REGION READ TAB LINE FEED CARRIAGE RETURN + + ELSE // all other characters //REGION ALL OTHER SIGNS IF (statIgnorableCharacter = TRUE) THEN @@ -615,8 +726,10 @@ NAMESPACE Simatic.Ax.LStream statRawIndex := tempRawIndex; + // Call for Timeout timer to update value + TimeoutTimer(); //quit and resume in next cycle if watchdogtimer is exceed - IF (TimeoutTimer.TimerFunction()) THEN + IF (TimeoutTimer.output) THEN statRawIndex := statRawIndex + INCREMENT_BY_ONE; EXIT; END_IF; @@ -626,22 +739,22 @@ NAMESPACE Simatic.Ax.LStream IF statValue <> 'NULL' AND statRawIndex = statRawEndIndex THEN tree[statTreeIndex - DECREMENT_BY_ONE].value := statValue; tree[statTreeIndex - DECREMENT_BY_ONE].key := statKey; - // vizsgálat mennyi nem ignore vagy zárójel karakter volt annyi a depth + // investigation how many non-ignore or parenthetical characters were there in the depth tempRawIndex := statRawIndex; - //at the last carachter stat depth already at 0 so need to found the last element depth + // at the last character stat depth already at 0 so need to find the last element depth WHILE tempRawIndex > statRawStartIndex DO CASE TO_INT(raw[tempRawIndex]) OF JSON_BRACES_CLOSE_INT, JSON_BRACKETS_CLOSE_INT: //if elements were closed tempDepth should increase search for the last element further statLastDepth := statLastDepth + TO_SINT(INCREMENT_BY_ONE); tempRawIndex := tempRawIndex - DECREMENT_BY_ONE; - TAB_INT, LINE_FEED_INT, CARRIAGE_RETURN_INT, SPACE_INT, NUL_INT: // if there was just ignorable charachters or search for the last element further + TAB_INT, LINE_FEED_INT, CARRIAGE_RETURN_INT, SPACE_INT, NUL_INT: // if there were just ignorable characters or search for the last element further tempRawIndex := tempRawIndex - DECREMENT_BY_ONE; - ELSE // any other charachter means we found the end of the last charachter + ELSE // any other character means we found the end of the last character tempRawIndex := statRawStartIndex; END_CASE; END_WHILE; - //tempLastdepth is the last element dept + //tempLastDepth is the last element depth tree[statTreeIndex - DECREMENT_BY_ONE].depth := statLastDepth; statLastDepth := SINT#-1; END_IF; @@ -650,7 +763,7 @@ NAMESPACE Simatic.Ax.LStream tree[statTreeIndex - DECREMENT_BY_ONE].types := statType; END_IF; //if parser is through the entire raw data or end of tree array reached - IF (statRawIndex >= statRawEndIndex) OR (statTreeIndex > statCountOfTree) THEN + IF ((statRawIndex >= statRawEndIndex) OR (statTreeIndex > statCountOfTree)) AND NOT (statStatus.%X15 = TRUE) THEN statStatus := STATUS_EXECUTION_FINISHED; statResultCount := statTreeIndex - statKeyNotFoundCount; END_IF; @@ -666,32 +779,32 @@ NAMESPACE Simatic.Ax.LStream //REGION OUTPUTS // Write outputs IF (statStatus = STATUS_EXECUTION_FINISHED) AND (statDone = FALSE) THEN // Execution finished without errors - //REGION EXECUTION FINSIHED + //REGION EXECUTION FINISHED statDone := TRUE; statBusy := FALSE; statError := FALSE; // execution aborted --> set state no processing statFBState := FB_STATE_NO_PROCESSING; - //END_REGION EXECUTION FINSIHED + //END_REGION EXECUTION FINISHED ELSIF (statStatus.%X15 = TRUE) AND (statError = FALSE) THEN // Error occurred (#statStatus is 16#8000 to 16#FFFF) - //REGION ERROR OCCURD + //REGION ERROR OCCURRED statDone := FALSE; statBusy := FALSE; statError := TRUE; // execution aborted --> set state no processing statFBState := FB_STATE_NO_PROCESSING; - //END_REGION ERROR OCCURD + //END_REGION ERROR OCCURRED ELSIF (tempExecute = FALSE) AND ((statDone = TRUE) OR (statError = TRUE)) THEN // Reset outputs - //REGION EXECUTE RESETED + //REGION EXECUTE RESET statDone := FALSE; statBusy := FALSE; statError := FALSE; statStatus := STATUS_NO_CALL; // Reset application specific outputs statResultCount := 0; - //END_REGION EXECUTE RESETED + //END_REGION EXECUTE RESET END_IF; //REGION WRITE STATIC VALUES TO OUTPUTS @@ -708,4 +821,4 @@ NAMESPACE Simatic.Ax.LStream END_FUNCTION_BLOCK -END_NAMESPACE +END_NAMESPACE \ No newline at end of file diff --git a/src/LStream/LStream_JsonSerializer.st b/src/LStream/LStream_JsonSerializer.st index 0e0096c..88c6cc7 100644 --- a/src/LStream/LStream_JsonSerializer.st +++ b/src/LStream/LStream_JsonSerializer.st @@ -1,10 +1,10 @@ -//REGION BLOCK INFO HEADER +//REGION BLOCK INFO HEADER //=============================================================================== // SIEMENS AG / (c)Copyright 2023 //------------------------------------------------------------------------------- // Title: LStream_JsonSerializer - // Comment/Function: parses a JSON string provided in the raw paramter - // and rebuilds the coinatend bytes into a tree structure + // Comment/Function: parses a JSON string provided in the raw parameter + // and rebuilds the contained bytes into a tree structure // Library/Family: LStream // Author: DI FA S SUP E&C // Tested with: S7-1500 V2.8 @@ -21,7 +21,6 @@ // 01.06.04 | 2023-07-28 | DI FA S SUP E&C | Bug fix (JsonByte and Tree array start index issue) //=============================================================================== //END_REGION -USING Simatic.Ax.Timer; USING System.Timer; USING System.Strings; USING System.Serialization; @@ -33,126 +32,209 @@ NAMESPACE Simatic.Ax.LStream // Author : DI_FA_S_SUP_EuC // Family : LStream // Version : 1.0 - //parses a JSON string provided in the raw paramter and rebuilds the contained bytes into a tree structure + //parses a JSON string provided in the raw parameter and rebuilds the contained bytes into a tree structure VAR_INPUT - execute : Bool; // Rising edge starts action once + /// Rising edge starts action once + execute : Bool; END_VAR VAR_OUTPUT - done : Bool; // TRUE: Commanded functionality has been completed successfully - busy : Bool; // TRUE: FB is not finished and new output values can be expected - error : Bool; // TRUE: An error occurred during the execution of the FB - status : Word := STATUS_NO_CALL; // 16#0000 - 16#7FFF: Status of the FB, 16#8000 - 16#FFFF: Error identification - count : UInt; // Count of char/ byte info elements in byte array + /// TRUE: Commanded functionality has been completed successfully + done : Bool; + /// TRUE: FB is not finished and new output values can be expected + busy : Bool; + /// TRUE: An error occurred during the execution of the FB + error : Bool; + /// 16#0000 - 16#7FFF: Status of the FB, 16#8000 - 16#FFFF: Error identification + status : Word := STATUS_NO_CALL; + /// Count of char/ byte info elements in byte array + count : UInt; END_VAR VAR PUBLIC - TimeoutTimer : ITimerFunctions; + TimeoutTimer : System.Timer.OnDelay; END_VAR VAR_IN_OUT tree : Array[*] of LStream_typeElement; - jsonByteArray : Array[*] of Byte; // JSON structure as array of bytes + /// JSON structure as array of bytes + jsonByteArray : Array[*] of Byte; END_VAR VAR - statExecuteOld : Bool := FALSE; // Old value of 'execute' input for edge detection - statDone : Bool := FALSE; // Static value for output 'done' - statBusy : Bool := FALSE; // Static value for output 'busy' - statError : Bool := FALSE; // Static value for output 'error' - statCount : UInt := UINT#0; // Static value for ouput 'count' - statStatus : Word := STATUS_NO_CALL; // Static value for output 'status' - statFbState : DInt := FB_STATE_NO_PROCESSING; // State in the state machine of the FB - statFbPreviousState : DInt := FB_STATE_NO_PROCESSING; // Previos state in the state machine of the FB - statFbNextState : DInt := FB_STATE_NO_PROCESSING; // Next state in the state machine of the FB - statIndexJsonByteArray : UInt := UINT#0; // Index indicating current element of byte array to be written - statIndexTreeArray : UInt := UINT#0; // Index indicating current element of tree array to be read - statTreeLen : DInt := 0; // Length of tree array - statInfoToWrite : String := ''; // Static value of current information that should be written to XML - statIsLastElement : Bool := FALSE; // TRUE: is last element in tree array - statLastElementIsClosed : Bool := FALSE; // TRUE: Last JSON element is closed - statStackIndexOpenElement : Array[0..STACK_SIZE] of Char; // Stack of open elements - statStackIndex : Int := -1; // Stack pointer and current depth - instWatchdog : TimerFunctionsImpl; // Instance for watchdog timer - statIsArrayOpen : Bool; // Static value indicating weather an array is open - statIsArrayFieldClosed : Bool; // Static value indicating weather an array field was closed - statIsFirstElement : Bool; // TRUE: first field of simple array - statFirstArrayKey : String; // First key of array, used to check for new array beginning - statArrayName : String; // Name of Array - statJsonEndIndex : Int; // Static Json array starting index - statJsonStartIndex : Int; // Static Json array ending index - statTreeStartIndex : Int; // Static tree array starting index + /// Old value of 'execute' input for edge detection + statExecuteOld : Bool := FALSE; + /// Static value for output 'done' + statDone : Bool := FALSE; + /// Static value for output 'busy' + statBusy : Bool := FALSE; + /// Static value for output 'error' + statError : Bool := FALSE; + /// Static value for output 'count' + statCount : UInt := UINT#0; + /// Static value for output 'status' + statStatus : Word := STATUS_NO_CALL; + /// State in the state machine of the FB + statFbState : DInt := FB_STATE_NO_PROCESSING; + /// Previous state in the state machine of the FB + statFbPreviousState : DInt := FB_STATE_NO_PROCESSING; + /// Next state in the state machine of the FB + statFbNextState : DInt := FB_STATE_NO_PROCESSING; + /// Index indicating current element of byte array to be written + statIndexJsonByteArray : UInt := UINT#0; + /// Index indicating current element of tree array to be read + statIndexTreeArray : UInt := UINT#0; + /// Length of tree array + statTreeLen : DInt := 0; + /// Static value of current information that should be written to XML + statInfoToWrite : String := ''; + /// TRUE: is last element in tree array + statIsLastElement : Bool := FALSE; + /// TRUE: Last JSON element is closed + statLastElementIsClosed : Bool := FALSE; + /// Stack of open elements + statStackIndexOpenElement : Array[0..STACK_SIZE] of Char; + /// Stack pointer and current depth + statStackIndex : Int := -1; + /// Static value indicating whether an array is open + statIsArrayOpen : Bool; + /// Static value indicating whether an array field was closed + statIsArrayFieldClosed : Bool; + /// TRUE: first field of simple array + statIsFirstElement : Bool; + /// First key of array, used to check for new array beginning + statFirstArrayKey : String; + /// Name of Array + statArrayName : String; + /// Static Json array starting index + statJsonEndIndex : Int; + /// Static Json array ending index + statJsonStartIndex : Int; + /// Static tree array starting index + statTreeStartIndex : Int; END_VAR VAR_TEMP - tempExecute : Bool; // Temporary value for input 'execute' - tempCntCharsAdded : UDInt; // Temporary value indicating count of chars added to byte array //REVIEW - tempIteral : Int; // Temporary integer for clearing byte array for cycle + /// Temporary value for input 'execute' + tempExecute : Bool; + /// Temporary value indicating count of chars added to byte array //REVIEW + tempCntCharsAdded : UDInt; + /// Temporary integer for clearing byte array for cycle + tempIteral : Int; Index : INT; tempOffsetArray : ARRAY[0..255] OF BYTE; END_VAR VAR CONSTANT - FB_STATE_NO_PROCESSING : DInt := 0; // FB state: No processing - FB_STATE_CLEAR_BYTE_ARRAY : DInt := 1; // FB state: Clearing the output JSON byte array - FB_STATE_NEXT_ELEMENT : DInt := 2; // FB state: Get Next Element - FB_STATE_VALUE_TYPE : DInt := 3; // FB state: Switch Value type - FB_STATE_WRITE_TO_JSON : DInt := 4; // FB state: Add Informationto xml byte array - FB_STATE_CLOSE_STACK_ELEMENT : DInt := 5; // FB state: Close Parent Element - STATUS_EXECUTION_FINISHED : Word := WORD#16#0000; // Execution finished without errors - STATUS_NO_CALL : Word := WORD#16#7000; // No job being currently processed - STATUS_FIRST_CALL : Word := WORD#16#7001; // First call after incoming new job (rising edge 'execute') - STATUS_SUBSEQUENT_CALL : Word := WORD#16#7002; // Subsequent call during active processing without further details - ERR_UNDEFINED_STATE : Word := WORD#16#8600; // Error: due to an undefined state in state machine - ERR_IN_BLOCK_OPERATION : Word := WORD#16#8001; // Error: wrong operation of the function block - ERR_PARAMETRIZATION : Word := WORD#16#8200; // Error: during parameterization - ERR_PROCESSING_EXTERN : Word := WORD#16#8400; // Error: when processing from outside (e. g. wrong I/O signals, axis not referenced) - ERR_UNDEFINED_TYPE : Word := WORD#16#8401; // Error: user enter undefined type - ERR_UNEXPECTED_DEPTH : Word := WORD#16#8402; // Error: provided json tree structure is too deep - ERR_DEPTH_MISSING : Word := WORD#16#8403; // Error: provided json tree structure does not contain correct depth - ERR_PROCESSING_INTERN : Word := WORD#16#8600; // Error: when processing internally (e. g. when calling a system function) - ERR_TREE_OUT_OF_BOUNDS : Word := WORD#16#8601; // Error: tree array out of bounds - ERR_JSON_OUT_OF_BOUNDS : Word := WORD#16#8602; // Error: byte array out of bounds - ERR_STACK_OUT_OF_BOUNDS : Word := WORD#16#8603; // Error: stack array out of bounds - ERR_AREA_RESERVED : Word := WORD#16#8800; // Error: reserved area - ERR_USER_DEFINED_CLASSES : Word := WORD#16#9000; // Error: user-defined error classes - QUTATIONMARK : Char := '"'; // ASCII code for '"' - COLON : Char := ':'; // ASCII code for ':' - COMMA : Char := ','; // ASCII code for ',' - BRACES_OPEN : Char := '{'; // ASCII code for '{' - BRACES_CLOSE : Char := '}'; // ASCII code for '}' - BRACKETS_OPEN : Char := '['; // ASCII code for '[' - BRACKETS_CLOSE : Char := ']'; // ASCII code for ] - PROTECTION_KEY : Char := '\'; // ASCII coder for \ indicates closing - LINE_FEED : Char := '$L'; // ASCII code for line feed - TAB : Char := '$T'; // ASCII code for TAB - CARRIAGE_RETURN : Char := '$R'; // ASCII code for carriage return - SPACE : Char := ' '; // ASCII code for blank space - FIRST_DIM : USInt := USINT#1; // Constant for first array dimension - MAX_REPEAT_TIME : Time := T#3ms; // Max duration of loop, will be continued in next cycle - INVALID_SEARCH : DInt := -1; // Return value if Search in LParse_FindStringInCharrArrayAdv was not successfull - KEY_NOT_FOUND : String := 'Key not found'; // Value string in case key wasn't found - INCREMENT_BY_ONE : USInt := USINT#1; // Constant to increment by one - DECREMENT_BY_ONE : SInt := SINT#1; // Constant to decrement by one - OBJECT : SInt := Sint#0; // Constant for value typ object - ARRAYCONST : SInt := SINT#1; // Constant for value typ array - STRINGCONST : SInt := SINT#2; // Constant for value type string - NUMBER : SInt := SINT#3; // Constant for value type number - BOOLEAN : SInt := SINT#4; // Constant for value type boolean - NEXT_INDEX : UInt := UINT#1; // Constant value to get next index - STACK_SIZE : UInt := UINT#10; // Constant for Stack Size, equals max depth - WHITE_SPACE : Char := ' '; // ASCII code for white space - NOT_INITALIZED : SInt := SINT#-1; // Constant value representing not initalyzed tree element - EMPTY : String := ''; // Constant value representing an empty string - EMPTY_BYTE : Byte := BYTE#0; // Constant value representing an empty byte + /// FB state: No processing + FB_STATE_NO_PROCESSING : DInt := 0; + /// FB state: Clearing the output JSON byte array + FB_STATE_CLEAR_BYTE_ARRAY : DInt := 1; + /// FB state: Get Next Element + FB_STATE_NEXT_ELEMENT : DInt := 2; + /// FB state: Switch Value type + FB_STATE_VALUE_TYPE : DInt := 3; + /// FB state: Add Information to xml byte array + FB_STATE_WRITE_TO_JSON : DInt := 4; + /// FB state: Close Parent Element + FB_STATE_CLOSE_STACK_ELEMENT : DInt := 5; + /// Execution finished without errors + STATUS_EXECUTION_FINISHED : Word := WORD#16#0000; + /// No job being currently processed + STATUS_NO_CALL : Word := WORD#16#7000; + /// First call after incoming new job (rising edge 'execute') + STATUS_FIRST_CALL : Word := WORD#16#7001; + /// Subsequent call during active processing without further details + STATUS_SUBSEQUENT_CALL : Word := WORD#16#7002; + /// Error: due to an undefined state in state machine + ERR_UNDEFINED_STATE : Word := WORD#16#8600; + /// Error: wrong operation of the function block + ERR_IN_BLOCK_OPERATION : Word := WORD#16#8001; + /// Error: during parameterization + ERR_PARAMETRIZATION : Word := WORD#16#8200; + /// Error: when processing from outside (e. g. wrong I/O signals, axis not referenced) + ERR_PROCESSING_EXTERN : Word := WORD#16#8400; + /// Error: user enter undefined type + ERR_UNDEFINED_TYPE : Word := WORD#16#8401; + /// Error: provided json tree structure is too deep + ERR_UNEXPECTED_DEPTH : Word := WORD#16#8402; + /// Error: provided json tree structure does not contain correct depth + ERR_DEPTH_MISSING : Word := WORD#16#8403; + /// Error: when processing internally (e. g. when calling a system function) + ERR_PROCESSING_INTERN : Word := WORD#16#8600; + /// Error: tree array out of bounds + ERR_TREE_OUT_OF_BOUNDS : Word := WORD#16#8601; + /// Error: byte array out of bounds + ERR_JSON_OUT_OF_BOUNDS : Word := WORD#16#8602; + /// Error: stack array out of bounds + ERR_STACK_OUT_OF_BOUNDS : Word := WORD#16#8603; + /// Error: reserved area + ERR_AREA_RESERVED : Word := WORD#16#8800; + /// Error: user-defined error classes + ERR_USER_DEFINED_CLASSES : Word := WORD#16#9000; + /// ASCII code for '"' + QUOTATION_MARK : Char := '"'; + /// ASCII code for ':' + COLON : Char := ':'; + /// ASCII code for ',' + COMMA : Char := ','; + /// ASCII code for '{' + BRACES_OPEN : Char := '{'; + /// ASCII code for '}' + BRACES_CLOSE : Char := '}'; + /// ASCII code for '[' + BRACKETS_OPEN : Char := '['; + /// ASCII code for ] + BRACKETS_CLOSE : Char := ']'; + /// ASCII coder for \ indicates closing + PROTECTION_KEY : Char := '\'; + /// ASCII code for line feed + LINE_FEED : Char := '$L'; + /// ASCII code for TAB + TAB : Char := '$T'; + /// ASCII code for carriage return + CARRIAGE_RETURN : Char := '$R'; + /// ASCII code for blank space + SPACE : Char := ' '; + /// Constant for first array dimension + FIRST_DIM : USInt := USINT#1; + /// Max duration of loop, will be continued in next cycle + MAX_REPEAT_TIME : Time := T#3ms; + /// Return value if Search in LStream_FindStringInByteCharArrayAdv was not successful + INVALID_SEARCH : DInt := -1; + /// Value string in case key wasn't found + KEY_NOT_FOUND : String := 'Key not found'; + /// Constant to increment by one + INCREMENT_BY_ONE : USInt := USINT#1; + /// Constant to decrement by one + DECREMENT_BY_ONE : SInt := SINT#1; + /// Constant for value typ object + OBJECT : SInt := Sint#0; + /// Constant for value typ array + ARRAY_CONST : SInt := SINT#1; + /// Constant for value type string + STRING_CONST : SInt := SINT#2; + /// Constant for value type number + NUMBER : SInt := SINT#3; + /// Constant for value type boolean + BOOLEAN : SInt := SINT#4; + /// Constant value to get next index + NEXT_INDEX : UInt := UINT#1; + /// Constant for Stack Size, equals max depth + STACK_SIZE : UInt := UINT#10; + /// ASCII code for white space + WHITE_SPACE : Char := ' '; + /// Constant value representing not initialized tree element + NOT_initialized : SInt := SINT#-1; + /// Constant value representing an empty string + EMPTY : String := ''; + /// Constant value representing an empty byte + EMPTY_BYTE : Byte := BYTE#0; //EMPTY_CHAR : Char := ' '; // Constant value representing an empty char - NULLCONST : String := 'NULL'; // Constant value representing null - MULTI_ARRAY : String := 'Multi@$$@y'; // Constant key value for identify multidimensional arrays + /// Constant value representing null + NULL_CONST : String := 'NULL'; + /// Constant key value for identify multidimensional arrays + MULTI_ARRAY : String := 'MultiArray'; END_VAR - - IF TimeoutTimer = NULL THEN - TimeoutTimer := instWatchDog; - END_IF; - tempExecute := execute; // Work with temporary value / create process image @@ -194,10 +276,10 @@ NAMESPACE Simatic.Ax.LStream //REGION STATE_MACHINE //rest and start watchdog here so that repeat until has a maximum duration of constant MAX_LOOP_TIME - TimeoutTimer.TimerFunction(signal := FALSE, + TimeoutTimer(signal := FALSE, duration := MAX_REPEAT_TIME); //start watchdog - TimeoutTimer.TimerFunction(signal := TRUE, + TimeoutTimer(signal := TRUE, duration := MAX_REPEAT_TIME); REPEAT statFbPreviousState := statFbState; @@ -223,9 +305,9 @@ NAMESPACE Simatic.Ax.LStream FB_STATE_NEXT_ELEMENT: // Work on next Element //REGION NEXT_ELEMENT //REGION ERROR_HANDLING - //check for last Element, either end of provided tree reached, or tree element is -1 = not initalized + //check for last Element, either end of provided tree reached, or tree element is -1 = not initialized //this prevents index out of bounds error - IF (statIndexTreeArray = statTreeLen -1 ) OR tree[statIndexTreeArray + NEXT_INDEX].types = NOT_INITALIZED THEN + IF (statIndexTreeArray = statTreeLen -1 ) OR tree[statIndexTreeArray + NEXT_INDEX].types = NOT_initialized THEN statIsLastElement := TRUE; ELSIF (statIndexTreeArray = statTreeLen) THEN @@ -237,16 +319,16 @@ NAMESPACE Simatic.Ax.LStream //max depth size allowed is size of stack, otherwise array out of bounds error will occur IF (TO_DINT(tree[statIndexTreeArray].depth) = STACK_SIZE) THEN statStatus := ERR_UNEXPECTED_DEPTH; - ELSIF (tree[statIndexTreeArray].depth = NOT_INITALIZED) THEN + ELSIF (tree[statIndexTreeArray].depth = NOT_initialized) THEN //depth wasn't provided by user statStatus := ERR_DEPTH_MISSING; END_IF; //END_REGION ERROR_HANDLING //REGION FIRST_ELEMENT - // stackIndex is used as current depth, is initalized wih -1 therefore first element, will always open an object + // stackIndex is used as current depth, is initialized with -1 therefore first element, will always open an object IF statStackIndex < 0 THEN - //open object if first tree element depth less then 1 + //open object if first tree element depth less than 1 IF tree[statIndexTreeArray].depth < 1 THEN statInfoToWrite := BRACES_OPEN; //METHOD TO CONVERT SINGLE CHAR IN STRING //increase stack index and push closing elements on to it @@ -271,13 +353,13 @@ NAMESPACE Simatic.Ax.LStream //END_REGION NEXT_ELEMENT FB_STATE_VALUE_TYPE: // Depending on value type, write value - //REGION VALUE DEPENDECY OF TYPE + //REGION VALUE DEPENDENCY OF TYPE CASE tree[statIndexTreeArray].types OF OBJECT: //REGION OBJECT - //nothing to do when type is object just add line feed, { openning will occur with next depth - statInfoToWrite := Concat(string1 := QUTATIONMARK, string2 := tree[statIndexTreeArray].key, string3 := QUTATIONMARK, + //nothing to do when type is object just add line feed, { opening will occur with next depth + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].key, string3 := QUOTATION_MARK, string4 := COLON); statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := BRACES_OPEN); @@ -286,12 +368,12 @@ NAMESPACE Simatic.Ax.LStream statStackIndexOpenElement[statStackIndex] := BRACES_CLOSE; //END_REGION OBJECT - ARRAYCONST: + ARRAY_CONST: //REGION ARRAY // checking for nested object inside the array //or nested array IF tree[statIndexTreeArray].depth + 1 < tree[statIndexTreeArray + NEXT_INDEX].depth THEN - statInfoToWrite := Concat(string1 := QUTATIONMARK, string2 := tree[statIndexTreeArray].key, string3 := QUTATIONMARK, + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].key, string3 := QUOTATION_MARK, string4 := COLON); statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := BRACKETS_OPEN, string3 := BRACES_OPEN); @@ -302,7 +384,7 @@ NAMESPACE Simatic.Ax.LStream statStackIndexOpenElement[statStackIndex] := BRACES_CLOSE; //there are no nested elements ELSIF tree[statIndexTreeArray].depth + 1 = tree[statIndexTreeArray + NEXT_INDEX].depth AND tree[statIndexTreeArray].key <> MULTI_ARRAY THEN - statInfoToWrite := Concat(string1 := QUTATIONMARK, string2 := tree[statIndexTreeArray].key, string3 := QUTATIONMARK, + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].key, string3 := QUOTATION_MARK, string4 :=COLON); statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := BRACKETS_OPEN); //increase stack index and push closing elements on to it @@ -313,32 +395,48 @@ NAMESPACE Simatic.Ax.LStream //increase stack index and push closing elements on to it statStackIndex := statStackIndex + INCREMENT_BY_ONE; statStackIndexOpenElement[statStackIndex] := BRACKETS_CLOSE; + ELSIF statIsLastElement OR (tree[statIndexTreeArray + NEXT_INDEX].depth <= tree[statIndexTreeArray].depth) THEN + // Empty array: output "key":[] + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].key, string3 := QUOTATION_MARK, string4 := COLON); + statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := BRACKETS_OPEN, string3 := BRACKETS_CLOSE); + // Handle closing element transition (same as STRING/NUMBER handlers) + IF statIndexTreeArray < TO_UINT(statTreeLen) THEN + IF (NOT statIsLastElement) AND tree[statIndexTreeArray].closingElement AND tree[statIndexTreeArray].depth = tree[statIndexTreeArray + NEXT_INDEX].depth THEN + IF statStackIndexOpenElement[statStackIndex] = BRACES_CLOSE THEN + statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := BRACES_CLOSE, string3 := COMMA, string4 := BRACES_OPEN); + ELSIF statStackIndexOpenElement[statStackIndex] = BRACKETS_CLOSE THEN + statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := BRACKETS_CLOSE, string3 := COMMA, string4 := BRACKETS_OPEN); + ELSE + statStatus := ERR_UNEXPECTED_DEPTH; + END_IF; + END_IF; + END_IF; ELSE statStatus := ERR_TREE_OUT_OF_BOUNDS; END_IF; //END_REGION ARRAY - STRINGCONST: + STRING_CONST: //REGION STRING IF TO_DINT(statIndexTreeArray) > statTreeStartIndex THEN - //wirte value as string - value enclosed with quotation mark - IF (tree[statIndexTreeArray].value <> NULLCONST) AND (tree[statIndexTreeArray].key <> tree[statIndexTreeArray - UINT#1].key) THEN - statInfoToWrite := Concat(string1 := QUTATIONMARK, string2 := tree[statIndexTreeArray].key, string3 := QUTATIONMARK, + //write value as string - value enclosed with quotation mark + IF (tree[statIndexTreeArray].value <> NULL_CONST) AND (tree[statIndexTreeArray].key <> tree[statIndexTreeArray - UINT#1].key) THEN + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].key, string3 := QUOTATION_MARK, string4 := COLON); - statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := QUTATIONMARK, string3 := tree[statIndexTreeArray].value, string4 := QUTATIONMARK); + statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := QUOTATION_MARK, string3 := tree[statIndexTreeArray].value, string4 := QUOTATION_MARK); - ELSIF (tree[statIndexTreeArray].value <> NULLCONST) AND (tree[statIndexTreeArray].key = tree[statIndexTreeArray - UINT#1].key) THEN - statInfoToWrite := Concat(string1 := QUTATIONMARK, string2 := tree[statIndexTreeArray].value, string3 := QUTATIONMARK); + ELSIF (tree[statIndexTreeArray].value <> NULL_CONST) AND (tree[statIndexTreeArray].key = tree[statIndexTreeArray - UINT#1].key) THEN + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].value, string3 := QUOTATION_MARK); ELSE // empty value still needs "" otherwise syntax error - statInfoToWrite := Concat(string1 := QUTATIONMARK, string2 := tree[statIndexTreeArray].key, string3 := QUTATIONMARK, + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].key, string3 := QUOTATION_MARK, string4 := COLON); - statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := QUTATIONMARK, string3 := QUTATIONMARK); + statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := QUOTATION_MARK, string3 := QUOTATION_MARK); END_IF; IF statIndexTreeArray < statTreeLen THEN - //if this is a nested closing element could be that next element has a same depth cause the next element could start a new element + //if this is a nested closing element could be that next element has the same depth because the next element could start a new element IF tree[statIndexTreeArray].closingElement AND tree[statIndexTreeArray].depth = tree[statIndexTreeArray + INCREMENT_BY_ONE].depth THEN IF statStackIndexOpenElement[statStackIndex] = BRACES_CLOSE THEN statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := BRACES_CLOSE, string3 := COMMA, string4 := BRACES_OPEN); @@ -352,9 +450,9 @@ NAMESPACE Simatic.Ax.LStream END_IF; //bug?? ELSE - statInfoToWrite := Concat(string1 := QUTATIONMARK, string2 := tree[statIndexTreeArray].key, string3 := QUTATIONMARK, + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].key, string3 := QUOTATION_MARK, string4 := COLON); - statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := QUTATIONMARK, string3 := tree[statIndexTreeArray].value, string4 := QUTATIONMARK); + statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := QUOTATION_MARK, string3 := tree[statIndexTreeArray].value, string4 := QUOTATION_MARK); END_IF; //END_REGION STRING @@ -362,18 +460,18 @@ NAMESPACE Simatic.Ax.LStream //REGION NUMBER_BOOLEAN //write value as number or boolean - value enclosed without quotation mark IF TO_DINT(statIndexTreeArray) > statTreeStartIndex THEN - IF ((tree[statIndexTreeArray].value <> NULLCONST) AND (tree[statIndexTreeArray].key <> tree[statIndexTreeArray - UINT#1].key) AND (tree[statIndexTreeArray].key <> '')) THEN - statInfoToWrite := Concat(string1 := QUTATIONMARK, string2 := tree[statIndexTreeArray].key, string3 := QUTATIONMARK, + IF ((tree[statIndexTreeArray].value <> NULL_CONST) AND (tree[statIndexTreeArray].key <> tree[statIndexTreeArray - UINT#1].key) AND (tree[statIndexTreeArray].key <> '')) THEN + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].key, string3 := QUOTATION_MARK, string4 := COLON); statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := tree[statIndexTreeArray].value); //deserializer gives back in the simple array the numbers and the booleans with the key value of the array key - ELSIF ((tree[statIndexTreeArray].value <> NULLCONST) AND (tree[statIndexTreeArray].key = tree[statIndexTreeArray - UINT#1].key)) THEN + ELSIF ((tree[statIndexTreeArray].value <> NULL_CONST) AND (tree[statIndexTreeArray].key = tree[statIndexTreeArray - UINT#1].key)) THEN statInfoToWrite := tree[statIndexTreeArray].value; END_IF; IF statIndexTreeArray < statTreeLen THEN - //if this is a nested closing element could be that next element has a same depth cause the next element could start a new element + //if this is a nested closing element could be that next element has the same depth because the next element could start a new element IF tree[statIndexTreeArray].closingElement AND tree[statIndexTreeArray].depth = tree[statIndexTreeArray + INCREMENT_BY_ONE].depth THEN IF statStackIndexOpenElement[statStackIndex] = BRACES_CLOSE THEN statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := BRACES_CLOSE, string3 := COMMA, string4 := BRACES_OPEN); @@ -386,7 +484,7 @@ NAMESPACE Simatic.Ax.LStream END_IF; END_IF; ELSE - statInfoToWrite := Concat(string1 := QUTATIONMARK, string2 := tree[statIndexTreeArray].key, string3 := QUTATIONMARK, string4 := COLON); + statInfoToWrite := Concat(string1 := QUOTATION_MARK, string2 := tree[statIndexTreeArray].key, string3 := QUOTATION_MARK, string4 := COLON); statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := tree[statIndexTreeArray].value); END_IF; @@ -422,14 +520,14 @@ NAMESPACE Simatic.Ax.LStream //increase tree index to access next element statIndexTreeArray := statIndexTreeArray + INCREMENT_BY_ONE; - //END_REGION VALUE DEPENDECY OF TYPE + //END_REGION VALUE DEPENDENCY OF TYPE FB_STATE_WRITE_TO_JSON: // Write to Byte Array //REGION WRITE TO JSON //add information to json byte array IF ((TO_UINT(LengthOf(statInfoToWrite)) + TO_DINT(statIndexJsonByteArray)) < statJsonEndIndex) THEN - tempCntCharsAdded := Serialize(offset := UDINT#0, value := statInfoToWrite, buffer := tempOffsetArray); //NOT WORKING BECAUSE STRING SIZE IS WRITTEN IN FIRST POSITION OF OUTPUT ARRAY - //WRITE INTO OUTPUT ARRAY WITH AN OFFSET TO AVOID WRITTING STRING SIZE + tempCntCharsAdded := Serialize(offset := UDINT#0, value := statInfoToWrite, buffer := tempOffsetArray); + //WRITE INTO OUTPUT ARRAY WITH AN OFFSET TO AVOID WRITING STRING SIZE FOR Index := 0 TO TO_INT(tempOffsetArray[0]) - 1 DO jsonByteArray[TO_INT(statIndexJsonByteArray) + Index] := tempOffsetArray[Index + 1]; END_FOR; @@ -476,10 +574,10 @@ NAMESPACE Simatic.Ax.LStream IF NOT statIsLastElement AND statStackIndex = tree[statIndexTreeArray].depth THEN //move to next element //write comma as JSON dictates same depth a divided by comma, - //here we move on to the next elment with the same depth + //here we move on to the next element with the same depth statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := COMMA); - // //if Array is still open and the same lenght is reached, + // //if Array is still open and the same length is reached, // //new element in array, distinguish new entry with BRACES IF statIsArrayOpen THEN statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := BRACES_CLOSE, string3 := COMMA); @@ -523,46 +621,49 @@ NAMESPACE Simatic.Ax.LStream //END_REGION UNDEFINED STATE END_CASE; + + // Call for Timeout timer to update value + TimeoutTimer(); //Leave state machine if one of the following condition is true //1. state has not changed - //2. error has occured + //2. error has occurred //3. watchdog timer expired //4. execution has finished // otherwise stay in state machine UNTIL (statFbPreviousState = statFbState OR statStatus.%X15 - OR TimeoutTimer.TimerFunction() OR statStatus = STATUS_EXECUTION_FINISHED) + OR TimeoutTimer.output OR statStatus = STATUS_EXECUTION_FINISHED) END_REPEAT; //END_REGION STATE_MACHINE //REGION OUTPUTS // Write outputs IF (statStatus = STATUS_EXECUTION_FINISHED) AND (statDone = FALSE) THEN // Execution finished without errors - //REGION EXECUTION FINSIHED + //REGION EXECUTION FINISHED statDone := TRUE; statBusy := FALSE; statError := FALSE; // execution aborted --> set state no processing statFbState := FB_STATE_NO_PROCESSING; - //END_REGION EXECUTION FINSIHED + //END_REGION EXECUTION FINISHED ELSIF (statStatus.%X15 = TRUE) AND (statError = FALSE) THEN // Error occurred (#statStatus is 16#8000 to 16#FFFF) - //REGION ERROR OCCURED + //REGION ERROR OCCURRED statDone := FALSE; statBusy := FALSE; statError := TRUE; // execution aborted --> set state no processing statFbState := FB_STATE_NO_PROCESSING; - //END_REGION ERROR OCCURED + //END_REGION ERROR OCCURRED ELSIF (tempExecute = FALSE) AND ((statDone = TRUE) OR (statError = TRUE)) THEN // Reset outputs - //REGION EXECUTE RESETTED + //REGION EXECUTE RESET statDone := FALSE; statBusy := FALSE; statError := FALSE; statStatus := STATUS_NO_CALL; // Reset application specific outputs statCount := UINT#0; - //END_REGION EXECUTE RESETTED + //END_REGION EXECUTE RESET END_IF; //REGION WRITE STATIC VALUES TO OUTPUTS @@ -580,4 +681,4 @@ NAMESPACE Simatic.Ax.LStream END_FUNCTION_BLOCK -END_NAMESPACE +END_NAMESPACE \ No newline at end of file diff --git a/src/LStream/LStream_XmlDeserializer.st b/src/LStream/LStream_XmlDeserializer.st index 15a8aa2..d7f5ed9 100644 --- a/src/LStream/LStream_XmlDeserializer.st +++ b/src/LStream/LStream_XmlDeserializer.st @@ -21,8 +21,6 @@ //=============================================================================== //end_region - -USING Simatic.Ax.Timer; USING System.Timer; USING System.Strings; USING Simatic.Ax.LStream.Utilities; @@ -30,120 +28,205 @@ USING Simatic.Ax.LStream.Models; NAMESPACE Simatic.Ax.LStream FUNCTION_BLOCK LStream_XmlDeserializer VAR_INPUT - execute : BOOL; // Rising edge starts action once - search : BOOL; // TRUE: Searches rawdata for the value of the provided treekeys + /// Rising edge starts action once + execute : BOOL; + /// TRUE: Searches raw data for the value of the provided tree keys + search : BOOL; END_VAR VAR_OUTPUT - done : BOOL; // TRUE: Commanded functionality has been completed successfully - busy : BOOL; // TRUE: FB is not finished and new output values can be expected - error : BOOL; // TRUE: An error occurred during the execution of the FB - status : WORD := STATUS_NO_CALL; //16#0000 - 16#7FFF: Status of the FB, 16#8000 - 16#FFFF: Error identification + /// TRUE: Commanded functionality has been completed successfully + done : BOOL; + /// TRUE: FB is not finished and new output values can be expected + busy : BOOL; + /// TRUE: An error occurred during the execution of the FB + error : BOOL; + /// 16#0000 - 16#7FFF: Status of the FB, 16#8000 - 16#FFFF: Error identification + status : WORD := STATUS_NO_CALL; + /// Current parse position in the raw XML data pointer : DINT; - resultCount : DINT; // Count of parsed XML elements + /// Count of parsed XML elements + resultCount : DINT; END_VAR VAR_IN_OUT - tree : ARRAY [*] of LStream_typeElement; // Tree parsed information is written to, if search option active, keys must be provided - raw : ARRAY[*] of BYTE; // Byte array of raw xml data + /// Tree parsed information is written to, if search option active, keys must be provided + tree : ARRAY [*] of LStream_typeElement; + /// Byte array of raw xml data + raw : ARRAY[*] of BYTE; END_VAR VAR PUBLIC - TimeoutTimer : ITimerFunctions; + TimeoutTimer : System.Timer.OnDelay; END_VAR VAR - statOldExecute : BOOL; // Old value of 'execute' input for edge detection - statDepth : SINT := SINT#-1; // Static value of xml depth - statError : BOOL; // Static value of error - statBusy : BOOL; // Static value of busy - statStatus : WORD := STATUS_NO_CALL; // Static value of status - statFBState : USINT := FB_STATE_NO_PROCESSING; // Static value of fb state - statDone : BOOL; // Static value of done - statResultCount : UINT; // Static value of result count - statRawIndex : INT; // Static value of raw index - statTreeIndex : UINT; // Static value of tree index - statCountOfTree : INT; // Static count of tree fields - statCountOfRaw : INT; // Static count of raw fields - statIsKeyOpen : BOOL; // TRUE: key is open, internal flag - statIsValueOpen : BOOL; // TRUE: value is open, internal flag - statElementOpen : UINT; // Determines the amount of open elements; if large than 0 then at least one element is still open at the current parse position - statIsElementOpen : BOOL; // TRUE: element is open, internal flag - statInfoLen : UINT; // Current information length key or value - statInfoStartIndex : INT; // Start index of current info in raw key or value - statKeyWritten: BOOL; // TRUE: key was written into tree - statKey : STRING; // Static key string - statValue : STRING; // Static value string - statSearch : BOOL; // TRUE: Only keys provided in tree will be parsed - statKeyFound : BOOL; // TRUE: Key exisits in raw data - statKeyNotFoundCount : UINT; // Count of provided keys that weren't found - statSearchIndex : DINT; // Static search index - statOpenElements : ARRAY[0.. MAX_CONCURRENT_OPEN_ELEMENTS] of UINT; // Backtrackarray for getting the last elements open - statAttrDelimiter : BYTE := BYTE#00; // Delimiter for attributes - instWatchDog : TimerFunctionsImpl; + /// Old value of 'execute' input for edge detection + statOldExecute : BOOL; + /// Static value of xml depth + statDepth : SINT := SINT#-1; + /// Static value of error + statError : BOOL; + /// Static value of busy + statBusy : BOOL; + /// Static value of status + statStatus : WORD := STATUS_NO_CALL; + /// Static value of fb state + statFBState : USINT := FB_STATE_NO_PROCESSING; + /// Static value of done + statDone : BOOL; + /// Static value of result count + statResultCount : UINT; + /// Static value of raw index + statRawIndex : INT; + /// Static value of tree index + statTreeIndex : UINT; + /// Static count of tree fields + statCountOfTree : INT; + /// Static count of raw fields + statCountOfRaw : INT; + /// TRUE: key is open, internal flag + statIsKeyOpen : BOOL; + /// TRUE: value is open, internal flag + statIsValueOpen : BOOL; + /// Determines the amount of open elements; if larger than 0 then at least one element is still open at the current parse position + statElementOpen : UINT; + /// TRUE: element is open, internal flag + statIsElementOpen : BOOL; + /// Current information length key or value + statInfoLen : UINT; + /// Start index of current info in raw key or value + statInfoStartIndex : INT; + /// TRUE: Current key has already been written to the tree + statKeyWritten: BOOL; + /// Static key string + statKey : STRING; + /// Static value string + statValue : STRING; + /// TRUE: Only keys provided in tree will be parsed + statSearch : BOOL; + /// TRUE: Key exists in raw data + statKeyFound : BOOL; + /// Count of provided keys that weren't found + statKeyNotFoundCount : UINT; + /// Static search index + statSearchIndex : DINT; + /// Backtrack array for getting the last elements open + statOpenElements : ARRAY[0.. MAX_CONCURRENT_OPEN_ELEMENTS] of UINT; + /// Delimiter for attributes + statAttrDelimiter : BYTE := BYTE#00; END_VAR VAR_TEMP - tempRawIndex : INT; // Index of raw field - tempExecute : BOOL; // Temp of input execute - tempKeyToFind : STRING; // Provided key to which the value should be found - tempSearchResult : DINT; // Temp value of search result - tempChar : BYTE; // Temp value of a byte for searching / looping + /// Index of raw field + tempRawIndex : INT; + /// Temp of input execute + tempExecute : BOOL; + /// Provided key to which the value should be found + tempKeyToFind : STRING; + /// Temp value of search result + tempSearchResult : DINT; + /// Temp value of a byte for searching / looping + tempChar : BYTE; END_VAR VAR CONSTANT - FB_STATE_NO_PROCESSING : USINT := USINT#0; // FB state no processing, ideal state - FB_STATE_PARSE : USINT := USINT#10; // FB state parse xml - STATUS_EXECUTION_FINISHED : WORD := WORD#16#0000; // NO error - execution finished without errors - STATUS_NO_CALL : WORD := WORD#16#7000; // No job being currently processed - STATUS_FIRST_CALL : WORD := WORD#16#7001; // First call after incoming new job (rising edge 'execute') - STATUS_SUBSEQUENT_CALL : WORD := WORD#16#7002; // Subsequent call during active processing without further details - ERR_TREE_ARRAY_TOO_SMALL : WORD := WORD#16#8201; // Error: provided array to too small - ERR_EMPTY_RAW_DATA : WORD := WORD#16#8401; // Error: no raw data provided - ERR_UNMATCHED_ELEMENT_CLOSE : WORD := WORD#16#8402; // Error: Encountered more closing tags than there are opening tags - ERR_UNEXPECTED_QUOTES : WORD := WORD#16#8404; // Error: Encountered an unexpected quotes character - ERR_MALFORMED : WORD := WORD#16#8406; // Error: Collection of all not further specified errors, e.g. unclosed elements before file end etc. - ERR_UNDEFINED_STATE : WORD := WORD#16#8600; // Error: due to an undefined state in state machine - CONTROL_CHARACTERS_UPPER : WORD := WORD#16#1F; // ASCII code for Unit Separator which is numerically the hightest control character - QUESTIONMARK : BYTE := BYTE#16#3F; // ASCII code for ? indicates xml header - EXCLAMATION : BYTE := BYTE#16#21; // ASCII code for ! indicates comment - DOUBLEQUOTES : BYTE := BYTE#16#22; // ASCII code for " frames value - QUOTES : BYTE := BYTE#16#27; // ASCII code for ' frames value - SMALLER : BYTE := BYTE#16#3C; // ASCII code for < indicates a new key - EQUALS : BYTE := BYTE#16#3D; // ASCII code for '=' indicates key is finished follows by value - GREATER : BYTE := BYTE#16#3E; // ASCII code for > indicates key can be written - SPACE : BYTE := BYTE#16#20; // ASCII codefor SPACE indicates key is finished - SLASH : BYTE := BYTE#16#2F; // ASCII code for / indicates closing - CARRIAGE_RETURN : BYTE := BYTE#16#0D; // ASCII code for carriage return - LINE_FEED : BYTE := BYTE#16#0A; // ASCII code for line feed - TAB : BYTE := BYTE#16#09; // ASCII code for TAB - MINUS : BYTE := BYTE#16#2D; // ASCII code for - - EMPTY : SINT := SINT#0; // Constant for zero value - ELEMENT : SINT := SINT#0; // Numerical identifier for XML type elemt - ATTRIBUTE : SINT := SINT#1; // Numerical identifier for XML type attirbute - FIRST_DIM : USINT := USINT#1; // Constant for first array dimension - MAX_LOOP_TIME : TIME := T#3ms; // Max duration of loop, will be continued in next cycle - INVALID_SEARCH : DINT := -1; // Return value if Search in LParse_FindStringInCharrArrayAdv was not successfull - KEY_NOT_FOUND : STRING := 'Key not found'; // String value in case key wasn't found - STRING_NULL : STRING := 'NULL'; // String is default empty with string content named 'NULL' - MAX_CONCURRENT_OPEN_ELEMENTS : UINT := UINT#150; // Maximal number of elements that can be open at any time - STRING_EMPTY : STRING := ''; // Empty string for resetting of fields - + /// FB state no processing, ideal state + FB_STATE_NO_PROCESSING : USINT := USINT#0; + /// FB state parse xml + FB_STATE_PARSE : USINT := USINT#10; + /// NO error - execution finished without errors + STATUS_EXECUTION_FINISHED : WORD := WORD#16#0000; + /// No job being currently processed + STATUS_NO_CALL : WORD := WORD#16#7000; + /// First call after incoming new job (rising edge 'execute') + STATUS_FIRST_CALL : WORD := WORD#16#7001; + /// Subsequent call during active processing without further details + STATUS_SUBSEQUENT_CALL : WORD := WORD#16#7002; + /// Error: provided array to too small + ERR_TREE_ARRAY_TOO_SMALL : WORD := WORD#16#8201; + /// Error: no raw data provided + ERR_EMPTY_RAW_DATA : WORD := WORD#16#8401; + /// Error: Encountered more closing tags than there are opening tags + ERR_UNMATCHED_ELEMENT_CLOSE : WORD := WORD#16#8402; + /// Error: Encountered an unexpected quotes character + ERR_UNEXPECTED_QUOTES : WORD := WORD#16#8404; + /// Error: Collection of all not further specified errors, e.g. unclosed elements before file end etc. + ERR_MALFORMED : WORD := WORD#16#8406; + /// Error: due to an undefined state in state machine + ERR_UNDEFINED_STATE : WORD := WORD#16#8600; + /// ASCII code for Unit Separator which is numerically the highest control character + CONTROL_CHARACTERS_UPPER : WORD := WORD#16#1F; + /// ASCII code for ? indicates xml header + QUESTIONMARK : BYTE := BYTE#16#3F; + /// ASCII code for ! indicates comment + EXCLAMATION : BYTE := BYTE#16#21; + /// ASCII code for " frames value + DOUBLE_QUOTES : BYTE := BYTE#16#22; + /// ASCII code for ' frames value + QUOTES : BYTE := BYTE#16#27; + /// ASCII code for < indicates a new key + SMALLER : BYTE := BYTE#16#3C; + /// ASCII code for '=' indicates key is finished follows by value + EQUALS : BYTE := BYTE#16#3D; + /// ASCII code for > indicates key can be written + GREATER : BYTE := BYTE#16#3E; + /// ASCII code for SPACE indicates key is finished + SPACE : BYTE := BYTE#16#20; + /// ASCII code for / indicates closing + SLASH : BYTE := BYTE#16#2F; + /// ASCII code for carriage return + CARRIAGE_RETURN : BYTE := BYTE#16#0D; + /// ASCII code for line feed + LINE_FEED : BYTE := BYTE#16#0A; + /// ASCII code for TAB + TAB : BYTE := BYTE#16#09; + /// ASCII code for - + MINUS : BYTE := BYTE#16#2D; + /// Constant for zero value + EMPTY : SINT := SINT#0; + /// Numerical identifier for XML type element + ELEMENT : SINT := SINT#0; + /// Numerical identifier for XML type attribute + ATTRIBUTE : SINT := SINT#1; + /// Constant for first array dimension + FIRST_DIM : USINT := USINT#1; + /// Max duration of loop, will be continued in next cycle + MAX_LOOP_TIME : TIME := T#3ms; + /// Return value if Search in LStream_FindStringInByteCharArrayAdv was not successful + INVALID_SEARCH : DINT := -1; + /// String value in case key wasn't found + KEY_NOT_FOUND : STRING := 'Key not found'; + /// String is default empty with string content named 'NULL' + STRING_NULL : STRING := 'NULL'; + /// Maximal number of elements that can be open at any time + MAX_CONCURRENT_OPEN_ELEMENTS : UINT := UINT#150; + /// Empty string for resetting of fields + STRING_EMPTY : STRING := ''; - QUESTIONMARKINT : INT := 63; - EXCLAMATIONINT : INT := 33; - DOUBLEQUOTESINT : INT := 34; - QUOTESINT : INT := 39; - SMALLERINT : INT := 60; - EQUALSINT : INT := 61; - GREATERINT : INT := 62; - SPACEINT : INT := 32; - SLASHINT : INT := 47; - CARRIAGE_RETURNINT : INT := 13; - LINE_FEEDINT : INT := 10; - TABINT : INT := 9; - MINUSINT : INT := 45; + /// ASCII code for ? + QUESTIONMARK_INT : INT := 63; + /// ASCII code for ! + EXCLAMATION_INT : INT := 33; + /// ASCII code for " + DOUBLE_QUOTES_INT : INT := 34; + /// ASCII code for ' + QUOTES_INT : INT := 39; + /// ASCII code for < + SMALLER_INT : INT := 60; + /// ASCII code for = + EQUALS_INT : INT := 61; + /// ASCII code for > + GREATER_INT : INT := 62; + /// ASCII code for SPACE + SPACE_INT : INT := 32; + /// ASCII code for / + SLASH_INT : INT := 47; + /// ASCII code for carriage return + CARRIAGE_RETURN_INT : INT := 13; + /// ASCII code for line feed + LINE_FEED_INT : INT := 10; + /// ASCII code for TAB + TAB_INT : INT := 9; + /// ASCII code for - + MINUS_INT : INT := 45; END_VAR tempExecute := execute; - IF TimeoutTimer = NULL THEN - TimeoutTimer := instWatchDog; - END_IF; - //region TRIGGERING IF ((tempExecute = TRUE) AND (statOldExecute = FALSE) AND (statStatus = STATUS_NO_CALL)) THEN @@ -211,10 +294,10 @@ NAMESPACE Simatic.Ax.LStream // #region PARSE XML //reset and start watchdog here so that for loop runs for a maximum of 5 seconds - TimeoutTimer.TimerFunction(signal := FALSE, duration := MAX_LOOP_TIME); + TimeoutTimer(signal := FALSE, duration := MAX_LOOP_TIME); //start watchdog - TimeoutTimer.TimerFunction(signal := TRUE, duration := MAX_LOOP_TIME); + TimeoutTimer(signal := TRUE, duration := MAX_LOOP_TIME); tempRawIndex := statRawIndex; @@ -240,12 +323,12 @@ NAMESPACE Simatic.Ax.LStream END_IF; statRawIndex := tempRawIndex; - // Swich case of current byte + // Switch case of current byte CASE TO_INT(raw[tempRawIndex]) OF //BYTE/ WORD Can not be selected as CASE selector - SMALLERINT: - //region READ OPENING KEY SIMBOLS + SMALLER_INT: + //region READ OPENING KEY SYMBOLS - // There MUST be some characters after a open < + // There MUST be some characters after an open < // so if the array is too short the xml is malformed IF ((tempRawIndex + 1) > statCountOfRaw) THEN statStatus := ERR_MALFORMED; @@ -279,13 +362,13 @@ NAMESPACE Simatic.Ax.LStream IF (raw[tempRawIndex +1] = QUESTIONMARK) OR (raw[tempRawIndex + 1] = EXCLAMATION) THEN CASE TO_INT(raw[tempRawIndex + 1]) OF //SAME CASE BYTE NOT ALLOWED - EXCLAMATIONINT: + EXCLAMATION_INT: IF raw[tempRawIndex +2] = MINUS THEN tempChar := MINUS; ELSE tempChar := GREATER; END_IF; - QUESTIONMARKINT: + QUESTIONMARK_INT: tempChar := QUESTIONMARK; END_CASE; @@ -488,10 +571,10 @@ NAMESPACE Simatic.Ax.LStream statIsValueOpen := FALSE; END_IF; - //endregion READ OPENING KEY SIMBOLS + //endregion READ OPENING KEY SYMBOLS - SLASHINT: - //region READ DELIMETER / + SLASH_INT: + //region READ DELIMITER / IF statIsValueOpen THEN statInfoLen := statInfoLen + UINT#1; @@ -546,21 +629,25 @@ NAMESPACE Simatic.Ax.LStream END_IF; END_IF; - //endregion READ DELIMETER / + //endregion READ DELIMITER / - SPACEINT: + SPACE_INT: IF statIsValueOpen THEN statInfoLen := statInfoLen + UINT#1; - + + ELSIF statIsKeyOpen THEN + + tempRawIndex := tempRawIndex; // Ignore "empty space" between a key name and '=' so spaces are not added to the key + ELSE statIsKeyOpen := TRUE; statInfoStartIndex := tempRawIndex + 1; statInfoLen := UINT#0; END_IF; - GREATERINT: + GREATER_INT: //region READ WRITING KEY SYMBOLS IF NOT statIsValueOpen THEN @@ -573,7 +660,7 @@ NAMESPACE Simatic.Ax.LStream //endregion READ WRITING KEY SYMBOLS - EQUALSINT: + EQUALS_INT: IF statIsValueOpen THEN @@ -649,7 +736,7 @@ NAMESPACE Simatic.Ax.LStream // This is not valid, as a equals character '=' that is not within an element // has to open an attribute with either ' or " - IF raw[tempRawIndex] <> DOUBLEQUOTES AND raw[tempRawIndex] <> QUOTES THEN + IF raw[tempRawIndex] <> DOUBLE_QUOTES AND raw[tempRawIndex] <> QUOTES THEN statStatus := ERR_MALFORMED; statDone := TRUE; @@ -719,8 +806,8 @@ NAMESPACE Simatic.Ax.LStream END_IF; - QUOTESINT: - //region READ VALUE SYMBOLES + QUOTES_INT: + //region READ VALUE SYMBOLS // Quotes can never occur within a key, which is always closed by a '=', '/>' or '>' IF statIsKeyOpen THEN statStatus := ERR_UNEXPECTED_QUOTES; @@ -731,7 +818,7 @@ NAMESPACE Simatic.Ax.LStream statInfoLen := statInfoLen + UINT#1; - //endregion READ VALUE SYMBOLES + //endregion READ VALUE SYMBOLS ELSE//all other characters //region READ OTHER SIGN @@ -742,9 +829,11 @@ NAMESPACE Simatic.Ax.LStream END_CASE; statRawIndex := tempRawIndex; - + + // Call for Timeout timer to update value + TimeoutTimer(); //quit and resume in next cycle if watchdogtimer is exceed - IF (TimeoutTimer.TimerFunction()) THEN + IF (TimeoutTimer.output) THEN statRawIndex := statRawIndex + 1; EXIT; ELSIF tempRawIndex <= statCountOfRaw THEN @@ -781,23 +870,23 @@ NAMESPACE Simatic.Ax.LStream //endregion EXECUTION FINISHED ELSIF (statStatus.%X15 = TRUE) AND (statError = FALSE) THEN // Error occurred (#statStatus is 16#8000 to 16#FFFF) - //region ERROR OCCURED + //region ERROR OCCURRED statDone := FALSE; statBusy := FALSE; statError := TRUE; // execution aborted --> set state no processing statFBState := FB_STATE_NO_PROCESSING; - //endregion ERROR OCCURED + //endregion ERROR OCCURRED ELSIF (tempExecute = FALSE) AND ((statDone = TRUE) OR (statError = TRUE)) THEN // Reset outputs - //region EXECUTE RESETED + //region EXECUTE RESET statDone := FALSE; statBusy := FALSE; statError := FALSE; statStatus := STATUS_NO_CALL; // Reset application specific outputs statResultCount := UINT#0; - //endregion EXECUTE RESETED + //endregion EXECUTE RESET END_IF; //region WRITE STATIC VALUES TO OUTPUTS diff --git a/src/LStream/LStream_XmlSerializer.st b/src/LStream/LStream_XmlSerializer.st index 165abd8..899f64e 100644 --- a/src/LStream/LStream_XmlSerializer.st +++ b/src/LStream/LStream_XmlSerializer.st @@ -21,7 +21,6 @@ //=============================================================================== //end_region -USING Simatic.Ax.Timer; USING System.Timer; USING Simatic.Ax.LStream.Utilities; USING System.Strings; @@ -29,102 +28,175 @@ USING Simatic.Ax.LStream.Models; NAMESPACE Simatic.Ax.LStream FUNCTION_BLOCK LStream_XmlSerializer VAR_INPUT - execute : BOOL; // Rising edge starts action once + /// Rising edge starts action once + execute : BOOL; END_VAR VAR_OUTPUT - done : BOOL; // TRUE: Commanded functionality has been completed successfully - busy : BOOL; // TRUE: FB is not finished and new output values can be expected - error : BOOL; // TRUE: An error occurred during the execution of the FB - status : WORD := STATUS_NO_CALL; // 16#0000 - 16#7FFF: Status of the FB, 16#8000 - 16#FFFF: Error identification - count : UINT; // Count of char/ byte info elements in byte array + /// TRUE: Commanded functionality has been completed successfully + done : BOOL; + /// TRUE: FB is not finished and new output values can be expected + busy : BOOL; + /// TRUE: An error occurred during the execution of the FB + error : BOOL; + /// 16#0000 - 16#7FFF: Status of the FB, 16#8000 - 16#FFFF: Error identification + status : WORD := STATUS_NO_CALL; + /// Count of char/ byte info elements in byte array + count : UINT; END_VAR VAR_IN_OUT - tree : ARRAY [*] of LStream_typeElement; // Describes the element for LStream libraries, by providing key-value pair and the depth of the element - xmlByteArray : ARRAY[*] of BYTE; // XML structure as array of bytes + /// Describes the element for LStream libraries, by providing key-value pair and the depth of the element + tree : ARRAY [*] of LStream_typeElement; + /// XML structure as array of bytes + xmlByteArray : ARRAY[*] of BYTE; END_VAR VAR PUBLIC - TimeoutTimer : ITimerFunctions; + TimeoutTimer : System.Timer.OnDelay; END_VAR VAR - statExecuteOld : BOOL; // Old value of 'execute' input for edge detection - statDone : BOOL; // Static value for output 'done' - statBusy : BOOL; // Static value for output 'busy' - statError : BOOL; // Static value for output 'error' - statCount : UINT; // Static value for ouput 'count' - statStatus : WORD := STATUS_NO_CALL; // Static value for output 'status' - statFBState : DINT := FB_STATE_NO_PROCESSING; // State in the state machine of the FB - statFBPreviousState : DINT := FB_STATE_NO_PROCESSING; // Previos state in the state machine of the FB - statFBNextState : DINT := FB_STATE_NO_PROCESSING; // Next state in the state machine of the FB - statIndexXmlByteArray : UINT; // Index indicating current element of byte array to be written - statIndexTreeArray : UINT; // Index indicating current element of tree array to be read - statTreeLen : DINT; // Length of tree array - statXmlByteLen : DINT; // Length of xml byte array - statInfoToWrite : STRING; // Static value of current information that should be written to XML - statIsLastElement : BOOL; // TRUE: is last element in tree array - statStackIndexOpenElement : ARRAY[0..STACK_SIZE] OF UINT; // Stack of open elements - statStackIndex : INT := -1; // Stack pointer - instWatchDog : TimerFunctionsImpl; // Instance for watchdog timer - statCurrentDepth : INT; // Depth of the current tree element + /// Old value of 'execute' input for edge detection + statExecuteOld : BOOL; + /// Static value for output 'done' + statDone : BOOL; + /// Static value for output 'busy' + statBusy : BOOL; + /// Static value for output 'error' + statError : BOOL; + /// Static value for output 'count' + statCount : UINT; + /// Static value for output 'status' + statStatus : WORD := STATUS_NO_CALL; + /// State in the state machine of the FB + statFBState : DINT := FB_STATE_NO_PROCESSING; + /// Previous state in the state machine of the FB + statFBPreviousState : DINT := FB_STATE_NO_PROCESSING; + /// Next state in the state machine of the FB + statFBNextState : DINT := FB_STATE_NO_PROCESSING; + /// Index indicating current element of byte array to be written + statIndexXmlByteArray : UINT; + /// Index indicating current element of tree array to be read + statIndexTreeArray : UINT; + /// Length of tree array + statTreeLen : DINT; + /// Length of xml byte array + statXmlByteLen : DINT; + /// Static value of current information that should be written to XML + statInfoToWrite : STRING; + /// TRUE: is last element in tree array + statIsLastElement : BOOL; + /// Stack of open elements + statStackIndexOpenElement : ARRAY[0..STACK_SIZE] OF UINT; + /// Stack pointer + statStackIndex : INT := -1; + /// Depth of the current tree element + statCurrentDepth : INT; statWorkingDepth : INT; END_VAR VAR_TEMP - tempExecute : BOOL; // Temporary value for input 'execute' - tempCntCharsAdded : INT; // Temporary value indicating count of chars added to byte array - tempIterator : UINT; // Temporary iterator value - tempHasNested : BOOL; // Temporary value indicating that the currrent element has nested elements + /// Temporary value for input 'execute' + tempExecute : BOOL; + /// Temporary value indicating count of chars added to byte array + tempCntCharsAdded : INT; + /// Temporary iterator value + tempIterator : UINT; + /// Temporary value indicating that the current element has nested elements + tempHasNested : BOOL; END_VAR VAR CONSTANT - FB_STATE_NO_PROCESSING : DINT := 0; // FB state: No processing - FB_STATE_CLEAR_BYTE_ARRAY : DINT := 1; // FB state: Clean the entire byte array - FB_STATE_XML_HEADER : DINT := 2; // FB state: Write XML Header - FB_STATE_NEXT_ELEMENT : DINT := 3; // FB state: Get Next Element - FB_STATE_ELEMENT_TYPE_OPEN : DINT := 4; // FB state: Switch Element type - FB_STATE_WRITE_TO_XML : DINT := 5; // FB state: Add Informationto xml byte array - FB_STATE_CLOSE_STACK_ELEMENT : DINT := 6; // FB state: Close Parent Element - FB_STATE_ERROR : DINT := 7; // FB state: Error - FB_STATE_DONE : DINT := 8; // FB state: Done - STATUS_EXECUTION_FINISHED : WORD := WORD#16#0000; // Execution finished without errors - STATUS_NO_CALL : WORD := WORD#16#7000; // No job being currently processed - STATUS_FIRST_CALL : WORD := WORD#16#7001; // First call after incoming new job (rising edge 'execute') - STATUS_SUBSEQUENT_CALL : WORD := WORD#16#7002; // Subsequent call during active processing without further details - ERR_UNDEFINED_STATE : WORD := WORD#16#8600; // Subsequent call during active processing without further details - ERR_IN_BLOCK_OPERATION : WORD := WORD#16#8601; // Error: wrong operation of the function block - ERR_PARAMETRIZATION : WORD := WORD#16#8200; // Error: during parameterization - ERR_PROCESSING_EXTERN : WORD := WORD#16#8400; // Error: when processing from outside (e. g. wrong I/O signals, axis not referenced) - ERR_UNDEFINED_TYPE : WORD := WORD#16#8401; // Error: user enter undefined type - ERR_UNEXPECTED_DEPTH : WORD := WORD#16#8402; // Error: provided xml tree structure is too deep - ERR_DEPTH_MISSING : WORD := WORD#16#8403; // Error: provided xml tree structure does not contain correct depth - ERR_PROCESSING_INTERN : WORD := WORD#16#8600; // Error: when processing internally (e. g. when calling a system function) - ERR_TREE_OUT_OF_BOUNDS : WORD := WORD#16#8601; // Error: tree array out of bounds - ERR_XML_OUT_OF_BOUNDS : WORD := WORD#16#8602; // Error: byte array out of bounds - ERR_STACK_OUT_OF_BOUNDS : WORD := WORD#16#8603; // Error: stack array out of bounds - ERR_AREA_RESERVED : WORD := WORD#16#8800; // Error: reserved area - ERR_USER_DEFINED_CLASSES : WORD := WORD#16#9000; // Error: user-defined error classes - INCREMENT_BY_ONE : SINT := SINT#1; // Constant to increment by one - DECREMENT_BY_ONE : SINT := SINT#1; // Constant to decrement by one - ELEMENT : SINT := SINT#0; // Numerical identifier for XML type element - ATTRIBUTE : SINT := SINT#1; // Numerical identifier for XML type attirbute - TEXT : SINT := SINT#2; // Numerical identifier for XML type text - FIRST_DIM : USINT := USINT#1; // Constant for first array dimension - MAX_REPEAT_TIME : TIME := T#6ms; // Max duration of loop, will be continued in next cycle - XML_HEADER : STRING := ''; // XML Header, leads every XML File - XML_KEY_OPEN : CHAR := '<'; // ASCII code for < indicates a new key - XML_ATTRIBUTE_OPEN : STRING := '="'; // ASCII code for '=' indicates key is finished follows by value - XML_KEY_CLOSE_GREATER : CHAR := '>'; // ASCII code for > indicates key can be written - XML_KEY_CLOSE_SLASH : CHAR := '/'; // ASCII code for / indicates closing - XML_ATTRIBUTE_CLOSE : CHAR := '"'; // ASCII code for " frames value - NEXT_INDEX : UINT := UINT#1; // Constant value to get next index - STACK_SIZE : UINT := UINT#30; // Constant for Stack Size, equals max depth - WHITE_SPACE: CHAR := ' '; // ASCII code for white space - NOT_INITALYZED : SINT := SINT#-1; // Constant value representing not initalyzed tree element - NULLCONST : STRING := STRING#'NULL'; // String for NULL value - EMPTY_STRING : STRING := STRING#''; // Empty string for checking if value is contained within the string - EMPTY_BYTE : BYTE := BYTE#16#0; // Empty byte for cleaning the byte array + /// FB state: No processing + FB_STATE_NO_PROCESSING : DINT := 0; + /// FB state: Clean the entire byte array + FB_STATE_CLEAR_BYTE_ARRAY : DINT := 1; + /// FB state: Write XML Header + FB_STATE_XML_HEADER : DINT := 2; + /// FB state: Get Next Element + FB_STATE_NEXT_ELEMENT : DINT := 3; + /// FB state: Switch Element type + FB_STATE_ELEMENT_TYPE_OPEN : DINT := 4; + /// FB state: Add Information to xml byte array + FB_STATE_WRITE_TO_XML : DINT := 5; + /// FB state: Close Parent Element + FB_STATE_CLOSE_STACK_ELEMENT : DINT := 6; + /// FB state: Error + FB_STATE_ERROR : DINT := 7; + /// FB state: Done + FB_STATE_DONE : DINT := 8; + /// Execution finished without errors + STATUS_EXECUTION_FINISHED : WORD := WORD#16#0000; + /// No job being currently processed + STATUS_NO_CALL : WORD := WORD#16#7000; + /// First call after incoming new job (rising edge 'execute') + STATUS_FIRST_CALL : WORD := WORD#16#7001; + /// Subsequent call during active processing without further details + STATUS_SUBSEQUENT_CALL : WORD := WORD#16#7002; + /// Subsequent call during active processing without further details + ERR_UNDEFINED_STATE : WORD := WORD#16#8600; + /// Error: wrong operation of the function block + ERR_IN_BLOCK_OPERATION : WORD := WORD#16#8601; + /// Error: during parameterization + ERR_PARAMETRIZATION : WORD := WORD#16#8200; + /// Error: when processing from outside (e. g. wrong I/O signals, axis not referenced) + ERR_PROCESSING_EXTERN : WORD := WORD#16#8400; + /// Error: user enter undefined type + ERR_UNDEFINED_TYPE : WORD := WORD#16#8401; + /// Error: provided xml tree structure is too deep + ERR_UNEXPECTED_DEPTH : WORD := WORD#16#8402; + /// Error: provided xml tree structure does not contain correct depth + ERR_DEPTH_MISSING : WORD := WORD#16#8403; + /// Error: when processing internally (e. g. when calling a system function) + ERR_PROCESSING_INTERN : WORD := WORD#16#8600; + /// Error: tree array out of bounds + ERR_TREE_OUT_OF_BOUNDS : WORD := WORD#16#8601; + /// Error: byte array out of bounds + ERR_XML_OUT_OF_BOUNDS : WORD := WORD#16#8602; + /// Error: stack array out of bounds + ERR_STACK_OUT_OF_BOUNDS : WORD := WORD#16#8603; + /// Error: reserved area + ERR_AREA_RESERVED : WORD := WORD#16#8800; + /// Error: user-defined error classes + ERR_USER_DEFINED_CLASSES : WORD := WORD#16#9000; + /// Constant to increment by one + INCREMENT_BY_ONE : SINT := SINT#1; + /// Constant to decrement by one + DECREMENT_BY_ONE : SINT := SINT#1; + /// Numerical identifier for XML type element + ELEMENT : SINT := SINT#0; + /// Numerical identifier for XML type attribute + ATTRIBUTE : SINT := SINT#1; + /// Numerical identifier for XML type text + TEXT : SINT := SINT#2; + /// Constant for first array dimension + FIRST_DIM : USINT := USINT#1; + /// Max duration of loop, will be continued in next cycle + MAX_REPEAT_TIME : TIME := T#6ms; + /// XML Header, leads every XML File + XML_HEADER : STRING := ''; + /// ASCII code for < indicates a new key + XML_KEY_OPEN : CHAR := '<'; + /// ASCII code for '=' indicates key is finished follows by value + XML_ATTRIBUTE_OPEN : STRING := '="'; + /// ASCII code for > indicates key can be written + XML_KEY_CLOSE_GREATER : CHAR := '>'; + /// ASCII code for / indicates closing + XML_KEY_CLOSE_SLASH : CHAR := '/'; + /// ASCII code for " frames value + XML_ATTRIBUTE_CLOSE : CHAR := '"'; + /// Constant value to get next index + NEXT_INDEX : UINT := UINT#1; + /// Constant for Stack Size, equals max depth + STACK_SIZE : UINT := UINT#30; + /// ASCII code for white space + WHITE_SPACE: CHAR := ' '; + /// Constant value representing not initialized tree element + NOT_INITIALIZED : SINT := SINT#-1; + /// String for NULL value + NULL_CONST : STRING := STRING#'NULL'; + /// Empty string for checking if value is contained within the string + EMPTY_STRING : STRING := STRING#''; + /// Empty byte for cleaning the byte array + EMPTY_BYTE : BYTE := BYTE#16#0; END_VAR; - IF TimeoutTimer = NULL THEN - TimeoutTimer := instWatchDog; - END_IF; + tempExecute := execute; // Work with temporary value / create process image //region TRIGGERING @@ -162,9 +234,9 @@ NAMESPACE Simatic.Ax.LStream //region STATE_MACHINE //rest and start watchdog here so that repeat until has a maximum duration of constant MAX_LOOP_TIME - TimeoutTimer.TimerFunction(signal := FALSE, duration := MAX_REPEAT_TIME); + TimeoutTimer(signal := FALSE, duration := MAX_REPEAT_TIME); //start watchdog - TimeoutTimer.TimerFunction(signal := TRUE, duration := MAX_REPEAT_TIME); + TimeoutTimer(signal := TRUE, duration := MAX_REPEAT_TIME); REPEAT statFbPreviousState := statFbState; @@ -309,7 +381,8 @@ NAMESPACE Simatic.Ax.LStream string2 := tree[tempIterator].key, string3 := XML_ATTRIBUTE_OPEN, string4 := tree[tempIterator].value); - statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := XML_ATTRIBUTE_CLOSE); //ADD Temp String because 5 concats is not supported + /// ADD Temp String because 5 concat is not supported + statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := XML_ATTRIBUTE_CLOSE); /*statInfoToWrite := Concat( IN1 := WHITE_SPACE, IN2 := tree[tempIterator].key, @@ -355,25 +428,20 @@ NAMESPACE Simatic.Ax.LStream END_IF; - IF tree[statIndexTreeArray].value <> NULLCONST OR tempHasNested THEN + IF tree[statIndexTreeArray].value <> NULL_CONST OR tempHasNested THEN // Write attribute infos to the string, assuming that they are in combination shorter than the string datatype statInfoToWrite := XML_KEY_CLOSE_GREATER; - IF tree[statIndexTreeArray].value <> NULLCONST THEN + IF tree[statIndexTreeArray].value <> NULL_CONST THEN statInfoToWrite := Concat(string1 := XML_KEY_CLOSE_GREATER, string2 := tree[statIndexTreeArray].value); IF NOT tempHasNested THEN statInfoToWrite := Concat(string1 := statInfoToWrite, - string2 := statInfoToWrite, - string3 := XML_KEY_OPEN, - string4 := XML_KEY_CLOSE_SLASH); //tempString ADDED for concatenate more than 4 strings + string2 := XML_KEY_OPEN, + string3 := XML_KEY_CLOSE_SLASH); statInfoToWrite := Concat(string1 := statInfoToWrite, string2 := tree[statIndexTreeArray].key, string3 := XML_KEY_CLOSE_GREATER); - /*statInfoToWrite := Concat(string1 := statInfoToWrite, - string2 := XML_KEY_OPEN, - string3 := XML_KEY_CLOSE_SLASH, - string4 := tree[statIndexTreeArray].key, - string5 := XML_KEY_CLOSE_GREATER);*/ + END_IF; END_IF; @@ -390,11 +458,30 @@ NAMESPACE Simatic.Ax.LStream statIndexXmlByteArray := statIndexXmlByteArray + TO_UINT(tempCntCharsAdded) + UINT#1; // Close element with short form '/>' - ELSIF tree[statIndexTreeArray].value = NULLCONST THEN + ELSIF tree[statIndexTreeArray].value = NULL_CONST THEN + + // This IF considers the situation in which the value of the tree node is 'NULL' + IF NOT tempHasNested THEN + + statInfoToWrite := XML_KEY_CLOSE_GREATER; + + statInfoToWrite := Concat(string1 := statInfoToWrite, + string2 := XML_KEY_OPEN, + string3 := XML_KEY_CLOSE_SLASH); + statInfoToWrite := Concat(string1 := statInfoToWrite, + string2 := tree[statIndexTreeArray].key, + string3 := XML_KEY_CLOSE_GREATER); + + tempCntCharsAdded := LStream_WriteOutString(offset := statIndexXmlByteArray, toWrite := statInfoToWrite, xmlByteArray := xmlByteArray); + + ELSE // Write attribute infos to the string, assuming that they are in combination shorter than the string datatype statInfoToWrite := Concat(string1 := XML_KEY_CLOSE_SLASH, string2 := XML_KEY_CLOSE_GREATER); tempCntCharsAdded := LStream_WriteOutString(offset := statIndexXmlByteArray, toWrite := statInfoToWrite, xmlByteArray := xmlByteArray); + + END_IF; + IF tempCntCharsAdded = -1 THEN statStatus := ERR_XML_OUT_OF_BOUNDS; statFbState := FB_STATE_ERROR; @@ -491,48 +578,49 @@ NAMESPACE Simatic.Ax.LStream //endregion UNDEFINED STATE END_CASE; + + // Call for Timeout timer to update value + TimeoutTimer(); //Leave state machine if one of the following condition is true //1. state has not changed - //2. error has occured + //2. error has occurred //3. watchdog timer expired //4. execution has finished // otherwise stay in state machine - - UNTIL (statFbPreviousState = statFbState OR statStatus.%X15 - OR TimeoutTimer.TimerFunction() OR statStatus = STATUS_EXECUTION_FINISHED) + OR TimeoutTimer.output OR statStatus = STATUS_EXECUTION_FINISHED) END_REPEAT; //endregion STATE_MACHINE // REGION OUTPUTS // Write outputs IF (statStatus = STATUS_EXECUTION_FINISHED) AND (statDone = FALSE) THEN // Execution finished without errors - // REGION EXECUTION FINSIHED + // REGION EXECUTION FINISHED statDone := TRUE; statBusy := FALSE; statError := FALSE; // execution aborted --> set state no processing statFbState := FB_STATE_NO_PROCESSING; - // END_REGION EXECUTION FINSIHED + // END_REGION EXECUTION FINISHED ELSIF (statStatus.%X15 = TRUE) AND (statError = FALSE) THEN // Error occurred (statStatus is 168000 to 16FFFF) - // REGION ERROR OCCURED + // REGION ERROR OCCURRED statDone := FALSE; statBusy := FALSE; statError := TRUE; // execution aborted --> set state no processing statFbState := FB_STATE_NO_PROCESSING; - // END_REGION ERROR OCCURED + // END_REGION ERROR OCCURRED ELSIF (tempExecute = FALSE) AND ((statDone = TRUE) OR (statError = TRUE)) THEN // Reset outputs - // REGION EXECUTE RESETTED + // REGION EXECUTE RESET statDone := FALSE; statBusy := FALSE; statError := FALSE; statStatus := STATUS_NO_CALL; // Reset application specific outputs statCount := UINT#0; - // END_REGION EXECUTE RESETTED + // END_REGION EXECUTE RESET END_IF; // REGION WRITE STATIC VALUES TO OUTPUTS diff --git a/src/LStream/Models/Lstream_TypeElement.st b/src/LStream/Models/Lstream_TypeElement.st index ea369a6..d8a7f15 100644 --- a/src/LStream/Models/Lstream_TypeElement.st +++ b/src/LStream/Models/Lstream_TypeElement.st @@ -1,10 +1,18 @@ NAMESPACE Simatic.Ax.LStream.Models TYPE + {OpcUa = ReadWrite} + {S7.Extern = ReadWrite} + /// Represents a parsed element or attribute from an XML/JSON structure with its metadata LStream_TypeElement : STRUCT + /// Type identifier: 0 = Element, 1 = Attribute, -1 = Not initialized types : SINT := SINT#-1; + /// The name or key of the element or attribute key : STRING := ''; + /// The value associated with the element or attribute; default 'NULL' if not set value : STRING := 'NULL'; + /// Nesting depth in the document hierarchy; -1 indicates not initialized depth : SINT := SINT#-1; + /// TRUE if this element marks a closing tag or end of a structure closingElement : BOOL; END_STRUCT; END_TYPE diff --git a/src/LStream/Utilities/LStream_FindStringInByteCharArrayAdv.st b/src/LStream/Utilities/LStream_FindStringInByteCharArrayAdv.st index d9e2c56..c32bfed 100644 --- a/src/LStream/Utilities/LStream_FindStringInByteCharArrayAdv.st +++ b/src/LStream/Utilities/LStream_FindStringInByteCharArrayAdv.st @@ -24,23 +24,34 @@ USING System.Math; NAMESPACE Simatic.Ax.LStream.Utilities FUNCTION LStream_FindStringInByteCharArrayAdv : DINT //Returns the index of found string VAR_INPUT - searchFor : STRING; // Text that is searched for - startPosition : DINT; // Start position in array //CHANGE TO UDINT; + /// Text that is searched for + searchFor : STRING; + /// Start position in array //CHANGE TO UDINT; + startPosition : DINT; END_VAR VAR_IN_OUT - searchIn : ARRAY[*] of BYTE; // Array of Char to search in + /// Array of Char to search in + searchIn : ARRAY[*] of BYTE; END_VAR VAR_TEMP - tempLowerBound : DINT; // Lower bound of given array - tempNumElements : UDINT; // Number of elements in given array - tempLenSearchFor : INT; // Length of text that is searched for - tempPosInArray : DINT; // Position in array during search (0-based) - tempPosInString : INT; // Position in working string during search - tempString : STRING; // Temporary string to work with + /// Lower bound of given array + tempLowerBound : DINT; + /// Number of elements in given array + tempNumElements : UDINT; + /// Length of text that is searched for + tempLenSearchFor : INT; + /// Position in array during search (0-based) + tempPosInArray : DINT; + /// Position in working string during search + tempPosInString : INT; + /// Temporary string to work with + tempString : STRING; END_VAR VAR CONSTANT - FIRST_DIM : USINT := USINT#1; // Constant for first array dimension - MAX_LEN_STRING : UINT := UINT#254; // Max.length of String + /// Constant for first array dimension + FIRST_DIM : USINT := USINT#1; + /// Max.length of String + MAX_LEN_STRING : UINT := UINT#254; END_VAR // Initialization @@ -68,7 +79,7 @@ NAMESPACE Simatic.Ax.LStream.Utilities result => tempString); // Search for text at beginning - tempPosInString := PositionOf(value := tempString, seekValue := searchFor); //CHECH HOW TO IMPLEMENT + tempPosInString := PositionOf(value := tempString, seekValue := searchFor); //CHECK HOW TO IMPLEMENT // Keyword was found IF tempPosInString > 0 THEN diff --git a/src/LStream/Utilities/LStream_WriteOutString.st b/src/LStream/Utilities/LStream_WriteOutString.st index 602d1b2..3951150 100644 --- a/src/LStream/Utilities/LStream_WriteOutString.st +++ b/src/LStream/Utilities/LStream_WriteOutString.st @@ -3,21 +3,28 @@ USING System.Serialization; NAMESPACE Simatic.Ax.LStream.Utilities FUNCTION LStream_WriteOutString : INT //Offset after the data has been written; -1 if there would have been an overflow VAR_INPUT - offset : UINT; // Position in the array to start writing + /// Position in the array to start writing + offset : UINT; END_VAR VAR_IN_OUT - toWrite : STRING; // The string that has to be written to the byte array - xmlByteArray : ARRAY[*] of BYTE; // The byte representation of the XML to write into + /// The string that has to be written to the byte array + toWrite : STRING; + /// The byte representation of the XML to write into + xmlByteArray : ARRAY[*] of BYTE; END_VAR VAR_TEMP - tempXmlUpperBound : DINT; // The upper bound index of the XML Array - tempCountWritten : UDINT; // Amount of characters written to the char array + /// The upper bound index of the XML Array + tempXmlUpperBound : DINT; + /// Amount of characters written to the char array + tempCountWritten : UDINT; tempOffsetArray : ARRAY[0..255] of BYTE; Index : INT; END_VAR VAR CONSTANT - RETURN_OUT_OF_BOUNDS : INT := -1; // Return value, if the write would be out of bounds, no write happens - MAX_LEN_STRING : UINT := UINT#254; // Max.length of String + /// Return value, if the write would be out of bounds, no write happens + RETURN_OUT_OF_BOUNDS : INT := -1; + /// Max.length of String + MAX_LEN_STRING : UINT := UINT#254; END_VAR tempXmlUpperBound := UPPER_BOUND(xmlByteArray, 1); @@ -35,7 +42,7 @@ NAMESPACE Simatic.Ax.LStream.Utilities // //fixed serialize tempCountWritten := Serialize(offset := UDINT#0, value := toWrite, buffer := tempOffsetArray); //NOT WORKING BECAUSE STRING SIZE IS WRITTEN IN FIRST POSITION OF OUTPUT ARRAY - //WRITE INTO OUTPUT ARRAY WITH AN OFFSET TO AVOID WRITTING STRING SIZE - USE SIZE IN FIRST BYTE FOR LIMIT NUMBER OF ITERATIONS + //WRITE INTO OUTPUT ARRAY WITH AN OFFSET TO AVOID WRITING STRING SIZE - USE SIZE IN FIRST BYTE FOR LIMIT NUMBER OF ITERATIONS FOR Index := 0 TO TO_INT(tempOffsetArray[0]) - 1 DO xmlByteArray[TO_INT(offset) + Index] := tempOffsetArray[Index + 1]; END_FOR; diff --git a/src/LStream/Utilities/StringToByteArray.st b/src/LStream/Utilities/StringToByteArray.st index 2ac4878..e4149d4 100644 --- a/src/LStream/Utilities/StringToByteArray.st +++ b/src/LStream/Utilities/StringToByteArray.st @@ -4,13 +4,17 @@ NAMESPACE Simatic.Ax.LStream.Utilities FUNCTION StringToByteArray : BOOL VAR_INPUT string1 : STRING; - string2 : STRING := ''; // Optional - string3 : STRING := ''; // Optional - string4 : STRING := ''; // Optional + /// Optional + string2 : STRING := ''; + /// Optional + string3 : STRING := ''; + /// Optional + string4 : STRING := ''; END_VAR VAR_IN_OUT - byteArray : ARRAY[0..10000] OF BYTE; // Ensure this array is large enough to hold all bytes + /// Ensure this array is large enough to hold all bytes + byteArray : ARRAY[0..10000] OF BYTE; END_VAR VAR diff --git a/test/JsonDeserializerTests.st b/test/JsonDeserializerTests.st index 26568fc..f750537 100644 --- a/test/JsonDeserializerTests.st +++ b/test/JsonDeserializerTests.st @@ -1,5 +1,4 @@ USING Simatic.Ax; -USING Simatic.Ax.Timer; USING AxUnit; USING Simatic.Ax.LStream; USING Simatic.Ax.LStream.Utilities; @@ -12,155 +11,223 @@ NAMESPACE LStreamTest {TestFixture} CLASS JsonDeserializerTests VAR - //Mock Timer - TimerInst : TimerFunctionsMock; - //Test Variables Json Deserializer + //Test1 (Test to try the LStream_JsonDeserializer.st by deserializing a JSON (inputString1 & inputString2) and comparing its output with the expected tree (expectedTreeOutput1)) jsonDeserializer : LStream_JsonDeserializer; inputString1 : STRING := '{"sessionContext": {"sessionId": "154237171615905","locale": "en_US","persId": 0},"methodName": "valeo2WC.cfWipClose","inArgs": [{"mode": "0","stationNumber": "SubRib0101","step": "0010","cycleTime": "253.2","unitArray": ['; inputString2 : STRING := '{"unit": "HLF659734","pos": "1", "state": "1","failArray": [{"failCode": "Condensator position","compName": "BOMPOS60"}]},{"unit": "HLF659735","pos": "2", "state": "0"}]}]}'; - rawInput : ARRAY[0..10000] OF BYTE; + rawInput1 : ARRAY[0..10000] OF BYTE; actualTreeOutput : ARRAY[0..200] OF LStream_typeElement; - expectedTreeOutput : ARRAY[0..200] OF LStream_typeElement; + expectedTreeOutput1 : ARRAY[0..200] OF LStream_typeElement; + + + //Test2 (Test to try the LStream_JsonDeserializer.st when the input JSON has empty brackets as value in the key (In this example: "items":[] & "edges":[]). The deserialize output tree is compared with the expected tree (expectedTreeOutput2)) + jsonDeserializer2 : LStream_JsonDeserializer; + inputString3 : STRING := '{"id":"A","nodes":[{"nodeId":"CAT","items":[],"sequenceId":"A","released":"WORD","nodePosition":{"x":"1"}}],"edges":[]}'; + rawInput2 : ARRAY[0..10000] OF BYTE; + actualTreeOutput2 : ARRAY[0..200] OF LStream_typeElement; + expectedTreeOutput2 : ARRAY[0..200] OF LStream_typeElement; + END_VAR - {TestSetup} + {FixtureSetup} METHOD PUBLIC TestSetup - //Test Setup Json Deserializer case 1 - jsonDeserializer.TimeoutTimer := TimerInst; - StringToByteArray(string1 := inputString1, string2 := inputString2, byteArray := rawInput); - // Populate expected LStreamTypeElementArray - // Populate expectedTreeOutput - expectedTreeOutput[0].types := SINT#16#0; - expectedTreeOutput[0].key := 'sessionContext'; - expectedTreeOutput[0].value := 'NULL'; - expectedTreeOutput[0].depth := SINT#16#0; - expectedTreeOutput[0].closingElement := FALSE; - - expectedTreeOutput[1].types := SINT#16#02; - expectedTreeOutput[1].key := 'sessionId'; - expectedTreeOutput[1].value := '154237171615905'; - expectedTreeOutput[1].depth := SINT#16#01; - expectedTreeOutput[1].closingElement := FALSE; - - expectedTreeOutput[2].types := SINT#16#02; - expectedTreeOutput[2].key := 'locale'; - expectedTreeOutput[2].value := 'en_US'; - expectedTreeOutput[2].depth := SINT#16#01; - expectedTreeOutput[2].closingElement := FALSE; - - expectedTreeOutput[3].types := SINT#16#03; - expectedTreeOutput[3].key := 'persId'; - expectedTreeOutput[3].value := '0'; - expectedTreeOutput[3].depth := SINT#16#01; - expectedTreeOutput[3].closingElement := TRUE; - - expectedTreeOutput[4].types := SINT#16#02; - expectedTreeOutput[4].key := 'methodName'; - expectedTreeOutput[4].value := 'valeo2WC.cfWipClose'; - expectedTreeOutput[4].depth := SINT#16#0; - expectedTreeOutput[4].closingElement := FALSE; - - expectedTreeOutput[5].types := SINT#16#01; - expectedTreeOutput[5].key := 'inArgs'; - expectedTreeOutput[5].value := 'NULL'; - expectedTreeOutput[5].depth := SINT#16#0; - expectedTreeOutput[5].closingElement := false; - - expectedTreeOutput[6].types := SINT#16#02; - expectedTreeOutput[6].key := 'mode'; - expectedTreeOutput[6].value := '0'; - expectedTreeOutput[6].depth := SINT#16#02; - expectedTreeOutput[6].closingElement := false; - - expectedTreeOutput[7].types := SINT#16#02; - expectedTreeOutput[7].key := 'stationNumber'; - expectedTreeOutput[7].value := 'SubRib0101'; - expectedTreeOutput[7].depth := SINT#16#02; - expectedTreeOutput[7].closingElement := false; - - expectedTreeOutput[8].types := SINT#16#02; - expectedTreeOutput[8].key := 'step'; - expectedTreeOutput[8].value := '0010'; - expectedTreeOutput[8].depth := SINT#16#02; - expectedTreeOutput[8].closingElement := false; - - expectedTreeOutput[9].types := SINT#16#02; - expectedTreeOutput[9].key := 'cycleTime'; - expectedTreeOutput[9].value := '253.2'; - expectedTreeOutput[9].depth := SINT#16#02; - expectedTreeOutput[9].closingElement := false; - - expectedTreeOutput[10].types := SINT#16#01; - expectedTreeOutput[10].key := 'unitArray'; - expectedTreeOutput[10].value := 'NULL'; - expectedTreeOutput[10].depth := SINT#16#02; - expectedTreeOutput[10].closingElement := false; - - expectedTreeOutput[11].types := SINT#16#02; - expectedTreeOutput[11].key := 'unit'; - expectedTreeOutput[11].value := 'HLF659734'; - expectedTreeOutput[11].depth := SINT#16#04; - expectedTreeOutput[11].closingElement := false; - - expectedTreeOutput[12].types := SINT#16#02; - expectedTreeOutput[12].key := 'pos'; - expectedTreeOutput[12].value := '1'; - expectedTreeOutput[12].depth := SINT#16#04; - expectedTreeOutput[12].closingElement := false; - - expectedTreeOutput[13].types := SINT#16#02; - expectedTreeOutput[13].key := 'state'; - expectedTreeOutput[13].value := '1'; - expectedTreeOutput[13].depth := SINT#16#04; - expectedTreeOutput[13].closingElement := false; - - expectedTreeOutput[14].types := SINT#16#01; - expectedTreeOutput[14].key := 'failArray'; - expectedTreeOutput[14].value := 'NULL'; - expectedTreeOutput[14].depth := SINT#16#04; - expectedTreeOutput[14].closingElement := false; - - expectedTreeOutput[15].types := SINT#16#02; - expectedTreeOutput[15].key := 'failCode'; - expectedTreeOutput[15].value := 'Condensator position'; - expectedTreeOutput[15].depth := SINT#16#06; - expectedTreeOutput[15].closingElement := false; - - expectedTreeOutput[16].types := SINT#16#02; - expectedTreeOutput[16].key := 'compName'; - expectedTreeOutput[16].value := 'BOMPOS60'; - expectedTreeOutput[16].depth := SINT#16#06; - expectedTreeOutput[16].closingElement := true; - - expectedTreeOutput[17].types := SINT#16#02; - expectedTreeOutput[17].key := 'unit'; - expectedTreeOutput[17].value := 'HLF659735'; - expectedTreeOutput[17].depth := SINT#16#04; - expectedTreeOutput[17].closingElement := FALSE; - - expectedTreeOutput[18].types := SINT#16#02; - expectedTreeOutput[18].key := 'pos'; - expectedTreeOutput[18].value := '2'; - expectedTreeOutput[18].depth := SINT#16#04; - expectedTreeOutput[18].closingElement := FALSE; - - expectedTreeOutput[19].types := SINT#16#02; - expectedTreeOutput[19].key := 'state'; - expectedTreeOutput[19].value := '0'; - expectedTreeOutput[19].depth := SINT#16#04; - expectedTreeOutput[19].closingElement := true; + //Test1 Setup + StringToByteArray(string1 := inputString1, string2 := inputString2, byteArray := rawInput1); + + expectedTreeOutput1[0].types := SINT#16#0; + expectedTreeOutput1[0].key := 'sessionContext'; + expectedTreeOutput1[0].value := 'NULL'; + expectedTreeOutput1[0].depth := SINT#16#0; + expectedTreeOutput1[0].closingElement := FALSE; + + expectedTreeOutput1[1].types := SINT#16#02; + expectedTreeOutput1[1].key := 'sessionId'; + expectedTreeOutput1[1].value := '154237171615905'; + expectedTreeOutput1[1].depth := SINT#16#01; + expectedTreeOutput1[1].closingElement := FALSE; + + expectedTreeOutput1[2].types := SINT#16#02; + expectedTreeOutput1[2].key := 'locale'; + expectedTreeOutput1[2].value := 'en_US'; + expectedTreeOutput1[2].depth := SINT#16#01; + expectedTreeOutput1[2].closingElement := FALSE; + + expectedTreeOutput1[3].types := SINT#16#03; + expectedTreeOutput1[3].key := 'persId'; + expectedTreeOutput1[3].value := '0'; + expectedTreeOutput1[3].depth := SINT#16#01; + expectedTreeOutput1[3].closingElement := TRUE; + + expectedTreeOutput1[4].types := SINT#16#02; + expectedTreeOutput1[4].key := 'methodName'; + expectedTreeOutput1[4].value := 'valeo2WC.cfWipClose'; + expectedTreeOutput1[4].depth := SINT#16#0; + expectedTreeOutput1[4].closingElement := FALSE; + + expectedTreeOutput1[5].types := SINT#16#01; + expectedTreeOutput1[5].key := 'inArgs'; + expectedTreeOutput1[5].value := 'NULL'; + expectedTreeOutput1[5].depth := SINT#16#0; + expectedTreeOutput1[5].closingElement := false; + + expectedTreeOutput1[6].types := SINT#16#02; + expectedTreeOutput1[6].key := 'mode'; + expectedTreeOutput1[6].value := '0'; + expectedTreeOutput1[6].depth := SINT#16#02; + expectedTreeOutput1[6].closingElement := false; + + expectedTreeOutput1[7].types := SINT#16#02; + expectedTreeOutput1[7].key := 'stationNumber'; + expectedTreeOutput1[7].value := 'SubRib0101'; + expectedTreeOutput1[7].depth := SINT#16#02; + expectedTreeOutput1[7].closingElement := false; + + expectedTreeOutput1[8].types := SINT#16#02; + expectedTreeOutput1[8].key := 'step'; + expectedTreeOutput1[8].value := '0010'; + expectedTreeOutput1[8].depth := SINT#16#02; + expectedTreeOutput1[8].closingElement := false; + + expectedTreeOutput1[9].types := SINT#16#02; + expectedTreeOutput1[9].key := 'cycleTime'; + expectedTreeOutput1[9].value := '253.2'; + expectedTreeOutput1[9].depth := SINT#16#02; + expectedTreeOutput1[9].closingElement := false; + + expectedTreeOutput1[10].types := SINT#16#01; + expectedTreeOutput1[10].key := 'unitArray'; + expectedTreeOutput1[10].value := 'NULL'; + expectedTreeOutput1[10].depth := SINT#16#02; + expectedTreeOutput1[10].closingElement := false; + + expectedTreeOutput1[11].types := SINT#16#02; + expectedTreeOutput1[11].key := 'unit'; + expectedTreeOutput1[11].value := 'HLF659734'; + expectedTreeOutput1[11].depth := SINT#16#04; + expectedTreeOutput1[11].closingElement := false; + + expectedTreeOutput1[12].types := SINT#16#02; + expectedTreeOutput1[12].key := 'pos'; + expectedTreeOutput1[12].value := '1'; + expectedTreeOutput1[12].depth := SINT#16#04; + expectedTreeOutput1[12].closingElement := false; + + expectedTreeOutput1[13].types := SINT#16#02; + expectedTreeOutput1[13].key := 'state'; + expectedTreeOutput1[13].value := '1'; + expectedTreeOutput1[13].depth := SINT#16#04; + expectedTreeOutput1[13].closingElement := false; + + expectedTreeOutput1[14].types := SINT#16#01; + expectedTreeOutput1[14].key := 'failArray'; + expectedTreeOutput1[14].value := 'NULL'; + expectedTreeOutput1[14].depth := SINT#16#04; + expectedTreeOutput1[14].closingElement := false; + + expectedTreeOutput1[15].types := SINT#16#02; + expectedTreeOutput1[15].key := 'failCode'; + expectedTreeOutput1[15].value := 'Condensator position'; + expectedTreeOutput1[15].depth := SINT#16#06; + expectedTreeOutput1[15].closingElement := false; + + expectedTreeOutput1[16].types := SINT#16#02; + expectedTreeOutput1[16].key := 'compName'; + expectedTreeOutput1[16].value := 'BOMPOS60'; + expectedTreeOutput1[16].depth := SINT#16#06; + expectedTreeOutput1[16].closingElement := true; + + expectedTreeOutput1[17].types := SINT#16#02; + expectedTreeOutput1[17].key := 'unit'; + expectedTreeOutput1[17].value := 'HLF659735'; + expectedTreeOutput1[17].depth := SINT#16#04; + expectedTreeOutput1[17].closingElement := FALSE; + + expectedTreeOutput1[18].types := SINT#16#02; + expectedTreeOutput1[18].key := 'pos'; + expectedTreeOutput1[18].value := '2'; + expectedTreeOutput1[18].depth := SINT#16#04; + expectedTreeOutput1[18].closingElement := FALSE; + + expectedTreeOutput1[19].types := SINT#16#02; + expectedTreeOutput1[19].key := 'state'; + expectedTreeOutput1[19].value := '0'; + expectedTreeOutput1[19].depth := SINT#16#04; + expectedTreeOutput1[19].closingElement := true; + + + + //Test Setup Json Deserializer case 2 + StringToByteArray(string1 := inputString3, byteArray := rawInput2); + + expectedTreeOutput2[0].types := SINT#16#02; + expectedTreeOutput2[0].key := 'id'; + expectedTreeOutput2[0].value := 'A'; + expectedTreeOutput2[0].depth := SINT#16#0; + expectedTreeOutput2[0].closingElement := FALSE; + + expectedTreeOutput2[1].types := SINT#16#01; + expectedTreeOutput2[1].key := 'nodes'; + expectedTreeOutput2[1].value := 'NULL'; + expectedTreeOutput2[1].depth := SINT#16#0; + expectedTreeOutput2[1].closingElement := FALSE; + + expectedTreeOutput2[2].types := SINT#16#02; + expectedTreeOutput2[2].key := 'nodeId'; + expectedTreeOutput2[2].value := 'CAT'; + expectedTreeOutput2[2].depth := SINT#16#02; + expectedTreeOutput2[2].closingElement := FALSE; + + expectedTreeOutput2[3].types := SINT#16#01; + expectedTreeOutput2[3].key := 'items'; + expectedTreeOutput2[3].value := 'NULL'; + expectedTreeOutput2[3].depth := SINT#16#02; + expectedTreeOutput2[3].closingElement := FALSE; + + expectedTreeOutput2[4].types := SINT#16#02; + expectedTreeOutput2[4].key := 'sequenceId'; + expectedTreeOutput2[4].value := 'A'; + expectedTreeOutput2[4].depth := SINT#16#02; + expectedTreeOutput2[4].closingElement := FALSE; + + expectedTreeOutput2[5].types := SINT#16#02; + expectedTreeOutput2[5].key := 'released'; + expectedTreeOutput2[5].value := 'WORD'; + expectedTreeOutput2[5].depth := SINT#16#02; + expectedTreeOutput2[5].closingElement := FALSE; + + expectedTreeOutput2[6].types := SINT#16#0; + expectedTreeOutput2[6].key := 'nodePosition'; + expectedTreeOutput2[6].value := 'NULL'; + expectedTreeOutput2[6].depth := SINT#16#02; + expectedTreeOutput2[6].closingElement := FALSE; + + expectedTreeOutput2[7].types := SINT#16#02; + expectedTreeOutput2[7].key := 'x'; + expectedTreeOutput2[7].value := '1'; + expectedTreeOutput2[7].depth := SINT#16#03; + expectedTreeOutput2[7].closingElement := TRUE; + + expectedTreeOutput2[8].types := SINT#16#01; + expectedTreeOutput2[8].key := 'edges'; + expectedTreeOutput2[8].value := 'NULL'; + expectedTreeOutput2[8].depth := SINT#16#0; + expectedTreeOutput2[8].closingElement := TRUE; + END_METHOD + //Test1 {Test} - METHOD PUBLIC TestJsonDeserializer + METHOD PUBLIC TestJsonDeserializer VAR i : INT; END_VAR; - TimerInst.SetTimerValue := FALSE; + // Disable timeout timer for the test + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + jsonDeserializer.execute := TRUE; WHILE (TRUE) DO - jsonDeserializer(raw := rawInput, tree := actualTreeOutput); + jsonDeserializer(raw := rawInput1, tree := actualTreeOutput); IF(jsonDeserializer.done = TRUE) THEN EXIT; END_IF; @@ -173,8 +240,142 @@ NAMESPACE LStreamTest AXUnit.Assert.Equal(actual := jsonDeserializer.busy, expected := FALSE); AXUnit.Assert.Equal(actual := jsonDeserializer.error, expected := FALSE); AXUnit.Assert.Equal(actual := jsonDeserializer.done, expected := TRUE); + AXUnit.Assert.Equal(actual := jsonDeserializer.status, expected := WORD#16#0000); + //Tests for block output content + Assert.LStreamTypeElementEqual(actualTreeOutput, expectedTreeOutput1); + END_METHOD + + //Test2 + {Test} + METHOD PUBLIC TestJsonDeserializer_EmptyBrackets + VAR + i : INT; + END_VAR; + // Disable timeout timer for the test + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + + jsonDeserializer2.execute := TRUE; + WHILE (TRUE) DO + jsonDeserializer2(raw := rawInput2, tree := actualTreeOutput2); + IF(jsonDeserializer2.done = TRUE) THEN + EXIT; + END_IF; + IF(jsonDeserializer2.error = TRUE) THEN + EXIT; + END_IF; + END_WHILE; + //Tests for block execution + AxUnit.Assert.Equal(actual := jsonDeserializer2.resultCount, expected := 9); + AXUnit.Assert.Equal(actual := jsonDeserializer2.busy, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer2.error, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer2.done, expected := TRUE); + AXUnit.Assert.Equal(actual := jsonDeserializer2.status, expected := WORD#16#0000); //Tests for block output content - Assert.LStreamTypeElementEqual(actualTreeOutput, expectedTreeOutput); + Assert.LStreamTypeElementEqual(actualTreeOutput2, expectedTreeOutput2); + END_METHOD + END_CLASS + + {TestFixture} + CLASS JsonDeserializerStatusTest + VAR + //Test1 (Test to try the LStream_JsonDeserializer.st by deserializing a JSON (inputString1 & inputString2) and comparing its output with the expected tree (expectedTreeOutput1)) + jsonDeserializer : LStream_JsonDeserializer; + jsonDeserializerEmpty : LStream_JsonDeserializer; + + // Test Error when given input tree is empty + rawInputEmpty : ARRAY[0..5] OF BYTE; + // Test Error when given output tree is empty + rawInputTreeTooSmall : ARRAY[0..10000] OF BYTE; + + actualTreeOutput : ARRAY[0..0] OF LStream_typeElement; + actualTreeOutputEmpty : ARRAY[0..0] OF LStream_typeElement; + inputStringSimple : STRING := '{"id": "A","name":"Too Small Test","year":"2026","sessionContext": {"sessionId":"154237171615905","locale": "en_US","persId": 0}'; + + END_VAR + + VAR CONSTANT + /// No job being currently processed + STATUS_NO_CALL : Word := WORD#16#7000; + /// Error: provided array to too small + ERR_TREE_ARRAY_TOO_SMALL : Word := WORD#16#8201; + /// Error: no raw data provided + ERR_EMPTY_RAW_DATA : Word := WORD#16#8401; + /// Error: due to an undefined state in state machine + ERR_UNDEFINED_STATE : Word := WORD#16#8600; + END_VAR + + + {TestSetup} + METHOD PUBLIC TestSetup + jsonDeserializer := jsonDeserializerEmpty; + actualTreeOutput := actualTreeOutputEmpty; + END_METHOD + + {Test} + METHOD PUBLIC TestJsonDeserializer_ErrorEmptyRawData + // Disable timeout timer for deterministic parsing progress + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + + jsonDeserializer.execute := FALSE; + jsonDeserializer(raw := rawInputEmpty, tree := actualTreeOutput); + + jsonDeserializer.execute := TRUE; + WHILE (TRUE) DO + jsonDeserializer(raw := rawInputEmpty, tree := actualTreeOutput); + IF(jsonDeserializer.done = TRUE) THEN + EXIT; + END_IF; + IF(jsonDeserializer.error = TRUE) THEN + EXIT; + END_IF; + END_WHILE; + + AXUnit.Assert.Equal(actual := jsonDeserializer.done, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.error, expected := TRUE); + AXUnit.Assert.Equal(actual := jsonDeserializer.busy, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.status, expected := ERR_EMPTY_RAW_DATA); + + // Check if error reset + jsonDeserializer(execute := FALSE, raw := rawInputEmpty, tree := actualTreeOutput); + AXUnit.Assert.Equal(actual := jsonDeserializer.done, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.error, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.busy, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.status, expected := STATUS_NO_CALL); + END_METHOD + + {Test} + METHOD PUBLIC TestJsonDeserializer_ErrorTreeTooSmall + // Disable timeout timer for deterministic parsing progress + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + + // StringToByteArray(string1 := inputStringSimple, byteArray := rawInputTreeTooSmall); + System.Serialization.Serialize(offset := 0, value := inputStringSimple, buffer := rawInputTreeTooSmall); + + jsonDeserializer.execute := FALSE; + jsonDeserializer(raw := rawInputTreeTooSmall, tree := actualTreeOutput); + + jsonDeserializer.execute := TRUE; + WHILE (TRUE) DO + jsonDeserializer(raw := rawInputTreeTooSmall, tree := actualTreeOutput); + IF(jsonDeserializer.done = TRUE) THEN + EXIT; + END_IF; + IF(jsonDeserializer.error = TRUE) THEN + EXIT; + END_IF; + END_WHILE; + + AXUnit.Assert.Equal(actual := jsonDeserializer.done, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.error, expected := TRUE); + AXUnit.Assert.Equal(actual := jsonDeserializer.busy, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.status, expected := ERR_TREE_ARRAY_TOO_SMALL); + + // Check if error reset + jsonDeserializer(execute := FALSE, raw := rawInputTreeTooSmall, tree := actualTreeOutput); + AXUnit.Assert.Equal(actual := jsonDeserializer.done, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.error, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.busy, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonDeserializer.status, expected := STATUS_NO_CALL); END_METHOD END_CLASS END_NAMESPACE diff --git a/test/JsonSerializerTests.st b/test/JsonSerializerTests.st index 2214798..69a5718 100644 --- a/test/JsonSerializerTests.st +++ b/test/JsonSerializerTests.st @@ -1,5 +1,4 @@ USING Simatic.Ax; -USING Simatic.Ax.Timer; USING AxUnit; USING Simatic.Ax.LStream; USING Simatic.Ax.LStream.Utilities; @@ -12,7 +11,8 @@ NAMESPACE LStreamTest {TestFixture} CLASS JsonSerializerTests VAR - timerInst : TimerFunctionsMock; + + //Test1 (Test to try the LStream_JsonSerializer.st by serializing a tree (treeInput) and comparing its output with the expected byte array (expectedJsonByteOutputResult)) jsonSerializer : LStream_JsonSerializer; jsonByteOutputResult : ARRAY[0..600] OF BYTE; treeInput : ARRAY[0..200] OF LStream_typeElement; @@ -43,15 +43,36 @@ NAMESPACE LStreamTest BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0 ]; + + + //Test2 (Test to try the LStream_JsonSerializer.st when the input tree (treeInput2) has empty arrays (treeInput2[8] & treeInput2[3]) nodes (Coming from a JSON with empty brackets [] in its structure)) + jsonSerializer2 : LStream_JsonSerializer; + jsonByteOutputResult2 : ARRAY[0..600] OF BYTE; + treeInput2 : ARRAY[0..200] OF LStream_typeElement; + expectedJsonByteOutputResult2 : ARRAY[0..119] OF BYTE := [ + BYTE#16#7B, BYTE#16#22, BYTE#16#69, BYTE#16#64, BYTE#16#22, BYTE#16#3A, BYTE#16#22, BYTE#16#41, BYTE#16#22, BYTE#16#2C, + BYTE#16#22, BYTE#16#6E, BYTE#16#6F, BYTE#16#64, BYTE#16#65, BYTE#16#73, BYTE#16#22, BYTE#16#3A, BYTE#16#5B, BYTE#16#7B, + BYTE#16#22, BYTE#16#6E, BYTE#16#6F, BYTE#16#64, BYTE#16#65, BYTE#16#49, BYTE#16#64, BYTE#16#22, BYTE#16#3A, BYTE#16#22, + BYTE#16#43, BYTE#16#41, BYTE#16#54, BYTE#16#22, BYTE#16#2C, BYTE#16#22, BYTE#16#69, BYTE#16#74, BYTE#16#65, BYTE#16#6D, + BYTE#16#73, BYTE#16#22, BYTE#16#3A, BYTE#16#5B, BYTE#16#5D, BYTE#16#2C, BYTE#16#22, BYTE#16#73, BYTE#16#65, BYTE#16#71, + BYTE#16#75, BYTE#16#65, BYTE#16#6E, BYTE#16#63, BYTE#16#65, BYTE#16#49, BYTE#16#64, BYTE#16#22, BYTE#16#3A, BYTE#16#22, + BYTE#16#41, BYTE#16#22, BYTE#16#2C, BYTE#16#22, BYTE#16#72, BYTE#16#65, BYTE#16#6C, BYTE#16#65, BYTE#16#61, BYTE#16#73, + BYTE#16#65, BYTE#16#64, BYTE#16#22, BYTE#16#3A, BYTE#16#22, BYTE#16#57, BYTE#16#4F, BYTE#16#52, BYTE#16#44, BYTE#16#22, + BYTE#16#2C, BYTE#16#22, BYTE#16#6E, BYTE#16#6F, BYTE#16#64, BYTE#16#65, BYTE#16#50, BYTE#16#6F, BYTE#16#73, BYTE#16#69, + BYTE#16#74, BYTE#16#69, BYTE#16#6F, BYTE#16#6E, BYTE#16#22, BYTE#16#3A, BYTE#16#7B, BYTE#16#22, BYTE#16#78, BYTE#16#22, + BYTE#16#3A, BYTE#16#22, BYTE#16#31, BYTE#16#22, BYTE#16#7D, BYTE#16#7D, BYTE#16#5D, BYTE#16#2C, BYTE#16#22, BYTE#16#65, + BYTE#16#64, BYTE#16#67, BYTE#16#65, BYTE#16#73, BYTE#16#22, BYTE#16#3A, BYTE#16#5B, BYTE#16#5D, BYTE#16#7D, BYTE#16#00 +]; + END_VAR - {TestSetup} + {FixtureSetup} METHOD PUBLIC TestSetup - jsonSerializer.TimeoutTimer := timerInst; + //Test1 Setup //Populate expected LStreamTypeElementArray - // Populate expectedTreeOutput - treeInput[0].types := SINT#16#0; + //Populate expectedTreeOutput + treeInput[0].types := SINT#16#0; treeInput[0].key := 'sessionContext'; treeInput[0].value := 'NULL'; treeInput[0].depth := SINT#16#0; @@ -170,13 +191,75 @@ NAMESPACE LStreamTest treeInput[19].value := '0'; treeInput[19].depth := SINT#16#04; treeInput[19].closingElement := true; + + + //Test2 Setup + //Populate expected LStreamTypeElementArray + //Populate expectedTreeOutput + treeInput2[0].types := SINT#16#02; + treeInput2[0].key := 'id'; + treeInput2[0].value := 'A'; + treeInput2[0].depth := SINT#16#0; + treeInput2[0].closingElement := FALSE; + + treeInput2[1].types := SINT#16#01; + treeInput2[1].key := 'nodes'; + treeInput2[1].value := 'NULL'; + treeInput2[1].depth := SINT#16#0; + treeInput2[1].closingElement := FALSE; + + treeInput2[2].types := SINT#16#02; + treeInput2[2].key := 'nodeId'; + treeInput2[2].value := 'CAT'; + treeInput2[2].depth := SINT#16#02; + treeInput2[2].closingElement := FALSE; + + treeInput2[3].types := SINT#16#01; + treeInput2[3].key := 'items'; + treeInput2[3].value := 'NULL'; + treeInput2[3].depth := SINT#16#02; + treeInput2[3].closingElement := FALSE; + + treeInput2[4].types := SINT#16#02; + treeInput2[4].key := 'sequenceId'; + treeInput2[4].value := 'A'; + treeInput2[4].depth := SINT#16#02; + treeInput2[4].closingElement := FALSE; + + treeInput2[5].types := SINT#16#02; + treeInput2[5].key := 'released'; + treeInput2[5].value := 'WORD'; + treeInput2[5].depth := SINT#16#02; + treeInput2[5].closingElement := FALSE; + + treeInput2[6].types := SINT#16#0; + treeInput2[6].key := 'nodePosition'; + treeInput2[6].value := 'NULL'; + treeInput2[6].depth := SINT#16#02; + treeInput2[6].closingElement := FALSE; + + treeInput2[7].types := SINT#16#02; + treeInput2[7].key := 'x'; + treeInput2[7].value := '1'; + treeInput2[7].depth := SINT#16#03; + treeInput2[7].closingElement := TRUE; + + treeInput2[8].types := SINT#16#01; + treeInput2[8].key := 'edges'; + treeInput2[8].value := 'NULL'; + treeInput2[8].depth := SINT#16#0; + treeInput2[8].closingElement := TRUE; END_METHOD + + //Test1 {Test} METHOD PUBLIC TestJsonSerializer VAR i : INT; END_VAR; - timerInst.SetTimerValue := FALSE; + // Disable timeout timer for the test + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + jsonSerializer.execute := TRUE; WHILE (TRUE) DO jsonSerializer(tree := treeInput, jsonByteArray := jsonByteOutputResult); @@ -195,8 +278,42 @@ NAMESPACE LStreamTest //Test Output Content FOR i := 0 TO 400 DO AxUnit.Assert.Equal(jsonByteOutputResult[i], expectedJsonByteOutputResult[i]); + + END_FOR; + END_METHOD + + + + + //Test2 + {Test} + METHOD PUBLIC TestJsonSerializer_EmptyBrackets + VAR + i : INT; + END_VAR; + // Disable timeout timer for the test + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + + jsonSerializer2.execute := TRUE; + WHILE (TRUE) DO + jsonSerializer2(tree := treeInput2, jsonByteArray := jsonByteOutputResult2); + IF(jsonSerializer2.done = TRUE) THEN + EXIT; + END_IF; + IF(jsonSerializer2.error = TRUE) THEN + EXIT; + END_IF; + END_WHILE; + //Test block execution + AxUnit.Assert.Equal(actual := jsonSerializer2.count, expected := UINT#119); + AXUnit.Assert.Equal(actual := jsonSerializer2.busy, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonSerializer2.error, expected := FALSE); + AXUnit.Assert.Equal(actual := jsonSerializer2.done, expected := TRUE); + //Test Output Content + FOR i := 0 TO 119 DO + AxUnit.Assert.Equal(jsonByteOutputResult2[i], expectedJsonByteOutputResult2[i]); + END_FOR; END_METHOD END_CLASS - END_NAMESPACE \ No newline at end of file diff --git a/test/XmlDeserializerTests.st b/test/XmlDeserializerTests.st index 6ebf4e2..a95aedf 100644 --- a/test/XmlDeserializerTests.st +++ b/test/XmlDeserializerTests.st @@ -1,5 +1,4 @@ USING Simatic; -USING Simatic.Ax.Timer; USING AxUnit; USING Simatic.Ax.LStream; USING Simatic.Ax.LStream.Utilities; @@ -12,165 +11,229 @@ NAMESPACE LStreamTest {TestFixture} CLASS XmlDeserializerTests VAR + //Test1 (Test to try the LStream_XmlDeserializer.st by deserializing XML (rawInput1, rawInput2, rawInput3) and comparing its output with the expected tree (expectedTreeOutput1)) xmlDeserializer : LStream_XmlDeserializer; rawInput : ARRAY[0..10000] OF BYTE; actualTreeOutput : ARRAY[0..200] OF LStream_typeElement; - expectedTreeOutput : ARRAY[0..200] OF LStream_typeElement; - timerInst : TimerFunctionsMock; - rawInput1 : STRING := 'The Great GatsbyF. Scott Fitzgerald192510.99'; - rawInput2 : STRING := 'Learning XMLErik T. Ray200339.95'; + expectedTreeOutput1 : ARRAY[0..200] OF LStream_typeElement; + + rawInput1 : STRING := 'The Great GatsbyF. Scott Fitzgerald1925'; + rawInput2 : STRING := '10.99Learning XMLErik T. Ray200339.95'; rawInput3 : STRING := 'Harry Potter and the Philosophers StoneJ.K. Rowling199720.00'; + + //Test2 (Test to try the LStream_XmlDeserializer.st when the input XML has spaces before the equals sign in attributes (category = "Science Fiction" or lang ="en"). + //The deserialize output tree is compared with the expected tree (expectedTreeOutput2)) + xmlDeserializer2 : LStream_XmlDeserializer; + rawInput4 : STRING := 'The Great GatsbyF. Scott Fitzgerald192510.99'; + rawInput_2 : ARRAY[0..10000] OF BYTE; + actualTreeOutput2 : ARRAY[0..200] OF LStream_typeElement; + expectedTreeOutput2 : ARRAY[0..200] OF LStream_typeElement; + END_VAR {TestSetup} - METHOD PUBLIC TestSetup - //Init timer and expected outputs - xmlDeserializer.TimeoutTimer := timerInst; - expectedTreeOutput[0].types := SINT#16#0; - expectedTreeOutput[0].key := 'bookstore'; - expectedTreeOutput[0].value := 'NULL'; - expectedTreeOutput[0].depth := SINT#16#01; - expectedTreeOutput[0].closingElement := FALSE; - - expectedTreeOutput[1].types := SINT#16#0; - expectedTreeOutput[1].key := 'book'; - expectedTreeOutput[1].value := 'NULL'; - expectedTreeOutput[1].depth := SINT#16#02; - expectedTreeOutput[1].closingElement := FALSE; - - expectedTreeOutput[2].types := SINT#16#01; - expectedTreeOutput[2].key := 'category'; - expectedTreeOutput[2].value := 'Fiction'; - expectedTreeOutput[2].depth := SINT#16#02; - expectedTreeOutput[2].closingElement := FALSE; - - expectedTreeOutput[3].types := SINT#16#0; - expectedTreeOutput[3].key := 'title'; - expectedTreeOutput[3].value := 'The Great Gatsby'; - expectedTreeOutput[3].depth := SINT#16#03; - expectedTreeOutput[3].closingElement := FALSE; - - expectedTreeOutput[4].types := SINT#16#01; - expectedTreeOutput[4].key := 'lang'; - expectedTreeOutput[4].value := 'en'; - expectedTreeOutput[4].depth := SINT#16#03; - expectedTreeOutput[4].closingElement := FALSE; - - expectedTreeOutput[5].types := SINT#16#0; - expectedTreeOutput[5].key := 'author'; - expectedTreeOutput[5].value := 'F. Scott Fitzgerald'; - expectedTreeOutput[5].depth := SINT#16#03; - expectedTreeOutput[5].closingElement := false; - - expectedTreeOutput[6].types := SINT#16#0; - expectedTreeOutput[6].key := 'year'; - expectedTreeOutput[6].value := '1925'; - expectedTreeOutput[6].depth := SINT#16#03; - expectedTreeOutput[6].closingElement := false; - - expectedTreeOutput[7].types := SINT#16#0; - expectedTreeOutput[7].key := 'price'; - expectedTreeOutput[7].value := '10.99'; - expectedTreeOutput[7].depth := SINT#16#03; - expectedTreeOutput[7].closingElement := false; - - expectedTreeOutput[8].types := SINT#16#0; - expectedTreeOutput[8].key := 'book'; - expectedTreeOutput[8].value := 'NULL'; - expectedTreeOutput[8].depth := SINT#16#03; - expectedTreeOutput[8].closingElement := false; - - expectedTreeOutput[9].types := SINT#16#01; - expectedTreeOutput[9].key := 'category'; - expectedTreeOutput[9].value := 'Programming'; - expectedTreeOutput[9].depth := SINT#16#03; - expectedTreeOutput[9].closingElement := false; - - expectedTreeOutput[10].types := SINT#16#0; - expectedTreeOutput[10].key := 'title'; - expectedTreeOutput[10].value := 'Learning XML'; - expectedTreeOutput[10].depth := SINT#16#04; - expectedTreeOutput[10].closingElement := false; - - expectedTreeOutput[11].types := SINT#16#01; - expectedTreeOutput[11].key := 'lang'; - expectedTreeOutput[11].value := 'en'; - expectedTreeOutput[11].depth := SINT#16#04; - expectedTreeOutput[11].closingElement := FALSE; - - expectedTreeOutput[12].types := SINT#16#0; - expectedTreeOutput[12].key := 'author'; - expectedTreeOutput[12].value := 'Erik T. Ray'; - expectedTreeOutput[12].depth := SINT#16#04; - expectedTreeOutput[12].closingElement := false; - - expectedTreeOutput[13].types := SINT#16#0; - expectedTreeOutput[13].key := 'year'; - expectedTreeOutput[13].value := '2003'; - expectedTreeOutput[13].depth := SINT#16#04; - expectedTreeOutput[13].closingElement := false; - - expectedTreeOutput[14].types := SINT#16#0; - expectedTreeOutput[14].key := 'price'; - expectedTreeOutput[14].value := '39.95'; - expectedTreeOutput[14].depth := SINT#16#04; - expectedTreeOutput[14].closingElement := false; - - expectedTreeOutput[15].types := SINT#16#0; - expectedTreeOutput[15].key := 'book'; - expectedTreeOutput[15].value := 'NULL'; - expectedTreeOutput[15].depth := SINT#16#03; - expectedTreeOutput[15].closingElement := false; - - expectedTreeOutput[16].types := SINT#16#01; - expectedTreeOutput[16].key := 'category'; - expectedTreeOutput[16].value := 'Fantasy'; - expectedTreeOutput[16].depth := SINT#16#03; - expectedTreeOutput[16].closingElement := FALSE; - - expectedTreeOutput[17].types := SINT#16#0; - expectedTreeOutput[17].key := 'title'; - expectedTreeOutput[17].value := 'Harry Potter and the Philosophers Stone'; - expectedTreeOutput[17].depth := SINT#16#04; - expectedTreeOutput[17].closingElement := FALSE; - - expectedTreeOutput[18].types := SINT#16#01; - expectedTreeOutput[18].key := 'lang'; - expectedTreeOutput[18].value := 'en'; - expectedTreeOutput[18].depth := SINT#16#04; - expectedTreeOutput[18].closingElement := FALSE; - - expectedTreeOutput[19].types := SINT#16#0; - expectedTreeOutput[19].key := 'author'; - expectedTreeOutput[19].value := 'J.K. Rowling'; - expectedTreeOutput[19].depth := SINT#16#04; - expectedTreeOutput[19].closingElement := FALSE; - - expectedTreeOutput[20].types := SINT#16#0; - expectedTreeOutput[20].key := 'year'; - expectedTreeOutput[20].value := '1997'; - expectedTreeOutput[20].depth := SINT#16#04; - expectedTreeOutput[20].closingElement := false; - - expectedTreeOutput[21].types := SINT#16#0; - expectedTreeOutput[21].key := 'price'; - expectedTreeOutput[21].value := '20.00'; - expectedTreeOutput[21].depth := SINT#16#04; - expectedTreeOutput[21].closingElement := false; + METHOD PUBLIC TestSetup + // Disable timeout timer for all tests + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + //Init timer and expected outputs + expectedTreeOutput1[0].types := SINT#16#0; + expectedTreeOutput1[0].key := 'bookstore'; + expectedTreeOutput1[0].value := 'NULL'; + expectedTreeOutput1[0].depth := SINT#16#01; + expectedTreeOutput1[0].closingElement := FALSE; + + expectedTreeOutput1[1].types := SINT#16#0; + expectedTreeOutput1[1].key := 'book'; + expectedTreeOutput1[1].value := 'NULL'; + expectedTreeOutput1[1].depth := SINT#16#02; + expectedTreeOutput1[1].closingElement := FALSE; + + expectedTreeOutput1[2].types := SINT#16#01; + expectedTreeOutput1[2].key := 'category'; + expectedTreeOutput1[2].value := 'Fiction'; + expectedTreeOutput1[2].depth := SINT#16#02; + expectedTreeOutput1[2].closingElement := FALSE; + + expectedTreeOutput1[3].types := SINT#16#0; + expectedTreeOutput1[3].key := 'title'; + expectedTreeOutput1[3].value := 'The Great Gatsby'; + expectedTreeOutput1[3].depth := SINT#16#03; + expectedTreeOutput1[3].closingElement := FALSE; + + expectedTreeOutput1[4].types := SINT#16#01; + expectedTreeOutput1[4].key := 'lang'; + expectedTreeOutput1[4].value := 'en'; + expectedTreeOutput1[4].depth := SINT#16#03; + expectedTreeOutput1[4].closingElement := FALSE; + + expectedTreeOutput1[5].types := SINT#16#0; + expectedTreeOutput1[5].key := 'author'; + expectedTreeOutput1[5].value := 'F. Scott Fitzgerald'; + expectedTreeOutput1[5].depth := SINT#16#03; + expectedTreeOutput1[5].closingElement := false; + + expectedTreeOutput1[6].types := SINT#16#0; + expectedTreeOutput1[6].key := 'year'; + expectedTreeOutput1[6].value := '1925'; + expectedTreeOutput1[6].depth := SINT#16#03; + expectedTreeOutput1[6].closingElement := false; + + expectedTreeOutput1[7].types := SINT#16#0; + expectedTreeOutput1[7].key := 'price'; + expectedTreeOutput1[7].value := '10.99'; + expectedTreeOutput1[7].depth := SINT#16#03; + expectedTreeOutput1[7].closingElement := false; + + expectedTreeOutput1[8].types := SINT#16#0; + expectedTreeOutput1[8].key := 'book'; + expectedTreeOutput1[8].value := 'NULL'; + expectedTreeOutput1[8].depth := SINT#16#02; + expectedTreeOutput1[8].closingElement := false; + + expectedTreeOutput1[9].types := SINT#16#01; + expectedTreeOutput1[9].key := 'category'; + expectedTreeOutput1[9].value := 'Programming'; + expectedTreeOutput1[9].depth := SINT#16#02; + expectedTreeOutput1[9].closingElement := false; + + expectedTreeOutput1[10].types := SINT#16#0; + expectedTreeOutput1[10].key := 'title'; + expectedTreeOutput1[10].value := 'Learning XML'; + expectedTreeOutput1[10].depth := SINT#16#03; + expectedTreeOutput1[10].closingElement := false; + + expectedTreeOutput1[11].types := SINT#16#01; + expectedTreeOutput1[11].key := 'lang'; + expectedTreeOutput1[11].value := 'en'; + expectedTreeOutput1[11].depth := SINT#16#03; + expectedTreeOutput1[11].closingElement := FALSE; + + expectedTreeOutput1[12].types := SINT#16#0; + expectedTreeOutput1[12].key := 'author'; + expectedTreeOutput1[12].value := 'Erik T. Ray'; + expectedTreeOutput1[12].depth := SINT#16#03; + expectedTreeOutput1[12].closingElement := false; + + expectedTreeOutput1[13].types := SINT#16#0; + expectedTreeOutput1[13].key := 'year'; + expectedTreeOutput1[13].value := '2003'; + expectedTreeOutput1[13].depth := SINT#16#03; + expectedTreeOutput1[13].closingElement := false; + + expectedTreeOutput1[14].types := SINT#16#0; + expectedTreeOutput1[14].key := 'price'; + expectedTreeOutput1[14].value := '39.95'; + expectedTreeOutput1[14].depth := SINT#16#03; + expectedTreeOutput1[14].closingElement := false; + + expectedTreeOutput1[15].types := SINT#16#0; + expectedTreeOutput1[15].key := 'book'; + expectedTreeOutput1[15].value := 'NULL'; + expectedTreeOutput1[15].depth := SINT#16#02; + expectedTreeOutput1[15].closingElement := false; + + expectedTreeOutput1[16].types := SINT#16#01; + expectedTreeOutput1[16].key := 'category'; + expectedTreeOutput1[16].value := 'Fantasy'; + expectedTreeOutput1[16].depth := SINT#16#02; + expectedTreeOutput1[16].closingElement := FALSE; + + expectedTreeOutput1[17].types := SINT#16#0; + expectedTreeOutput1[17].key := 'title'; + expectedTreeOutput1[17].value := 'Harry Potter and the Philosophers Stone'; + expectedTreeOutput1[17].depth := SINT#16#03; + expectedTreeOutput1[17].closingElement := FALSE; + + expectedTreeOutput1[18].types := SINT#16#01; + expectedTreeOutput1[18].key := 'lang'; + expectedTreeOutput1[18].value := 'en'; + expectedTreeOutput1[18].depth := SINT#16#03; + expectedTreeOutput1[18].closingElement := FALSE; + + expectedTreeOutput1[19].types := SINT#16#0; + expectedTreeOutput1[19].key := 'author'; + expectedTreeOutput1[19].value := 'J.K. Rowling'; + expectedTreeOutput1[19].depth := SINT#16#03; + expectedTreeOutput1[19].closingElement := FALSE; + + expectedTreeOutput1[20].types := SINT#16#0; + expectedTreeOutput1[20].key := 'year'; + expectedTreeOutput1[20].value := '1997'; + expectedTreeOutput1[20].depth := SINT#16#03; + expectedTreeOutput1[20].closingElement := false; + + expectedTreeOutput1[21].types := SINT#16#0; + expectedTreeOutput1[21].key := 'price'; + expectedTreeOutput1[21].value := '20.00'; + expectedTreeOutput1[21].depth := SINT#16#03; + expectedTreeOutput1[21].closingElement := false; + + //Test2 Setup + + expectedTreeOutput2[0].types := SINT#16#0; + expectedTreeOutput2[0].key := 'bookstore'; + expectedTreeOutput2[0].value := 'NULL'; + expectedTreeOutput2[0].depth := SINT#16#01; + expectedTreeOutput2[0].closingElement := FALSE; + + expectedTreeOutput2[1].types := SINT#16#0; + expectedTreeOutput2[1].key := 'book'; + expectedTreeOutput2[1].value := 'NULL'; + expectedTreeOutput2[1].depth := SINT#16#02; + expectedTreeOutput2[1].closingElement := FALSE; + + expectedTreeOutput2[2].types := SINT#16#01; + expectedTreeOutput2[2].key := 'category'; + expectedTreeOutput2[2].value := 'Science Fiction'; + expectedTreeOutput2[2].depth := SINT#16#02; + expectedTreeOutput2[2].closingElement := FALSE; + + expectedTreeOutput2[3].types := SINT#16#01; + expectedTreeOutput2[3].key := 'lang'; + expectedTreeOutput2[3].value := 'en'; + expectedTreeOutput2[3].depth := SINT#16#02; + expectedTreeOutput2[3].closingElement := FALSE; + + expectedTreeOutput2[4].types := SINT#16#0; + expectedTreeOutput2[4].key := 'title'; + expectedTreeOutput2[4].value := 'The Great Gatsby'; + expectedTreeOutput2[4].depth := SINT#16#03; + expectedTreeOutput2[4].closingElement := FALSE; + + expectedTreeOutput2[5].types := SINT#16#0; + expectedTreeOutput2[5].key := 'author'; + expectedTreeOutput2[5].value := 'F. Scott Fitzgerald'; + expectedTreeOutput2[5].depth := SINT#16#03; + expectedTreeOutput2[5].closingElement := FALSE; + + expectedTreeOutput2[6].types := SINT#16#0; + expectedTreeOutput2[6].key := 'year'; + expectedTreeOutput2[6].value := '1925'; + expectedTreeOutput2[6].depth := SINT#16#03; + expectedTreeOutput2[6].closingElement := FALSE; + + expectedTreeOutput2[7].types := SINT#16#0; + expectedTreeOutput2[7].key := 'price'; + expectedTreeOutput2[7].value := '10.99'; + expectedTreeOutput2[7].depth := SINT#16#03; + expectedTreeOutput2[7].closingElement := FALSE; END_METHOD + //Test1 {Test} METHOD PUBLIC TestXmlDeserializer VAR i : INT; END_VAR; - timerInst.SetTimerValue := FALSE; + // Disable timeout timer for the test + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + xmlDeserializer.search := FALSE; xmlDeserializer.execute := TRUE; StringToByteArray(string1 := rawInput1, string2 := rawInput2, string3 := rawInput3, byteArray := rawInput); WHILE (TRUE) DO xmlDeserializer(raw := rawInput, tree := actualTreeOutput); - // Copy data from the fixed-size byte array to jsonDeserializer.raw + // Copy data from the fixed-size byte array to xmlDeserializer.raw IF(xmlDeserializer.done = TRUE) THEN EXIT; END_IF; @@ -183,8 +246,36 @@ NAMESPACE LStreamTest AXUnit.Assert.Equal(actual := xmlDeserializer.busy, expected := FALSE); AXUnit.Assert.Equal(actual := xmlDeserializer.error, expected := FALSE); AXUnit.Assert.Equal(actual := xmlDeserializer.done, expected := TRUE); - //Test Blopck Content - Assert.LStreamTypeElementEqual(actualTreeOutput,expectedTreeOutput); + //Test Block Content + Assert.LStreamTypeElementEqual(actualTreeOutput,expectedTreeOutput1); + END_METHOD + + //Test2 + {Test} + METHOD PUBLIC TestXmlDeserializer_Empty_Spaces + // Disable timeout timer for the test + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + + xmlDeserializer2.search := FALSE; + xmlDeserializer2.execute := TRUE; + + StringToByteArray(string1 := rawInput4, byteArray := rawInput_2); + + WHILE (TRUE) DO + xmlDeserializer2(raw := rawInput_2, tree := actualTreeOutput2); + IF (xmlDeserializer2.done = TRUE) THEN + EXIT; + END_IF; + IF (xmlDeserializer2.error = TRUE) THEN + EXIT; + END_IF; + END_WHILE; + + AxUnit.Assert.Equal(actual := xmlDeserializer2.resultCount, expected := 8); + AxUnit.Assert.Equal(actual := xmlDeserializer2.busy, expected := FALSE); + AxUnit.Assert.Equal(actual := xmlDeserializer2.error, expected := FALSE); + AxUnit.Assert.Equal(actual := xmlDeserializer2.done, expected := TRUE); + Assert.LStreamTypeElementEqual(actualTreeOutput2, expectedTreeOutput2); END_METHOD END_CLASS END_NAMESPACE \ No newline at end of file diff --git a/test/XmlSerializerTests.st b/test/XmlSerializerTests.st index 33a1290..11b86b7 100644 --- a/test/XmlSerializerTests.st +++ b/test/XmlSerializerTests.st @@ -1,5 +1,4 @@ USING Simatic; -USING Simatic.Ax.Timer; USING AxUnit; USING Simatic.Ax.LStream; USING Simatic.Ax.LStream.Utilities; @@ -12,20 +11,77 @@ NAMESPACE LStreamTest {TestFixture} CLASS XmlSerializerTests VAR + + //Test1 (Test to try the LStream_XmlSerializer.st by serializing a tree (treeInput) and comparing its output (actualXmlByteArrayOutput) with the expected byte array (expectedXmlByteOutputResult)) xmlSerializer : LStream_XmlSerializer; - TimerInst : TimerFunctionsMock; treeInput : ARRAY[0..200] OF LStream_typeElement; - actualXmlByteArrayOutput : ARRAY[0..400] OF BYTE; - expectedXmlByteOutputResult : ARRAY[0..400] OF BYTE := [ - BYTE#16#3C, BYTE#16#3F, BYTE#16#78, BYTE#16#6D, BYTE#16#6C, BYTE#16#20, BYTE#16#76, BYTE#16#65, BYTE#16#72, BYTE#16#73, BYTE#16#69, BYTE#16#6F, BYTE#16#6E, BYTE#16#3D, BYTE#16#22, BYTE#16#31, BYTE#16#2E, BYTE#16#30, BYTE#16#22, BYTE#16#20, BYTE#16#65, BYTE#16#6E, BYTE#16#63, BYTE#16#6F, BYTE#16#64, BYTE#16#69, BYTE#16#6E, BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#41, BYTE#16#53, BYTE#16#43, BYTE#16#49, BYTE#16#49, BYTE#16#22, BYTE#16#20, BYTE#16#3F, BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#73, BYTE#16#74, BYTE#16#6F, BYTE#16#72, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#20, BYTE#16#63, BYTE#16#61, BYTE#16#74, BYTE#16#65, BYTE#16#67, BYTE#16#6F, BYTE#16#72, BYTE#16#79, BYTE#16#3D, BYTE#16#22, BYTE#16#46, BYTE#16#69, BYTE#16#63, BYTE#16#74, BYTE#16#69, BYTE#16#6F, BYTE#16#6E, BYTE#16#22, BYTE#16#3E, BYTE#16#3C, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#20, BYTE#16#6C, BYTE#16#61, BYTE#16#6E, BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#65, BYTE#16#6E, BYTE#16#22, BYTE#16#3C, BYTE#16#2F, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3C, BYTE#16#2F, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3C, BYTE#16#2F, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3C, BYTE#16#2F, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#20, BYTE#16#63, BYTE#16#61, BYTE#16#74, BYTE#16#65, BYTE#16#67, BYTE#16#6F, BYTE#16#72, BYTE#16#79, BYTE#16#3D, BYTE#16#22, BYTE#16#50, BYTE#16#72, BYTE#16#6F, BYTE#16#67, BYTE#16#72, BYTE#16#61, BYTE#16#6D, BYTE#16#6D, BYTE#16#69, BYTE#16#6E, BYTE#16#67, BYTE#16#22, BYTE#16#3E, BYTE#16#3C, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#20, BYTE#16#6C, BYTE#16#61, BYTE#16#6E, BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#65, BYTE#16#6E, BYTE#16#22, BYTE#16#3C, BYTE#16#2F, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3C, BYTE#16#2F, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3C, BYTE#16#2F, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3C, BYTE#16#2F, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#2F, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#20, BYTE#16#63, BYTE#16#61, BYTE#16#74, BYTE#16#65, BYTE#16#67, BYTE#16#6F, BYTE#16#72, BYTE#16#79, BYTE#16#3D, BYTE#16#22, BYTE#16#46, BYTE#16#61, BYTE#16#6E, BYTE#16#74, BYTE#16#61, BYTE#16#73, BYTE#16#79, BYTE#16#22, BYTE#16#3E, BYTE#16#3C, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#20, BYTE#16#6C, BYTE#16#61, BYTE#16#6E, BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#65, BYTE#16#6E, BYTE#16#22, BYTE#16#3C, BYTE#16#2F, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3C, BYTE#16#2F, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3C, BYTE#16#2F, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3C, BYTE#16#2F, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#2F, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#3E, BYTE#16#3C, BYTE#16#2F, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#3E, BYTE#16#3C, BYTE#16#2F, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#73, BYTE#16#74, BYTE#16#6F, BYTE#16#72, BYTE#16#65, BYTE#16#3E, - BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0 ,BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, - BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0 ,BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, - BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0 ,BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0, BYTE#16#0]; + actualXmlByteArrayOutput : ARRAY[0..700] OF BYTE; + expectedXmlByteOutputResult : ARRAY[0..507] OF BYTE := [ + BYTE#16#3C, BYTE#16#3F, BYTE#16#78, BYTE#16#6D, BYTE#16#6C, BYTE#16#20, BYTE#16#76, BYTE#16#65, BYTE#16#72, BYTE#16#73, BYTE#16#69, BYTE#16#6F, + BYTE#16#6E, BYTE#16#3D, BYTE#16#22, BYTE#16#31, BYTE#16#2E, BYTE#16#30, BYTE#16#22, BYTE#16#20, BYTE#16#65, BYTE#16#6E, BYTE#16#63, BYTE#16#6F, + BYTE#16#64, BYTE#16#69, BYTE#16#6E, BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#41, BYTE#16#53, BYTE#16#43, BYTE#16#49, BYTE#16#49, BYTE#16#22, + BYTE#16#3F, BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#73, BYTE#16#74, BYTE#16#6F, BYTE#16#72, BYTE#16#65, + BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#20, BYTE#16#63, BYTE#16#61, BYTE#16#74, BYTE#16#65, BYTE#16#67, + BYTE#16#6F, BYTE#16#72, BYTE#16#79, BYTE#16#3D, BYTE#16#22, BYTE#16#46, BYTE#16#69, BYTE#16#63, BYTE#16#74, BYTE#16#69, BYTE#16#6F, BYTE#16#6E, + BYTE#16#22, BYTE#16#3E, BYTE#16#3C, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#20, BYTE#16#6C, BYTE#16#61, BYTE#16#6E, + BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#65, BYTE#16#6E, BYTE#16#22, BYTE#16#3E, BYTE#16#54, BYTE#16#68, BYTE#16#65, BYTE#16#20, BYTE#16#47, + BYTE#16#72, BYTE#16#65, BYTE#16#61, BYTE#16#74, BYTE#16#20, BYTE#16#47, BYTE#16#61, BYTE#16#74, BYTE#16#73, BYTE#16#62, BYTE#16#79, BYTE#16#3C, + BYTE#16#2F, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, + BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#46, BYTE#16#2E, BYTE#16#20, BYTE#16#53, BYTE#16#63, BYTE#16#6F, BYTE#16#74, BYTE#16#74, BYTE#16#20, + BYTE#16#46, BYTE#16#69, BYTE#16#74, BYTE#16#7A, BYTE#16#67, BYTE#16#65, BYTE#16#72, BYTE#16#61, BYTE#16#6C, BYTE#16#64, BYTE#16#3C, BYTE#16#2F, + BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, + BYTE#16#3E, BYTE#16#31, BYTE#16#39, BYTE#16#32, BYTE#16#35, BYTE#16#3C, BYTE#16#2F, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3E, + BYTE#16#3C, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3E, BYTE#16#31, BYTE#16#30, BYTE#16#2E, BYTE#16#39, BYTE#16#39, + BYTE#16#3C, BYTE#16#2F, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#2F, BYTE#16#62, BYTE#16#6F, + BYTE#16#6F, BYTE#16#6B, BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#20, BYTE#16#63, BYTE#16#61, BYTE#16#74, + BYTE#16#65, BYTE#16#67, BYTE#16#6F, BYTE#16#72, BYTE#16#79, BYTE#16#3D, BYTE#16#22, BYTE#16#50, BYTE#16#72, BYTE#16#6F, BYTE#16#67, BYTE#16#72, + BYTE#16#61, BYTE#16#6D, BYTE#16#6D, BYTE#16#69, BYTE#16#6E, BYTE#16#67, BYTE#16#22, BYTE#16#3E, BYTE#16#3C, BYTE#16#74, BYTE#16#69, BYTE#16#74, + BYTE#16#6C, BYTE#16#65, BYTE#16#20, BYTE#16#6C, BYTE#16#61, BYTE#16#6E, BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#65, BYTE#16#6E, BYTE#16#22, + BYTE#16#3E, BYTE#16#4C, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#6E, BYTE#16#69, BYTE#16#6E, BYTE#16#67, BYTE#16#20, BYTE#16#58, BYTE#16#4D, + BYTE#16#4C, BYTE#16#3C, BYTE#16#2F, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#61, BYTE#16#75, + BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#45, BYTE#16#72, BYTE#16#69, BYTE#16#6B, BYTE#16#20, BYTE#16#54, BYTE#16#2E, + BYTE#16#20, BYTE#16#52, BYTE#16#61, BYTE#16#79, BYTE#16#3C, BYTE#16#2F, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, + BYTE#16#3E, BYTE#16#3C, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3E, BYTE#16#32, BYTE#16#30, BYTE#16#30, BYTE#16#33, BYTE#16#3C, + BYTE#16#2F, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, + BYTE#16#3E, BYTE#16#33, BYTE#16#39, BYTE#16#2E, BYTE#16#39, BYTE#16#35, BYTE#16#3C, BYTE#16#2F, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, + BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#2F, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, + BYTE#16#6F, BYTE#16#6B, BYTE#16#20, BYTE#16#63, BYTE#16#61, BYTE#16#74, BYTE#16#65, BYTE#16#67, BYTE#16#6F, BYTE#16#72, BYTE#16#79, BYTE#16#3D, + BYTE#16#22, BYTE#16#46, BYTE#16#61, BYTE#16#6E, BYTE#16#74, BYTE#16#61, BYTE#16#73, BYTE#16#79, BYTE#16#22, BYTE#16#3E, BYTE#16#3C, BYTE#16#74, + BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#20, BYTE#16#6C, BYTE#16#61, BYTE#16#6E, BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#65, + BYTE#16#6E, BYTE#16#22, BYTE#16#3E, BYTE#16#48, BYTE#16#61, BYTE#16#72, BYTE#16#72, BYTE#16#79, BYTE#16#20, BYTE#16#50, BYTE#16#6F, BYTE#16#74, + BYTE#16#74, BYTE#16#65, BYTE#16#72, BYTE#16#20, BYTE#16#61, BYTE#16#6E, BYTE#16#64, BYTE#16#20, BYTE#16#74, BYTE#16#68, BYTE#16#65, BYTE#16#20, + BYTE#16#50, BYTE#16#68, BYTE#16#69, BYTE#16#6C, BYTE#16#6F, BYTE#16#73, BYTE#16#6F, BYTE#16#70, BYTE#16#68, BYTE#16#65, BYTE#16#72, BYTE#16#73, + BYTE#16#20, BYTE#16#53, BYTE#16#74, BYTE#16#6F, BYTE#16#6E, BYTE#16#65, BYTE#16#3C, BYTE#16#2F, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, + BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#4A, BYTE#16#2E, + BYTE#16#4B, BYTE#16#2E, BYTE#16#20, BYTE#16#52, BYTE#16#6F, BYTE#16#77, BYTE#16#6C, BYTE#16#69, BYTE#16#6E, BYTE#16#67, BYTE#16#3C, BYTE#16#2F, + BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, + BYTE#16#3E, BYTE#16#31, BYTE#16#39, BYTE#16#39, BYTE#16#37, BYTE#16#3C, BYTE#16#2F, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3E, + BYTE#16#3C, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3E, BYTE#16#32, BYTE#16#30, BYTE#16#2E, BYTE#16#30, BYTE#16#30, + BYTE#16#3C, BYTE#16#2F, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#2F, BYTE#16#62, BYTE#16#6F, + BYTE#16#6F, BYTE#16#6B, BYTE#16#3E, BYTE#16#3C, BYTE#16#2F, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#73, BYTE#16#74, BYTE#16#6F, + BYTE#16#72, BYTE#16#65, BYTE#16#3E, BYTE#16#0]; + + + // Test 2 (Test to try the LStream_XmlSerializer.st when the input tree (treeInput2) contains empty nodes (either 'NULL' or ''). In this example, the empty nodes are treeInput2[5] and treeInput2[7]) + xmlSerializer2 : LStream_XmlSerializer; + treeInput2 : ARRAY[0..200] OF LStream_typeElement; + actualXmlByteArrayOutput2 : ARRAY[0..700] OF BYTE; + expectedXmlByteOutputResult2 : ARRAY[0..170] OF BYTE := [ + BYTE#16#3C, BYTE#16#3F, BYTE#16#78, BYTE#16#6D, BYTE#16#6C, BYTE#16#20, BYTE#16#76, BYTE#16#65, BYTE#16#72, BYTE#16#73, BYTE#16#69, BYTE#16#6F, BYTE#16#6E, BYTE#16#3D, BYTE#16#22, BYTE#16#31, + BYTE#16#2E, BYTE#16#30, BYTE#16#22, BYTE#16#20, BYTE#16#65, BYTE#16#6E, BYTE#16#63, BYTE#16#6F, BYTE#16#64, BYTE#16#69, BYTE#16#6E, BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#41, BYTE#16#53, + BYTE#16#43, BYTE#16#49, BYTE#16#49, BYTE#16#22, BYTE#16#3F, BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#73, BYTE#16#74, BYTE#16#6F, BYTE#16#72, BYTE#16#65, + BYTE#16#3E, BYTE#16#3C, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#20, BYTE#16#63, BYTE#16#61, BYTE#16#74, BYTE#16#65, BYTE#16#67, BYTE#16#6F, BYTE#16#72, BYTE#16#79, BYTE#16#3D, + BYTE#16#22, BYTE#16#46, BYTE#16#69, BYTE#16#63, BYTE#16#74, BYTE#16#69, BYTE#16#6F, BYTE#16#6E, BYTE#16#22, BYTE#16#3E, BYTE#16#3C, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, + BYTE#16#20, BYTE#16#6C, BYTE#16#61, BYTE#16#6E, BYTE#16#67, BYTE#16#3D, BYTE#16#22, BYTE#16#65, BYTE#16#6E, BYTE#16#22, BYTE#16#3E, BYTE#16#4A, BYTE#16#61, BYTE#16#77, BYTE#16#73, BYTE#16#3C, + BYTE#16#2F, BYTE#16#74, BYTE#16#69, BYTE#16#74, BYTE#16#6C, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, + BYTE#16#2F, BYTE#16#61, BYTE#16#75, BYTE#16#74, BYTE#16#68, BYTE#16#6F, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3E, BYTE#16#32, BYTE#16#30, + BYTE#16#30, BYTE#16#31, BYTE#16#3C, BYTE#16#2F, BYTE#16#79, BYTE#16#65, BYTE#16#61, BYTE#16#72, BYTE#16#3E, BYTE#16#3C, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3E, + BYTE#16#3C, BYTE#16#2F, BYTE#16#70, BYTE#16#72, BYTE#16#69, BYTE#16#63, BYTE#16#65, BYTE#16#3E, BYTE#16#3C, BYTE#16#2F, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#3E, BYTE#16#3C, + BYTE#16#2F, BYTE#16#62, BYTE#16#6F, BYTE#16#6F, BYTE#16#6B, BYTE#16#73, BYTE#16#74, BYTE#16#6F, BYTE#16#72, BYTE#16#65, BYTE#16#3E]; END_VAR - {TestSetup} + {FixtureSetup} METHOD PUBLIC TestSetup - xmlSerializer.TimeoutTimer := TimerInst; //Populate tree input treeInput[0].types := SINT#16#0; treeInput[0].key := 'bookstore'; @@ -78,87 +134,136 @@ CLASS XmlSerializerTests treeInput[8].types := SINT#16#0; treeInput[8].key := 'book'; treeInput[8].value := 'NULL'; - treeInput[8].depth := SINT#16#03; + treeInput[8].depth := SINT#16#02; treeInput[8].closingElement := false; treeInput[9].types := SINT#16#01; treeInput[9].key := 'category'; treeInput[9].value := 'Programming'; - treeInput[9].depth := SINT#16#03; + treeInput[9].depth := SINT#16#02; treeInput[9].closingElement := false; treeInput[10].types := SINT#16#0; treeInput[10].key := 'title'; treeInput[10].value := 'Learning XML'; - treeInput[10].depth := SINT#16#04; + treeInput[10].depth := SINT#16#03; treeInput[10].closingElement := false; treeInput[11].types := SINT#16#01; treeInput[11].key := 'lang'; treeInput[11].value := 'en'; - treeInput[11].depth := SINT#16#04; + treeInput[11].depth := SINT#16#03; treeInput[11].closingElement := FALSE; treeInput[12].types := SINT#16#0; treeInput[12].key := 'author'; treeInput[12].value := 'Erik T. Ray'; - treeInput[12].depth := SINT#16#04; + treeInput[12].depth := SINT#16#03; treeInput[12].closingElement := false; treeInput[13].types := SINT#16#0; treeInput[13].key := 'year'; treeInput[13].value := '2003'; - treeInput[13].depth := SINT#16#04; + treeInput[13].depth := SINT#16#03; treeInput[13].closingElement := false; treeInput[14].types := SINT#16#0; treeInput[14].key := 'price'; treeInput[14].value := '39.95'; - treeInput[14].depth := SINT#16#04; + treeInput[14].depth := SINT#16#03; treeInput[14].closingElement := false; treeInput[15].types := SINT#16#0; treeInput[15].key := 'book'; treeInput[15].value := 'NULL'; - treeInput[15].depth := SINT#16#03; + treeInput[15].depth := SINT#16#02; treeInput[15].closingElement := false; treeInput[16].types := SINT#16#01; treeInput[16].key := 'category'; treeInput[16].value := 'Fantasy'; - treeInput[16].depth := SINT#16#03; + treeInput[16].depth := SINT#16#02; treeInput[16].closingElement := FALSE; treeInput[17].types := SINT#16#0; treeInput[17].key := 'title'; treeInput[17].value := 'Harry Potter and the Philosophers Stone'; - treeInput[17].depth := SINT#16#04; + treeInput[17].depth := SINT#16#03; treeInput[17].closingElement := FALSE; treeInput[18].types := SINT#16#01; treeInput[18].key := 'lang'; treeInput[18].value := 'en'; - treeInput[18].depth := SINT#16#04; + treeInput[18].depth := SINT#16#03; treeInput[18].closingElement := FALSE; treeInput[19].types := SINT#16#0; treeInput[19].key := 'author'; treeInput[19].value := 'J.K. Rowling'; - treeInput[19].depth := SINT#16#04; + treeInput[19].depth := SINT#16#03; treeInput[19].closingElement := FALSE; treeInput[20].types := SINT#16#0; treeInput[20].key := 'year'; treeInput[20].value := '1997'; - treeInput[20].depth := SINT#16#04; + treeInput[20].depth := SINT#16#03; treeInput[20].closingElement := false; treeInput[21].types := SINT#16#0; treeInput[21].key := 'price'; treeInput[21].value := '20.00'; - treeInput[21].depth := SINT#16#04; + treeInput[21].depth := SINT#16#03; treeInput[21].closingElement := false; + //Test2 Setup + treeInput2[0].types := SINT#16#0; + treeInput2[0].key := 'bookstore'; + treeInput2[0].value := 'NULL'; + treeInput2[0].depth := SINT#16#01; + treeInput2[0].closingElement := FALSE; + + treeInput2[1].types := SINT#16#0; + treeInput2[1].key := 'book'; + treeInput2[1].value := 'NULL'; + treeInput2[1].depth := SINT#16#02; + treeInput2[1].closingElement := FALSE; + + treeInput2[2].types := SINT#16#01; + treeInput2[2].key := 'category'; + treeInput2[2].value := 'Fiction'; + treeInput2[2].depth := SINT#16#02; + treeInput2[2].closingElement := FALSE; + + treeInput2[3].types := SINT#16#0; + treeInput2[3].key := 'title'; + treeInput2[3].value := 'Jaws'; + treeInput2[3].depth := SINT#16#03; + treeInput2[3].closingElement := FALSE; + + treeInput2[4].types := SINT#16#01; + treeInput2[4].key := 'lang'; + treeInput2[4].value := 'en'; + treeInput2[4].depth := SINT#16#03; + treeInput2[4].closingElement := FALSE; + + treeInput2[5].types := SINT#16#0; + treeInput2[5].key := 'author'; + treeInput2[5].value := 'NULL'; + treeInput2[5].depth := SINT#16#03; + treeInput2[5].closingElement := FALSE; + + treeInput2[6].types := SINT#16#0; + treeInput2[6].key := 'year'; + treeInput2[6].value := '2001'; + treeInput2[6].depth := SINT#16#03; + treeInput2[6].closingElement := FALSE; + + treeInput2[7].types := SINT#16#0; + treeInput2[7].key := 'price'; + treeInput2[7].value := ''; + treeInput2[7].depth := SINT#16#03; + treeInput2[7].closingElement := FALSE; + END_METHOD @@ -167,8 +272,10 @@ CLASS XmlSerializerTests METHOD PUBLIC TestXmlSerializer VAR i : INT; - END_VAR; - TimerInst.SetTimerValue := FALSE; + END_VAR; + // Disable timeout timer for the test + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + xmlSerializer.execute := TRUE; WHILE (i < 200) DO xmlSerializer(tree := treeInput, xmlByteArray := actualXmlByteArrayOutput); @@ -182,20 +289,56 @@ CLASS XmlSerializerTests END_WHILE; //Test Block execution - AxUnit.Assert.Equal(actual := xmlSerializer.count, expected := UINT#360); //19 + AxUnit.Assert.Equal(actual := xmlSerializer.count, expected := UINT#507); //19 AXUnit.Assert.Equal(actual := xmlSerializer.busy, expected := FALSE); AXUnit.Assert.Equal(actual := xmlSerializer.error, expected := FALSE); AXUnit.Assert.Equal(actual := xmlSerializer.done, expected := TRUE); - //Test Content + //Test Content + + //Test Output Content + FOR i := 0 TO 507 DO + AxUnit.Assert.Equal(actualXmlByteArrayOutput[i], expectedXmlByteOutputResult[i]); + END_FOR; + + END_METHOD + + + {Test} + METHOD PUBLIC TestXmlSerializer_EmptyValueNodes + VAR + i : INT; + END_VAR; + // Disable timeout timer for the test + AxUnit.Mocking.Mock(mockeeFn := NAME_OF(System.Timer.OnDelay), mockFn := NAME_OF(Simatic.Ax.Mocks.OnDelayMock_false)); + + xmlSerializer2.execute := TRUE; + + WHILE (i < 200) DO + xmlSerializer2(tree := treeInput2, xmlByteArray := actualXmlByteArrayOutput2); + IF(xmlSerializer2.done = TRUE) THEN + EXIT; + END_IF; + IF(xmlSerializer2.error = TRUE) THEN + EXIT; + END_IF; + i := i + 1; + END_WHILE; + + //Test Block execution + AxUnit.Assert.Equal(actual := xmlSerializer2.count, expected := UINT#171); + AXUnit.Assert.Equal(actual := xmlSerializer2.busy, expected := FALSE); + AXUnit.Assert.Equal(actual := xmlSerializer2.error, expected := FALSE); + AXUnit.Assert.Equal(actual := xmlSerializer2.done, expected := TRUE); - //Test Output Content - FOR i := 0 TO 400 DO - AxUnit.Assert.Equal(actualXmlByteArrayOutput[i], expectedXmlByteOutputResult[i]); - END_FOR; + //Test Content + //Test Output Content + FOR i := 0 TO 170 DO + AxUnit.Assert.Equal(actualXmlByteArrayOutput2[i], expectedXmlByteOutputResult2[i]); + END_FOR; END_METHOD END_CLASS -END_NAMESPACE \ No newline at end of file +END_NAMESPACE