- Add PrinterSettings to PortFactory Create/PortAvailable methods - Add ApplicationController->LogInfo for status reporting - Remove printer coupling in tcp/x3g for status reporting - Issue MatterHackers/MCCentral#4549 Remove ActivePrinter from ApplicationController
311 lines
7.2 KiB
C#
311 lines
7.2 KiB
C#
using System;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using MatterHackers.MatterControl;
|
|
using MatterHackers.MatterControl.SlicerConfiguration;
|
|
using MatterHackers.SerialPortCommunication.FrostedSerial;
|
|
|
|
namespace TcpipDriver
|
|
{
|
|
class TcpipSerialPort :IFrostedSerialPort
|
|
{
|
|
// Telnet protocol characters
|
|
const byte IAC = 255; // escape
|
|
const byte DONT = 254; // negotiation
|
|
const byte DO = 253;// negotiation
|
|
const byte WILL = 251; // negotiation
|
|
const byte SB = 250; // subnegotiation begin
|
|
const byte SE = 240; // subnegotiation end
|
|
const byte ComPortOpt = 44; // COM port options
|
|
const byte SetBaud = 1; // Set baud rate
|
|
const byte SetDataSize = 2; // Set data size
|
|
const byte SetParity = 3; // Set parity
|
|
const byte SetControl = 5; // Set control lines
|
|
const byte DTR_ON = 8; // used here to reset microcontroller
|
|
const byte DTR_OFF = 9;
|
|
const byte RTS_ON = 11; // used here to signal ISP (in-system-programming) to uC
|
|
const byte RTS_OFF = 12;
|
|
|
|
private Socket socket;
|
|
private NetworkStream stream;//Seems to have more in common with the socket so we will use to make this interface easier
|
|
private IPAddress ipAddress;
|
|
private int port;
|
|
private IPEndPoint ipEndPoint;
|
|
private byte[] readBuffer;
|
|
private int bufferIndex;
|
|
|
|
//These get set before open is called but the stream is not created until open is called. Preserver values to be set after stream is created.
|
|
private int tempReadTimeout;
|
|
private int tempWriteTimeout;
|
|
|
|
private bool reconnecting = false;
|
|
private PrinterSettings settings;
|
|
|
|
public TcpipSerialPort(PrinterSettings settings, string name)
|
|
{
|
|
this.settings = settings;
|
|
|
|
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
|
|
if (int.TryParse(settings.GetValue("ip_port"), out port)
|
|
&& IPAddress.TryParse(settings.GetValue("ip_address"), out ipAddress))
|
|
{
|
|
ipEndPoint = new IPEndPoint(ipAddress, port);
|
|
readBuffer = new byte[1024];
|
|
bufferIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
this.IsValid = false;
|
|
}
|
|
}
|
|
|
|
public bool IsValid { get; } = true;
|
|
|
|
public int BaudRate { get; set; }
|
|
|
|
public int BytesToRead
|
|
{
|
|
get
|
|
{
|
|
if(stream.DataAvailable)
|
|
{
|
|
int bytesRead = stream.Read(readBuffer, bufferIndex, readBuffer.Length);
|
|
bufferIndex += bytesRead;
|
|
}
|
|
|
|
return bufferIndex;
|
|
}
|
|
}
|
|
|
|
public bool DtrEnable
|
|
{
|
|
get { return dtrEnable; }
|
|
set
|
|
{
|
|
if (stream != null)
|
|
{
|
|
SetDtrEnable(value);
|
|
}
|
|
dtrEnable = value;
|
|
}
|
|
}
|
|
private bool dtrEnable;
|
|
|
|
// Eventually I will need to find out how to check that the port is open and connectable
|
|
public bool IsOpen { get; } = true;
|
|
|
|
public int ReadTimeout
|
|
{
|
|
get
|
|
{
|
|
return stream.ReadTimeout;
|
|
}
|
|
|
|
set
|
|
{
|
|
if(stream != null)
|
|
{
|
|
stream.ReadTimeout = value;
|
|
}
|
|
else
|
|
{
|
|
tempReadTimeout = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool RtsEnable { get; set; }
|
|
|
|
public int WriteTimeout
|
|
{
|
|
get
|
|
{
|
|
return stream.WriteTimeout;
|
|
}
|
|
|
|
set
|
|
{
|
|
if(stream != null)
|
|
{
|
|
stream.WriteTimeout = value;
|
|
}
|
|
else
|
|
{
|
|
tempWriteTimeout = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
socket.Close();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
stream.Dispose();
|
|
}
|
|
|
|
public void Open()
|
|
{
|
|
try
|
|
{
|
|
// Attempt to connect Message to just the console
|
|
this.LogInfo("Attempting to connect to: " + ipEndPoint.Address + " on port " + ipEndPoint.Port);
|
|
socket.Connect(ipEndPoint);
|
|
stream = new NetworkStream(socket);
|
|
this.LogInfo("Connected to: " + ipEndPoint.Address + " on port " + ipEndPoint.Port);
|
|
if (this.BaudRate != 0)
|
|
{
|
|
//Send Telnet handshake so that esp will enter the telnet mode allowing us to set baud and reset board
|
|
byte[] bytes = new byte[] { IAC, WILL, ComPortOpt };
|
|
Write(bytes, 0, bytes.Length);
|
|
//Set baud and reset board
|
|
SetBaudRate(this.BaudRate);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
ApplicationController.Instance.LogError("Exception:" + e.Message);
|
|
}
|
|
|
|
//These were set before and are now set in the stream
|
|
// if (stream != null)
|
|
{
|
|
stream.WriteTimeout = tempWriteTimeout;
|
|
stream.ReadTimeout = tempReadTimeout;
|
|
}
|
|
}
|
|
|
|
private void LogInfo(string message)
|
|
{
|
|
ApplicationController.Instance.LogInfo(message);
|
|
}
|
|
|
|
public int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
Array.Copy(readBuffer, offset, buffer, 0, count);
|
|
Array.Clear(buffer, 0, count);
|
|
bufferIndex -= count;
|
|
Array.Copy(readBuffer, count, readBuffer, 0, bufferIndex);//THis may throw an exception as the target and source are the same
|
|
|
|
return count;
|
|
}
|
|
|
|
public string ReadExisting()
|
|
{
|
|
string bufferAsString = ConvertBytesToString(readBuffer, bufferIndex);
|
|
Array.Clear(readBuffer, 0, bufferIndex);
|
|
bufferIndex = 0;
|
|
return bufferAsString;
|
|
}
|
|
|
|
public void Write(string str)
|
|
{
|
|
var buffer = ConvertStringToBytes(str);
|
|
Write(buffer, 0, buffer.Length);
|
|
}
|
|
|
|
public void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
if (!reconnecting)
|
|
{
|
|
try
|
|
{
|
|
stream.Write(buffer, offset, count);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
this.LogInfo("Exception:" + e.Message);
|
|
Reconnect();
|
|
stream.Write(buffer, offset, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static byte[] ConvertStringToBytes(string str)
|
|
{
|
|
byte[] bytes = new byte[str.Length];
|
|
for (int i = 0; i < str.Length; i++)
|
|
{
|
|
bytes[i] = Convert.ToByte(str[i]);
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
private string ConvertBytesToString(byte[] inputBytes, int bytesRead)
|
|
{
|
|
var builder = new StringBuilder();
|
|
|
|
for (int index = 0; index < bytesRead; index++)
|
|
{
|
|
builder.Append(Convert.ToChar(inputBytes[index]));
|
|
}
|
|
|
|
return builder.ToString();
|
|
}
|
|
|
|
private void Reconnect()
|
|
{
|
|
reconnecting = true;
|
|
try
|
|
{
|
|
socket?.Close();
|
|
}
|
|
catch{ }
|
|
|
|
for(int i =0; i< 5; i++)
|
|
{
|
|
ipEndPoint = new IPEndPoint(ipAddress, port);
|
|
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
try
|
|
{
|
|
// Attempt to connect Message to just the console
|
|
this.LogInfo("Attempting to connect to: " + ipEndPoint.Address + " on port " + ipEndPoint.Port);
|
|
socket.Connect(ipEndPoint);
|
|
stream = new NetworkStream(socket);
|
|
this.LogInfo("Connected to: " + ipEndPoint.Address + " on port " + ipEndPoint.Port);
|
|
|
|
// Send telnet handshake
|
|
byte[] bytes = new byte[] { IAC, WILL, ComPortOpt };
|
|
Write(bytes, 0, bytes.Length);
|
|
break;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
ApplicationController.Instance.LogError("Exception:" + e.Message);
|
|
Thread.Sleep((int)(500 * Math.Pow(i,2)));
|
|
}
|
|
}
|
|
|
|
reconnecting = false;
|
|
}
|
|
|
|
private void SetDtrEnable(bool dtr)
|
|
{
|
|
byte dtrEnabled = dtr ? DTR_ON : DTR_OFF;
|
|
|
|
//Create Sequence of bytes that will cause board to be reset
|
|
byte[] bytes = new byte[] { IAC, SB, ComPortOpt, SetControl, dtrEnabled, IAC, SE };
|
|
|
|
Write(bytes, 0, bytes.Length);
|
|
}
|
|
|
|
private void SetBaudRate(int baudRate)
|
|
{
|
|
byte[] baudBytes = BitConverter.GetBytes(baudRate);
|
|
if (BitConverter.IsLittleEndian)
|
|
{
|
|
Array.Reverse(baudBytes);
|
|
}
|
|
//Create Sequence of bytes that will set baudrate
|
|
byte[] bytes = new byte[] { IAC, SB, ComPortOpt, SetBaud, baudBytes[0], baudBytes[1], baudBytes[2], baudBytes[3], IAC, SE };
|
|
|
|
Write(bytes, 0, bytes.Length);
|
|
}
|
|
}
|
|
}
|