SiLA2.Client.Dynamic
10.2.2
dotnet add package SiLA2.Client.Dynamic --version 10.2.2
NuGet\Install-Package SiLA2.Client.Dynamic -Version 10.2.2
<PackageReference Include="SiLA2.Client.Dynamic" Version="10.2.2" />
<PackageVersion Include="SiLA2.Client.Dynamic" Version="10.2.2" />
<PackageReference Include="SiLA2.Client.Dynamic" />
paket add SiLA2.Client.Dynamic --version 10.2.2
#r "nuget: SiLA2.Client.Dynamic, 10.2.2"
#:package SiLA2.Client.Dynamic@10.2.2
#addin nuget:?package=SiLA2.Client.Dynamic&version=10.2.2
#tool nuget:?package=SiLA2.Client.Dynamic&version=10.2.2
SiLA2.Client.Dynamic
Introduction
SiLA2.Client.Dynamic is a .NET 10 package that enables runtime-based client development for SiLA2 servers without requiring compile-time generated gRPC stubs. This package allows developers to build generic SiLA2 client applications that can discover servers on the network, introspect their capabilities dynamically, and invoke commands and properties without pre-generating client code from .proto files.
What is SiLA2?
SiLA2 (Standardization in Lab Automation) is an open connectivity standard for laboratory instruments and automation systems. It uses gRPC for communication and defines a Feature Definition Language (FDL) for describing device capabilities in XML format.
Why SiLA2.Client.Dynamic?
Traditional SiLA2 client development requires:
- Obtaining .proto files for each feature
- Generating client stubs at compile time using protoc
- Rebuilding your application whenever features change
SiLA2.Client.Dynamic eliminates these requirements by:
- Discovering servers and features at runtime via mDNS
- Retrieving FDL definitions dynamically from servers
- Generating protobuf types and gRPC calls on-the-fly using reflection
- Adapting to server capabilities without recompilation
When to Use SiLA2.Client.Dynamic
Use this package when:
- Building generic SiLA2 client tools (e.g., Universal Client, testing frameworks)
- The server's feature set is unknown at compile time
- Rapid prototyping without code generation overhead
- Creating discovery tools or server monitoring applications
- Testing multiple servers with different feature sets
Use standard SiLA2.Client when:
- You know the exact features your client will use
- You want compile-time type safety and IntelliSense support
- Performance is critical (dynamic invocation has slight overhead)
- You're building a production client for specific devices
Key Features
- Automatic Server Discovery: Find SiLA2 servers on the network via mDNS/DNS-SD
- Runtime Feature Introspection: Retrieve and parse FDL definitions dynamically
- Dynamic Protobuf Generation: Generate message types at runtime without .proto files
- Full SiLA2 Support: Unobservable and observable commands, properties, and subscriptions
- Type Flexibility: Work with dynamic types or cast to known protobuf types as needed
- Minimal Configuration: Simple appsettings.json setup with network interface selection
Installation
NuGet Package Manager Console
Install-Package SiLA2.Client.Dynamic
.NET CLI
dotnet add package SiLA2.Client.Dynamic
Prerequisites
- .NET 10 SDK (Download)
- IDE (optional):
- Windows: Visual Studio 2026 Community
- Cross-platform: Visual Studio Code with C# Dev Kit
- Alternative: JetBrains Rider
Quick Start
1. Create Configuration File
Add appsettings.json to your project:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Grpc": "Information",
"Microsoft": "Information"
}
},
"Connection": {
"FQHN": "localhost",
"Port": 50052,
"ServerDiscovery": {
"NIC": "",
"ServiceName": "_sila._tcp"
}
}
}
Configuration Options:
FQHN: Fallback IP address, CIDR notation, or fully qualified hostnamePort: Default port for manual connections (standard SiLA2 port is 50052)ServerDiscovery.NIC: Network interface name (e.g., "Ethernet", "eth0", "WLAN0"). Leave empty to use default interfaceServerDiscovery.ServiceName: mDNS service type suffix (standard is "_sila._tcp")
2. Initialize the Client
using Microsoft.Extensions.Configuration;
using SiLA2.Client.Dynamic;
using SiLA2.Network.Discovery.mDNS;
// Load configuration
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
var configuration = configBuilder.Build();
// Create dynamic configurator
var client = new DynamicConfigurator(configuration, args);
3. Discover Servers and Features
Option A: Automatic Discovery via mDNS
// Search for servers on the network and retrieve their features
var serverFeatureMap = await client.GetFeatures();
// List discovered servers
foreach (var server in client.ServerFeatureMap)
{
Console.WriteLine($"Server: {server.Key.ServerName} at {server.Key.Address}:{server.Key.Port}");
foreach (var feature in server.Value)
{
Console.WriteLine($" - {feature.Key}");
}
}
Option B: Manual Connection (Known Server)
// Connect to a specific server
var connectionInfo = new ConnectionInfo("127.0.0.1", 50052);
var serverFeatureMap = await client.GetFeatures(new[] { connectionInfo });
4. Invoke a Simple Property
using Grpc.Net.Client;
// Get a gRPC channel to the server
var connectionInfo = client.ServerFeatureMap.Keys.First();
var channel = await client.GetChannel(connectionInfo.Address, connectionInfo.Port);
// Find the SiLAService core feature
var silaServiceFeature = client.ServerFeatureMap[connectionInfo]
.Values.Single(x => x.FullyQualifiedIdentifier == "org.silastandard/core/SiLAService/v1");
// Call the ServerUUID property
dynamic serverUUIDResponse = client.DynamicMessageService
.GetUnobservableProperty("ServerUUID", channel, silaServiceFeature);
Console.WriteLine($"Server UUID: {serverUUIDResponse.ServerUUID.Value}");
Architecture Overview
How It Works
SiLA2.Client.Dynamic follows this workflow:
1. Server Discovery (mDNS)
└─> Finds servers announcing "_sila._tcp" service
2. Feature Retrieval (gRPC call to SiLAService.Get_ImplementedFeatures)
└─> Gets list of feature identifiers (FQIs)
3. FDL Definition Retrieval (gRPC call to SiLAService.GetFeatureDefinition)
└─> Downloads XML feature definitions for each feature
4. Dynamic Type Generation (Runtime IL Emission)
└─> PayloadFactory generates protobuf message types from FDL
5. Dynamic Invocation (Reflection-based gRPC calls)
└─> DynamicMessageService invokes commands/properties using generated types
Key Components
| Component | Description |
|---|---|
| DynamicConfigurator | Main entry point; handles server discovery and provides access to services |
| ServerFeatureMap | Dictionary mapping servers to their feature definitions (ConnectionInfo → Feature dictionary) |
| DynamicMessageService | Service for invoking commands and properties dynamically |
| PayloadFactory | Generates runtime protobuf message types from FDL metadata |
| Feature | Parsed representation of a SiLA2 FDL XML definition |
ServerFeatureMap Structure
IDictionary<ConnectionInfo, IDictionary<string, Feature>>
↓ ↓ ↓
Server Info Feature FQI Feature Definition
// Example access pattern:
var feature = client.ServerFeatureMap[connectionInfo]["org.silastandard/core/SiLAService/v1"];
Usage Examples
Discovering Servers
Automatic Discovery (Recommended)
// Discovers all SiLA2 servers on the network via mDNS
var serverFeatureMap = await client.GetFeatures();
Console.WriteLine($"Found {client.ServerFeatureMap.Count} servers:");
foreach (var server in client.ServerFeatureMap)
{
Console.WriteLine($"\nServer: {server.Key.ServerName}");
Console.WriteLine($" Address: {server.Key.Address}:{server.Key.Port}");
Console.WriteLine($" UUID: {server.Key.ServerUuid}");
Console.WriteLine($" Features:");
foreach (var feature in server.Value)
{
Console.WriteLine($" - {feature.Key}");
}
}
Manual Connection (Known Servers)
// Connect to specific servers when addresses are known
var connections = new[]
{
new ConnectionInfo("192.168.1.100", 50052),
new ConnectionInfo("192.168.1.101", 50052)
};
var serverFeatureMap = await client.GetFeatures(connections);
Calling Unobservable Properties
Unobservable properties return a single value immediately.
// Get a gRPC channel
var connectionInfo = client.ServerFeatureMap.Keys.First();
var channel = await client.GetChannel(connectionInfo.Address, connectionInfo.Port);
// Find the feature containing the property
var silaServiceFeature = client.ServerFeatureMap[connectionInfo]
.Values.Single(x => x.FullyQualifiedIdentifier == "org.silastandard/core/SiLAService/v1");
// Call the property using dynamic typing
dynamic serverUUIDResponse = client.DynamicMessageService
.GetUnobservableProperty("ServerUUID", channel, silaServiceFeature);
Console.WriteLine($"Server UUID: {serverUUIDResponse.ServerUUID.Value}");
// Alternative: Cast to specific protobuf type if known
dynamic serverNameResponse = client.DynamicMessageService
.GetUnobservableProperty("ServerName", channel, silaServiceFeature);
Console.WriteLine($"Server Name: {serverNameResponse.ServerName.Value}");
Output:
Server UUID: 12345678-1234-1234-1234-123456789abc
Server Name: Temperature Controller
Calling Unobservable Commands
Unobservable commands execute immediately and return a result.
// Find the feature containing the command
var testFeature = client.ServerFeatureMap[connectionInfo]
.Values.Single(x => x.FullyQualifiedIdentifier == "org.silastandard/test/UnobservableCommandTest/v1");
// Prepare parameters as a dictionary (parameter names must match FDL identifiers)
var payloadMap = new Dictionary<string, object>
{
{ "Integer", new Sila2.Org.Silastandard.Protobuf.Integer { Value = 42 } },
{ "String", new Sila2.Org.Silastandard.Protobuf.String { Value = "Hello" } }
};
// Execute the command
dynamic response = client.DynamicMessageService.ExecuteUnobservableCommand(
"JoinIntegerAndString",
channel,
testFeature,
payloadMap);
Console.WriteLine($"Joined Result: {response.JoinedParameters.Value}");
Output:
Joined Result: 42-Hello
Parameter Types: Use standard SiLA2 protobuf types from the Sila2.Org.Silastandard.Protobuf namespace:
Integer(int)Real(double)String(string)Boolean(bool)Binary(byte[])Date/Time/Timestamp- Custom structures and lists (see Advanced Topics)
Subscribing to Observable Properties
Observable properties stream continuous value updates.
// Find the feature with the observable property
var observablePropertyFeature = client.ServerFeatureMap[connectionInfo]
.Values.Single(x => x.FullyQualifiedIdentifier == "org.silastandard/test/ObservablePropertyTest/v1");
// Subscribe to the property stream
var propertyStream = client.DynamicMessageService.SubcribeObservableProperty(
"Alternating",
channel,
observablePropertyFeature);
// Consume the stream using async enumeration
await foreach (var value in propertyStream)
{
dynamic propertyValue = value;
Console.WriteLine($"Property value: {propertyValue.Alternating.Value}");
// You can break out of the loop to stop subscription
if (someCondition) break;
}
Output:
Property value: True
Property value: False
Property value: True
Property value: False
...
Stream Control: The subscription remains active until:
- You break out of the
await foreachloop - The server stops streaming values
- The gRPC channel is disposed
Executing Observable Commands
Observable commands are long-running operations that provide progress updates and eventual results.
// Find the feature with the observable command
var observableCommandFeature = client.ServerFeatureMap[connectionInfo]
.Values.Single(x => x.FullyQualifiedIdentifier == "org.silastandard/test/ObservableCommandTest/v1");
// Prepare parameters
var payloadMap = new Dictionary<string, object>
{
{ "N", new Sila2.Org.Silastandard.Protobuf.Integer { Value = 10 } },
{ "Delay", new Sila2.Org.Silastandard.Protobuf.Real { Value = 1.0 } }
};
// Execute the command (returns immediately with a UUID and streams)
var result = client.DynamicMessageService.ExecuteObservableCommand(
"Count",
channel,
observableCommandFeature,
payloadMap);
var confirmation = result.Item1; // CommandConfirmation (with UUID)
var executionInfoStream = result.Item2; // ExecutionInfo stream (progress)
var intermediateStream = result.Item3; // Intermediate responses (if defined)
var responseType = result.Item4; // Type for final result retrieval
Console.WriteLine($"Command started with UUID: {confirmation.CommandExecutionUUID.Value}");
// Monitor execution progress
await foreach (var executionInfo in executionInfoStream)
{
Console.WriteLine($"Status: {executionInfo.CommandStatus}");
Console.WriteLine($"Progress: {executionInfo.ProgressInfo?.Value ?? 0:P0}");
if (executionInfo.CommandStatus == ExecutionInfo.Types.CommandStatus.FinishedSuccessfully
|| executionInfo.CommandStatus == ExecutionInfo.Types.CommandStatus.FinishedWithError)
{
break;
}
}
// Retrieve the final result
dynamic finalResult = client.DynamicMessageService.GetObservableCommandResult(
confirmation.CommandExecutionUUID,
"Count",
channel,
observableCommandFeature,
responseType);
Console.WriteLine($"Final result: {finalResult.IterationResponse.Value}");
Output:
Command started with UUID: 87654321-4321-4321-4321-abcdef123456
Status: Running
Progress: 0%
Status: Running
Progress: 10%
...
Status: FinishedSuccessfully
Progress: 100%
Final result: 10
Intermediate Responses: Some observable commands define intermediate responses in their FDL for detailed progress information beyond ExecutionInfo. Access them via result.Item3 if not null.
API Reference
DynamicConfigurator
Constructor
public DynamicConfigurator(IConfiguration configuration, string[] args)
Initializes the configurator with application configuration and command-line arguments.
Properties
ServerFeatureMap: Dictionary mapping servers to their feature definitionsDynamicMessageService: Service for invoking commands and propertiesPayloadFactory: Factory for creating dynamic protobuf typesDiscoveredServers: Dictionary of discovered servers (inherited from Configurator)
Methods
// Discover servers and retrieve feature definitions
Task<IDictionary<ConnectionInfo, IDictionary<string, Feature>>> GetFeatures(
IEnumerable<ConnectionInfo> connections = null)
// Get a gRPC channel to a server
Task<GrpcChannel> GetChannel(string address, int port)
// Search for servers via mDNS (inherited from Configurator)
Task<IDictionary<Guid, ConnectionInfo>> SearchForServers(
TimeSpan? searchDuration = null)
DynamicMessageService
Unobservable Property
object GetUnobservableProperty(
string propertyName,
GrpcChannel channel,
Feature feature,
Grpc.Core.Metadata metadata = null)
Observable Property
IAsyncEnumerable<object> SubcribeObservableProperty(
string propertyName,
GrpcChannel channel,
Feature feature,
Grpc.Core.Metadata metadata = null)
Unobservable Command
object ExecuteUnobservableCommand(
string operationName,
GrpcChannel channel,
Feature feature,
IDictionary<string, object> payloadMap = null,
Grpc.Core.Metadata metadata = null)
Observable Command
Tuple<CommandConfirmation, IAsyncEnumerable<ExecutionInfo>, IAsyncEnumerable<object>, Type>
ExecuteObservableCommand(
string operationName,
GrpcChannel channel,
Feature feature,
IDictionary<string, object> payloadMap = null,
Grpc.Core.Metadata metadata = null)
Observable Command Result
object GetObservableCommandResult(
CommandExecutionUUID cmdId,
string operationName,
GrpcChannel channel,
Feature feature,
Type responseType,
Grpc.Core.Metadata metadata = null)
PayloadFactory
// Generate property payload types
Tuple<Type, Type> GetPropertyPayloadTypes(Feature feature, string operation)
// Generate command payload types
Tuple<Type, Type> GetCommandPayloadTypes(Feature feature, string operation)
// Generate observable command payload types (with intermediate responses)
Tuple<Type, Type, Type> GetCommandPayloadTypesWithIntermediateCommandResponseType(
Feature feature, string operation)
Configuration Reference
Complete appsettings.json Structure
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Grpc": "Debug",
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Warning"
}
},
"Connection": {
"FQHN": "localhost",
"Port": 50052,
"ServerDiscovery": {
"NIC": "Ethernet",
"ServiceName": "_sila._tcp"
}
}
}
Configuration Options Explained
| Key | Description | Default | Examples |
|---|---|---|---|
Connection.FQHN |
Fallback host for manual connections | localhost |
192.168.1.100, temperature-server.local |
Connection.Port |
Fallback port for manual connections | 50052 |
50052 (standard SiLA2 port) |
ServerDiscovery.NIC |
Network interface for mDNS discovery | (default) | "Ethernet", "eth0", "WLAN0", "" (empty = default) |
ServerDiscovery.ServiceName |
mDNS service type suffix | _sila._tcp |
_sila._tcp (standard) |
Logging.LogLevel.Grpc |
gRPC diagnostic level | Information |
Debug (verbose), Warning (minimal) |
Network Interface Selection
Windows Examples:
"Ethernet"- Wired Ethernet adapter"Wi-Fi"- Wireless adapter"vEthernet (Default Switch)"- Hyper-V virtual adapter
Linux/macOS Examples:
"eth0"- Primary Ethernet"wlan0"- Wireless interface"en0"- macOS Ethernet/Wi-Fi
Leave empty ("") to use the system's default network interface.
Comparison: SiLA2.Client vs SiLA2.Client.Dynamic
| Feature | SiLA2.Client | SiLA2.Client.Dynamic |
|---|---|---|
| Proto Files Required | Yes | No |
| Compile-Time Code Gen | Yes (protoc) | No |
| Type Safety | Strong (compile-time) | Dynamic (runtime) |
| IntelliSense Support | Full | Limited (dynamic typing) |
| Runtime Flexibility | None | Full |
| Feature Discovery | Manual | Automatic |
| Performance | Fastest | Slight overhead (reflection) |
| Use Case | Production clients for known devices | Generic tools, testing, discovery apps |
| Code Changes on Feature Update | Rebuild required | None |
| Complexity | Higher initial setup | Lower initial setup |
When to Choose:
- SiLA2.Client: Production clients with known feature sets, performance-critical applications
- SiLA2.Client.Dynamic: Universal clients, testing tools, discovery applications, rapid prototyping
Advanced Topics
Working with Complex Data Types
Structures (Nested Messages)
// For commands that accept structures, build nested objects
var structure = new Dictionary<string, object>
{
{ "FieldName1", new Sila2.Org.Silastandard.Protobuf.String { Value = "Value" } },
{ "FieldName2", new Sila2.Org.Silastandard.Protobuf.Integer { Value = 123 } }
};
var payloadMap = new Dictionary<string, object>
{
{ "ParameterName", structure }
};
Lists (Repeated Fields)
// For commands that accept lists/arrays
var list = new List<Sila2.Org.Silastandard.Protobuf.Integer>
{
new Sila2.Org.Silastandard.Protobuf.Integer { Value = 1 },
new Sila2.Org.Silastandard.Protobuf.Integer { Value = 2 },
new Sila2.Org.Silastandard.Protobuf.Integer { Value = 3 }
};
var payloadMap = new Dictionary<string, object>
{
{ "ValueList", list }
};
Constrained Data Types
SiLA2 supports constrained data types (e.g., Real with min/max, String with regex). The underlying protobuf types are the same; validation happens server-side:
// Even if the FDL defines constraints, use the base type
var payloadMap = new Dictionary<string, object>
{
{ "Temperature", new Sila2.Org.Silastandard.Protobuf.Real { Value = 25.5 } }
};
// Server will validate against constraints defined in FDL
Error Handling
Catching SiLA2 Errors
using Grpc.Core;
try
{
dynamic response = client.DynamicMessageService.ExecuteUnobservableCommand(
"SetTemperature", channel, feature, payloadMap);
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.InvalidArgument)
{
Console.WriteLine($"Invalid parameter: {ex.Status.Detail}");
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.Unavailable)
{
Console.WriteLine($"Server unavailable: {ex.Status.Detail}");
}
catch (RpcException ex)
{
// Check for SiLA2 DefinedExecutionError in metadata
var silaError = ex.Trailers.Get("sila2-error");
if (silaError != null)
{
Console.WriteLine($"SiLA2 Error: {silaError.Value}");
}
else
{
Console.WriteLine($"gRPC Error: {ex.Message}");
}
}
Checking Observable Command Errors
await foreach (var executionInfo in executionInfoStream)
{
if (executionInfo.CommandStatus == ExecutionInfo.Types.CommandStatus.FinishedWithError)
{
Console.WriteLine($"Command failed!");
Console.WriteLine($"Error: {executionInfo.Message?.Value ?? "Unknown error"}");
break;
}
}
Performance Considerations
Caching Considerations:
- Feature definitions are retrieved once during
GetFeatures()and stored inServerFeatureMap - Generated protobuf types are cached by
PayloadFactoryto avoid redundant IL emission - Reusing gRPC channels improves performance (avoid creating new channels for each call)
Best Practices:
// Good: Reuse channel
var channel = await client.GetChannel(server.Address, server.Port);
for (int i = 0; i < 100; i++)
{
var result = client.DynamicMessageService.GetUnobservableProperty(..., channel, ...);
}
// Bad: Creating new channel each time (overhead)
for (int i = 0; i < 100; i++)
{
var channel = await client.GetChannel(server.Address, server.Port);
var result = client.DynamicMessageService.GetUnobservableProperty(..., channel, ...);
}
Performance Impact:
- First call to a command/property: ~5-10ms overhead (type generation)
- Subsequent calls: ~1-2ms overhead (reflection-based invocation)
- For high-frequency operations (>1000 calls/sec), consider standard SiLA2.Client
Thread Safety
Thread-Safe Components:
DynamicConfigurator- Safe for concurrent accessServerFeatureMap- Read-only after population, safe for concurrent readsDynamicMessageService- Stateless, safe for concurrent calls
Not Thread-Safe:
- gRPC channels should not be disposed while in use
- Observable property/command streams should be consumed sequentially
// Safe: Multiple threads calling different commands
await Task.WhenAll(
Task.Run(() => client.DynamicMessageService.GetUnobservableProperty(...)),
Task.Run(() => client.DynamicMessageService.ExecuteUnobservableCommand(...))
);
Examples and Resources
Integration Test Example
A complete working example is available in the repository:
Location: src/Tests/SiLA2.IntegrationTests.DynamicClientApp/
This example demonstrates:
- Server discovery via mDNS
- Feature enumeration and introspection
- Calling unobservable properties and commands
- Subscribing to observable properties
- Executing and monitoring observable commands
Run the example:
# Start the test server
dotnet run --project src/Tests/SiLA2.IntegrationTests.ServerApp
# Run the dynamic client (in another terminal)
dotnet run --project src/Tests/SiLA2.IntegrationTests.DynamicClientApp
Additional Resources
- Main Repository: https://gitlab.com/SiLA2/sila_csharp
- Repository Wiki: https://gitlab.com/SiLA2/sila_csharp/-/wikis/home
- SiLA2 Standard: https://sila-standard.com
- SiLA Universal Client (Python): https://gitlab.com/SiLA2/universal-sila-client/sila_universal_client
- SiLA Universal Client (.NET): https://gitlab.com/SiLA2/sila_csharp/-/tree/master/src/SiLA2.UniversalClient.Net
Troubleshooting
mDNS Not Finding Servers
Symptoms: GetFeatures() returns empty ServerFeatureMap
Solutions:
Check network interface:
"ServerDiscovery": { "NIC": "Ethernet" // Try different interface names }Use
ipconfig(Windows) orifconfig(Linux/macOS) to list interface namesVerify server is announcing:
- Ensure the SiLA2 server has called
Start()to begin mDNS announcement - Check server logs for "mDNS announcement started" message
- Ensure the SiLA2 server has called
Firewall/network issues:
- mDNS uses UDP port 5353 - ensure it's not blocked
- Check that client and server are on the same subnet
Use manual connection:
var serverFeatureMap = await client.GetFeatures(new[] { new ConnectionInfo("192.168.1.100", 50052) });
Certificate Errors
Symptoms: RpcException with StatusCode.Unavailable or SSL/TLS errors
Solutions:
Development environments: Most SiLA2 servers use self-signed certificates
// Note: DynamicConfigurator handles certificate validation automatically // For custom certificate handling, see the main repository documentationAccept self-signed certificates: The first connection to a server may prompt a certificate warning in browsers (for web-based servers)
Feature Not Found Errors
Symptoms: ArgumentException when calling commands/properties
Solutions:
Verify feature identifier:
// List all available features foreach (var feature in client.ServerFeatureMap[connectionInfo].Keys) { Console.WriteLine(feature); }Check command/property name:
// Inspect feature definition var feature = client.ServerFeatureMap[connectionInfo]["org.example/feature/MyFeature/v1"]; Console.WriteLine(feature); // Prints full FDL XMLCase sensitivity: Command and property names are case-sensitive
gRPC Connection Timeouts
Symptoms: Operations hang or timeout
Solutions:
Check server is running:
netstat -an | grep 50052 # Linux/macOS netstat -an | findstr 50052 # WindowsIncrease timeout:
// Configure channel options (if needed) var channel = GrpcChannel.ForAddress($"https://{address}:{port}", new GrpcChannelOptions { HttpHandler = new SocketsHttpHandler { PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan, KeepAlivePingDelay = TimeSpan.FromSeconds(60), KeepAlivePingTimeout = TimeSpan.FromSeconds(30) } });Check gRPC logs: Set
"Logging.LogLevel.Grpc": "Debug"in appsettings.json
Debugging Tips
Enable verbose logging:
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Grpc": "Debug",
"SiLA2": "Debug"
}
}
}
Inspect feature definitions:
// Print full FDL XML
var feature = client.ServerFeatureMap[connectionInfo]["feature.fqi"];
Console.WriteLine(feature.ToString()); // Shows XML structure
Test with official tools:
- Use the SiLA Universal Client (Python) to verify server is working
- Compare behavior with standard SiLA2.Client implementation
Get Help
Contributing
This project is open source under the MIT License. Contributions are welcome!
Repository: https://gitlab.com/SiLA2/sila_csharp
How to contribute:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a merge request
See the main repository README for development setup instructions.
License
MIT License
This software is provided under the MIT License. See the main repository for full license text.
Maintainer
Christoph Pohl (@Chamundi)
Vulnerability Policy
For security issues, please follow the SiLA2 Vulnerability Policy.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.3)
- Microsoft.Extensions.Logging (>= 10.0.3)
- SiLA2.Client (>= 10.2.2)
- SiLA2.Communication (>= 10.2.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.2.2 | 37 | 2/12/2026 |
| 10.2.1 | 92 | 1/25/2026 |
| 10.2.0 | 190 | 12/23/2025 |
| 10.1.0 | 138 | 11/29/2025 |
| 10.0.0 | 302 | 11/11/2025 |
| 9.0.4 | 226 | 6/25/2025 |
| 9.0.3 | 169 | 6/21/2025 |
| 9.0.2 | 212 | 1/6/2025 |
| 9.0.1 | 202 | 11/17/2024 |
| 9.0.0 | 184 | 11/13/2024 |
| 8.1.2 | 218 | 10/20/2024 |
| 8.1.1 | 220 | 8/31/2024 |
| 8.1.0 | 794 | 2/11/2024 |
| 8.0.0 | 274 | 11/15/2023 |
| 7.5.4 | 208 | 10/27/2023 |
| 7.5.3 | 278 | 7/19/2023 |
| 7.5.2 | 258 | 7/3/2023 |
| 7.5.1 | 272 | 6/2/2023 |