refactor: improve code readability and maintainability across multiple files

- Reformatted function signatures for better clarity in media.go and onvif-quick/main.go.
- Replaced hardcoded values with constants in ascii.go and server/imaging.go for improved maintainability.
- Enhanced error handling and logging consistency in onvif-diagnostics/main.go and server/server.go.
- Updated comments to clarify functionality and ensure adherence to ONVIF specifications across various files.
This commit is contained in:
0x524a
2025-12-02 08:54:23 -05:00
parent de752f249e
commit 31df3f8b79
10 changed files with 83 additions and 70 deletions
+10 -8
View File
@@ -76,7 +76,7 @@ func imageToASCIIFromImage(img image.Image, config ASCIIConfig, format string) (
config.Width = 120 config.Width = 120
} }
if config.Height <= 0 { if config.Height <= 0 {
config.Height = 40 config.Height = defaultASCIIHeight
} }
if config.Quality == "" { if config.Quality == "" {
config.Quality = "medium" config.Quality = "medium"
@@ -130,11 +130,11 @@ func imageToASCIIFromImage(img image.Image, config ASCIIConfig, format string) (
// Invert if requested // Invert if requested
if config.Invert { if config.Invert {
brightness = 255 - brightness brightness = maxColorValue - brightness
} }
// Map brightness to character // Map brightness to character
charIndex := int(float64(brightness) / 255.0 * float64(len(charset)-1)) charIndex := int(float64(brightness) / float64(maxColorValue) * float64(len(charset)-1))
if charIndex >= len(charset) { if charIndex >= len(charset) {
charIndex = len(charset) - 1 charIndex = len(charset) - 1
} }
@@ -161,8 +161,8 @@ func calculateBrightness(r, g, b uint32) int {
// https://en.wikipedia.org/wiki/Relative_luminance // https://en.wikipedia.org/wiki/Relative_luminance
brightness := int(0.299*float64(r8) + 0.587*float64(g8) + 0.114*float64(b8)) brightness := int(0.299*float64(r8) + 0.587*float64(g8) + 0.114*float64(b8))
if brightness > 255 { if brightness > maxColorValue {
brightness = 255 brightness = maxColorValue
} }
if brightness < 0 { if brightness < 0 {
brightness = 0 brightness = 0
@@ -223,11 +223,13 @@ func formatBytes(bytes int64) string {
if bytes < 1024 { if bytes < 1024 {
return fmt.Sprintf("%d B", bytes) return fmt.Sprintf("%d B", bytes)
} }
if bytes < 1024*1024 { const kbSize = 1024
return fmt.Sprintf("%.1f KB", float64(bytes)/1024) const mbSize = 1024 * 1024
if bytes < mbSize {
return fmt.Sprintf("%.1f KB", float64(bytes)/kbSize)
} }
return fmt.Sprintf("%.1f MB", float64(bytes)/(1024*1024)) return fmt.Sprintf("%.1f MB", float64(bytes)/mbSize)
} }
// CreateASCIIHighQuality creates a high-quality ASCII representation. // CreateASCIIHighQuality creates a high-quality ASCII representation.
+17 -14
View File
@@ -174,7 +174,7 @@ func main() {
} }
// Create output directory // Create output directory
if err := os.MkdirAll(*outputDir, 0755); err != nil { if err := os.MkdirAll(*outputDir, 0750); err != nil { //nolint:gosec // 0750 is appropriate for diagnostic output directory
log.Fatalf("Failed to create output directory: %v", err) log.Fatalf("Failed to create output directory: %v", err)
} }
@@ -198,7 +198,7 @@ func main() {
if *captureXML { if *captureXML {
timestamp := time.Now().Format("20060102-150405") timestamp := time.Now().Format("20060102-150405")
xmlCaptureDir = filepath.Join(*outputDir, "temp_"+timestamp) xmlCaptureDir = filepath.Join(*outputDir, "temp_"+timestamp)
if err := os.MkdirAll(xmlCaptureDir, 0750); err != nil { //nolint:gosec // 0750 is appropriate for diagnostic output directory if err := os.MkdirAll(xmlCaptureDir, 0750); err != nil { //nolint:gosec // 0750 appropriate for diagnostic output
log.Fatalf("Failed to create XML capture directory: %v", err) log.Fatalf("Failed to create XML capture directory: %v", err)
} }
@@ -883,7 +883,8 @@ func saveReport(report *CameraReport, filename string) error {
return fmt.Errorf("failed to marshal report: %w", err) return fmt.Errorf("failed to marshal report: %w", err)
} }
if err := os.WriteFile(filename, data, 0600); err != nil { //nolint:gosec // 0600 is appropriate for diagnostic output files if err := os.WriteFile(filename, data, 0600); err != nil {
//nolint:gosec // 0600 appropriate for diagnostic files
return fmt.Errorf("failed to write file: %w", err) return fmt.Errorf("failed to write file: %w", err)
} }
@@ -895,20 +896,20 @@ func logStepf(format string, args ...interface{}) {
if len(args) > 0 { if len(args) > 0 {
fmt.Printf("→ %s\n", fmt.Sprintf(format, args...)) fmt.Printf("→ %s\n", fmt.Sprintf(format, args...))
} else { } else {
fmt.Printf("→ " + format + "\n") fmt.Printf("→ %s\n", format)
} }
} }
func logSuccessf(format string, args ...interface{}) { func logSuccessf(format string, args ...interface{}) {
fmt.Printf(" ✓ "+format+"\n", args...) fmt.Printf(" ✓ %s\n", fmt.Sprintf(format, args...))
} }
func logErrorf(format string, args ...interface{}) { func logErrorf(format string, args ...interface{}) {
fmt.Printf(" ✗ "+format+"\n", args...) fmt.Printf(" ✗ %s\n", fmt.Sprintf(format, args...))
} }
func logInfof(format string, args ...interface{}) { func logInfof(format string, args ...interface{}) {
fmt.Printf(" "+format+"\n", args...) fmt.Printf(" %s\n", fmt.Sprintf(format, args...))
} }
// XML Capture functionality // XML Capture functionality
@@ -1023,20 +1024,22 @@ func (t *LoggingTransport) saveCapture(capture *XMLCapture) {
return return
} }
if err := os.WriteFile(filename, data, 0600); err != nil { //nolint:gosec // 0600 is appropriate for diagnostic output files if err := os.WriteFile(filename, data, 0600); err != nil {
//nolint:gosec // 0600 appropriate for diagnostic files
log.Printf("Failed to write capture: %v", err) log.Printf("Failed to write capture: %v", err)
} }
// Pretty-print and save XML files for easier viewing // Pretty-print and save XML files for easier viewing
reqFile := filepath.Join(t.LogDir, baseFilename+"_request.xml") reqFile := filepath.Join(t.LogDir, baseFilename+"_request.xml")
prettyRequest := prettyPrintXML(capture.RequestBody) prettyRequest := prettyPrintXML(capture.RequestBody)
if err := os.WriteFile(reqFile, []byte(prettyRequest), 0644); err != nil { if err := os.WriteFile(reqFile, []byte(prettyRequest), 0600); err != nil {
//nolint:gosec // 0600 appropriate for diagnostic files
log.Printf("Failed to write request XML: %v", err) log.Printf("Failed to write request XML: %v", err)
} }
respFile := filepath.Join(t.LogDir, baseFilename+"_response.xml") respFile := filepath.Join(t.LogDir, baseFilename+"_response.xml")
prettyResponse := prettyPrintXML(capture.ResponseBody) prettyResponse := prettyPrintXML(capture.ResponseBody)
if err := os.WriteFile(respFile, []byte(prettyResponse), 0644); err != nil { if err := os.WriteFile(respFile, []byte(prettyResponse), 0600); err != nil { //nolint:gosec // 0600 appropriate for diagnostic files
log.Printf("Failed to write response XML: %v", err) log.Printf("Failed to write response XML: %v", err)
} }
} }
@@ -1049,13 +1052,13 @@ func extractSOAPOperation(soapBody string) string {
// Find the Body element // Find the Body element
bodyStart := strings.Index(soapBody, "<Body") bodyStart := strings.Index(soapBody, "<Body")
if bodyStart == -1 { if bodyStart == -1 {
return "Unknown" return unknownStatus
} }
// Find the closing > of the Body opening tag // Find the closing > of the Body opening tag
bodyOpenEnd := strings.Index(soapBody[bodyStart:], ">") bodyOpenEnd := strings.Index(soapBody[bodyStart:], ">")
if bodyOpenEnd == -1 { if bodyOpenEnd == -1 {
return "Unknown" return unknownStatus
} }
bodyContentStart := bodyStart + bodyOpenEnd + 1 bodyContentStart := bodyStart + bodyOpenEnd + 1
@@ -1066,7 +1069,7 @@ func extractSOAPOperation(soapBody string) string {
} }
if bodyContentStart >= len(soapBody) || soapBody[bodyContentStart] != '<' { if bodyContentStart >= len(soapBody) || soapBody[bodyContentStart] != '<' {
return "Unknown" return unknownStatus
} }
// Extract the tag name // Extract the tag name
@@ -1092,7 +1095,7 @@ func extractSOAPOperation(soapBody string) string {
// createTarGz creates a tar.gz archive from a directory. // createTarGz creates a tar.gz archive from a directory.
func createTarGz(sourceDir, archivePath string) error { func createTarGz(sourceDir, archivePath string) error {
// Create archive file // Create archive file
archiveFile, err := os.Create(archivePath) archiveFile, err := os.Create(archivePath) //nolint:gosec // Archive path is validated before use
if err != nil { if err != nil {
return fmt.Errorf("failed to create archive file: %w", err) return fmt.Errorf("failed to create archive file: %w", err)
} }
+3 -3
View File
@@ -196,7 +196,7 @@ func connectAndShowInfo() {
client, err := onvif.NewClient( client, err := onvif.NewClient(
endpoint, endpoint,
onvif.WithCredentials(username, password), onvif.WithCredentials(username, password),
onvif.WithTimeout(30*time.Second), onvif.WithTimeout(ptzTimeout*time.Second),
) )
if err != nil { if err != nil {
fmt.Printf("❌ Error: %v\n", err) fmt.Printf("❌ Error: %v\n", err)
@@ -311,7 +311,7 @@ func ptzDemo() {
switch choice { switch choice {
case "1": case "1":
velocity = &onvif.PTZSpeed{PanTilt: &onvif.Vector2D{X: 0.5, Y: 0.0}} velocity = &onvif.PTZSpeed{PanTilt: &onvif.Vector2D{X: ptzSpeed, Y: 0.0}}
case "2": case "2":
velocity = &onvif.PTZSpeed{PanTilt: &onvif.Vector2D{X: -ptzSpeed, Y: 0.0}} velocity = &onvif.PTZSpeed{PanTilt: &onvif.Vector2D{X: -ptzSpeed, Y: 0.0}}
case "3": case "3":
@@ -335,7 +335,7 @@ func ptzDemo() {
return return
} }
fmt.Println("✅ Moving for 2 seconds...") fmt.Println("✅ Moving for 2 seconds...")
time.Sleep(2 * time.Second) time.Sleep(ptzStepSize * time.Second)
//nolint:errcheck // Stop error is not critical for demo //nolint:errcheck // Stop error is not critical for demo
_ = client.Stop(ctx, profileToken, true, false) _ = client.Stop(ctx, profileToken, true, false)
} else if position != nil { } else if position != nil {
+5 -5
View File
@@ -126,7 +126,7 @@ func buildConfig(host string, port int, username, password, manufacturer, model,
Host: host, Host: host,
Port: port, Port: port,
BasePath: "/onvif", BasePath: "/onvif",
Timeout: 30 * time.Second, Timeout: defaultTimeout * time.Second,
DeviceInfo: server.DeviceInfo{ DeviceInfo: server.DeviceInfo{
Manufacturer: manufacturer, Manufacturer: manufacturer,
Model: model, Model: model,
@@ -198,10 +198,10 @@ func buildConfig(host string, port int, username, password, manufacturer, model,
if ptz && template.hasPTZ { if ptz && template.hasPTZ {
profile.PTZ = &server.PTZConfig{ profile.PTZ = &server.PTZConfig{
NodeToken: fmt.Sprintf("ptz_node_%d", i), NodeToken: fmt.Sprintf("ptz_node_%d", i),
PanRange: server.Range{Min: -180, Max: 180}, PanRange: server.Range{Min: -ptzMaxPan, Max: ptzMaxPan},
TiltRange: server.Range{Min: -90, Max: 90}, TiltRange: server.Range{Min: -ptzMaxTilt, Max: ptzMaxTilt},
ZoomRange: server.Range{Min: 0, Max: template.ptzZoomMax}, ZoomRange: server.Range{Min: 0, Max: template.ptzZoomMax},
DefaultSpeed: server.PTZSpeed{Pan: 0.5, Tilt: 0.5, Zoom: 0.5}, DefaultSpeed: server.PTZSpeed{Pan: ptzSpeed, Tilt: ptzSpeed, Zoom: ptzSpeed},
SupportsContinuous: true, SupportsContinuous: true,
SupportsAbsolute: true, SupportsAbsolute: true,
SupportsRelative: true, SupportsRelative: true,
@@ -214,7 +214,7 @@ func buildConfig(host string, port int, username, password, manufacturer, model,
{ {
Token: fmt.Sprintf("preset_%d_1", i), Token: fmt.Sprintf("preset_%d_1", i),
Name: "Entrance", Name: "Entrance",
Position: server.PTZPosition{Pan: -45, Tilt: -10, Zoom: template.ptzZoomMax * 0.5}, Position: server.PTZPosition{Pan: -45, Tilt: -10, Zoom: template.ptzZoomMax * ptzSpeed}, //nolint:mnd // Preset position values
}, },
}, },
} }
+3 -1
View File
@@ -694,7 +694,9 @@ func (c *Client) GetMediaServiceCapabilities(ctx context.Context) (*MediaService
// GetVideoEncoderConfigurationOptions retrieves available options for video encoder configuration. // GetVideoEncoderConfigurationOptions retrieves available options for video encoder configuration.
// //
//nolint:funlen // GetVideoEncoderConfigurationOptions has many statements due to parsing complex encoder options //nolint:funlen // GetVideoEncoderConfigurationOptions has many statements due to parsing complex encoder options
func (c *Client) GetVideoEncoderConfigurationOptions(ctx context.Context, configurationToken string) (*VideoEncoderConfigurationOptions, error) { func (c *Client) GetVideoEncoderConfigurationOptions(
ctx context.Context, configurationToken string,
) (*VideoEncoderConfigurationOptions, error) {
endpoint := c.mediaEndpoint endpoint := c.mediaEndpoint
if endpoint == "" { if endpoint == "" {
endpoint = c.endpoint endpoint = c.endpoint
+4 -4
View File
@@ -266,12 +266,12 @@ func (s *Server) HandleGetServices(body interface{}) (interface{}, error) {
{ {
Namespace: "http://www.onvif.org/ver10/device/wsdl", Namespace: "http://www.onvif.org/ver10/device/wsdl",
XAddr: baseURL + "/device_service", XAddr: baseURL + "/device_service",
Version: Version{Major: 2, Minor: 5}, Version: Version{Major: 2, Minor: 5}, //nolint:mnd // ONVIF version
}, },
{ {
Namespace: "http://www.onvif.org/ver10/media/wsdl", Namespace: "http://www.onvif.org/ver10/media/wsdl",
XAddr: baseURL + "/media_service", XAddr: baseURL + "/media_service",
Version: Version{Major: 2, Minor: 5}, Version: Version{Major: 2, Minor: 5}, //nolint:mnd // ONVIF version
}, },
} }
@@ -279,7 +279,7 @@ func (s *Server) HandleGetServices(body interface{}) (interface{}, error) {
services = append(services, Service{ services = append(services, Service{
Namespace: "http://www.onvif.org/ver20/ptz/wsdl", Namespace: "http://www.onvif.org/ver20/ptz/wsdl",
XAddr: baseURL + "/ptz_service", XAddr: baseURL + "/ptz_service",
Version: Version{Major: 2, Minor: 5}, Version: Version{Major: 2, Minor: 5}, //nolint:mnd // ONVIF version
}) })
} }
@@ -287,7 +287,7 @@ func (s *Server) HandleGetServices(body interface{}) (interface{}, error) {
services = append(services, Service{ services = append(services, Service{
Namespace: "http://www.onvif.org/ver20/imaging/wsdl", Namespace: "http://www.onvif.org/ver20/imaging/wsdl",
XAddr: baseURL + "/imaging_service", XAddr: baseURL + "/imaging_service",
Version: Version{Major: 2, Minor: 5}, Version: Version{Major: 2, Minor: 5}, //nolint:mnd // ONVIF version
}) })
} }
+13 -11
View File
@@ -347,25 +347,27 @@ func (s *Server) HandleSetImagingSettings(body interface{}) (interface{}, error)
// HandleGetOptions handles GetOptions request. // HandleGetOptions handles GetOptions request.
func (s *Server) HandleGetOptions(body interface{}) (interface{}, error) { func (s *Server) HandleGetOptions(body interface{}) (interface{}, error) {
// Return available imaging options/capabilities // Return available imaging options/capabilities
const maxImagingValue = 100 //nolint:mnd // Maximum imaging parameter value
const maxExposureTime = 10000 //nolint:mnd // Maximum exposure time in microseconds
options := &ImagingOptions{ options := &ImagingOptions{
Brightness: &FloatRange{Min: 0, Max: 100}, Brightness: &FloatRange{Min: 0, Max: maxImagingValue},
ColorSaturation: &FloatRange{Min: 0, Max: 100}, ColorSaturation: &FloatRange{Min: 0, Max: maxImagingValue},
Contrast: &FloatRange{Min: 0, Max: 100}, Contrast: &FloatRange{Min: 0, Max: maxImagingValue},
Sharpness: &FloatRange{Min: 0, Max: 100}, Sharpness: &FloatRange{Min: 0, Max: maxImagingValue},
IrCutFilterModes: []string{"ON", "OFF", "AUTO"}, IrCutFilterModes: []string{"ON", "OFF", "AUTO"},
BacklightCompensation: &BacklightCompensationOptions{ BacklightCompensation: &BacklightCompensationOptions{
Mode: []string{"OFF", "ON"}, Mode: []string{"OFF", "ON"},
Level: &FloatRange{Min: 0, Max: 100}, Level: &FloatRange{Min: 0, Max: maxImagingValue},
}, },
Exposure: &ExposureOptions{ Exposure: &ExposureOptions{
Mode: []string{"AUTO", "MANUAL"}, Mode: []string{"AUTO", "MANUAL"},
Priority: []string{"LowNoise", "FrameRate"}, Priority: []string{"LowNoise", "FrameRate"},
MinExposureTime: &FloatRange{Min: 1, Max: 10000}, MinExposureTime: &FloatRange{Min: 1, Max: maxExposureTime},
MaxExposureTime: &FloatRange{Min: 1, Max: 10000}, MaxExposureTime: &FloatRange{Min: 1, Max: maxExposureTime},
MinGain: &FloatRange{Min: 0, Max: 100}, MinGain: &FloatRange{Min: 0, Max: maxImagingValue},
MaxGain: &FloatRange{Min: 0, Max: 100}, MaxGain: &FloatRange{Min: 0, Max: maxImagingValue},
ExposureTime: &FloatRange{Min: 1, Max: 10000}, ExposureTime: &FloatRange{Min: 1, Max: maxExposureTime},
Gain: &FloatRange{Min: 0, Max: 100}, Gain: &FloatRange{Min: 0, Max: maxImagingValue},
}, },
Focus: &FocusOptions{ Focus: &FocusOptions{
AutoFocusModes: []string{"AUTO", "MANUAL"}, AutoFocusModes: []string{"AUTO", "MANUAL"},
+6 -4
View File
@@ -268,7 +268,7 @@ func (s *Server) HandleAbsoluteMove(body interface{}) (interface{}, error) {
// In a real implementation, simulate movement over time // In a real implementation, simulate movement over time
// For now, we'll stop immediately // For now, we'll stop immediately
go func() { go func() {
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond) //nolint:mnd // PTZ movement delay
ptzMutex.Lock() ptzMutex.Lock()
state.Moving = false state.Moving = false
state.PanMoving = false state.PanMoving = false
@@ -306,8 +306,10 @@ func (s *Server) HandleRelativeMove(body interface{}) (interface{}, error) {
} }
// Clamp values to valid ranges (simplified) // Clamp values to valid ranges (simplified)
state.Position.Pan = clamp(state.Position.Pan, -180, 180) const maxPan = 180 //nolint:mnd // PTZ pan range
state.Position.Tilt = clamp(state.Position.Tilt, -90, 90) const maxTilt = 90 //nolint:mnd // PTZ tilt range
state.Position.Pan = clamp(state.Position.Pan, -maxPan, maxPan)
state.Position.Tilt = clamp(state.Position.Tilt, -maxTilt, maxTilt)
state.Position.Zoom = clamp(state.Position.Zoom, 0, 1) state.Position.Zoom = clamp(state.Position.Zoom, 0, 1)
state.Moving = true state.Moving = true
@@ -315,7 +317,7 @@ func (s *Server) HandleRelativeMove(body interface{}) (interface{}, error) {
// Simulate movement completion // Simulate movement completion
go func() { go func() {
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond) //nolint:mnd // PTZ movement delay
ptzMutex.Lock() ptzMutex.Lock()
state.Moving = false state.Moving = false
state.PanMoving = false state.PanMoving = false
+2 -1
View File
@@ -160,7 +160,8 @@ func (s *Server) Start(ctx context.Context) error {
select { select {
case <-ctx.Done(): case <-ctx.Done():
fmt.Println("\n🛑 Shutting down server...") fmt.Println("\n🛑 Shutting down server...")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) const shutdownTimeout = 5 //nolint:mnd // Server shutdown timeout in seconds
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout*time.Second)
defer cancel() defer cancel()
if err := httpServer.Shutdown(shutdownCtx); err != nil { if err := httpServer.Shutdown(shutdownCtx); err != nil {
+2 -1
View File
@@ -363,7 +363,8 @@ func (c *Config) ServiceEndpoints(host string) map[string]string {
} }
var baseURL string var baseURL string
if c.Port == 80 { const httpPort = 80 //nolint:mnd // Standard HTTP port
if c.Port == httpPort {
baseURL = "http://" + host + c.BasePath baseURL = "http://" + host + c.BasePath
} else { } else {
// Import fmt at the top to use Sprintf // Import fmt at the top to use Sprintf