Set up your environment
By the end of this tutorial, you can:
- Find the headers, native library, .NET assembly and simulator in the SDK bundle.
- Set the same environment variables in every terminal.
- Build the C++ and C# tutorial examples from the SDK alone.
- Connect the two languages to each other and run the headless simulator to confirm the installation.
Before you begin
Section titled “Before you begin”You need:
- The PDComm SECS/GEM SDK bundle: a ZIP for Windows x64 or a tar.gz for Linux x64. If you received a macOS (Apple silicon) build, it has the same layout.
- A C++ compiler:
- Windows: the Visual Studio 2022 C++ x64 tools. Run
clfrom x64 Native Tools or Developer PowerShell. - Linux:
g++with C++14 or later. - macOS:
c++from the Xcode Command Line Tools.
- Windows: the Visual Studio 2022 C++ x64 tools. Run
- The .NET 10 SDK to build the C# examples; the .NET 10 runtime is enough to run them. Follow Microsoft’s instructions for Windows, Linux or macOS. The SDK bundle doesn’t include the .NET SDK.
1. Extract the bundle and look around
Section titled “1. Extract the bundle and look around”Extract the bundle into a folder you own. This tutorial uses C:\pdcomm-tutorial on Windows and $HOME/pdcomm-tutorial on Linux and macOS.
Expand-Archive .\pdcomm-secsgem-windows-x64-<version>.zip -DestinationPath C:\pdcomm-tutorialmkdir -p "$HOME/pdcomm-tutorial"tar -xzf pdcomm-secsgem-linux-x64-<version>.tar.gz -C "$HOME/pdcomm-tutorial"mkdir -p "$HOME/pdcomm-tutorial"# Extract the macOS build you received into this folder.The extracted folder looks like this. Keep the structure and the license notices together.
Directorypdcomm-tutorial/
Directorybin/ prebuilt tutorial examples
- pdcomm-tutorial(.exe) C++ example
- PDComm.Tutorial.dll C# example
- PDComm.dll .NET binding
Directorysdk/
Directoryinclude/ C headers (
pdcomm*.h) and C++ wrappers (pdcomm*.hpp)- …
Directorylib/ native library (
pdcomm_ffi.dllwithpdcomm_ffi.dll.lib,libpdcomm_ffi.soorlibpdcomm_ffi.dylib)- …
Directorymanaged/
- PDComm.dll .NET binding
Directoryexamples/
Directorytutorial/ C++ and C# tutorial sources
- …
Directorysimulator/ GUI, headless and MCP simulators with their libraries
- …
- The native library in
sdk/libis the SECS/GEM implementation. C, C++ and C# programs all load it. PDComm.dllis the .NET binding assembly. It doesn’t replace the native library.- The capability demos for .NET, the Qt examples and the logging examples ship in a separate examples bundle. These tutorials call the folder where you extract it
PDCOMM_EXAMPLES.
2. Set the environment variables
Section titled “2. Set the environment variables”Set these variables in every new terminal. All later tutorials use them.
$Root = 'C:\pdcomm-tutorial'$Sdk = Join-Path $Root 'sdk'$Native = Join-Path $Sdk 'lib\pdcomm_ffi.dll'$Simulator = Join-Path $Root 'simulator'$Examples = 'C:\pdcomm-examples'$env:PATH = "$Sdk\lib;$env:PATH"PDCOMM_ROOT="$HOME/pdcomm-tutorial"PDCOMM_SDK="$PDCOMM_ROOT/sdk"PDCOMM_NATIVE="$PDCOMM_SDK/lib/libpdcomm_ffi.so"PDCOMM_SIMULATOR="$PDCOMM_ROOT/simulator"PDCOMM_EXAMPLES="$HOME/pdcomm-examples"export LD_LIBRARY_PATH="$PDCOMM_SDK/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"PDCOMM_ROOT="$HOME/pdcomm-tutorial"PDCOMM_SDK="$PDCOMM_ROOT/sdk"PDCOMM_NATIVE="$PDCOMM_SDK/lib/libpdcomm_ffi.dylib"PDCOMM_SIMULATOR="$PDCOMM_ROOT/simulator"PDCOMM_EXAMPLES="$HOME/pdcomm-examples"3. Check your tools
Section titled “3. Check your tools”dotnet --infoUnder .NET SDK, Version should start with 10, for example Version: 10.0.401. If the command isn’t found, install the .NET SDK and open a new terminal.
Check the C++ compiler with cl on Windows, g++ --version on Linux or c++ --version on macOS.
4. Build the C++ tutorial example
Section titled “4. Build the C++ tutorial example”New-Item -ItemType Directory -Force "$Root\build" | Out-Nullcl /nologo /W4 /WX /EHsc /std:c++14 "/I$Sdk\include" "$Sdk\examples\tutorial\pdcomm_tutorial.cpp" "$Sdk\lib\pdcomm_ffi.dll.lib" "/Fe:$Root\build\pdcomm-tutorial.exe" "/Fo:$Root\build\tutorial.obj"mkdir -p "$PDCOMM_ROOT/build"g++ -std=c++14 -Wall -Wextra -Werror -pedantic -I "$PDCOMM_SDK/include" \ "$PDCOMM_SDK/examples/tutorial/pdcomm_tutorial.cpp" \ -L "$PDCOMM_SDK/lib" -lpdcomm_ffi -o "$PDCOMM_ROOT/build/pdcomm-tutorial"mkdir -p "$PDCOMM_ROOT/build"c++ -std=c++14 -Wall -Wextra -Werror -pedantic -I "$PDCOMM_SDK/include" \ "$PDCOMM_SDK/examples/tutorial/pdcomm_tutorial.cpp" \ -L "$PDCOMM_SDK/lib" -lpdcomm_ffi -Wl,-rpath,"$PDCOMM_SDK/lib" \ -o "$PDCOMM_ROOT/build/pdcomm-tutorial"The build succeeds when it prints no warnings. Because -Werror (/WX on Windows) turns warnings into errors, a mismatch between the headers and your compiler shows up right here.
5. Build the C# tutorial example
Section titled “5. Build the C# tutorial example”The C# project takes the SDK folder in the PDCommSdk property and references managed/PDComm.dll from it.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net10.0</TargetFramework><Nullable>enable</Nullable><ImplicitUsings>enable</ImplicitUsings><TreatWarningsAsErrors>true</TreatWarningsAsErrors></PropertyGroup> <ItemGroup Condition="'$(PDCommSdk)' == ''"><ProjectReference Include="../../../bindings/dotnet/PDComm/PDComm.csproj" /></ItemGroup> <ItemGroup Condition="'$(PDCommSdk)' != ''"><Reference Include="PDComm"><HintPath>$(PDCommSdk)/managed/PDComm.dll</HintPath></Reference></ItemGroup></Project>dotnet build "$Sdk\examples\tutorial\PDComm.Tutorial\PDComm.Tutorial.csproj" -c Release "-p:PDCommSdk=$Sdk" --artifacts-path "$Root\build\managed"dotnet build "$PDCOMM_SDK/examples/tutorial/PDComm.Tutorial/PDComm.Tutorial.csproj" -c Release \ "-p:PDCommSdk=$PDCOMM_SDK" --artifacts-path "$PDCOMM_ROOT/build/managed"dotnet build "$PDCOMM_SDK/examples/tutorial/PDComm.Tutorial/PDComm.Tutorial.csproj" -c Release \ "-p:PDCommSdk=$PDCOMM_SDK" --artifacts-path "$PDCOMM_ROOT/build/managed"The build succeeds when the output ends with Build succeeded., 0 Warning(s) and 0 Error(s). The result is build/managed/bin/PDComm.Tutorial/release/PDComm.Tutorial.dll.
All HSMS and GEM options of the C# example live in one file. Replace the timers and limits with the values from the equipment’s interface specification before you connect real equipment.
using PDComm;internal static class TutorialOptions{ internal static readonly SecsLimits Limits = new(8192,4096,512,24); internal static GemFormattedProgramConfiguration Configuration() => new( new(4, 128, 512, 8, 8, 16, 32), new(4, 4, 8, 1024, 8, 8, 16, 32), new(4, 4, 8, 1024, 8, 2048, 8, 8, 16, 32), new(4, 4, 8, 1024, 8, 8, 16, 32), new(4, 32, 4, 8, 4, 32, 1024, 4096, 8, 8, 16, 32), new(4, 128, 512, 8, 8, 8, 16, 32)); internal static GemFormattedProgramCapabilityCommand Capability() => new("RUN", "Run", true, 0, [], [], [], 0, [], [], []); internal static HsmsOptions Transport(HsmsMode mode) => new() { Mode = mode, SessionId = 7, InitialSystemBytes = 1, T3Milliseconds = 2000, T5Milliseconds = 20, T6Milliseconds = 2000, T7Milliseconds = 5000, T8Milliseconds = 2000, ConnectTimeoutMilliseconds = 100, ShutdownTimeoutMilliseconds = 1000, WriteTimeoutMilliseconds = 2000, MaxFrameBytes = 8206, MaxEvents = 128, MaxQueuedBytes = 65536, MaxWriteFrames = 64, MaxWriteBytes = 65536, MaxIoChunk = 4096, MaxApplicationEvents = 128, MaxApplicationBytes = 65536, PollBudget = 32, ItemLimits = Limits }; internal static GemHostOptions HostOptions() => new() { MaxPendingIds = 64, ItemLimits = Limits }; internal static GemEquipmentOptions EquipmentOptions() => new() { Model = "PDComm", SoftwareRevision = "0.1.0", InitialControl = GemControlState.HostOffline, OnlineMode = GemOnlineMode.Remote, MaxVariables = 8, MaxMetadataBytes = 1024, MaxValueBytes = 8192, ItemLimits = Limits }; internal static HsmsWorkerOptions WorkerOptions() => new() { CommandCapacity = 32, MaxCommandBytes = 65536, EventCapacity = 32, MaxEventBytes = 131072, PollIntervalMilliseconds = 1, ShutdownTimeoutMilliseconds = 2000 };}6. Connect the two languages
Section titled “6. Connect the two languages”Run the C++ equipment against the C# host in automatic mode to confirm that the SDK, the native library and the .NET binding work together. Start the equipment in the first terminal:
"$PDCOMM_ROOT/build/pdcomm-tutorial" equipment passive 127.0.0.1:5000 --autoStart the C# host in the second terminal:
dotnet "$PDCOMM_ROOT/build/managed/bin/PDComm.Tutorial/release/PDComm.Tutorial.dll" "$PDCOMM_NATIVE" host active 127.0.0.1:5000 --autoOn Windows, use & "$Root\build\pdcomm-tutorial.exe" ... and dotnet "$Root\build\managed\bin\PDComm.Tutorial\release\PDComm.Tutorial.dll" "$Native" ....
The C# host prints each result with its numeric codes and their names:
RESULT kind=2 request=1 outcome=2 status=CommunicationEstablishedSECS-II 0102210100010241065044436F6D6D4105302E312E30RESULT kind=2 request=2 outcome=4 status=EquipmentIdentitySECS-II 010241065044436F6D6D4105302E312E30RESULT kind=2 request=3 outcome=5 status=ValuesReceivedSECS-II 0102910441AC000041074C4F542D303031PASS tutorial host activeThe equipment’s last line is PASS tutorial equipment passive. Both programs exit with code 0.
7. Check the simulator
Section titled “7. Check the simulator”The headless simulator runs a scenario with an internal host and equipment pair and no GUI. Run the smoke scenario to check the installation:
& "$Simulator\pdcomm-simulator-headless.exe" --scenario smoke$LASTEXITCODE"$PDCOMM_SIMULATOR/pdcomm-simulator-headless" --scenario smoke; echo "exit=$?""$PDCOMM_SIMULATOR/pdcomm-simulator-headless" --scenario smoke; echo "exit=$?"It prints one JSON object per line and ends with a summary:
{"schema":1,"sequence":1,"scenario":"smoke","scope":"pair","kind":"start","detail":"scenario-started","truncated":false}...{"schema":1,"sequence":6,"scenario":"smoke","scope":"pair","kind":"summary","outcome":"passed","exit_code":0,"elapsed_ms":10,"records_emitted":6,"max_records":128,"truncated":false,"status_names":0,"status_values":0}Your setup is complete when the summary shows "outcome":"passed" and "exit_code":0, and the process exit code is also 0. The elapsed_ms value differs on every run.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause and fix |
|---|---|
Windows reports that VCRUNTIME140.dll or MSVCP140.dll is missing. |
The x64 Visual C++ Redistributable isn’t installed. Install it and try again. |
Linux reports error while loading shared libraries: libpdcomm_ffi.so. |
LD_LIBRARY_PATH isn’t set in this terminal. Repeat step 2. |
macOS reports Library not loaded: @rpath/libpdcomm_ffi.dylib. |
You built without -Wl,-rpath. Rebuild with the macOS command from step 4. |
C# throws System.DllNotFoundException: Unable to load shared library .... |
The native library path in the first argument is wrong. Check that PDCOMM_NATIVE points to an existing file built for your OS and CPU architecture. |
C# throws NotSupportedException. |
PDComm.dll and the native library come from different versions. Use both files from the same bundle. |
The program prints Usage: PDComm.Tutorial <absolute-library-path> ... and exits with code 2. |
An argument is missing or wrong. The role is host or equipment and the mode is active or passive. |
| The simulator can’t find a Qt platform plugin. | The simulator folder wasn’t copied as a whole, or QT_PLUGIN_PATH points to another Qt installation. Keep the folder intact and clear that variable. |
Clean up
Section titled “Clean up”Automatic mode exits on its own. Delete the build folder if you no longer need the build output.
Next steps
Section titled “Next steps”In First communication, you change the communication state step by step with interactive commands and connect to the simulator GUI.