switch to new discovery method

This commit is contained in:
Kwimbee
2025-05-30 18:08:44 +02:00
parent 38ed61f881
commit 82ed837f62
9 changed files with 567 additions and 189 deletions

56
MainForm.Designer.cs generated
View File

@@ -35,13 +35,12 @@
toolStripStatusDisco = new ToolStripStatusLabel();
menuStrip1 = new MenuStrip();
discoveryToolStripMenuItem = new ToolStripMenuItem();
startToolStripMenuItem = new ToolStripMenuItem();
stopToolStripMenuItem = new ToolStripMenuItem();
forceRediscoverToolStripMenuItem = new ToolStripMenuItem();
manualAddToolStripMenuItem = new ToolStripMenuItem();
toolStripSeparator1 = new ToolStripSeparator();
saveToolStripMenuItem = new ToolStripMenuItem();
loadToolStripMenuItem = new ToolStripMenuItem();
manualAddToolStripMenuItem = new ToolStripMenuItem();
toolStripSeparator2 = new ToolStripSeparator();
discoverToolStripMenuItem = new ToolStripMenuItem();
statusStripMain.SuspendLayout();
menuStrip1.SuspendLayout();
SuspendLayout();
@@ -75,7 +74,7 @@
//
toolStripStatusDisco.Name = "toolStripStatusDisco";
toolStripStatusDisco.Size = new Size(111, 17);
toolStripStatusDisco.Text = "Discovery : Stopped";
toolStripStatusDisco.Text = "";
//
// menuStrip1
//
@@ -88,32 +87,17 @@
//
// discoveryToolStripMenuItem
//
discoveryToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { startToolStripMenuItem, stopToolStripMenuItem, manualAddToolStripMenuItem, forceRediscoverToolStripMenuItem, toolStripSeparator1, saveToolStripMenuItem, loadToolStripMenuItem });
discoveryToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { discoverToolStripMenuItem, toolStripSeparator2, manualAddToolStripMenuItem, toolStripSeparator1, saveToolStripMenuItem, loadToolStripMenuItem });
discoveryToolStripMenuItem.Name = "discoveryToolStripMenuItem";
discoveryToolStripMenuItem.Size = new Size(70, 20);
discoveryToolStripMenuItem.Text = "Discovery";
//
// startToolStripMenuItem
// manualAddToolStripMenuItem
//
startToolStripMenuItem.Name = "startToolStripMenuItem";
startToolStripMenuItem.Size = new Size(180, 22);
startToolStripMenuItem.Text = "Start";
startToolStripMenuItem.Click += startToolStripMenuItem_Click;
//
// stopToolStripMenuItem
//
stopToolStripMenuItem.Enabled = false;
stopToolStripMenuItem.Name = "stopToolStripMenuItem";
stopToolStripMenuItem.Size = new Size(180, 22);
stopToolStripMenuItem.Text = "Stop";
stopToolStripMenuItem.Click += stopToolStripMenuItem_Click;
//
// forceRediscoverToolStripMenuItem
//
forceRediscoverToolStripMenuItem.Name = "forceRediscoverToolStripMenuItem";
forceRediscoverToolStripMenuItem.Size = new Size(180, 22);
forceRediscoverToolStripMenuItem.Text = "Force re-discover";
forceRediscoverToolStripMenuItem.Click += forceReloadToolStripMenuItem_Click;
manualAddToolStripMenuItem.Name = "manualAddToolStripMenuItem";
manualAddToolStripMenuItem.Size = new Size(180, 22);
manualAddToolStripMenuItem.Text = "Add manually";
manualAddToolStripMenuItem.Click += manualAddToolStripMenuItem_Click;
//
// toolStripSeparator1
//
@@ -134,12 +118,16 @@
loadToolStripMenuItem.Text = "Load";
loadToolStripMenuItem.Click += loadToolStripMenuItem_Click;
//
// manualAddToolStripMenuItem
// toolStripSeparator2
//
manualAddToolStripMenuItem.Name = "manualAddToolStripMenuItem";
manualAddToolStripMenuItem.Size = new Size(180, 22);
manualAddToolStripMenuItem.Text = "Add manually";
manualAddToolStripMenuItem.Click += manualAddToolStripMenuItem_Click;
toolStripSeparator2.Name = "toolStripSeparator2";
toolStripSeparator2.Size = new Size(177, 6);
//
// discoverToolStripMenuItem
//
discoverToolStripMenuItem.Name = "discoverToolStripMenuItem";
discoverToolStripMenuItem.Size = new Size(180, 22);
discoverToolStripMenuItem.Text = "Discover cameras";
//
// MainForm
//
@@ -153,7 +141,6 @@
MainMenuStrip = menuStrip1;
Name = "MainForm";
Text = "RoboSpot MotionCamera finder";
FormClosing += MainForm_FormClosing;
statusStripMain.ResumeLayout(false);
statusStripMain.PerformLayout();
menuStrip1.ResumeLayout(false);
@@ -169,13 +156,12 @@
private ToolStripStatusLabel toolStripStatusLabel;
private MenuStrip menuStrip1;
private ToolStripMenuItem discoveryToolStripMenuItem;
private ToolStripMenuItem startToolStripMenuItem;
private ToolStripMenuItem stopToolStripMenuItem;
private ToolStripMenuItem forceRediscoverToolStripMenuItem;
private ToolStripMenuItem saveToolStripMenuItem;
private ToolStripMenuItem loadToolStripMenuItem;
private ToolStripSeparator toolStripSeparator1;
private ToolStripStatusLabel toolStripStatusDisco;
private ToolStripMenuItem manualAddToolStripMenuItem;
private ToolStripMenuItem discoverToolStripMenuItem;
private ToolStripSeparator toolStripSeparator2;
}
}

View File

@@ -1,27 +1,34 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Windows.Forms;
using Tmds.MDns;
using static robospot_camera_finder.MainForm;
using System.Runtime.InteropServices;
using System.Text;
namespace robospot_camera_finder
{
public partial class MainForm : Form
{
private readonly ServiceBrowser browser = new();
private BindingList<Camera> all_cameras = new();
public static string cam_username = "admin";
public static string cam_password = "RoboSpot10";
private static string browse_scope = "_rtsp._tcp";
// UDP discovery constants
private const int SEND_PORT = 7701;
private const int RECEIVE_PORT = 7711;
private const string BROADCAST_ADDRESS = "10.255.255.255";
private const byte DEF_REQ_SCAN = 1;
private const byte RES_REQ_SCAN = 11;
bool is_browsing = false;
private UdpClient sendClient;
private UdpClient receiveClient;
private bool isDiscovering = false;
public class Camera
{
@@ -33,8 +40,6 @@ namespace robospot_camera_finder
camera_serial = serial;
}
public Camera() { }
public string camera_name { get; set; }
public string camera_ip { get; set; }
@@ -44,6 +49,51 @@ namespace robospot_camera_finder
public string camera_serial { get; set; }
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DataPacketIPv4
{
public byte mode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)]
public byte[] packet_id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)]
public byte[] mac_addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] ip_addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] subnetmask;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] gateway;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] password;
public byte reserved1;
public ushort port;
public byte status;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] device_name;
public byte reserved2;
public ushort http_port;
public ushort device_port;
public ushort tcp_port;
public ushort udp_port;
public ushort upload_port;
public ushort multicast_port;
public byte network_mode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public byte[] ddns_url;
public byte reserved3;
}
public MainForm()
{
InitializeComponent();
@@ -66,6 +116,7 @@ namespace robospot_camera_finder
if (!got_proper_ip)
{
DialogResult open_netsettings = MessageBox.Show("No Ethernet interface on the 10.0.0.0/8 subnet found. Cameras might not be detected. Do you want to open network settings ?", "Wrong IP configuration", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
if (open_netsettings == DialogResult.Yes)
{
ProcessStartInfo startInfo = new ProcessStartInfo("NCPA.cpl");
@@ -73,87 +124,32 @@ namespace robospot_camera_finder
Process.Start(startInfo);
}
}
lbMain.DataSource = all_cameras;
lbMain.DisplayMember = "camera_name";
lbMain.ValueMember = "camera_ip";
browser.ServiceAdded += (sender, args) =>
{
ServiceAdded(sender, args);
};
// Initialize UDP clients
InitializeUdpClients();
browser.ServiceChanged += (sender, args) =>
{
ServiceAdded(sender, args);
};
browser.ServiceRemoved += (sender, args) =>
{
if (browser.IsBrowsing)
{
ServiceRemoved(sender, args);
}
};
browse(true);
// Discover cameras
discover_cameras();
}
private void browse(bool browse_needed)
private void InitializeUdpClients()
{
if (browse_needed)
try
{
if (!browser.IsBrowsing)
{
browser.StartBrowse(browse_scope);
is_browsing = true;
toolStripStatusDisco.Text = "Discovery : Started";
}
}
else
{
if (browser.IsBrowsing)
{
browser.StopBrowse();
is_browsing = false;
toolStripStatusDisco.Text = "Discovery : Stopped";
}
}
sendClient = new UdpClient();
sendClient.EnableBroadcast = true;
startToolStripMenuItem.Enabled = !browser.IsBrowsing;
stopToolStripMenuItem.Enabled = browser.IsBrowsing;
receiveClient = new UdpClient(RECEIVE_PORT);
}
private void ServiceAdded(object sender, ServiceAnnouncementEventArgs args)
catch (Exception ex)
{
foreach (var cam_addr in args.Announcement.Addresses)
{
if (cam_addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://" + cam_addr + "/stw-cgi/system.cgi?msubmenu=deviceinfo&action=view");
request.Credentials = new NetworkCredential(MainForm.cam_username, MainForm.cam_password);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string text;
using (var sr = new StreamReader(response.GetResponseStream())) { text = sr.ReadToEnd(); }
string[] resp_lines = text.Split('\n');
string location_line = Array.Find(resp_lines, line => line.Trim().StartsWith("DeviceLocation="));
string serial_line = Array.Find(resp_lines, line => line.Trim().StartsWith("SerialNumber="));
string location = "";
string serial = "";
if (location_line != null)
{
location = location_line.Substring("DeviceLocation=".Length).Trim();
}
if (serial_line != null)
{
serial = serial_line.Substring("SerialNumber=".Length).Trim();
}
Camera new_camera = new Camera("Camera " + cam_addr + " - " + location + " - " + serial, cam_addr.ToString(), location, serial);
add_camera(new_camera);
}
MessageBox.Show($"Failed to initialize UDP clients: {ex.Message}", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
@@ -179,29 +175,6 @@ namespace robospot_camera_finder
}
}
private void ServiceRemoved(object sender, ServiceAnnouncementEventArgs args)
{
foreach (var cam_ip in args.Announcement.Addresses)
{
if (cam_ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
foreach (var camera in all_cameras)
{
if (cam_ip.ToString() == camera.camera_ip)
{
all_cameras.Remove(camera);
return;
}
}
}
}
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
browser.StopBrowse();
}
private void lbMain_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (lbMain.SelectedItem != null)
@@ -214,27 +187,6 @@ namespace robospot_camera_finder
}
}
private void startToolStripMenuItem_Click(object sender, EventArgs e)
{
browse(true);
}
private void stopToolStripMenuItem_Click(object sender, EventArgs e)
{
browse(false);
}
private void forceReloadToolStripMenuItem_Click(object sender, EventArgs e)
{
DialogResult result = MessageBox.Show("Are you sure you want to clear discovered cameras and restart the discovery process ?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (result == DialogResult.Yes)
{
browse(false);
all_cameras.Clear();
browse(true);
}
}
private void loadToolStripMenuItem_Click(object sender, EventArgs e)
{
string json = "";
@@ -283,5 +235,274 @@ namespace robospot_camera_finder
add_camera(camera_to_add);
}
}
// Helper method to convert structure to byte array
private byte[] StructureToByteArray(DataPacketIPv4 packet)
{
int size = Marshal.SizeOf(packet);
byte[] array = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(packet, ptr, true);
Marshal.Copy(ptr, array, 0, size);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return array;
}
// Helper method to convert byte array to structure
private DataPacketIPv4 ByteArrayToStructure(byte[] data)
{
DataPacketIPv4 packet = new DataPacketIPv4();
int size = Marshal.SizeOf(packet);
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.Copy(data, 0, ptr, size);
packet = (DataPacketIPv4)Marshal.PtrToStructure(ptr, typeof(DataPacketIPv4));
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return packet;
}
// Helper method to create a string array field with null termination
private byte[] CreateStringField(string value, int fieldSize)
{
byte[] field = new byte[fieldSize];
if (!string.IsNullOrEmpty(value))
{
byte[] valueBytes = Encoding.ASCII.GetBytes(value);
int copyLength = Math.Min(valueBytes.Length, fieldSize - 1); // Leave space for null terminator
Array.Copy(valueBytes, field, copyLength);
}
return field;
}
// Helper method to extract string from byte array
private string ExtractString(byte[] data)
{
if (data == null) return "";
// Find the null terminator
int nullIndex = Array.IndexOf(data, (byte)0);
if (nullIndex >= 0)
{
return Encoding.ASCII.GetString(data, 0, nullIndex);
}
else
{
return Encoding.ASCII.GetString(data).TrimEnd('\0');
}
}
// Create discovery packet
private DataPacketIPv4 CreateDiscoveryPacket()
{
DataPacketIPv4 packet = new DataPacketIPv4
{
mode = DEF_REQ_SCAN,
packet_id = CreateStringField("CAM_FINDER_" + DateTime.Now.Ticks.ToString(), 18),
mac_addr = new byte[18],
ip_addr = new byte[16],
subnetmask = new byte[16],
gateway = new byte[16],
password = new byte[20],
reserved1 = 0,
port = 0,
status = 0,
device_name = new byte[10],
reserved2 = 0,
http_port = 0,
device_port = 0,
tcp_port = 0,
udp_port = 0,
upload_port = 0,
multicast_port = 0,
network_mode = 1,
ddns_url = new byte[128],
reserved3 = 0
};
return packet;
}
// Send discovery packet
private async Task<bool> SendDiscoveryPacketAsync(DataPacketIPv4 packet)
{
try
{
byte[] data = StructureToByteArray(packet);
IPEndPoint broadcastEndPoint = new IPEndPoint(IPAddress.Parse(BROADCAST_ADDRESS), SEND_PORT);
int bytesSent = await sendClient.SendAsync(data, data.Length, broadcastEndPoint);
return bytesSent > 0;
}
catch (Exception ex)
{
MessageBox.Show($"Error sending discovery packet: {ex.Message}", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
// Listen for camera responses
private async Task ListenForResponsesAsync(int timeoutMs = 5000)
{
var startTime = DateTime.Now;
var endTime = startTime.AddMilliseconds(timeoutMs);
try
{
while (DateTime.Now < endTime && isDiscovering)
{
var receiveTask = receiveClient.ReceiveAsync();
var timeoutTask = Task.Delay(1000); // Check every second
var completedTask = await Task.WhenAny(receiveTask, timeoutTask);
if (completedTask == receiveTask)
{
var result = await receiveTask;
ProcessCameraResponse(result.Buffer, result.RemoteEndPoint);
}
}
}
catch (Exception ex)
{
if (isDiscovering) // Only show error if we're still supposed to be discovering
{
this.Invoke(new Action(() =>
{
MessageBox.Show($"Error listening for responses: {ex.Message}", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}));
}
}
}
// Process received camera response
private void ProcessCameraResponse(byte[] data, IPEndPoint remoteEndPoint)
{
try
{
if (data.Length == Marshal.SizeOf<DataPacketIPv4>())
{
DataPacketIPv4 response = ByteArrayToStructure(data);
// Check if this is a valid camera response
if (response.mode == RES_REQ_SCAN)
{
string cameraIP = ExtractString(response.ip_addr);
string deviceName = ExtractString(response.device_name);
string macAddress = ExtractString(response.mac_addr);
string packetId = ExtractString(response.packet_id);
// Use the IP from the response or fall back to the sender's IP
if (string.IsNullOrEmpty(cameraIP))
{
cameraIP = remoteEndPoint.Address.ToString();
}
// Create camera name
string cameraName = !string.IsNullOrEmpty(deviceName) ? deviceName : $"Camera {cameraIP}";
// Create camera object
Camera discoveredCamera = new Camera(
$"{deviceName} - ({cameraIP})",
cameraIP,
$"MAC: {macAddress}",
packetId // Using packet_id as serial for uniqueness
);
// Add camera to list (must be done on UI thread)
this.Invoke(new Action(() =>
{
add_camera(discoveredCamera);
toolStripStatusLabel.Text = $"Found camera: {cameraName} ({cameraIP})";
}));
}
}
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
{
MessageBox.Show($"Error processing camera response: {ex.Message}", "Processing Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}));
}
}
// Main discovery method
private async void discover_cameras()
{
if (isDiscovering)
{
MessageBox.Show("Discovery is already in progress.", "Discovery", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
if (sendClient == null || receiveClient == null)
{
InitializeUdpClients();
if (sendClient == null || receiveClient == null)
{
MessageBox.Show("Failed to initialize network clients.", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
isDiscovering = true;
toolStripStatusLabel.Text = "Discovering cameras...";
try
{
// Create and send discovery packet
DataPacketIPv4 discoveryPacket = CreateDiscoveryPacket();
bool sent = await SendDiscoveryPacketAsync(discoveryPacket);
if (sent)
{
// Listen for responses for 10 seconds
await ListenForResponsesAsync(10000);
}
else
{
MessageBox.Show("Failed to send discovery packet.", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception ex)
{
MessageBox.Show($"Discovery error: {ex.Message}", "Discovery Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
isDiscovering = false;
toolStripStatusLabel.Text = $"Discovery complete. Found {all_cameras.Count} cameras.";
}
}
// Clean up resources when form is closing
protected override void OnFormClosing(FormClosingEventArgs e)
{
isDiscovering = false;
try
{
sendClient?.Close();
receiveClient?.Close();
}
catch { }
base.OnFormClosing(e);
}
}
}

63
Properties/Resources.Designer.cs generated Normal file
View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace robospot_camera_finder.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("robospot_camera_finder.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

120
Properties/Resources.resx Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -39,8 +39,6 @@ namespace robospot_camera_finder
btnZoomMax = new Button();
tbRtspLink = new TextBox();
lblRtspLink = new Label();
labelSerial = new Label();
tbSerial = new TextBox();
cbTestZoom = new CheckBox();
timerZoomTestSeq = new System.Windows.Forms.Timer(components);
((System.ComponentModel.ISupportInitialize)videoView).BeginInit();
@@ -109,41 +107,22 @@ namespace robospot_camera_finder
// tbRtspLink
//
tbRtspLink.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
tbRtspLink.Location = new Point(312, 492);
tbRtspLink.Location = new Point(75, 492);
tbRtspLink.Name = "tbRtspLink";
tbRtspLink.ReadOnly = true;
tbRtspLink.Size = new Size(215, 23);
tbRtspLink.Size = new Size(559, 23);
tbRtspLink.TabIndex = 8;
//
// lblRtspLink
//
lblRtspLink.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
lblRtspLink.AutoSize = true;
lblRtspLink.Location = new Point(249, 494);
lblRtspLink.Location = new Point(12, 495);
lblRtspLink.Name = "lblRtspLink";
lblRtspLink.Size = new Size(57, 15);
lblRtspLink.TabIndex = 7;
lblRtspLink.Text = "RTSP Link";
//
// labelSerial
//
labelSerial.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
labelSerial.AutoSize = true;
labelSerial.Location = new Point(9, 494);
labelSerial.Name = "labelSerial";
labelSerial.Size = new Size(82, 15);
labelSerial.TabIndex = 6;
labelSerial.Text = "Serial Number";
//
// tbSerial
//
tbSerial.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
tbSerial.Location = new Point(102, 492);
tbSerial.Name = "tbSerial";
tbSerial.ReadOnly = true;
tbSerial.Size = new Size(124, 23);
tbSerial.TabIndex = 5;
//
// cbTestZoom
//
cbTestZoom.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
@@ -170,9 +149,7 @@ namespace robospot_camera_finder
Controls.Add(tbRtspLink);
Controls.Add(lblRtspLink);
Controls.Add(videoView);
Controls.Add(labelSerial);
Controls.Add(btnZoomMax);
Controls.Add(tbSerial);
Controls.Add(tbZoom);
Controls.Add(btnZoomMin);
Icon = (Icon)resources.GetObject("$this.Icon");
@@ -194,8 +171,6 @@ namespace robospot_camera_finder
private System.Windows.Forms.Timer timerUpdateZoom;
private Button btnZoomMin;
private Button btnZoomMax;
private Label labelSerial;
private TextBox tbSerial;
private TextBox tbRtspLink;
private Label lblRtspLink;
private CheckBox cbTestZoom;

View File

@@ -29,7 +29,6 @@ namespace robospot_camera_finder
updateZoomSlider();
tbSerial.Text = camera.camera_serial;
tbRtspLink.Text = rtsp_link;
videoView.MediaPlayer = _mp;

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<TargetFramework>net9.0-windows7.0</TargetFramework>
<RootNamespace>robospot_camera_finder</RootNamespace>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
@@ -19,8 +19,22 @@
<PackageReference Include="LibVLCSharp" Version="3.8.2" />
<PackageReference Include="LibVLCSharp.Forms" Version="3.8.2" />
<PackageReference Include="LibVLCSharp.WinForms" Version="3.8.2" />
<PackageReference Include="Tmds.MDns" Version="0.8.0" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>