Skip to content

Commit

Permalink
Merge pull request #1 from philipellisis/feature/concurrent-com-port
Browse files Browse the repository at this point in the history
Feature/concurrent com port
  • Loading branch information
philipellisis authored Feb 17, 2024
2 parents 4771235 + fa93989 commit 8c5c4d4
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ protected override void ConnectToController()
ComPort.StopBits = ComPortStopBits;
ComPort.ReadTimeout = ComPortTimeOutMs;
ComPort.WriteTimeout = ComPortTimeOutMs;
ComPort.DtrEnable = ComPortDtrEnable;


try
{
Expand All @@ -573,6 +573,7 @@ protected override void ConnectToController()
try
{
ComPort.Open();
ComPort.DtrEnable = ComPortDtrEnable;
}
catch (Exception E)
{
Expand Down
134 changes: 134 additions & 0 deletions DirectOutput/Cab/Out/PinOne/NamedPipeServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System;
using System.IO.Ports;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using PerCederberg.Grammatica.Runtime;

public class NamedPipeServer
{
private SerialPort serialPort;
private bool isRunning = true;
private string comPort = "";
private const string PipeName = "ComPortServerPipe";
CancellationToken clientToken = new CancellationToken();
CancellationToken serverToken = new CancellationToken();

public NamedPipeServer(string comPort)
{

this.comPort = comPort;
serialPort = new SerialPort(comPort, 2000000, Parity.None, 8, StopBits.One);
serialPort.NewLine = "\r\n";
serialPort.ReadTimeout = 500;
serialPort.WriteTimeout = 500;
serialPort.Open();
serialPort.DtrEnable = true;
}


public void StartServer()
{

Task.Run(async () =>
{
while (isRunning)
{
var serverStream = new NamedPipeServerStream(
PipeName,
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);

Console.WriteLine("Waiting for client connection...");
await serverStream.WaitForConnectionAsync(serverToken);

HandleClientConnectionAsync(serverStream);
}
});
}




private async Task HandleClientConnectionAsync(NamedPipeServerStream serverStream)
{
bool completed = false;
while (isRunning && !completed && serverStream.IsConnected)
{
try
{
var request = new byte[1024];
int bytesRead = await serverStream.ReadAsync(request, 0, request.Length, clientToken);
string requestStr = Encoding.UTF8.GetString(request, 0, bytesRead);

// Process request
if (requestStr.StartsWith("CONNECT"))
{
Console.WriteLine("Requesting Connect");
serialPort.Open();
serverStream.Write(Encoding.UTF8.GetBytes("OK"), 0, 2);
}
else if (requestStr.StartsWith("STOP_SERVER"))
{
isRunning = false;
}
else if (requestStr.StartsWith("DISCONNECT"))
{
serverStream.Disconnect();
completed = true;
Console.WriteLine("Requesting disconnect");
}
else if (requestStr.StartsWith("WRITE"))
{
var bytesToWrite = Convert.FromBase64String(requestStr.Substring(6));
serialPort.Write(bytesToWrite, 0, bytesToWrite.Length);
serverStream.Write(Encoding.UTF8.GetBytes("OK"), 0, 2);
}
else if (requestStr.StartsWith("READLINE"))
{
string response = serialPort.ReadLine();
serverStream.Write(Encoding.UTF8.GetBytes(response), 0, response.Length);
}
else if (requestStr.StartsWith("CHECK"))
{
string response = serialPort.IsOpen ? "TRUE" : "FALSE";
serverStream.Write(Encoding.UTF8.GetBytes(response), 0, response.Length);
}
else if (requestStr.StartsWith("COMPORT"))
{
Console.WriteLine("Requesting com port");
serverStream.Write(Encoding.UTF8.GetBytes(this.comPort), 0, this.comPort.Length);
}
}
catch (Exception ex)
{
serverStream.Disconnect();
isRunning = false;
}
finally
{
//Console.WriteLine("cleaning up, closing ports");
}

}
if (isRunning == false)
{

serverStream.Disconnect();
serverStream.Close();

}
}

public void StopServer()
{
serialPort.Close();
isRunning = false;
clientToken.ThrowIfCancellationRequested();
serverToken.ThrowIfCancellationRequested();
Thread.Sleep(300);
}
}
38 changes: 25 additions & 13 deletions DirectOutput/Cab/Out/PinOne/PinOne.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace DirectOutput.Cab.Out.PinOne
{
/// <summary>
/// The <a href="https://clevelandsoftwaredesign.com">PinOne Controller</a> is an open-source
/// The <a href="https://clevelandsoftwaredesign.com">PinOne Controller</a> is an free for non commercial use
/// software/hardware project based on the inexpensive, easy to use Arduino microcontroller development platform. Almost any
/// Arduino based board will work with the software, but the Cleveland Software Design PinOne board is
/// specifically designed to work with the software and has all the hardware components already added into it.
Expand Down Expand Up @@ -95,8 +95,9 @@ public int MinCommandIntervalMs
#region ComPort property core parts
private string _ComPort = "comm1";
private bool ComPortSet = false;
private SerialPort Port = null;
private object PortLocker = new object();
private bool isMaster = false;
private PinOneCommunication pinOneCommunication;

/// <summary>
/// Gets or sets the mininimal interval between command in miliseconds (Default: 1ms).
Expand Down Expand Up @@ -206,7 +207,7 @@ protected override void UpdateOutputs(byte[] NewOutputValues)
buf[0] = 0; // USB report ID - always 0
buf[1] = pfx; // message prefix
Array.Copy(NewOutputValues, i, buf, 2, lim - i);
Port.Write(buf, 0, buf.Length);
pinOneCommunication.Write(buf);

// the new values are now the current values on the device
Array.Copy(NewOutputValues, i, OldOutputValues, i, lim - i);
Expand Down Expand Up @@ -238,17 +239,28 @@ protected override void ConnectToController()
{
lock (PortLocker)
{
if (Port != null)
if (pinOneCommunication != null)
{
DisconnectFromController();
}

Port = new SerialPort(ComPort, 2000000, Parity.None, 8, StopBits.One);
Port.NewLine = "\r\n";
Port.ReadTimeout = 500;
Port.WriteTimeout = 500;
Port.Open();
Port.DtrEnable = true;
pinOneCommunication = new PinOneCommunication(ComPort);
if (!pinOneCommunication.ConnectToServer())
{
if(pinOneCommunication.CreateServer())
{
isMaster = true;
if (!pinOneCommunication.ConnectToServer())
{
throw new Exception("Unable to connect to server after new creation");
}
} else
{
throw new Exception("Unable to create server");
}


}
}
}
catch (Exception E)
Expand All @@ -266,10 +278,10 @@ protected override void DisconnectFromController()
{
lock (PortLocker)
{
if (Port != null)
if (pinOneCommunication != null)
{
Port.Close();
Port = null;
pinOneCommunication.DisconnectFromServer();

}

}
Expand Down
12 changes: 11 additions & 1 deletion DirectOutput/Cab/Out/PinOne/PinOneAutoConfigurator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public void AutoConfig(Cabinet Cabinet)
List<string> Preconfigured = new List<string>(Cabinet.OutputControllers.Where(OC => OC is PinOne).Select(PO => ((PinOne)PO).ComPort));
String comPort = GetDevice();


if (!Preconfigured.Contains(comPort) && comPort != "")
{
PinOne p = new PinOne(comPort);
Expand Down Expand Up @@ -85,7 +86,16 @@ public static String GetDevice()
}
}
}
return "";


string comPort = "";
PinOneCommunication communication = new PinOneCommunication("");
if (communication.ConnectToServer())
{
comPort = communication.GetCOMPort();
}

return comPort;
}

#endregion
Expand Down
138 changes: 138 additions & 0 deletions DirectOutput/Cab/Out/PinOne/PinOneCommunication.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using System;
using System.IO.Pipes;
using System.Text;
using System.Threading;

public class PinOneCommunication
{
private NamedPipeClientStream pipeClient;
private NamedPipeServer server = null;
private string pipeName = "ComPortServerPipe";
private string COMPort = "";

public PinOneCommunication(String COMPort)
{
this.COMPort = COMPort;
}

public bool ConnectToServer()
{
try
{
pipeClient = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.None);
pipeClient.Connect(100);
return true;
}
catch (Exception)
{
return false;
}
}

public bool DisconnectFromServer()
{
Disconnect();
if (server !=null)
{
server.StopServer();
return true;
}
return false;
}

public bool CreateServer()
{
try
{
if (!COMPort.IsNullOrEmpty())
{
server = new NamedPipeServer(COMPort);
Thread serverThread = new Thread(server.StartServer);
serverThread.IsBackground = true;
serverThread.Start();
Thread.Sleep(300);
return true;
}
}
catch (Exception)
{
return false;
}

return false;
}
public bool isComPortConnected()
{
SendMessage("CHECK");
var response = ReadMessage();
return response == "TRUE";
}
public void Disconnect()
{
SendMessage("DISCONNECT");
}

public void Write(byte[] bytesToWrite)
{
string base64Bytes = Convert.ToBase64String(bytesToWrite);
SendMessage($"WRITE {base64Bytes}");
ReadMessage(); // Expect OK
}

public string ReadLine()
{
SendMessage("READLINE");
return ReadMessage();
}

public string GetCOMPort()
{
SendMessage("COMPORT");
return ReadMessage();
}

private void SendMessage(string message)
{
try
{
byte[] request = Encoding.UTF8.GetBytes(message);
pipeClient.Write(request, 0, request.Length);
}
catch (Exception)
{
if (CreateServer() && ConnectToServer())
{
byte[] request = Encoding.UTF8.GetBytes(message);
pipeClient.Write(request, 0, request.Length);
}
else
{
throw new Exception("Unable to connect to board");
}

}

}

private string ReadMessage()
{
try
{
var response = new byte[1024];
int bytesRead = pipeClient.Read(response, 0, response.Length);
return Encoding.UTF8.GetString(response, 0, bytesRead);
}
catch (Exception)
{
if( CreateServer() && ConnectToServer())
{
return ReadMessage();

} else
{
return "";
}
}

}
}
Loading

0 comments on commit 8c5c4d4

Please sign in to comment.