RshipSdk 1.0.19
dotnet add package RshipSdk --version 1.0.19
NuGet\Install-Package RshipSdk -Version 1.0.19
<PackageReference Include="RshipSdk" Version="1.0.19" />
<PackageVersion Include="RshipSdk" Version="1.0.19" />
<PackageReference Include="RshipSdk" />
paket add RshipSdk --version 1.0.19
#r "nuget: RshipSdk, 1.0.19"
#:package RshipSdk@1.0.19
#addin nuget:?package=RshipSdk&version=1.0.19
#tool nuget:?package=RshipSdk&version=1.0.19
RshipSdk for .NET
RshipSdk is a comprehensive, high-level SDK for building real-time applications with the Rship platform. It provides a powerful abstraction layer for instance management, bidirectional action handling, real-time data emission, and seamless integration with the Rship ecosystem.
🚀 Features
- 🏗️ Instance Management: Create and manage distributed application instances with automatic registration
- ⚡ Action Handling: Define type-safe, async action handlers with automatic JSON schema generation
- 📡 Real-time Emitters: Send structured data through typed emitters with WebSocket communication
- 🎯 Target Management: Organize functionality into logical, hierarchical targets
- 🔒 Type Safety: Full C# type safety with automatic JSON schema validation
- 🔄 Auto-reconnection: Resilient WebSocket connections with automatic state restoration
- 📝 Comprehensive Logging: Built-in logging support via Microsoft.Extensions.Logging
- 🔧 Async/Await: Modern async patterns throughout the entire API
- 🎨 Flexible Architecture: Modular design supporting complex, distributed applications
📦 Installation
Package Manager
dotnet add package RshipSdk
PackageReference
<PackageReference Include="RshipSdk" Version="1.0.1" />
.NET CLI
Install-Package RshipSdk
🏃♂️ Quick Start
Here's a complete example showing the core concepts:
using Microsoft.Extensions.Logging;
using RshipSdk;
// 1. Define your data models
public class PlayVideoData
{
public string VideoPath { get; set; } = string.Empty;
public bool AutoPlay { get; set; } = true;
public double Volume { get; set; } = 1.0;
}
public class VideoStatusData
{
public string Status { get; set; } = string.Empty;
public double Progress { get; set; }
public TimeSpan Duration { get; set; }
}
// 2. Setup logging (recommended)
using var loggerFactory = LoggerFactory.Create(builder =>
builder.AddConsole().SetMinimumLevel(LogLevel.Information));
var logger = loggerFactory.CreateLogger<SdkClient>();
// Create and configure the SDK client
var sdk = SdkClient.Init(logger);
await sdk.SetAddressAsync("ws://localhost:8080/myko");
await sdk.AwaitConnectionAsync();
// Create an instance
var instance = await sdk.AddInstanceAsync(new InstanceArgs
{
Name = "My Application",
ShortId = "my-app",
Code = "my-app-code",
ServiceId = "my-service",
Color = "#FF0000",
MachineId = "my-machine",
Status = InstanceStatus.Available
});
// Create a target
var target = await instance.AddTargetAsync(new TargetArgs
{
Name = "My Target",
ShortId = "my-target",
Category = "automation"
});
// Add an action handler
await target.AddActionAsync(
ActionArgs<MyActionData>.New("Process Data", "process-data"),
async (action, data) =>
{
Console.WriteLine($"Processing: {data.Data}");
// Your action logic here
}
);
// Add an emitter
var emitter = await target.AddEmitterAsync(
EmitterArgs<MyEmitterType>.New("Status Updates", "status")
);
// Send data via emitter
await emitter.PulseAsync(new MyEmitterType
{
Data = "Hello from C#!"
});
📚 API Reference
SdkClient
The main entry point for the SDK. Manages WebSocket connections and provides methods to create instances.
Methods
static SdkClient Init(ILogger<SdkClient>? logger = null)
Creates a new SDK client instance.
// Without logging
var sdk = SdkClient.Init();
// With logging
using var loggerFactory = LoggerFactory.Create(builder =>
builder.AddConsole().SetMinimumLevel(LogLevel.Information));
var logger = loggerFactory.CreateLogger<SdkClient>();
var sdk = SdkClient.Init(logger);
async Task SetAddressAsync(string url)
Connects to the Rship server at the specified WebSocket URL.
await sdk.SetAddressAsync("ws://localhost:8080/myko");
async Task AwaitConnectionAsync()
Waits until the WebSocket connection is successfully established.
await sdk.AwaitConnectionAsync();
async Task<InstanceProxy> AddInstanceAsync(InstanceArgs args)
Creates and registers a new service instance.
var instance = await sdk.AddInstanceAsync(new InstanceArgs
{
Name = "My Service",
ShortId = "my-service-instance",
Code = "my-service-code",
ServiceId = "my-service",
Color = "#FF5722",
MachineId = "my-machine",
Status = InstanceStatus.Available
});
InstanceArgs
Configuration for creating a service instance.
Properties
Property | Type | Description |
---|---|---|
Name |
string |
Human-readable name for the instance |
ShortId |
string |
Unique short identifier for the instance |
Code |
string |
Service type code |
ServiceId |
string |
Service identifier |
ClusterId |
string? |
Optional cluster identifier |
Color |
string |
Hex color code for UI representation |
MachineId |
string |
Machine identifier where instance runs |
Message |
string? |
Optional status message |
Status |
InstanceStatus |
Current instance status |
InstanceStatus Enum
public enum InstanceStatus
{
Available, // Instance is ready to process requests
Busy, // Instance is currently processing
Offline, // Instance is not available
Error // Instance encountered an error
}
InstanceProxy
Represents a registered service instance and provides methods to manage targets.
Methods
async Task<TargetProxy> AddTargetAsync(TargetArgs args)
Creates and registers a new target within this instance.
var target = await instance.AddTargetAsync(new TargetArgs
{
Name = "Video Controller",
ShortId = "video-ctrl",
Category = "media"
});
Properties
Property | Type | Description |
---|---|---|
ShortId |
string |
The instance's short identifier |
ServiceId |
string |
The service identifier |
TargetArgs
Configuration for creating a target.
Properties
Property | Type | Description |
---|---|---|
Name |
string |
Human-readable name for the target |
ShortId |
string |
Unique short identifier for the target |
Category |
string |
Category for organizing targets |
TargetProxy
Represents a registered target and provides methods to manage actions and emitters.
Methods
async Task<ActionProxy<T>> AddActionAsync<T>(ActionArgs<T> args, Func<Action, T, Task> handler)
Adds a typed action handler to the target.
await target.AddActionAsync(
ActionArgs<PlayVideoData>.New("Play Video", "play-video"),
async (action, data) =>
{
Console.WriteLine($"Playing video: {data.VideoPath}");
// Your video playback logic here
await PlayVideoAsync(data.VideoPath, data.Volume);
}
);
async Task<EmitterProxy<T>> AddEmitterAsync<T>(EmitterArgs<T> args)
Adds a typed emitter to the target for sending data.
var statusEmitter = await target.AddEmitterAsync(
EmitterArgs<VideoStatusData>.New("Video Status", "video-status")
);
Properties
Property | Type | Description |
---|---|---|
ShortId |
string |
The target's short identifier |
Instance |
InstanceProxy |
Reference to the parent instance |
ActionArgs<T>
Configuration for creating a typed action.
Methods
static ActionArgs<T> New(string name, string shortId)
Creates a new action configuration.
var actionArgs = ActionArgs<MyDataType>.New("Process Data", "process-data");
Properties
Property | Type | Description |
---|---|---|
Name |
string |
Human-readable name for the action |
ShortId |
string |
Unique short identifier for the action |
ActionProxy<T>
Represents a registered action handler.
Properties
Property | Type | Description |
---|---|---|
ShortId |
string |
The action's short identifier |
EmitterArgs<T>
Configuration for creating a typed emitter.
Methods
static EmitterArgs<T> New(string name, string shortId)
Creates a new emitter configuration.
var emitterArgs = EmitterArgs<StatusData>.New("Status Updates", "status");
Properties
Property | Type | Description |
---|---|---|
Name |
string |
Human-readable name for the emitter |
ShortId |
string |
Unique short identifier for the emitter |
EmitterProxy<T>
Represents a registered emitter for sending typed data.
Methods
async Task PulseAsync(T data)
Sends data through the emitter to connected clients.
await emitter.PulseAsync(new VideoStatusData
{
Status = "playing",
Progress = 0.5,
Duration = TimeSpan.FromMinutes(10)
});
Properties
Property | Type | Description |
---|---|---|
ShortId |
string |
The emitter's short identifier |
💡 Complete Examples
Video Player Service
using Microsoft.Extensions.Logging;
using RshipSdk;
using RshipSdk.Entities;
using System.Text.Json.Serialization;
// Define data models
public class PlayVideoRequest
{
[JsonPropertyName("videoPath")]
public string VideoPath { get; set; } = "";
[JsonPropertyName("volume")]
public double Volume { get; set; } = 1.0;
[JsonPropertyName("autoPlay")]
public bool AutoPlay { get; set; } = true;
}
public class VideoStatus
{
[JsonPropertyName("status")]
public string Status { get; set; } = "";
[JsonPropertyName("progress")]
public double Progress { get; set; }
[JsonPropertyName("duration")]
public double Duration { get; set; }
}
public class VideoPlayerService
{
private EmitterProxy<VideoStatus>? _statusEmitter;
public async Task StartAsync()
{
// Setup logging
using var loggerFactory = LoggerFactory.Create(builder =>
builder.AddConsole().SetMinimumLevel(LogLevel.Information));
var logger = loggerFactory.CreateLogger<SdkClient>();
// Initialize SDK
var sdk = SdkClient.Init(logger);
await sdk.SetAddressAsync("ws://localhost:8080/myko");
await sdk.AwaitConnectionAsync();
// Create instance
var instance = await sdk.AddInstanceAsync(new InstanceArgs
{
Name = "Video Player Service",
ShortId = "video-player-001",
Code = "video-player",
ServiceId = "video-service",
Color = "#9C27B0",
MachineId = Environment.MachineName,
Status = InstanceStatus.Available
});
// Create target
var videoTarget = await instance.AddTargetAsync(new TargetArgs
{
Name = "Video Controller",
ShortId = "video-ctrl",
Category = "media"
});
// Add play action
await videoTarget.AddActionAsync(
ActionArgs<PlayVideoRequest>.New("Play Video", "play"),
HandlePlayVideo
);
// Add pause action
await videoTarget.AddActionAsync(
ActionArgs<object>.New("Pause Video", "pause"),
HandlePauseVideo
);
// Add status emitter
_statusEmitter = await videoTarget.AddEmitterAsync(
EmitterArgs<VideoStatus>.New("Video Status", "status")
);
Console.WriteLine("Video Player Service is ready!");
// Keep running
await Task.Delay(Timeout.Infinite);
}
private async Task HandlePlayVideo(Entities.Action action, PlayVideoRequest request)
{
Console.WriteLine($"Playing video: {request.VideoPath} at volume {request.Volume}");
// Simulate video playback
await EmitStatus("playing", 0.0, 300.0);
// Your actual video playback logic would go here
await SimulateVideoPlayback();
}
private async Task HandlePauseVideo(Entities.Action action, object data)
{
Console.WriteLine("Pausing video");
await EmitStatus("paused", 0.25, 300.0);
}
private async Task EmitStatus(string status, double progress, double duration)
{
if (_statusEmitter != null)
{
await _statusEmitter.PulseAsync(new VideoStatus
{
Status = status,
Progress = progress,
Duration = duration
});
}
}
private async Task SimulateVideoPlayback()
{
// Simulate progress updates
for (double progress = 0; progress <= 1.0; progress += 0.1)
{
await EmitStatus("playing", progress, 300.0);
await Task.Delay(1000);
}
await EmitStatus("finished", 1.0, 300.0);
}
}
IoT Sensor Monitor
public class SensorData
{
[JsonPropertyName("temperature")]
public double Temperature { get; set; }
[JsonPropertyName("humidity")]
public double Humidity { get; set; }
[JsonPropertyName("timestamp")]
public DateTime Timestamp { get; set; }
}
public class CalibrationRequest
{
[JsonPropertyName("temperatureOffset")]
public double TemperatureOffset { get; set; }
[JsonPropertyName("humidityOffset")]
public double HumidityOffset { get; set; }
}
public class IoTSensorService
{
private EmitterProxy<SensorData>? _sensorEmitter;
private readonly Random _random = new();
private double _tempOffset = 0;
private double _humidityOffset = 0;
public async Task StartAsync()
{
var sdk = SdkClient.Init();
await sdk.SetAddressAsync("ws://localhost:8080/myko");
await sdk.AwaitConnectionAsync();
var instance = await sdk.AddInstanceAsync(new InstanceArgs
{
Name = "IoT Sensor Monitor",
ShortId = "iot-sensor-001",
Code = "iot-sensor",
ServiceId = "iot-service",
Color = "#4CAF50",
MachineId = Environment.MachineName,
Status = InstanceStatus.Available
});
var sensorTarget = await instance.AddTargetAsync(new TargetArgs
{
Name = "Environmental Sensor",
ShortId = "env-sensor",
Category = "iot"
});
// Add calibration action
await sensorTarget.AddActionAsync(
ActionArgs<CalibrationRequest>.New("Calibrate Sensor", "calibrate"),
async (action, request) =>
{
_tempOffset = request.TemperatureOffset;
_humidityOffset = request.HumidityOffset;
Console.WriteLine($"Sensor calibrated: Temp offset {_tempOffset}°C, Humidity offset {_humidityOffset}%");
}
);
// Add sensor data emitter
_sensorEmitter = await sensorTarget.AddEmitterAsync(
EmitterArgs<SensorData>.New("Sensor Readings", "readings")
);
// Start sensor reading loop
_ = Task.Run(ReadSensorDataLoop);
Console.WriteLine("IoT Sensor Service is running!");
await Task.Delay(Timeout.Infinite);
}
private async Task ReadSensorDataLoop()
{
while (true)
{
var sensorData = new SensorData
{
Temperature = 20 + _random.NextDouble() * 10 + _tempOffset, // 20-30°C
Humidity = 40 + _random.NextDouble() * 20 + _humidityOffset, // 40-60%
Timestamp = DateTime.UtcNow
};
if (_sensorEmitter != null)
{
await _sensorEmitter.PulseAsync(sensorData);
}
await Task.Delay(5000); // Read every 5 seconds
}
}
}
Core Concepts
SdkClient
The main entry point for the SDK. Manages WebSocket connections to the Rship server and handles automatic reconnection. All instances are created through the SDK client.
Instance
Represents a service instance in the Rship ecosystem. Each instance has a unique identifier and can contain multiple targets. Instances automatically register with the server and maintain their status.
Target
A logical grouping of actions and emitters within an instance. Targets represent controllable entities in your application and organize functionality by category (e.g., "media", "automation", "iot").
Actions
Incoming message handlers that define what your application can do when triggered by the Rship platform. Actions are strongly typed and include automatic JSON schema generation for validation.
Emitters
Outgoing message producers that send structured data from your application to the Rship platform. Emitters support real-time data streaming to connected clients.
🔧 Advanced Configuration
Environment Variables
The SDK supports the following environment variables:
Variable | Default | Description |
---|---|---|
RSHIP_ADDRESS |
localhost |
Rship server hostname |
RSHIP_PORT |
8080 |
Rship server port |
Custom Logging Configuration
using Microsoft.Extensions.Logging;
// Console logging with custom formatting
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddConsole(options =>
{
options.IncludeScopes = true;
options.TimestampFormat = "[yyyy-MM-dd HH:mm:ss] ";
})
.AddDebug()
.SetMinimumLevel(LogLevel.Debug)
.AddFilter("RshipSdk", LogLevel.Information)
.AddFilter("MykoSdk", LogLevel.Warning);
});
var logger = loggerFactory.CreateLogger<SdkClient>();
var sdk = SdkClient.Init(logger);
Connection Management
// Wait for connection with timeout
var connectionTask = sdk.AwaitConnectionAsync();
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(30));
if (await Task.WhenAny(connectionTask, timeoutTask) == timeoutTask)
{
throw new TimeoutException("Failed to connect within 30 seconds");
}
Console.WriteLine("Successfully connected to Rship server");
🛠️ Error Handling
Exception Types
The SDK throws standard .NET exceptions in various scenarios:
InvalidOperationException
: Connection issues or SDK not properly initializedJsonException
: JSON serialization/deserialization errorsTaskCanceledException
: Operation timeout or cancellationArgumentException
: Invalid parameters passed to methods
Comprehensive Error Handling
public async Task HandleActionsWithErrorHandling()
{
try
{
// Add action with error handling
await target.AddActionAsync(
ActionArgs<RiskyOperation>.New("Risky Operation", "risky-op"),
async (action, data) =>
{
try
{
// Your risky operation here
await PerformRiskyOperation(data);
}
catch (Exception ex)
{
// Log the error
Console.WriteLine($"Error in action {action.Name}: {ex.Message}");
// Optionally emit error status
if (_errorEmitter != null)
{
await _errorEmitter.PulseAsync(new ErrorData
{
ActionId = action.Id,
Error = ex.Message,
Timestamp = DateTime.UtcNow
});
}
}
}
);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("connection"))
{
Console.WriteLine("Connection lost, attempting to reconnect...");
await sdk.AwaitConnectionAsync();
}
catch (JsonException ex)
{
Console.WriteLine($"JSON serialization error: {ex.Message}");
}
}
// Robust emitter with retry logic
public async Task EmitWithRetry<T>(EmitterProxy<T> emitter, T data, int maxRetries = 3)
{
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
await emitter.PulseAsync(data);
return; // Success
}
catch (InvalidOperationException ex) when (attempt < maxRetries)
{
Console.WriteLine($"Emit failed (attempt {attempt}/{maxRetries}): {ex.Message}");
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt))); // Exponential backoff
}
}
throw new InvalidOperationException($"Failed to emit after {maxRetries} attempts");
}
Graceful Shutdown
public class GracefulService
{
private SdkClient? _sdk;
private CancellationTokenSource _cancellationTokenSource = new();
public async Task StartAsync()
{
_sdk = SdkClient.Init();
// Handle shutdown signals
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
_cancellationTokenSource.Cancel();
};
try
{
await RunServiceAsync(_cancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Service is shutting down gracefully...");
}
finally
{
_sdk?.Dispose();
}
}
private async Task RunServiceAsync(CancellationToken cancellationToken)
{
// Your service logic here
while (!cancellationToken.IsCancellationRequested)
{
// Perform work
await Task.Delay(1000, cancellationToken);
}
}
}
Environment Variables
RSHIP_ADDRESS
: The Rship server address (default: localhost)RSHIP_PORT
: The Rship server port (default: 8080)
Example using environment variables:
// Load from environment or use defaults
var address = Environment.GetEnvironmentVariable("RSHIP_ADDRESS") ?? "localhost";
var port = Environment.GetEnvironmentVariable("RSHIP_PORT") ?? "8080";
var url = $"ws://{address}:{port}/myko";
await sdk.SetAddressAsync(url);
🔧 Advanced Usage
Custom Logging
using Microsoft.Extensions.Logging;
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddConsole()
.AddDebug()
.SetMinimumLevel(LogLevel.Debug);
});
var sdk = SdkClient.Init(loggerFactory.CreateLogger<SdkClient>());
Error Handling
try
{
await emitter.PulseAsync(data);
}
catch (InvalidOperationException ex)
{
// Handle connection issues
Console.WriteLine($"Connection error: {ex.Message}");
}
Connection Status Monitoring
// The SDK automatically handles reconnection
await sdk.AwaitConnectionAsync(); // Blocks until connected
JSON Schema Validation
The SDK automatically generates JSON schemas for your data types:
public class ComplexData
{
[JsonPropertyName("id")]
public required string Id { get; set; }
[JsonPropertyName("values")]
public List<double> Values { get; set; } = new();
[JsonPropertyName("metadata")]
public Dictionary<string, object> Metadata { get; set; } = new();
[JsonPropertyName("timestamp")]
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}
// Schema is automatically generated when adding actions/emitters
await target.AddActionAsync(
ActionArgs<ComplexData>.New("Process Complex Data", "process-complex"),
async (action, data) =>
{
// Data is automatically validated against the schema
Console.WriteLine($"Processing {data.Values.Count} values for ID: {data.Id}");
}
);
Recommended Project Structure Example
// Models/VideoModels.cs
namespace MyRshipApp.Models;
public class PlayVideoRequest
{
[JsonPropertyName("path")]
public required string Path { get; set; }
[JsonPropertyName("volume")]
public double Volume { get; set; } = 1.0;
}
public class VideoStatusUpdate
{
[JsonPropertyName("status")]
public required string Status { get; set; }
[JsonPropertyName("progress")]
public double Progress { get; set; }
}
// Services/VideoService.cs
namespace MyRshipApp.Services;
public class VideoService
{
private readonly ILogger<VideoService> _logger;
public VideoService(ILogger<VideoService> logger)
{
_logger = logger;
}
public async Task PlayVideoAsync(string path, double volume)
{
_logger.LogInformation("Playing video: {Path} at volume {Volume}", path, volume);
// Your video playback logic
await Task.CompletedTask;
}
}
// Handlers/VideoHandlers.cs
namespace MyRshipApp.Handlers;
public class VideoHandlers
{
private readonly VideoService _videoService;
private readonly EmitterProxy<VideoStatusUpdate> _statusEmitter;
public VideoHandlers(VideoService videoService, EmitterProxy<VideoStatusUpdate> statusEmitter)
{
_videoService = videoService;
_statusEmitter = statusEmitter;
}
public async Task HandlePlayVideo(RshipSdk.Entities.Action action, PlayVideoRequest request)
{
await _videoService.PlayVideoAsync(request.Path, request.Volume);
await _statusEmitter.PulseAsync(new VideoStatusUpdate
{
Status = "playing",
Progress = 0.0
});
}
}
Running Examples
cd Examples
dotnet restore
dotnet run
Dependencies
- .NET 8.0+
- System.Text.Json
- Microsoft.Extensions.Logging.Abstractions
- Websocket.Client
- NJsonSchema
License
AGPL-3.0-or-later
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- MykoSdk (>= 1.0.19)
- NJsonSchema (>= 11.0.2)
- System.Text.Json (>= 9.0.0)
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 |
---|---|---|
1.0.19 | 133 | 6/30/2025 |
1.0.18 | 131 | 6/30/2025 |
1.0.17 | 136 | 6/30/2025 |
1.0.16 | 129 | 6/30/2025 |
1.0.15 | 131 | 6/30/2025 |
1.0.14 | 130 | 6/30/2025 |
1.0.13 | 135 | 6/30/2025 |
1.0.11 | 67 | 6/27/2025 |
1.0.10 | 71 | 6/27/2025 |
1.0.9 | 134 | 6/25/2025 |
1.0.8 | 134 | 6/24/2025 |
1.0.7 | 135 | 6/24/2025 |
1.0.5 | 134 | 6/24/2025 |
1.0.4 | 131 | 6/24/2025 |
1.0.3 | 134 | 6/24/2025 |
1.0.0 | 136 | 6/23/2025 |