feat: add network interface selection to CLI tools

onvif-cli improvements:
- Add menu option to list network interfaces
- Add interface selection during discovery
- Display detailed interface information (up/down, multicast, addresses)
- Allow discovery by interface name or IP address
- Maintain backward compatibility with default interface

onvif-quick improvements:
- Add menu option to list network interfaces
- Add interface selection during discovery
- Simplified interface list display
- Quick discovery on specific network

Documentation:
- Add comprehensive CLI_NETWORK_INTERFACE_USAGE.md guide
- Include usage scenarios and workflows
- Troubleshooting section
- Integration examples
- Command reference table

These enhancements allow users to easily specify which network interface
to use for camera discovery, solving issues with multi-interface systems.
This commit is contained in:
ProtoTess
2025-11-17 17:41:03 +00:00
parent dfa113ad6d
commit 46035f4873
3 changed files with 656 additions and 17 deletions
+99 -11
View File
@@ -36,14 +36,16 @@ func main() {
case "1":
cli.discoverCameras()
case "2":
cli.connectToCamera()
cli.listNetworkInterfaces()
case "3":
cli.deviceOperations()
cli.connectToCamera()
case "4":
cli.mediaOperations()
cli.deviceOperations()
case "5":
cli.ptzOperations()
cli.mediaOperations()
case "6":
cli.ptzOperations()
case "7":
cli.imagingOperations()
case "0", "q", "quit", "exit":
fmt.Println("Goodbye! 👋")
@@ -58,14 +60,15 @@ func main() {
func (c *CLI) showMainMenu() {
fmt.Println("📋 Main Menu:")
fmt.Println(" 1. Discover Cameras on Network")
fmt.Println(" 2. Connect to Camera")
fmt.Println(" 2. List Network Interfaces")
fmt.Println(" 3. Connect to Camera")
if c.client != nil {
fmt.Println(" 3. Device Operations")
fmt.Println(" 4. Media Operations")
fmt.Println(" 5. PTZ Operations")
fmt.Println(" 6. Imaging Operations")
fmt.Println(" 4. Device Operations")
fmt.Println(" 5. Media Operations")
fmt.Println(" 6. PTZ Operations")
fmt.Println(" 7. Imaging Operations")
} else {
fmt.Println(" 3-6. (Connect to camera first)")
fmt.Println(" 4-7. (Connect to camera first)")
}
fmt.Println(" 0. Exit")
fmt.Println()
@@ -87,14 +90,99 @@ func (c *CLI) readInputWithDefault(prompt, defaultValue string) string {
return input
}
func (c *CLI) listNetworkInterfaces() {
fmt.Println("🌐 Available Network Interfaces")
fmt.Println("================================")
interfaces, err := discovery.ListNetworkInterfaces()
if err != nil {
fmt.Printf("❌ Error listing interfaces: %v\n", err)
return
}
if len(interfaces) == 0 {
fmt.Println("❌ No network interfaces found")
return
}
fmt.Printf("✅ Found %d interface(s):\n\n", len(interfaces))
for _, iface := range interfaces {
upStr := "⬆️ Up"
if !iface.Up {
upStr = "⬇️ Down"
}
multicastStr := "✓"
if !iface.Multicast {
multicastStr = "✗"
}
fmt.Printf("📡 %s (%s, Multicast: %s)\n", iface.Name, upStr, multicastStr)
if len(iface.Addresses) == 0 {
fmt.Println(" (No addresses assigned)")
} else {
for _, addr := range iface.Addresses {
fmt.Printf(" └─ %s\n", addr)
}
}
fmt.Println()
}
fmt.Println("💡 Use interface name or IP address when discovering cameras")
fmt.Println(" Example: eth0 or 192.168.1.100")
}
func (c *CLI) discoverCameras() {
fmt.Println("🔍 Discovering ONVIF cameras...")
fmt.Println("This may take a few seconds...")
// Ask user if they want to select a specific network interface
useSpecificInterface := c.readInput("Use specific network interface? (y/n) [n]: ")
useSpecificInterface = strings.ToLower(useSpecificInterface)
var opts *discovery.DiscoverOptions
if useSpecificInterface == "y" || useSpecificInterface == "yes" {
fmt.Println("\nAvailable network interfaces:")
interfaces, err := discovery.ListNetworkInterfaces()
if err != nil {
fmt.Printf("❌ Error listing interfaces: %v\n", err)
return
}
for i, iface := range interfaces {
fmt.Printf(" %d. %s\n", i+1, iface.Name)
for _, addr := range iface.Addresses {
fmt.Printf(" └─ %s\n", addr)
}
multicastStr := "No"
if iface.Multicast {
multicastStr = "Yes"
}
fmt.Printf(" (Up: %v, Multicast: %s)\n", iface.Up, multicastStr)
}
ifaceInput := c.readInput("\nEnter interface name or IP address: ")
ifaceInput = strings.TrimSpace(ifaceInput)
if ifaceInput != "" {
opts = &discovery.DiscoverOptions{
NetworkInterface: ifaceInput,
}
fmt.Printf("🎯 Using interface: %s\n\n", ifaceInput)
}
}
if opts == nil {
opts = &discovery.DiscoverOptions{}
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
devices, err := discovery.Discover(ctx, 5*time.Second)
devices, err := discovery.DiscoverWithOptions(ctx, 5*time.Second, opts)
if err != nil {
fmt.Printf("❌ Discovery failed: %v\n", err)
return
+84 -6
View File
@@ -22,9 +22,10 @@ func main() {
for {
fmt.Println("What would you like to do?")
fmt.Println("1. 🔍 Discover cameras")
fmt.Println("2. 📹 Connect to camera")
fmt.Println("3. 🎮 PTZ demo")
fmt.Println("4. 📡 Get stream URLs")
fmt.Println("2. 🌐 List network interfaces")
fmt.Println("3. 📹 Connect to camera")
fmt.Println("4. 🎮 PTZ demo")
fmt.Println("5. 📡 Get stream URLs")
fmt.Println("0. Exit")
fmt.Print("\nChoice: ")
@@ -35,10 +36,12 @@ func main() {
case "1":
discoverCameras()
case "2":
connectAndShowInfo()
listNetworkInterfaces()
case "3":
ptzDemo()
connectAndShowInfo()
case "4":
ptzDemo()
case "5":
getStreamURLs()
case "0", "q", "quit":
fmt.Println("Goodbye! 👋")
@@ -51,12 +54,48 @@ func main() {
}
func discoverCameras() {
reader := bufio.NewReader(os.Stdin)
fmt.Println("🔍 Discovering cameras on network...")
// Ask if user wants to use a specific interface
fmt.Print("Use specific network interface? (y/n) [n]: ")
useInterface, _ := reader.ReadString('\n')
useInterface = strings.ToLower(strings.TrimSpace(useInterface))
var opts *discovery.DiscoverOptions
if useInterface == "y" || useInterface == "yes" {
// List interfaces
interfaces, err := discovery.ListNetworkInterfaces()
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println("\nAvailable interfaces:")
for i, iface := range interfaces {
fmt.Printf(" %d. %s (%v)\n", i+1, iface.Name, iface.Addresses)
}
fmt.Print("\nEnter interface name or IP: ")
ifaceInput, _ := reader.ReadString('\n')
ifaceInput = strings.TrimSpace(ifaceInput)
if ifaceInput != "" {
opts = &discovery.DiscoverOptions{
NetworkInterface: ifaceInput,
}
}
}
if opts == nil {
opts = &discovery.DiscoverOptions{}
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
devices, err := discovery.Discover(ctx, 5*time.Second)
devices, err := discovery.DiscoverWithOptions(ctx, 5*time.Second, opts)
if err != nil {
fmt.Printf("❌ Error: %v\n", err)
return
@@ -73,6 +112,45 @@ func discoverCameras() {
}
}
func listNetworkInterfaces() {
fmt.Println("🌐 Network Interfaces")
fmt.Println("====================")
interfaces, err := discovery.ListNetworkInterfaces()
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
if len(interfaces) == 0 {
fmt.Println("No network interfaces found")
return
}
fmt.Printf("✅ Found %d interface(s):\n\n", len(interfaces))
for _, iface := range interfaces {
upStr := "Up"
if !iface.Up {
upStr = "Down"
}
multicastStr := "Yes"
if !iface.Multicast {
multicastStr = "No"
}
fmt.Printf("📡 %s (%s, Multicast: %s)\n", iface.Name, upStr, multicastStr)
if len(iface.Addresses) > 0 {
for _, addr := range iface.Addresses {
fmt.Printf(" └─ %s\n", addr)
}
}
}
}
func connectAndShowInfo() {
reader := bufio.NewReader(os.Stdin)