feat: add test server example and update project structure

This commit is contained in:
ProtoTess
2025-11-17 02:56:26 +00:00
parent ae891db72b
commit 42b875ce2b
5 changed files with 116 additions and 303 deletions
+3
View File
@@ -22,11 +22,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- **Project Structure**: Implemented ideal Go project layout
- Moved `soap/` to `internal/soap/` (private implementation)
- Moved `test/test-server.go` to `examples/test-server/` for clarity
- Removed empty `test/` directory
- Public API remains at root level for clean imports
- Follows Standard Go Project Layout for libraries
- Updated all imports throughout codebase
- See `docs/PROJECT_STRUCTURE.md` and `docs/ARCHITECTURE.md` for details
- Updated `docs/ARCHITECTURE.md` to reflect new project structure
- Updated module path from `github.com/0x524A/onvif-go` to `github.com/0x524a/onvif-go` (lowercase)
- ONVIF Client with context support
- Device service implementation
- GetDeviceInformation
+1 -1
View File
@@ -20,7 +20,7 @@ onvif-go/
├── cmd/ # Command-line tools
├── examples/ # Usage examples
├── docs/ # Documentation
├── test/ # Testing utilities
├── testing/ # Testing helpers
└── testdata/ # Test fixtures
```
+1 -1
View File
@@ -50,6 +50,7 @@ onvif-go/
│ ├── imaging-settings/ # Imaging configuration
│ ├── complete-demo/ # Full feature demo
│ ├── simplified-endpoint/ # Endpoint format demo
│ ├── test-server/ # Server testing example
│ └── .../ # Additional examples
├── docs/ # Documentation
@@ -58,7 +59,6 @@ onvif-go/
│ ├── SIMPLIFIED_ENDPOINT.md # Endpoint API docs
│ └── .../ # Additional documentation
├── test/ # Additional test utilities
├── testdata/ # Test fixtures and data
├── testing/ # Testing helpers
+111 -138
View File
@@ -7,184 +7,157 @@ import (
"time"
"github.com/0x524a/onvif-go"
"github.com/0x524a/onvif-go/server"
)
func main() {
fmt.Println("🧪 Testing ONVIF Server Implementation")
fmt.Println("======================================")
fmt.Println("🧪 Testing ONVIF Server with Client Library")
fmt.Println("===========================================")
fmt.Println()
// Create and start server in background
config := server.DefaultConfig()
config.Port = 8081 // Use different port to avoid conflicts
srv, err := server.New(config)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Start server in background
serverReady := make(chan bool)
go func() {
// Give server a moment to start
time.Sleep(500 * time.Millisecond)
serverReady <- true
if err := srv.Start(ctx); err != nil {
log.Printf("Server error: %v", err)
}
}()
// Wait for server to be ready
<-serverReady
fmt.Println("✅ Server started on port 8081")
fmt.Println()
// Create ONVIF client
// Create client
client, err := onvif.NewClient(
"http://localhost:8081/onvif/device_service",
"http://localhost:8080/onvif/device_service",
onvif.WithCredentials("admin", "admin"),
onvif.WithTimeout(10*time.Second),
onvif.WithTimeout(30*time.Second),
)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
log.Fatalf("Failed to create client: %v", err)
}
testCtx := context.Background()
ctx := context.Background()
// Test 1: Get Device Information
fmt.Println("Test 1: GetDeviceInformation")
info, err := client.GetDeviceInformation(testCtx)
// Test 1: Get device information
fmt.Println("📋 Test 1: Getting Device Information...")
info, err := client.GetDeviceInformation(ctx)
if err != nil {
log.Fatalf("❌ GetDeviceInformation failed: %v", err)
log.Fatalf("❌ Failed to get device info: %v", err)
}
fmt.Printf("✅ Device: %s %s (Firmware: %s)\n", info.Manufacturer, info.Model, info.FirmwareVersion)
fmt.Printf("✅ Device: %s %s\n", info.Manufacturer, info.Model)
fmt.Printf(" Firmware: %s\n", info.FirmwareVersion)
fmt.Printf(" Serial: %s\n", info.SerialNumber)
fmt.Println()
// Test 2: Get Capabilities
fmt.Println("Test 2: GetCapabilities")
if err := client.Initialize(testCtx); err != nil {
log.Fatalf("❌ Initialize (GetCapabilities) failed: %v", err)
// Test 2: Initialize and discover services
fmt.Println("🔍 Test 2: Discovering Services...")
if err := client.Initialize(ctx); err != nil {
log.Fatalf("❌ Failed to initialize: %v", err)
}
fmt.Println("✅ Capabilities retrieved successfully")
fmt.Println("✅ Services discovered successfully")
fmt.Println()
// Test 3: Get Profiles
fmt.Println("Test 3: GetProfiles")
profiles, err := client.GetProfiles(testCtx)
// Test 3: Get capabilities
fmt.Println("🔧 Test 3: Getting Capabilities...")
caps, err := client.GetCapabilities(ctx)
if err != nil {
log.Fatalf("❌ GetProfiles failed: %v", err)
log.Fatalf("❌ Failed to get capabilities: %v", err)
}
fmt.Printf("✅ Found %d profiles:\n", len(profiles))
fmt.Println("✅ Capabilities:")
if caps.Media != nil {
fmt.Println(" ✓ Media Service")
}
if caps.PTZ != nil {
fmt.Println(" ✓ PTZ Service")
}
if caps.Imaging != nil {
fmt.Println(" ✓ Imaging Service")
}
fmt.Println()
// Test 4: Get media profiles
fmt.Println("🎬 Test 4: Getting Media Profiles...")
profiles, err := client.GetProfiles(ctx)
if err != nil {
log.Fatalf("❌ Failed to get profiles: %v", err)
}
fmt.Printf("✅ Found %d camera profiles:\n", len(profiles))
for i, profile := range profiles {
fmt.Printf(" [%d] %s (Token: %s)\n", i+1, profile.Name, profile.Token)
fmt.Printf("\n Profile %d: %s\n", i+1, profile.Name)
fmt.Printf(" Token: %s\n", profile.Token)
if profile.VideoEncoderConfiguration != nil {
fmt.Printf(" Video: %dx%d @ %s\n",
fmt.Printf(" Video: %dx%d @ %s\n",
profile.VideoEncoderConfiguration.Resolution.Width,
profile.VideoEncoderConfiguration.Resolution.Height,
profile.VideoEncoderConfiguration.Encoding)
}
// Get stream URI
streamURI, err := client.GetStreamURI(ctx, profile.Token)
if err != nil {
fmt.Printf(" ⚠️ Failed to get stream URI: %v\n", err)
} else {
fmt.Printf(" RTSP: %s\n", streamURI.URI)
}
// Get snapshot URI if available
snapshotURI, err := client.GetSnapshotURI(ctx, profile.Token)
if err == nil {
fmt.Printf(" Snapshot: %s\n", snapshotURI.URI)
}
// Test PTZ if available
if profile.PTZConfiguration != nil {
fmt.Println(" PTZ: ✓ Enabled")
// Get PTZ status
status, err := client.GetStatus(ctx, profile.Token)
if err == nil {
fmt.Printf(" Position: Pan=%.1f°, Tilt=%.1f°, Zoom=%.2f\n",
status.Position.PanTilt.X,
status.Position.PanTilt.Y,
status.Position.Zoom.X)
}
// Get presets
presets, err := client.GetPresets(ctx, profile.Token)
if err == nil && len(presets) > 0 {
fmt.Printf(" Presets: %d available\n", len(presets))
}
}
}
fmt.Println()
// Test 4: Get Stream URI
if len(profiles) > 0 {
fmt.Println("Test 4: GetStreamURI")
streamURI, err := client.GetStreamURI(testCtx, profiles[0].Token)
if err != nil {
log.Fatalf("❌ GetStreamURI failed: %v", err)
}
fmt.Printf("✅ Stream URI: %s\n", streamURI.URI)
fmt.Println()
}
// Test 5: Get Snapshot URI
if len(profiles) > 0 {
fmt.Println("Test 5: GetSnapshotURI")
snapshotURI, err := client.GetSnapshotURI(testCtx, profiles[0].Token)
if err != nil {
log.Fatalf("❌ GetSnapshotURI failed: %v", err)
}
fmt.Printf("✅ Snapshot URI: %s\n", snapshotURI.URI)
fmt.Println()
}
// Test 6: PTZ Status (if PTZ is available)
// Test 5: PTZ control (if available)
if len(profiles) > 0 && profiles[0].PTZConfiguration != nil {
fmt.Println("Test 6: PTZ GetStatus")
status, err := client.GetStatus(testCtx, profiles[0].Token)
if err != nil {
log.Fatalf("❌ GetStatus failed: %v", err)
}
fmt.Printf("✅ PTZ Position: Pan=%.2f, Tilt=%.2f, Zoom=%.2f\n",
status.Position.PanTilt.X,
status.Position.PanTilt.Y,
status.Position.Zoom.X)
fmt.Println()
// Test 7: PTZ Absolute Move
fmt.Println("Test 7: PTZ AbsoluteMove")
fmt.Println("🎮 Test 5: Testing PTZ Control...")
profileToken := profiles[0].Token
// Absolute move to home position
fmt.Println(" Moving to home position...")
position := &onvif.PTZVector{
PanTilt: &onvif.Vector2D{X: 10.0, Y: -5.0},
Zoom: &onvif.Vector1D{X: 0.5},
PanTilt: &onvif.Vector2D{X: 0.0, Y: 0.0},
Zoom: &onvif.Vector1D{X: 0.0},
}
if err := client.AbsoluteMove(testCtx, profiles[0].Token, position, nil); err != nil {
log.Fatalf("❌ AbsoluteMove failed: %v", err)
if err := client.AbsoluteMove(ctx, profileToken, position, nil); err != nil {
fmt.Printf(" ⚠️ Failed to move: %v\n", err)
} else {
fmt.Println(" ✅ Moved to home position")
}
fmt.Println("✅ PTZ moved to absolute position")
fmt.Println()
// Wait a bit for movement to complete
time.Sleep(600 * time.Millisecond)
// Wait a moment
time.Sleep(500 * time.Millisecond)
// Verify new position
fmt.Println("Test 8: Verify PTZ Position")
status, err = client.GetStatus(testCtx, profiles[0].Token)
if err != nil {
log.Fatalf("❌ GetStatus failed: %v", err)
}
fmt.Printf("✅ New PTZ Position: Pan=%.2f, Tilt=%.2f, Zoom=%.2f\n",
status.Position.PanTilt.X,
status.Position.PanTilt.Y,
status.Position.Zoom.X)
fmt.Println()
// Test 9: PTZ Presets
fmt.Println("Test 9: Get PTZ Presets")
presets, err := client.GetPresets(testCtx, profiles[0].Token)
if err != nil {
log.Fatalf("❌ GetPresets failed: %v", err)
}
fmt.Printf("✅ Found %d presets:\n", len(presets))
for i, preset := range presets {
fmt.Printf(" [%d] %s (Token: %s)\n", i+1, preset.Name, preset.Token)
// Get status after move
status, err := client.GetStatus(ctx, profileToken)
if err == nil {
fmt.Printf(" New position: Pan=%.1f°, Tilt=%.1f°, Zoom=%.2f\n",
status.Position.PanTilt.X,
status.Position.PanTilt.Y,
status.Position.Zoom.X)
}
fmt.Println()
}
// Test 10: Get System Date and Time
fmt.Println("Test 10: GetSystemDateAndTime")
_, err = client.GetSystemDateAndTime(testCtx)
if err != nil {
log.Fatalf("❌ GetSystemDateAndTime failed: %v", err)
}
fmt.Println("✅ System date and time retrieved successfully")
// Summary
fmt.Println("╔════════════════════════════════════════════════════════════╗")
fmt.Println("║ ║")
fmt.Println("║ ✅ All Tests Passed! ✅ ║")
fmt.Println("║ ║")
fmt.Println("╚════════════════════════════════════════════════════════════╝")
fmt.Println()
// All tests passed!
fmt.Println("╔══════════════════════════════════════════════════════════╗")
fmt.Println("║ ║")
fmt.Println(" ✅ All Tests Passed! ONVIF Server is working! ✅ ║")
fmt.Println("║ ║")
fmt.Println("╚══════════════════════════════════════════════════════════╝")
fmt.Println()
// Stop the server
cancel()
time.Sleep(500 * time.Millisecond)
fmt.Println("🎉 ONVIF Server is working correctly!")
fmt.Println(" • Device Service: ✓")
fmt.Println(" • Media Service: ✓")
fmt.Println(" • PTZ Service: ✓")
fmt.Printf(" • Multi-lens Camera: ✓ (%d profiles)\n", len(profiles))
}
-163
View File
@@ -1,163 +0,0 @@
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/0x524a/onvif-go"
)
func main() {
fmt.Println("🧪 Testing ONVIF Server with Client Library")
fmt.Println("===========================================")
fmt.Println()
// Create client
client, err := onvif.NewClient(
"http://localhost:8080/onvif/device_service",
onvif.WithCredentials("admin", "admin"),
onvif.WithTimeout(30*time.Second),
)
if err != nil {
log.Fatalf("❌ Failed to create client: %v", err)
}
ctx := context.Background()
// Test 1: Get device information
fmt.Println("📋 Test 1: Getting Device Information...")
info, err := client.GetDeviceInformation(ctx)
if err != nil {
log.Fatalf("❌ Failed to get device info: %v", err)
}
fmt.Printf("✅ Device: %s %s\n", info.Manufacturer, info.Model)
fmt.Printf(" Firmware: %s\n", info.FirmwareVersion)
fmt.Printf(" Serial: %s\n", info.SerialNumber)
fmt.Println()
// Test 2: Initialize and discover services
fmt.Println("🔍 Test 2: Discovering Services...")
if err := client.Initialize(ctx); err != nil {
log.Fatalf("❌ Failed to initialize: %v", err)
}
fmt.Println("✅ Services discovered successfully")
fmt.Println()
// Test 3: Get capabilities
fmt.Println("🔧 Test 3: Getting Capabilities...")
caps, err := client.GetCapabilities(ctx)
if err != nil {
log.Fatalf("❌ Failed to get capabilities: %v", err)
}
fmt.Println("✅ Capabilities:")
if caps.Media != nil {
fmt.Println(" ✓ Media Service")
}
if caps.PTZ != nil {
fmt.Println(" ✓ PTZ Service")
}
if caps.Imaging != nil {
fmt.Println(" ✓ Imaging Service")
}
fmt.Println()
// Test 4: Get media profiles
fmt.Println("🎬 Test 4: Getting Media Profiles...")
profiles, err := client.GetProfiles(ctx)
if err != nil {
log.Fatalf("❌ Failed to get profiles: %v", err)
}
fmt.Printf("✅ Found %d camera profiles:\n", len(profiles))
for i, profile := range profiles {
fmt.Printf("\n Profile %d: %s\n", i+1, profile.Name)
fmt.Printf(" Token: %s\n", profile.Token)
if profile.VideoEncoderConfiguration != nil {
fmt.Printf(" Video: %dx%d @ %s\n",
profile.VideoEncoderConfiguration.Resolution.Width,
profile.VideoEncoderConfiguration.Resolution.Height,
profile.VideoEncoderConfiguration.Encoding)
}
// Get stream URI
streamURI, err := client.GetStreamURI(ctx, profile.Token)
if err != nil {
fmt.Printf(" ⚠️ Failed to get stream URI: %v\n", err)
} else {
fmt.Printf(" RTSP: %s\n", streamURI.URI)
}
// Get snapshot URI if available
snapshotURI, err := client.GetSnapshotURI(ctx, profile.Token)
if err == nil {
fmt.Printf(" Snapshot: %s\n", snapshotURI.URI)
}
// Test PTZ if available
if profile.PTZConfiguration != nil {
fmt.Println(" PTZ: ✓ Enabled")
// Get PTZ status
status, err := client.GetStatus(ctx, profile.Token)
if err == nil {
fmt.Printf(" Position: Pan=%.1f°, Tilt=%.1f°, Zoom=%.2f\n",
status.Position.PanTilt.X,
status.Position.PanTilt.Y,
status.Position.Zoom.X)
}
// Get presets
presets, err := client.GetPresets(ctx, profile.Token)
if err == nil && len(presets) > 0 {
fmt.Printf(" Presets: %d available\n", len(presets))
}
}
}
fmt.Println()
// Test 5: PTZ control (if available)
if len(profiles) > 0 && profiles[0].PTZConfiguration != nil {
fmt.Println("🎮 Test 5: Testing PTZ Control...")
profileToken := profiles[0].Token
// Absolute move to home position
fmt.Println(" Moving to home position...")
position := &onvif.PTZVector{
PanTilt: &onvif.Vector2D{X: 0.0, Y: 0.0},
Zoom: &onvif.Vector1D{X: 0.0},
}
if err := client.AbsoluteMove(ctx, profileToken, position, nil); err != nil {
fmt.Printf(" ⚠️ Failed to move: %v\n", err)
} else {
fmt.Println(" ✅ Moved to home position")
}
// Wait a moment
time.Sleep(500 * time.Millisecond)
// Get status after move
status, err := client.GetStatus(ctx, profileToken)
if err == nil {
fmt.Printf(" New position: Pan=%.1f°, Tilt=%.1f°, Zoom=%.2f\n",
status.Position.PanTilt.X,
status.Position.PanTilt.Y,
status.Position.Zoom.X)
}
fmt.Println()
}
// Summary
fmt.Println("╔════════════════════════════════════════════════════════════╗")
fmt.Println("║ ║")
fmt.Println("║ ✅ All Tests Passed! ✅ ║")
fmt.Println("║ ║")
fmt.Println("╚════════════════════════════════════════════════════════════╝")
fmt.Println()
fmt.Println("🎉 ONVIF Server is working correctly!")
fmt.Println(" • Device Service: ✓")
fmt.Println(" • Media Service: ✓")
fmt.Println(" • PTZ Service: ✓")
fmt.Printf(" • Multi-lens Camera: ✓ (%d profiles)\n", len(profiles))
}