Skip to content

Commit a66d687

Browse files
Rafal Maciagclaude
andcommitted
Add explicit NNG sink URL configuration support
- Add SEGMENTATION_SINK_URL, KEYPOINTS_SINK_URL, ACTIONS_SINK_URL env vars - Python SDK: get_nng_urls_from_env(), get_configured_nng_urls(), has_explicit_nng_urls() - C# SDK: LogNngConfiguration() for startup URL logging - Both SDKs now log NNG URL configuration at startup for debugging - Priority: explicit URLs > SessionId-derived URLs (backwards compatible) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 79dd9a2 commit a66d687

33 files changed

+859
-138
lines changed

build_docker_samples.sh

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ PYTHON_EXAMPLES=(
5858
"07-simple-with-data:simple-with-data:false"
5959
)
6060

61+
# C# examples definition: folder:name
62+
CSHARP_EXAMPLES=(
63+
"SimpleClient:simple"
64+
"BallDetection:ball-detection"
65+
)
66+
6167
print_info() { echo -e "${CYAN}$1${NC}"; }
6268
print_success() { echo -e "${GREEN}$1${NC}"; }
6369
print_error() { echo -e "${RED}$1${NC}"; }
@@ -132,6 +138,12 @@ while [[ $# -gt 0 ]]; do
132138
echo " --example NAME Build only specific example (e.g., 01-simple, yolo)"
133139
echo " --help Show this help message"
134140
echo ""
141+
echo "C# examples:"
142+
for example in "${CSHARP_EXAMPLES[@]}"; do
143+
IFS=':' read -r folder name <<< "$example"
144+
echo " - $folder ($name)"
145+
done
146+
echo ""
135147
echo "Python examples:"
136148
for example in "${PYTHON_EXAMPLES[@]}"; do
137149
IFS=':' read -r folder name needs_gpu <<< "$example"
@@ -179,30 +191,51 @@ if [ -n "$EXAMPLE_FILTER" ]; then
179191
echo " Example filter: ${EXAMPLE_FILTER}"
180192
fi
181193

182-
# Build C# sample client image
183-
if [ "$BUILD_CSHARP" = true ] && [ -z "$EXAMPLE_FILTER" ]; then
184-
print_section "Building C# Sample Client Docker Image"
194+
# Build C# sample client images
195+
if [ "$BUILD_CSHARP" = true ]; then
196+
cd "${SCRIPT_DIR}/csharp"
185197

186-
if [ "$USE_PLATFORM_TAG" = true ]; then
187-
CSHARP_IMAGE_TAG="${TAG_PREFIX}-client-csharp-${PLATFORM}:${TAG_VERSION}"
188-
else
189-
CSHARP_IMAGE_TAG="${TAG_PREFIX}-client-csharp:${TAG_VERSION}"
190-
fi
198+
for example in "${CSHARP_EXAMPLES[@]}"; do
199+
IFS=':' read -r folder name <<< "$example"
191200

192-
print_info "Building image: ${CSHARP_IMAGE_TAG}"
193-
cd "${SCRIPT_DIR}/csharp"
201+
# Skip if filter is set and doesn't match
202+
if [ -n "$EXAMPLE_FILTER" ]; then
203+
if [[ "$folder" != *"$EXAMPLE_FILTER"* ]] && [[ "$name" != *"$EXAMPLE_FILTER"* ]]; then
204+
continue
205+
fi
206+
fi
194207

195-
docker build ${DOCKER_BUILD_ARGS} \
196-
-t "${CSHARP_IMAGE_TAG}" \
197-
-f examples/SimpleClient/Dockerfile \
198-
.
199-
200-
if [ $? -eq 0 ]; then
201-
print_success "C# Docker image built successfully: ${CSHARP_IMAGE_TAG}"
202-
else
203-
print_error "Failed to build C# Docker image"
204-
exit 1
205-
fi
208+
# Check if example folder exists
209+
if [ ! -d "examples/$folder" ]; then
210+
print_warning "C# example folder not found: examples/$folder - skipping"
211+
continue
212+
fi
213+
214+
# Check if Dockerfile exists
215+
if [ ! -f "examples/$folder/Dockerfile" ]; then
216+
print_warning "No Dockerfile found in examples/$folder - skipping"
217+
continue
218+
fi
219+
220+
print_section "Building C# Example: $folder ($name)"
221+
222+
if [ "$USE_PLATFORM_TAG" = true ]; then
223+
CSHARP_IMAGE_TAG="${TAG_PREFIX}-client-csharp-${name}-${PLATFORM}:${TAG_VERSION}"
224+
else
225+
CSHARP_IMAGE_TAG="${TAG_PREFIX}-client-csharp-${name}:${TAG_VERSION}"
226+
fi
227+
228+
print_info "Building: ${CSHARP_IMAGE_TAG}"
229+
if docker build ${DOCKER_BUILD_ARGS} \
230+
-t "${CSHARP_IMAGE_TAG}" \
231+
-f "examples/$folder/Dockerfile" \
232+
.; then
233+
print_success "Built: ${CSHARP_IMAGE_TAG}"
234+
else
235+
print_error "Failed to build: ${CSHARP_IMAGE_TAG}"
236+
exit 1
237+
fi
238+
done
206239
fi
207240

208241
# Build Python sample client images
@@ -236,12 +269,10 @@ if [ "$BUILD_PYTHON" = true ]; then
236269
fi
237270

238271
print_info "Building: ${IMAGE_TAG}"
239-
docker build ${DOCKER_BUILD_ARGS} \
272+
if docker build ${DOCKER_BUILD_ARGS} \
240273
-t "${IMAGE_TAG}" \
241274
-f "examples/$folder/Dockerfile" \
242-
.
243-
244-
if [ $? -eq 0 ]; then
275+
.; then
245276
print_success "Built: ${IMAGE_TAG}"
246277
else
247278
print_error "Failed to build: ${IMAGE_TAG}"
@@ -254,12 +285,10 @@ if [ "$BUILD_PYTHON" = true ]; then
254285
JETSON_IMAGE_TAG="${TAG_PREFIX}-client-python-${name}:jetson"
255286

256287
print_info "Building Jetson variant: ${JETSON_IMAGE_TAG}"
257-
docker build ${DOCKER_BUILD_ARGS} \
288+
if docker build ${DOCKER_BUILD_ARGS} \
258289
-t "${JETSON_IMAGE_TAG}" \
259290
-f "examples/$folder/Dockerfile.jetson" \
260-
.
261-
262-
if [ $? -eq 0 ]; then
291+
.; then
263292
print_success "Built: ${JETSON_IMAGE_TAG}"
264293
else
265294
print_error "Failed to build: ${JETSON_IMAGE_TAG}"
@@ -272,12 +301,10 @@ if [ "$BUILD_PYTHON" = true ]; then
272301
PYTHON38_IMAGE_TAG="${TAG_PREFIX}-client-python-${name}:python38"
273302

274303
print_info "Building Python 3.8 variant: ${PYTHON38_IMAGE_TAG}"
275-
docker build ${DOCKER_BUILD_ARGS} \
304+
if docker build ${DOCKER_BUILD_ARGS} \
276305
-t "${PYTHON38_IMAGE_TAG}" \
277306
-f "examples/$folder/Dockerfile.python38" \
278-
.
279-
280-
if [ $? -eq 0 ]; then
307+
.; then
281308
print_success "Built: ${PYTHON38_IMAGE_TAG}"
282309
else
283310
print_error "Failed to build: ${PYTHON38_IMAGE_TAG}"

csharp/RocketWelder.SDK/RocketWelderClient.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,20 @@ private RocketWelderClient(ConnectionString connection, ILoggerFactory? loggerFa
731731
?? Environment.GetEnvironmentVariable(RocketWelderConfigKeys.KeyPointsSinkUrlEnv);
732732
}
733733

734+
/// <summary>
735+
/// Logs the NNG sink URL configuration at startup for debugging.
736+
/// </summary>
737+
private void LogNngConfiguration()
738+
{
739+
var segUrl = GetSegmentationSinkUrl();
740+
var kpUrl = GetKeyPointsSinkUrl();
741+
742+
_logger.LogInformation(
743+
"NNG sink URLs configured: seg={SegUrl}, kp={KpUrl}",
744+
segUrl ?? "(not configured)",
745+
kpUrl ?? "(not configured)");
746+
}
747+
734748
/// <summary>
735749
/// Creates or returns the segmentation result sink.
736750
/// </summary>
@@ -1016,6 +1030,9 @@ public void Start(Action<Mat, ISegmentationResultWriter, IKeyPointsWriter, Mat>
10161030
{
10171031
_logger.LogInformation("Starting RocketWelder client with AI output support: {Connection}", Connection);
10181032

1033+
// Log NNG sink URL configuration at startup (for debugging)
1034+
LogNngConfiguration();
1035+
10191036
// Initialize sinks (will throw if not configured)
10201037
var segSink = GetOrCreateSegmentationSink();
10211038
var kpSink = GetOrCreateKeyPointsSink();
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net10.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
9+
<PublishSingleFile>false</PublishSingleFile>
10+
<SelfContained>false</SelfContained>
11+
<!-- Force all NuGet dependencies including native libraries to be copied -->
12+
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
13+
<!-- Include native libraries for proper extraction -->
14+
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
15+
</PropertyGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="../../RocketWelder.SDK/RocketWelder.SDK.csproj" />
19+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
20+
<PackageReference Include="Emgu.CV" Version="4.11.0.5746" />
21+
<PackageReference Include="Emgu.CV.runtime.ubuntu-x64" Version="4.11.0.5746" />
22+
</ItemGroup>
23+
24+
<!-- Manually include native libraries from Emgu.CV.runtime.ubuntu-x64 -->
25+
<ItemGroup>
26+
<Content Include="$(NuGetPackageRoot)emgu.cv.runtime.ubuntu-x64/4.11.0.5746/runtimes/ubuntu-x64/native/*">
27+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
28+
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
29+
<Link>runtimes/ubuntu-x64/native/%(Filename)%(Extension)</Link>
30+
</Content>
31+
</ItemGroup>
32+
33+
</Project>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Multi-stage build for C# BallDetection example
2+
# Sink-only example - detects ball and outputs via NNG (no frame modification)
3+
FROM mcr.microsoft.com/dotnet/sdk:10.0-noble AS build
4+
WORKDIR /src
5+
6+
# Copy the SDK project files first
7+
COPY RocketWelder.SDK/RocketWelder.SDK.csproj RocketWelder.SDK/
8+
9+
# Copy the BallDetection project file
10+
COPY examples/BallDetection/BallDetection.csproj examples/BallDetection/
11+
12+
# Restore dependencies
13+
WORKDIR /src
14+
RUN dotnet restore examples/BallDetection/BallDetection.csproj
15+
16+
# Copy the source code
17+
COPY RocketWelder.SDK/ RocketWelder.SDK/
18+
COPY examples/BallDetection/ examples/BallDetection/
19+
20+
# Build and publish
21+
WORKDIR /src/examples/BallDetection
22+
RUN dotnet publish -c Release -o /app/publish
23+
24+
# Runtime stage - Using Ubuntu 24.04 (Noble) for GLIBC 2.38+ compatibility
25+
FROM mcr.microsoft.com/dotnet/runtime:10.0-noble
26+
WORKDIR /app
27+
28+
# Copy published app first (to leverage cache for apt-get layer)
29+
COPY --from=build /app/publish .
30+
31+
# Install all dependencies in a single RUN command to reduce layers
32+
RUN apt-get update && apt-get install -y --no-install-recommends \
33+
# Core dependencies
34+
libgomp1 \
35+
libgdiplus \
36+
libc6-dev \
37+
libicu-dev \
38+
libssl-dev \
39+
ca-certificates \
40+
# OpenCV dependencies
41+
libgtk-3-0 \
42+
libavcodec-dev \
43+
libavformat-dev \
44+
libswscale-dev \
45+
libv4l-dev \
46+
libxvidcore-dev \
47+
libx264-dev \
48+
libjpeg-dev \
49+
libpng-dev \
50+
libtiff-dev \
51+
libatlas-base-dev \
52+
gfortran \
53+
libgstreamer1.0-0 \
54+
libgstreamer-plugins-base1.0-0 \
55+
# EmguCV/OpenCV native dependencies
56+
libgeotiff5 \
57+
libdc1394-25 \
58+
libopenexr-3-1-30 \
59+
libhdf5-103-1 \
60+
libvtk9.1t64 \
61+
# X11 for preview
62+
libx11-6 \
63+
libxext6 \
64+
libxrender1 \
65+
libxtst6 \
66+
libxi6 \
67+
libxrandr2 \
68+
libxcursor1 \
69+
libxinerama1 \
70+
libxkbcommon-x11-0 \
71+
libglu1-mesa \
72+
# Debugging tools
73+
procps \
74+
iputils-ping \
75+
net-tools \
76+
&& rm -rf /var/lib/apt/lists/*
77+
78+
# Create symlink for Emgu.CV native library
79+
RUN ln -s /app/runtimes/ubuntu-x64/native/libcvextern.so /app/libcvextern.so || true
80+
81+
# Ensure Emgu.CV native libraries are accessible
82+
ENV LD_LIBRARY_PATH=/app/runtimes/ubuntu-x64/native:/app:${LD_LIBRARY_PATH:-}
83+
84+
# Set up logging
85+
ENV ZEROBUFFER_LOG_LEVEL=INFO
86+
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
87+
88+
# Disable RocketWelder UI by default (sink-only example doesn't need UI)
89+
ENV DisableRocketWelderUI=true
90+
91+
# Health check
92+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
93+
CMD pgrep -f BallDetection || exit 1
94+
95+
# Entry point
96+
ENTRYPOINT ["dotnet", "BallDetection.dll"]

0 commit comments

Comments
 (0)