From 508c5c8efc43ff6b3dda327e619d4eaa3eb739cc Mon Sep 17 00:00:00 2001 From: Peter Hall <33176108+IamPete1@users.noreply.github.com> Date: Sat, 28 Sep 2024 15:47:13 +0100 Subject: [PATCH 1/2] Controls: SerialOutputPass: add WebSocket Sever --- Controls/SerialOutputPass.Designer.cs | 3 ++- Controls/SerialOutputPass.cs | 11 +++++++++++ Controls/SerialOutputPass.resx | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Controls/SerialOutputPass.Designer.cs b/Controls/SerialOutputPass.Designer.cs index abba9801cd..6c43af0c3c 100644 --- a/Controls/SerialOutputPass.Designer.cs +++ b/Controls/SerialOutputPass.Designer.cs @@ -100,7 +100,8 @@ private void InitializeComponent() this.Type.Items.AddRange(new object[] { "Serial", "TCP", - "UDP"}); + "UDP", + "WS"}); this.Type.Name = "Type"; this.Type.Resizable = System.Windows.Forms.DataGridViewTriState.True; this.Type.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; diff --git a/Controls/SerialOutputPass.cs b/Controls/SerialOutputPass.cs index bc53c77fee..98886ea51b 100644 --- a/Controls/SerialOutputPass.cs +++ b/Controls/SerialOutputPass.cs @@ -241,6 +241,17 @@ private void myDataGridView1_CellContentClick(object sender, DataGridViewCellEve MainV2.comPort.MirrorStream.BaudRate = int.Parse(extra); MainV2.comPort.MirrorStream.Open(); } + else if (protocol == "WS") + { + // Only support WebSocket server for now + if (direction == "Outbound") + { + var server = new MissionPlanner.Comms.WebSocketServer(); + server.EndPoint = new IPEndPoint(IPAddress.Parse(extra), int.Parse(port)); + server.Open(); + MainV2.comPort.MirrorStream = server; + } + } } catch (Exception ex) { CustomMessageBox.Show("Error: " + ex.Message); diff --git a/Controls/SerialOutputPass.resx b/Controls/SerialOutputPass.resx index 8ca2592752..30b270f53f 100644 --- a/Controls/SerialOutputPass.resx +++ b/Controls/SerialOutputPass.resx @@ -304,7 +304,7 @@ myDataGridView1 - MissionPlanner.Controls.MyDataGridView, MissionPlanner, Version=1.3.8177.19206, Culture=neutral, PublicKeyToken=null + MissionPlanner.Controls.MyDataGridView, MissionPlanner, Version=1.3.9036.40033, Culture=neutral, PublicKeyToken=null $this From 28f1b6c20078ac6483aeb8491be338a4d37a29f1 Mon Sep 17 00:00:00 2001 From: Peter Hall <33176108+IamPete1@users.noreply.github.com> Date: Sat, 28 Sep 2024 15:47:56 +0100 Subject: [PATCH 2/2] ExtLibs: Comms: add WebSocketServer using Fleck --- ExtLibs/Comms/CommsWebSocketServer.cs | 262 ++++++++++++++++++++++ ExtLibs/Comms/MissionPlanner.Comms.csproj | 1 + 2 files changed, 263 insertions(+) create mode 100644 ExtLibs/Comms/CommsWebSocketServer.cs diff --git a/ExtLibs/Comms/CommsWebSocketServer.cs b/ExtLibs/Comms/CommsWebSocketServer.cs new file mode 100644 index 0000000000..46f88efe99 --- /dev/null +++ b/ExtLibs/Comms/CommsWebSocketServer.cs @@ -0,0 +1,262 @@ +using System; +using System.IO; +using System.Net; +using System.Text; +using System.Threading; +using log4net; + +using Fleck; +using System.Collections.Generic; + + +namespace MissionPlanner.Comms +{ + public class WebSocketServer : CommsBase, ICommsSerial, IDisposable + { + private static readonly ILog log = LogManager.GetLogger(typeof(WebSocketServer)); + private bool inOpen; + private bool closed; + public IPEndPoint EndPoint = new IPEndPoint(IPAddress.Any, 0); + + public string ConfigRef { get; set; } = ""; + + public WebSocketServer() { } + + public string Port { get; set; } + + public int WriteBufferSize { get; set; } + public int WriteTimeout { get; set; } + public bool RtsEnable { get; set; } + public Stream BaseStream => new CommsStream(this, 0); + + public void toggleDTR() + { + } + + public int ReadTimeout + { + get; + set; + } + + public int ReadBufferSize { get; set; } + + public int BaudRate { get; set; } + + public int DataBits { get; set; } + + public string PortName + { + get + { + return "WS " + EndPoint.ToString(); + } + set{} + } + + MemoryStream readMemoryStream = new MemoryStream(); + private int readCursor = 0; + + public int BytesToRead + { + get + { + lock (readMemoryStream) + return (int)(readMemoryStream.Length - readCursor); + } + } + + public int BytesToWrite => 0; + + public bool IsOpen + { + get + { + return allSockets.Count > 0; + } + } + + public bool DtrEnable { get; set; } + public string Host { get; set; } = ""; + + private List allSockets = new List(); + + public void Open() + { + // Close any open connections + allSockets.ForEach(s => s.Close()); + + // New server + var server = new Fleck.WebSocketServer("ws://" + EndPoint.ToString()); + + // Open connection + server.Start(socket => { + socket.OnOpen = () => { + // Only allow single client for now + if (allSockets.Count != 0) + { + socket.Close(); + } + else + { + allSockets.Add(socket); + } + }; + socket.OnClose = () => { + allSockets.Remove(socket); + }; + socket.OnBinary = b => { + // Add incoming data to buffer + lock (readMemoryStream) + { + readMemoryStream.Seek(0, SeekOrigin.End); + readMemoryStream.Write(b, 0, b.Length); + } + }; + }); + } + + public int Read(byte[] readto, int offset, int length) + { + try + { + if (length < 1) return 0; + int read = 0; + lock (readMemoryStream) + { + readMemoryStream.Seek(readCursor, SeekOrigin.Begin); + read = readMemoryStream.Read(readto, offset, length); + readCursor += read; + if (BytesToRead == 0) + { + readMemoryStream.SetLength(0); + readCursor = 0; + } + } + + return read; + } + catch + { + throw new Exception("Socket Closed"); + } + } + + public int ReadByte() + { + var count = 0; + while (BytesToRead == 0) + { + Thread.Sleep(1); + if (count > ReadTimeout) + throw new Exception("NetSerial Timeout on read"); + count++; + } + + var buffer = new byte[1]; + Read(buffer, 0, 1); + return buffer[0]; + } + + public int ReadChar() + { + return ReadByte(); + } + + public string ReadExisting() + { + var data = new byte[BytesToRead]; + if (data.Length > 0) + Read(data, 0, data.Length); + + var line = Encoding.ASCII.GetString(data, 0, data.Length); + + return line; + } + + public void WriteLine(string line) + { + line = line + "\n"; + Write(line); + } + + public void Write(string line) + { + var data = new ASCIIEncoding().GetBytes(line); + Write(data, 0, data.Length); + } + + public void Write(byte[] write, int offset, int length) + { + var temp = new byte[length]; + Array.Copy(write, offset, temp, 0, length); + allSockets.ForEach(s => s.Send(temp)); + } + + public void DiscardInBuffer() + { + var size = BytesToRead; + var crap = new byte[size]; + log.InfoFormat("WSServer DiscardInBuffer {0}", size); + Read(crap, 0, size); + } + + public string ReadLine() + { + var temp = new byte[4000]; + var count = 0; + var timeout = 0; + + while (timeout <= 100) + { + if (!IsOpen) break; + if (BytesToRead > 0) + { + var letter = (byte)ReadByte(); + + temp[count] = letter; + + if (letter == '\n') // normal line + break; + + count++; + if (count == temp.Length) + break; + timeout = 0; + } + else + { + timeout++; + Thread.Sleep(5); + } + } + + Array.Resize(ref temp, count + 1); + + return Encoding.ASCII.GetString(temp, 0, temp.Length); + } + + public void Close() + { + // Close any open connections + allSockets.ForEach(s => s.Close()); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // dispose managed resources + Close(); + } + + // free native resources + } + } +} diff --git a/ExtLibs/Comms/MissionPlanner.Comms.csproj b/ExtLibs/Comms/MissionPlanner.Comms.csproj index 741dd27661..a93e682794 100644 --- a/ExtLibs/Comms/MissionPlanner.Comms.csproj +++ b/ExtLibs/Comms/MissionPlanner.Comms.csproj @@ -32,6 +32,7 @@ + all