diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
new file mode 100644
index 000000000..71ecdfd33
--- /dev/null
+++ b/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-reportgenerator-globaltool": {
+ "version": "5.2.4",
+ "commands": [
+ "reportgenerator"
+ ]
+ }
+ }
+}
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 24b5cfb48..6a49c4365 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -1,137 +1,100 @@
-name: MTConnect.NET
+name: build-test-coverage
+
+# Restrict the GITHUB_TOKEN to read-only contents access. The job only
+# needs to checkout the repo, run dotnet build / test, and upload TRX +
+# coverage artifacts; no commit / release / package-write privileges are
+# required. Defense-in-depth against supply-chain attacks via a
+# compromised dependency or a test-side RCE.
+permissions:
+ contents: read
+
on:
push:
+ branches:
+ - master
+ paths-ignore:
+ - 'README.md'
+ - 'docs/**'
pull_request:
- branches: [ main ]
+ types: [opened, synchronize, reopened, ready_for_review]
+ branches:
+ - master
paths-ignore:
- - 'README.md'
-env:
- DOTNET_VERSION: '7.0.x'
+ - 'README.md'
+ - 'docs/**'
+
jobs:
- MTConnect-NET-Common:
- name: build-and-test-${{matrix.os}}-MTConnect-NET-Common
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest]
- steps:
- - uses: actions/checkout@v2
- - name: Setup .NET
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: ${{ env.DOTNET_VERSION }}
- - name: Clean
- run: dotnet clean --configuration Release
- working-directory: src/MTConnect.NET-Common
- - name: Build
- run: dotnet build --configuration Release --force
- working-directory: src/MTConnect.NET-Common
- - name: Test
- run: dotnet test --no-restore --verbosity normal
- working-directory: src/MTConnect.NET-Common
- MTConnect-NET-HTTP:
- name: build-and-test-${{matrix.os}}-MTConnect-NET-HTTP
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest]
- steps:
- - uses: actions/checkout@v2
- - name: Setup .NET
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: ${{ env.DOTNET_VERSION }}
- - name: Clean
- run: dotnet clean --configuration Release
- working-directory: src/MTConnect.NET-HTTP
- - name: Build
- run: dotnet build --configuration Release --force
- working-directory: src/MTConnect.NET-HTTP
- - name: Test
- run: dotnet test --no-restore --verbosity normal
- working-directory: src/MTConnect.NET-HTTP
- MTConnect-NET-HTTP-AspNetCore:
- name: build-and-test-${{matrix.os}}-MTConnect-NET-HTTP-AspNetCore
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest]
- steps:
- - uses: actions/checkout@v2
- - name: Setup .NET
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: ${{ env.DOTNET_VERSION }}
- - name: Clean
- run: dotnet clean --configuration Release
- working-directory: src/MTConnect.NET-HTTP-AspNetCore
- - name: Build
- run: dotnet build --configuration Release --force
- working-directory: src/MTConnect.NET-HTTP-AspNetCore
- - name: Test
- run: dotnet test --no-restore --verbosity normal
- working-directory: src/MTConnect.NET-HTTP-AspNetCore
- MTConnect-NET-XML:
- name: build-and-test-${{matrix.os}}-MTConnect-NET-XML
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest]
- steps:
- - uses: actions/checkout@v2
- - name: Setup .NET
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: ${{ env.DOTNET_VERSION }}
- - name: Clean
- run: dotnet clean --configuration Release
- working-directory: src/MTConnect.NET-XML
- - name: Build
- run: dotnet build --configuration Release --force
- working-directory: src/MTConnect.NET-XML
- - name: Test
- run: dotnet test --no-restore --verbosity normal
- working-directory: src/MTConnect.NET-XML
- MTConnect-NET-SHDR:
- name: build-and-test-${{matrix.os}}-MTConnect-NET-SHDR
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest]
- steps:
- - uses: actions/checkout@v2
- - name: Setup .NET
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: ${{ env.DOTNET_VERSION }}
- - name: Clean
- run: dotnet clean --configuration Release
- working-directory: src/MTConnect.NET-SHDR
- - name: Build
- run: dotnet build --configuration Release --force
- working-directory: src/MTConnect.NET-SHDR
- - name: Test
- run: dotnet test --no-restore --verbosity normal
- working-directory: src/MTConnect.NET-SHDR
- MTConnect-NET-MQTT:
- name: build-and-test-${{matrix.os}}-MTConnect-NET-MQTT
+ build-and-test:
+ name: build-and-test-${{ matrix.os }}
+ # Skip drafts: run only on push-to-master + ready (non-draft) PRs.
+ # The pull_request `types` list above includes `ready_for_review` so
+ # CI fires the moment a draft is flipped to ready.
+ if: github.event_name == 'push' || github.event.pull_request.draft == false
runs-on: ${{ matrix.os }}
strategy:
+ fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
+
steps:
- - uses: actions/checkout@v2
- - name: Setup .NET
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: ${{ env.DOTNET_VERSION }}
- - name: Clean
- run: dotnet clean --configuration Release
- working-directory: src/MTConnect.NET-MQTT
- - name: Build
- run: dotnet build --configuration Release --force
- working-directory: src/MTConnect.NET-MQTT
- - name: Test
- run: dotnet test --no-restore --verbosity normal
- working-directory: src/MTConnect.NET-MQTT
+ - name: Checkout
+ uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+
+ - name: Setup .NET 8.0 + 9.0
+ uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
+ with:
+ dotnet-version: |
+ 8.0.x
+ 9.0.x
+
+ - name: Restore dotnet tools (ReportGenerator)
+ run: dotnet tool restore
+
+ - name: Restore solution
+ run: dotnet restore MTConnect.NET.sln
+
+ - name: Build (Debug)
+ run: dotnet build MTConnect.NET.sln --configuration Debug --no-restore
+
+ - name: Run unit + integration tests with coverage
+ run: |
+ dotnet test MTConnect.NET.sln \
+ --configuration Debug \
+ --no-build \
+ --settings tests/coverlet.runsettings \
+ --results-directory TestResults \
+ --logger "trx;LogFileName=test-results-${{ matrix.os }}.trx" \
+ --filter "Category!=RequiresDocker&Category!=XsdLoadStrict"
+ shell: bash
+
+ - name: Generate coverage HTML + summary
+ if: always()
+ run: |
+ dotnet reportgenerator \
+ -reports:"TestResults/**/coverage.cobertura.xml" \
+ -targetdir:"coverage-report" \
+ -reporttypes:"Html;TextSummary;MarkdownSummary"
+ shell: bash
+
+ - name: Upload TRX + coverage artifacts
+ if: always()
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: test-results-${{ matrix.os }}
+ path: |
+ TestResults/**/*.trx
+ TestResults/**/coverage.cobertura.xml
+ coverage-report/
+ if-no-files-found: warn
+ retention-days: 14
+ - name: Surface coverage summary in job log
+ if: always()
+ run: |
+ if [ -f coverage-report/Summary.txt ]; then
+ echo "### Coverage summary (${{ matrix.os }})" >> "$GITHUB_STEP_SUMMARY"
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
+ cat coverage-report/Summary.txt >> "$GITHUB_STEP_SUMMARY"
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
+ fi
+ shell: bash
diff --git a/MTConnect.NET.sln b/MTConnect.NET.sln
index 07e9db7d2..5060a77b9 100644
--- a/MTConnect.NET.sln
+++ b/MTConnect.NET.sln
@@ -64,7 +64,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MTConnect.NET-Applications-
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MTConnect.NET-Applications-Adapter", "adapter\MTConnect.NET-Applications-Adapter\MTConnect.NET-Applications-Adapter.csproj", "{59076253-A3F6-42C7-96CD-E001008ED70E}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "tests\IntegrationTests\IntegrationTests.csproj", "{50649CF1-7DD2-42CC-9721-A941750210F0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MTConnect.NET-Integration-Tests", "tests\MTConnect.NET-Integration-Tests\MTConnect.NET-Integration-Tests.csproj", "{50649CF1-7DD2-42CC-9721-A941750210F0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MTConnect.NET-Common-Tests", "tests\MTConnect.NET-Common-Tests\MTConnect.NET-Common-Tests.csproj", "{C0BBADFF-D741-4FEB-8235-9335F58FB55E}"
EndProject
@@ -119,6 +119,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Agent", "templates\mtconnec
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MTConnect.NET.Builder", "build\MTConnect.NET.Builder\MTConnect.NET.Builder.csproj", "{FC9965F9-63B4-3BE9-FD8E-28B7C8E19A19}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MTConnect.NET-JSON-cppagent-Tests", "tests\MTConnect.NET-JSON-cppagent-Tests\MTConnect.NET-JSON-cppagent-Tests.csproj", "{011E192C-E842-4208-8613-504D0A51EA24}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MTConnect.NET-JSON-Tests", "tests\MTConnect.NET-JSON-Tests\MTConnect.NET-JSON-Tests.csproj", "{E04B4AE0-0719-47CC-B163-BAE9C5978522}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "agent", "agent", "{A49A60F5-AA68-4B79-97F2-4F30300B9E1E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{D00C616E-6DFC-447A-B4B6-9FD7687249D7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compliance", "Compliance", "{94E2A2D0-71FD-4563-B1A3-FC58136017E0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MTConnect-Compliance-Tests", "tests\Compliance\MTConnect-Compliance-Tests\MTConnect-Compliance-Tests.csproj", "{37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MTConnect.NET-AgentModule-MqttRelay-Tests", "tests\MTConnect.NET-AgentModule-MqttRelay-Tests\MTConnect.NET-AgentModule-MqttRelay-Tests.csproj", "{E726EF85-4464-47D9-91EF-AD435D14F9D6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -423,6 +437,38 @@ Global
{FC9965F9-63B4-3BE9-FD8E-28B7C8E19A19}.Package|Any CPU.Build.0 = Debug|Any CPU
{FC9965F9-63B4-3BE9-FD8E-28B7C8E19A19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC9965F9-63B4-3BE9-FD8E-28B7C8E19A19}.Release|Any CPU.Build.0 = Release|Any CPU
+ {011E192C-E842-4208-8613-504D0A51EA24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {011E192C-E842-4208-8613-504D0A51EA24}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {011E192C-E842-4208-8613-504D0A51EA24}.Docker|Any CPU.ActiveCfg = Debug|Any CPU
+ {011E192C-E842-4208-8613-504D0A51EA24}.Docker|Any CPU.Build.0 = Debug|Any CPU
+ {011E192C-E842-4208-8613-504D0A51EA24}.Package|Any CPU.ActiveCfg = Debug|Any CPU
+ {011E192C-E842-4208-8613-504D0A51EA24}.Package|Any CPU.Build.0 = Debug|Any CPU
+ {011E192C-E842-4208-8613-504D0A51EA24}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {011E192C-E842-4208-8613-504D0A51EA24}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E04B4AE0-0719-47CC-B163-BAE9C5978522}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E04B4AE0-0719-47CC-B163-BAE9C5978522}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E04B4AE0-0719-47CC-B163-BAE9C5978522}.Docker|Any CPU.ActiveCfg = Debug|Any CPU
+ {E04B4AE0-0719-47CC-B163-BAE9C5978522}.Docker|Any CPU.Build.0 = Debug|Any CPU
+ {E04B4AE0-0719-47CC-B163-BAE9C5978522}.Package|Any CPU.ActiveCfg = Debug|Any CPU
+ {E04B4AE0-0719-47CC-B163-BAE9C5978522}.Package|Any CPU.Build.0 = Debug|Any CPU
+ {E04B4AE0-0719-47CC-B163-BAE9C5978522}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E04B4AE0-0719-47CC-B163-BAE9C5978522}.Release|Any CPU.Build.0 = Release|Any CPU
+ {37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6}.Docker|Any CPU.ActiveCfg = Debug|Any CPU
+ {37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6}.Docker|Any CPU.Build.0 = Debug|Any CPU
+ {37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6}.Package|Any CPU.ActiveCfg = Debug|Any CPU
+ {37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6}.Package|Any CPU.Build.0 = Debug|Any CPU
+ {37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E726EF85-4464-47D9-91EF-AD435D14F9D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E726EF85-4464-47D9-91EF-AD435D14F9D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E726EF85-4464-47D9-91EF-AD435D14F9D6}.Docker|Any CPU.ActiveCfg = Debug|Any CPU
+ {E726EF85-4464-47D9-91EF-AD435D14F9D6}.Docker|Any CPU.Build.0 = Debug|Any CPU
+ {E726EF85-4464-47D9-91EF-AD435D14F9D6}.Package|Any CPU.ActiveCfg = Debug|Any CPU
+ {E726EF85-4464-47D9-91EF-AD435D14F9D6}.Package|Any CPU.Build.0 = Debug|Any CPU
+ {E726EF85-4464-47D9-91EF-AD435D14F9D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E726EF85-4464-47D9-91EF-AD435D14F9D6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -468,6 +514,13 @@ Global
{24C98CF3-CC93-4696-A036-8FD1E16F2E7E} = {FFF032D3-7446-4CAF-A3E3-CF9C4E1A5DCC}
{FF3FACB1-C470-4C7F-9A4B-F364BE1E32B3} = {D7873DF2-16DB-4B19-A100-C0089DF37488}
{FC9965F9-63B4-3BE9-FD8E-28B7C8E19A19} = {BBF53739-168D-4635-8595-083AC0C65E4C}
+ {011E192C-E842-4208-8613-504D0A51EA24} = {14375E03-6BF8-45E6-B868-D2399368992B}
+ {E04B4AE0-0719-47CC-B163-BAE9C5978522} = {14375E03-6BF8-45E6-B868-D2399368992B}
+ {A49A60F5-AA68-4B79-97F2-4F30300B9E1E} = {14375E03-6BF8-45E6-B868-D2399368992B}
+ {D00C616E-6DFC-447A-B4B6-9FD7687249D7} = {A49A60F5-AA68-4B79-97F2-4F30300B9E1E}
+ {94E2A2D0-71FD-4563-B1A3-FC58136017E0} = {14375E03-6BF8-45E6-B868-D2399368992B}
+ {37320C1F-5E50-4CDF-B00D-0E0ADB4F1AE6} = {94E2A2D0-71FD-4563-B1A3-FC58136017E0}
+ {E726EF85-4464-47D9-91EF-AD435D14F9D6} = {14375E03-6BF8-45E6-B868-D2399368992B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CC13D3AD-18BF-4695-AB2A-087EF0885B20}
diff --git a/README.md b/README.md
index f9906ff6a..2ffcd50c6 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@
## Overview
-MTConnect.NET is a fully featured and fully Open Source **[.NET](https://dotnet.microsoft.com/)** library for **[MTConnect](https://www.mtconnect.org/)** to develop Agents, Adapters, and Clients. Supports MTConnect Versions up to 2.5. A pre-compiled Agent application is available to download as well as an Adapter application that can be easily customized.
+MTConnect.NET is a fully featured and fully Open Source **[.NET](https://dotnet.microsoft.com/)** library for **[MTConnect](https://www.mtconnect.org/)** to develop Agents, Adapters, and Clients. Supports MTConnect versions up to 2.7. A pre-compiled Agent application is available to download as well as an Adapter application that can be easily customized.
- .NET Native MTConnect Agent
- Adapter framework used to send data to an MTConnect Agent
@@ -45,7 +45,7 @@ MTConnect.NET is a fully featured and fully Open Source **[.NET](https://dotnet.
- Module based Agent & Adapter architecture
- Supports running as Windows Service with easy to use command line arguments
- Presistent Agent Buffers that are backed up on the File System. Retains state after Agent is restarted
-- Fully compatible up to the latest MTConnect v2.5
+- Fully compatible up to the latest MTConnect 2.7
- Kept up to date by utilizing the MTConnect SysML Model to generate source files
- Supports multiple MTConnect Version output. Automatically removes data that is not compatible with the requested version
- Full client support for requesting data from any MTConnect Agent (Probe, Current, Sample Stream, Assets, etc.).
diff --git a/adapter/MTConnect.NET-Applications-Adapter/MTConnect.NET-Applications-Adapter.csproj b/adapter/MTConnect.NET-Applications-Adapter/MTConnect.NET-Applications-Adapter.csproj
index 0f3370e0d..ea7f60619 100644
--- a/adapter/MTConnect.NET-Applications-Adapter/MTConnect.NET-Applications-Adapter.csproj
+++ b/adapter/MTConnect.NET-Applications-Adapter/MTConnect.NET-Applications-Adapter.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-Applications-Adapter contains classes to fully implement an MTConnect SHDR Adapter application. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-Applications-Adapter contains classes to fully implement an MTConnect SHDR Adapter application. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
true
diff --git a/adapter/Modules/MTConnect.NET-AdapterModule-MQTT/MTConnect.NET-AdapterModule-MQTT.csproj b/adapter/Modules/MTConnect.NET-AdapterModule-MQTT/MTConnect.NET-AdapterModule-MQTT.csproj
index 94327dc5b..67a6772c9 100644
--- a/adapter/Modules/MTConnect.NET-AdapterModule-MQTT/MTConnect.NET-AdapterModule-MQTT.csproj
+++ b/adapter/Modules/MTConnect.NET-AdapterModule-MQTT/MTConnect.NET-AdapterModule-MQTT.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-AdapterModule-MQTT implements an adapter to send input data to an MQTT Broker to be read by an MTConnect Agent for Adapter Applications. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-AdapterModule-MQTT implements an adapter to send input data to an MQTT Broker to be read by an MTConnect Agent for Adapter Applications. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
README-Nuget.md
diff --git a/adapter/Modules/MTConnect.NET-AdapterModule-SHDR/MTConnect.NET-AdapterModule-SHDR.csproj b/adapter/Modules/MTConnect.NET-AdapterModule-SHDR/MTConnect.NET-AdapterModule-SHDR.csproj
index b4e06724e..1faedf150 100644
--- a/adapter/Modules/MTConnect.NET-AdapterModule-SHDR/MTConnect.NET-AdapterModule-SHDR.csproj
+++ b/adapter/Modules/MTConnect.NET-AdapterModule-SHDR/MTConnect.NET-AdapterModule-SHDR.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-AdapterModule-SHDR implements the MTConnect SHDR Protocol for Adapter Applications. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-AdapterModule-SHDR implements the MTConnect SHDR Protocol for Adapter Applications. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
README-Nuget.md
diff --git a/agent/MTConnect.NET-Applications-Agents/MTConnect.NET-Applications-Agents.csproj b/agent/MTConnect.NET-Applications-Agents/MTConnect.NET-Applications-Agents.csproj
index 63c4139d9..00ba1ec91 100644
--- a/agent/MTConnect.NET-Applications-Agents/MTConnect.NET-Applications-Agents.csproj
+++ b/agent/MTConnect.NET-Applications-Agents/MTConnect.NET-Applications-Agents.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-Applications-Agents contains classes to fully implement an MTConnect Agent application. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-Applications-Agents contains classes to fully implement an MTConnect Agent application. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
true
diff --git a/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/MTConnect.NET-AgentModule-HttpAdapter.csproj b/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/MTConnect.NET-AgentModule-HttpAdapter.csproj
index 23a01a10b..25a162e04 100644
--- a/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/MTConnect.NET-AgentModule-HttpAdapter.csproj
+++ b/agent/Modules/MTConnect.NET-AgentModule-HttpAdapter/MTConnect.NET-AgentModule-HttpAdapter.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-AgentModule-HttpAdapter implements the MTConnect HTTP Client Protocol to read from other MTConnect Agents for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agent library. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-AgentModule-HttpAdapter implements the MTConnect HTTP Client Protocol to read from other MTConnect Agents for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agent library. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
README-Nuget.md
diff --git a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnect.NET-AgentModule-HttpServer.csproj b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnect.NET-AgentModule-HttpServer.csproj
index df3a2aaca..f80a831d7 100644
--- a/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnect.NET-AgentModule-HttpServer.csproj
+++ b/agent/Modules/MTConnect.NET-AgentModule-HttpServer/MTConnect.NET-AgentModule-HttpServer.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-AgentModule-HttpServer implements a server for the MTConnect HTTP REST Protocol for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agents library. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-AgentModule-HttpServer implements a server for the MTConnect HTTP REST Protocol for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agents library. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
README-Nuget.md
diff --git a/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/MTConnect.NET-AgentModule-MqttAdapter.csproj b/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/MTConnect.NET-AgentModule-MqttAdapter.csproj
index 65840d03f..b8ef774fa 100644
--- a/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/MTConnect.NET-AgentModule-MqttAdapter.csproj
+++ b/agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/MTConnect.NET-AgentModule-MqttAdapter.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-AgentModule-MqttAdapter implements an Adapter to read data from an MQTT Broker for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agent library. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-AgentModule-MqttAdapter implements an Adapter to read data from an MQTT Broker for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agent library. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
README-Nuget.md
diff --git a/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/MTConnect.NET-AgentModule-MqttBroker.csproj b/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/MTConnect.NET-AgentModule-MqttBroker.csproj
index f9eaadcca..ad790c35d 100644
--- a/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/MTConnect.NET-AgentModule-MqttBroker.csproj
+++ b/agent/Modules/MTConnect.NET-AgentModule-MqttBroker/MTConnect.NET-AgentModule-MqttBroker.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-AgentModule-MqttBroker implements an MQTT Broker for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agent library. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-AgentModule-MqttBroker implements an MQTT Broker for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agent library. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
README-Nuget.md
diff --git a/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/MTConnect.NET-AgentModule-MqttRelay.csproj b/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/MTConnect.NET-AgentModule-MqttRelay.csproj
index a1a757796..950779fbd 100644
--- a/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/MTConnect.NET-AgentModule-MqttRelay.csproj
+++ b/agent/Modules/MTConnect.NET-AgentModule-MqttRelay/MTConnect.NET-AgentModule-MqttRelay.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-AgentModule-MqttRelay implements MQTT with MTConnect to publish to an external broker. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-AgentModule-MqttRelay implements MQTT with MTConnect to publish to an external broker. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
README-Nuget.md
diff --git a/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/MTConnect.NET-AgentModule-ShdrAdapter.csproj b/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/MTConnect.NET-AgentModule-ShdrAdapter.csproj
index 4c41e3d0d..7a18de441 100644
--- a/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/MTConnect.NET-AgentModule-ShdrAdapter.csproj
+++ b/agent/Modules/MTConnect.NET-AgentModule-ShdrAdapter/MTConnect.NET-AgentModule-ShdrAdapter.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-AgentModule-ShdrAdapter implements the SHDR protocol for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agents library. Supports MTConnect Versions up to 2.2. Supports .NET Framework 4.6.1 up to .NET 8
+ MTConnect.NET-AgentModule-ShdrAdapter implements the SHDR protocol for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agents library. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
README-Nuget.md
diff --git a/agent/Processors/MTConnect.NET-AgentProcessor-Python/MTConnect.NET-AgentProcessor-Python.csproj b/agent/Processors/MTConnect.NET-AgentProcessor-Python/MTConnect.NET-AgentProcessor-Python.csproj
index a4b5cb386..7d942b3e8 100644
--- a/agent/Processors/MTConnect.NET-AgentProcessor-Python/MTConnect.NET-AgentProcessor-Python.csproj
+++ b/agent/Processors/MTConnect.NET-AgentProcessor-Python/MTConnect.NET-AgentProcessor-Python.csproj
@@ -18,7 +18,7 @@
MTConnect
Debug;Release;Package
- MTConnect.NET-AgentProcessor-Python implements using Python scripts for Agent Processing for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agent library. Supports MTConnect Versions up to 2.5. Supports .NET Framework 4.6.1 up to .NET 9
+ MTConnect.NET-AgentProcessor-Python implements using Python scripts for Agent Processing for use with the MTConnectAgentApplication class in the MTConnect.NET-Applications-Agent library. Supports MTConnect versions up to 2.7. Supports .NET Framework 4.6.1 up to .NET 9
README-Nuget.md
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/ClassModel.cs b/build/MTConnect.NET-SysML-Import/CSharp/ClassModel.cs
index e3572fa35..320d08060 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/ClassModel.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/ClassModel.cs
@@ -1,10 +1,7 @@
using MTConnect.NET_SysML_Import.CSharp;
using MTConnect.SysML.Xmi;
using MTConnect.SysML.Xmi.UML;
-using Scriban;
-using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
namespace MTConnect.SysML.CSharp
@@ -86,77 +83,24 @@ public static ClassModel Create(MTConnectClassModel importModel)
public string RenderModel()
{
- var templateFilename = $"Model.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (HasModel && File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ if (!HasModel) return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Model.scriban");
+ return template.Render(this);
}
public string RenderInterface()
{
- var templateFilename = $"Interface.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (HasInterface && File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ if (!HasInterface) return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Interface.scriban");
+ return template.Render(this);
}
public string RenderDescriptions()
{
- if (Properties != null && Properties.Count > 0)
- {
- var templateFilename = $"ModelDescriptions.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (HasDescriptions && File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
- }
-
- return null;
+ if (Properties == null || Properties.Count == 0) return null;
+ if (!HasDescriptions) return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "ModelDescriptions.scriban");
+ return template.Render(this);
}
}
}
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/ComponentType.cs b/build/MTConnect.NET-SysML-Import/CSharp/ComponentType.cs
index 7f7ae9a51..d78dcc663 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/ComponentType.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/ComponentType.cs
@@ -2,7 +2,6 @@
using MTConnect.SysML.Models.Devices;
using MTConnect.SysML.Xmi;
using MTConnect.SysML.Xmi.UML;
-using Scriban;
namespace MTConnect.SysML.CSharp
{
@@ -40,7 +39,10 @@ public static ComponentType Create(MTConnectComponentType importModel)
var propertyValue = importProperty.GetValue(importModel);
var exportProperty = exportProperties.FirstOrDefault(o => o.Name == importProperty.Name);
- if (exportProperty != null)
+ // Require matching PropertyType so SetValue cannot throw
+ // ArgumentException when a property of the same name has
+ // a different declared type on the export model.
+ if (exportProperty != null && exportProperty.PropertyType == importProperty.PropertyType)
{
exportProperty.SetValue(exportModel, propertyValue);
}
@@ -59,26 +61,8 @@ public static ComponentType Create(MTConnectComponentType importModel)
public string RenderModel()
{
- var templateFilename = $"Devices.ComponentType.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Devices.ComponentType.scriban");
+ return template.Render(this);
}
public string RenderInterface() => null;
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/CompositionType.cs b/build/MTConnect.NET-SysML-Import/CSharp/CompositionType.cs
index 5f0865585..2f099583a 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/CompositionType.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/CompositionType.cs
@@ -2,7 +2,6 @@
using MTConnect.SysML.Models.Devices;
using MTConnect.SysML.Xmi;
using MTConnect.SysML.Xmi.UML;
-using Scriban;
namespace MTConnect.SysML.CSharp
{
@@ -40,7 +39,10 @@ public static CompositionType Create(MTConnectCompositionType importModel)
var propertyValue = importProperty.GetValue(importModel);
var exportProperty = exportProperties.FirstOrDefault(o => o.Name == importProperty.Name);
- if (exportProperty != null)
+ // Require matching PropertyType so SetValue cannot throw
+ // ArgumentException when a property of the same name has
+ // a different declared type on the export model.
+ if (exportProperty != null && exportProperty.PropertyType == importProperty.PropertyType)
{
exportProperty.SetValue(exportModel, propertyValue);
}
@@ -59,26 +61,8 @@ public static CompositionType Create(MTConnectCompositionType importModel)
public string RenderModel()
{
- var templateFilename = $"Devices.CompositionType.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Devices.CompositionType.scriban");
+ return template.Render(this);
}
public string RenderInterface() => null;
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/CuttingToolMeasurementModel.cs b/build/MTConnect.NET-SysML-Import/CSharp/CuttingToolMeasurementModel.cs
index 047cb6986..e844cc8c4 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/CuttingToolMeasurementModel.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/CuttingToolMeasurementModel.cs
@@ -1,6 +1,5 @@
using MTConnect.NET_SysML_Import.CSharp;
using MTConnect.SysML.Models.Assets;
-using Scriban;
namespace MTConnect.SysML.CSharp
{
@@ -47,26 +46,8 @@ public static CuttingToolMeasurementModel Create(MTConnectMeasurementModel impor
public string RenderModel()
{
- var templateFilename = $"Assets.CuttingToolMeasurement.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Assets.CuttingToolMeasurement.scriban");
+ return template.Render(this);
}
public string RenderInterface() => null;
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/DataItemType.cs b/build/MTConnect.NET-SysML-Import/CSharp/DataItemType.cs
index 465e09926..cd87f19ec 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/DataItemType.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/DataItemType.cs
@@ -2,10 +2,8 @@
using MTConnect.SysML.Models.Devices;
using MTConnect.SysML.Xmi;
using MTConnect.SysML.Xmi.UML;
-using Scriban;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
namespace MTConnect.SysML.CSharp
@@ -51,12 +49,17 @@ public static DataItemType Create(MTConnectDataItemType importModel)
var propertyValue = importProperty.GetValue(importModel);
var exportProperty = exportProperties.FirstOrDefault(o => o.Name == importProperty.Name);
- if (exportProperty != null)
+ // Require matching PropertyType so SetValue cannot throw
+ // ArgumentException — a future divergence in property
+ // types between the import and export hierarchies would
+ // otherwise blow up at runtime instead of silently
+ // skipping the mismatched property.
+ if (exportProperty != null && exportProperty.PropertyType == importProperty.PropertyType)
{
exportProperty.SetValue(exportModel, propertyValue);
}
}
-
+
if (exportModel.Units != null)
{
exportModel.Units = exportModel.Units.Replace("NativeUnitsEnum", "NativeUnits");
@@ -69,6 +72,11 @@ public static DataItemType Create(MTConnectDataItemType importModel)
exportModel.ResultType = ModelHelper.RemoveEnumSuffix(importModel.Result);
}
+ // Guard before `+= "DataItem"` so a null Id/Name does not silently yield the literal "DataItem".
+ if (exportModel.Id == null)
+ throw new InvalidOperationException("DataItemType has null Id, cannot append 'DataItem' suffix.");
+ if (exportModel.Name == null)
+ throw new InvalidOperationException($"DataItemType '{exportModel.Id}' has null Name, cannot append 'DataItem' suffix.");
exportModel.Id += "DataItem";
exportModel.Name += "DataItem";
exportModel.Description = DescriptionHelper.GetTextDescription(importModel.Description);
@@ -85,26 +93,8 @@ public static DataItemType Create(MTConnectDataItemType importModel)
public virtual string RenderModel()
{
- var templateFilename = $"Devices.DataItemType.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Devices.DataItemType.scriban");
+ return template.Render(this);
}
public string RenderInterface() => null;
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/DataSetResultModel.cs b/build/MTConnect.NET-SysML-Import/CSharp/DataSetResultModel.cs
index 84c9e16fb..1e65a5efe 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/DataSetResultModel.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/DataSetResultModel.cs
@@ -1,9 +1,6 @@
using MTConnect.NET_SysML_Import.CSharp;
using MTConnect.SysML.Models.Assets;
using MTConnect.SysML.Models.Observations;
-using Scriban;
-using System;
-using System.IO;
using System.Linq;
namespace MTConnect.SysML.CSharp
@@ -19,7 +16,11 @@ public static DataSetResultModel Create(MTConnectClassModel importModel)
{
if (importModel != null)
{
- var type = typeof(MTConnectClassModel);
+ // Use the export type (DataSetResultModel) so reflection picks up
+ // the export-side properties; the previous `typeof(MTConnectClassModel)`
+ // pointed at the parent and silently dropped DataSetResult-specific
+ // properties.
+ var type = typeof(DataSetResultModel);
var importProperties = importModel.GetType().GetProperties();
var exportProperties = type.GetProperties();
@@ -56,26 +57,8 @@ public static DataSetResultModel Create(MTConnectClassModel importModel)
public string RenderModel()
{
- var templateFilename = $"Observations.DataSetResults.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Observations.DataSetResults.scriban");
+ return template.Render(this);
}
public string RenderInterface() => null;
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/EnumModel.cs b/build/MTConnect.NET-SysML-Import/CSharp/EnumModel.cs
index e0ccb140b..30c97191f 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/EnumModel.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/EnumModel.cs
@@ -1,9 +1,7 @@
using MTConnect.NET_SysML_Import.CSharp;
using MTConnect.SysML.Xmi;
using MTConnect.SysML.Xmi.UML;
-using Scriban;
using System;
-using System.IO;
using System.Linq;
namespace MTConnect.SysML.CSharp
@@ -53,6 +51,10 @@ public static EnumModel Create(MTConnectEnumModel importModel, Func null;
public string RenderDescriptions()
{
- if (Values != null && Values.Count > 0)
- {
- var templateFilename = $"EnumDescriptions.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
- }
-
- return null;
+ if (Values == null || Values.Count == 0) return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "EnumDescriptions.scriban");
+ return template.Render(this);
}
}
}
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/EnumStringModel.cs b/build/MTConnect.NET-SysML-Import/CSharp/EnumStringModel.cs
index ebdc17429..6ce799937 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/EnumStringModel.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/EnumStringModel.cs
@@ -1,9 +1,7 @@
using MTConnect.NET_SysML_Import.CSharp;
using MTConnect.SysML.Xmi;
using MTConnect.SysML.Xmi.UML;
-using Scriban;
using System;
-using System.IO;
using System.Linq;
namespace MTConnect.SysML.CSharp
@@ -82,52 +80,16 @@ public static EnumStringModel Create(MTConnectEnumModel importModel, Func null;
public string RenderDescriptions()
{
- var templateFilename = $"EnumStringDescriptions.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "EnumStringDescriptions.scriban");
+ return template.Render(this);
}
}
}
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/InterfaceDataItemType.cs b/build/MTConnect.NET-SysML-Import/CSharp/InterfaceDataItemType.cs
index 2d6d60ef2..cd363caf6 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/InterfaceDataItemType.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/InterfaceDataItemType.cs
@@ -2,10 +2,8 @@
using MTConnect.SysML.Models.Devices;
using MTConnect.SysML.Xmi;
using MTConnect.SysML.Xmi.UML;
-using Scriban;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
namespace MTConnect.SysML.CSharp
@@ -36,12 +34,20 @@ public static InterfaceDataItemType Create(MTConnectInterfaceDataItemType import
var propertyValue = importProperty.GetValue(importModel);
var exportProperty = exportProperties.FirstOrDefault(o => o.Name == importProperty.Name);
- if (exportProperty != null)
+ // Require matching PropertyType so SetValue cannot throw
+ // ArgumentException when a property of the same name has
+ // a different declared type on the export model.
+ if (exportProperty != null && exportProperty.PropertyType == importProperty.PropertyType)
{
exportProperty.SetValue(exportModel, propertyValue);
}
}
+ // Guard before `+= "DataItem"` so a null Id/Name does not silently yield the literal "DataItem".
+ if (exportModel.Id == null)
+ throw new InvalidOperationException("InterfaceDataItemType has null Id, cannot append 'DataItem' suffix.");
+ if (exportModel.Name == null)
+ throw new InvalidOperationException($"InterfaceDataItemType '{exportModel.Id}' has null Name, cannot append 'DataItem' suffix.");
exportModel.Id += "DataItem";
exportModel.Name += "DataItem";
exportModel.Description = DescriptionHelper.GetTextDescription(importModel.Description);
@@ -57,26 +63,8 @@ public static InterfaceDataItemType Create(MTConnectInterfaceDataItemType import
public override string RenderModel()
{
- var templateFilename = $"Interfaces.InterfaceDataItemType.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Interfaces.InterfaceDataItemType.scriban");
+ return template.Render(this);
}
public string RenderInterface() => null;
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/MeasurementModel.cs b/build/MTConnect.NET-SysML-Import/CSharp/MeasurementModel.cs
index 7d5e53bd9..54d8b47d2 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/MeasurementModel.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/MeasurementModel.cs
@@ -1,6 +1,5 @@
using MTConnect.NET_SysML_Import.CSharp;
using MTConnect.SysML.Models.Assets;
-using Scriban;
namespace MTConnect.SysML.CSharp
{
@@ -47,26 +46,8 @@ public static MeasurementModel Create(MTConnectMeasurementModel importModel)
public string RenderModel()
{
- var templateFilename = $"Assets.Measurement.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Assets.Measurement.scriban");
+ return template.Render(this);
}
public string RenderInterface() => null;
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/ObservationModel.cs b/build/MTConnect.NET-SysML-Import/CSharp/ObservationModel.cs
index 62b3ccbbb..e5d1a9af7 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/ObservationModel.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/ObservationModel.cs
@@ -1,8 +1,5 @@
using MTConnect.NET_SysML_Import.CSharp;
using MTConnect.SysML.Models.Observations;
-using Scriban;
-using System;
-using System.IO;
using System.Linq;
namespace MTConnect.SysML.CSharp
@@ -50,52 +47,16 @@ public static ObservationModel Create(MTConnectObservationModel importModel)
public string RenderModel()
{
- var templateFilename = $"Observations.Observation.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "Observations.Observation.scriban");
+ return template.Render(this);
}
public string RenderInterface() => null;
public string RenderDescriptions()
{
- var templateFilename = $"EnumDescriptions.scriban";
- var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "csharp", "templates", templateFilename);
- if (File.Exists(templatePath))
- {
- try
- {
- var templateContents = File.ReadAllText(templatePath);
- if (templateContents != null)
- {
- var template = Template.Parse(templateContents);
- return template.Render(this);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
- return null;
+ var template = TemplateLoader.LoadOrThrow("CSharp", "Templates", "EnumDescriptions.scriban");
+ return template.Render(this);
}
}
}
diff --git a/build/MTConnect.NET-SysML-Import/CSharp/TemplateRenderer.cs b/build/MTConnect.NET-SysML-Import/CSharp/TemplateRenderer.cs
index 5f5a29f57..35785d006 100644
--- a/build/MTConnect.NET-SysML-Import/CSharp/TemplateRenderer.cs
+++ b/build/MTConnect.NET-SysML-Import/CSharp/TemplateRenderer.cs
@@ -21,10 +21,10 @@ public static void Render(MTConnectModel mtconnectModel, string outputPath)
{
if (!string.IsNullOrEmpty(classModel.Name))
{
- if (!dClassModels.ContainsKey(classModel.Name)) dClassModels.Add(classModel.Name, classModel);
+ // TryAdd preserves first-wins semantics with a single hash lookup.
+ dClassModels.TryAdd(classModel.Name, classModel);
}
}
- //var dClassModels = classModels.Where(o => o.Name != null).ToDictionary(o => o.Name);
var enumModels = exportModels.Where(o => typeof(MTConnectEnumModel).IsAssignableFrom(o.GetType())).Select(o => (MTConnectEnumModel)o);
@@ -34,10 +34,9 @@ public static void Render(MTConnectModel mtconnectModel, string outputPath)
{
if (!string.IsNullOrEmpty(enumModel.Name))
{
- if (!dEnumModels.ContainsKey(enumModel.Name)) dEnumModels.Add(enumModel.Name, enumModel);
+ dEnumModels.TryAdd(enumModel.Name, enumModel);
}
}
- //var dEnumModels = enumModels.Where(o => o.Name != null).ToDictionary(o => o.Name);
var templates = new List();
@@ -98,11 +97,27 @@ public static void Render(MTConnectModel mtconnectModel, string outputPath)
}
else if (typeof(MTConnectMeasurementModel).IsAssignableFrom(type))
{
- if (exportModel.Id.StartsWith("Assets.CuttingTools.")) template = CuttingToolMeasurementModel.Create((MTConnectMeasurementModel)exportModel);
- //else if (exportModel.Id.StartsWith("Assets.Pallet.")) template = MeasurementModel.Create((MTConnectMeasurementModel)exportModel);
+ if (exportModel.Id?.StartsWith("Assets.CuttingTools.") == true)
+ {
+ template = CuttingToolMeasurementModel.Create((MTConnectMeasurementModel)exportModel);
+ }
+ else
+ {
+ // Non-CuttingTools measurement (e.g. Assets.Pallet.*) — no fallback
+ // template exists yet, so log and continue rather than silently
+ // dropping the model.
+ Console.Error.WriteLine(
+ $"warn: MeasurementModel '{exportModel.Id}' has no template — " +
+ "only Assets.CuttingTools.* is currently rendered. Skipping.");
+ }
}
- else if (exportModel.Id.EndsWith("Result"))
+ else if (typeof(MTConnectClassModel).IsAssignableFrom(type) && exportModel.Id?.EndsWith("Result") == true)
{
+ // Suffix-based DataSetResult selector. Type guard required because the recursive
+ // GetExportModels walk surfaces both classes AND properties; a property whose Id
+ // happens to end in "Result" (e.g. `Devices.Configurations.DataSet.Result` — the
+ // `result` field on the v2.7 DataSet base class) would otherwise crash with
+ // InvalidCastException when forced into MTConnectClassModel.
template = DataSetResultModel.Create((MTConnectClassModel)exportModel);
}
else if (typeof(MTConnectClassModel).IsAssignableFrom(type)) template = ClassModel.Create((MTConnectClassModel)exportModel);
@@ -147,76 +162,30 @@ public static void Render(MTConnectModel mtconnectModel, string outputPath)
case "Assets.Asset": ((ClassModel)template).IsPartial = true; break;
case "Assets.PhysicalAsset": ((ClassModel)template).IsPartial = true; break;
- case "Assets.ComponentConfigurationParameters.ComponentConfigurationParameters":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
- break;
- case "Assets.CuttingTools.CuttingTool":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
- break;
+ case "Assets.ComponentConfigurationParameters.ComponentConfigurationParameters":
+ case "Assets.CuttingTools.CuttingTool":
case "Assets.CuttingTools.CuttingToolArchetype":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
- break;
- case "Assets.CuttingTools.CuttingToolLifeCycle": ((ClassModel)template).IsPartial = true; break;
- case "Assets.CuttingTools.CuttingItem": ((ClassModel)template).IsPartial = true; break;
- case "Assets.CuttingTools.ToolLife": ((ClassModel)template).IsPartial = true; break;
- case "Assets.CuttingTools.Measurement":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).IsAbstract = false;
- break;
- case "Assets.CuttingTools.ToolingMeasurement":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).IsAbstract = false;
- break;
- case "Assets.Files.File":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
- break;
+ case "Assets.Files.File":
case "Assets.Files.FileArchetype":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
- break;
case "Assets.Files.AbstractFile":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
+ case "Assets.QIF.QIFDocumentWrapper":
+ case "Assets.RawMaterials.RawMaterial":
+ ApplyAssetSuffix((ClassModel)template, alsoSuffixParent: true);
break;
case "Assets.Fixture.Fixture":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- //if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
- break;
case "Assets.Pallet.Pallet":
- ((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- //if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
+ ApplyAssetSuffix((ClassModel)template, alsoSuffixParent: false);
break;
- case "Assets.QIF.QIFDocumentWrapper":
+ case "Assets.CuttingTools.CuttingToolLifeCycle": ((ClassModel)template).IsPartial = true; break;
+ case "Assets.CuttingTools.CuttingItem": ((ClassModel)template).IsPartial = true; break;
+ case "Assets.CuttingTools.ToolLife": ((ClassModel)template).IsPartial = true; break;
+ case "Assets.CuttingTools.Measurement":
((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
+ ((ClassModel)template).IsAbstract = false;
break;
- case "Assets.RawMaterials.RawMaterial":
+ case "Assets.CuttingTools.ToolingMeasurement":
((ClassModel)template).IsPartial = true;
- ((ClassModel)template).Id += "Asset";
- ((ClassModel)template).Name += "Asset";
- if (((ClassModel)template).ParentName != null && ((ClassModel)template).ParentName != "Asset") ((ClassModel)template).ParentName += "Asset";
+ ((ClassModel)template).IsAbstract = false;
break;
}
@@ -236,7 +205,7 @@ public static void Render(MTConnectModel mtconnectModel, string outputPath)
containerModel.IsPartial = true;
containerModel.HasModel = false;
containerModel.HasDescriptions = false;
- foreach (var property in ((ClassModel)componentModel).Properties?.Where(o => o.Name != "Components" && o.Name != "Compositions"))
+ foreach (var property in (((ClassModel)componentModel).Properties ?? Enumerable.Empty()).Where(o => o.Name != "Components" && o.Name != "Compositions"))
{
containerModel.Properties.Add(PropertyModel.Create(property));
}
@@ -272,7 +241,7 @@ public static void Render(MTConnectModel mtconnectModel, string outputPath)
}
// Remove redundant Properties (inherits from IContainer)
- foreach (var property in ((ClassModel)componentModel).Properties?.Where(o => o.Name != "Components" && o.Name != "Compositions"))
+ foreach (var property in (((ClassModel)componentModel).Properties ?? Enumerable.Empty()).Where(o => o.Name != "Components" && o.Name != "Compositions"))
{
property.ExportToInterface = false;
}
@@ -293,43 +262,60 @@ public static void Render(MTConnectModel mtconnectModel, string outputPath)
private static IEnumerable GetExportModels(object model)
{
+ // Track visited reference-type instances to break cycles. The
+ // SysML model graph is generated and can contain back-references
+ // (e.g. parent ⇄ child) which would otherwise drive an unbounded
+ // recursion → StackOverflowException. HashSet keyed by
+ // reference equality so two distinct strings or value-typed
+ // boxes don't collide on Equals.
+ var visited = new HashSet