Moved X3G & TCP drivers into MC from our plug-in system

This commit is contained in:
Matt Moening 2018-07-27 14:07:16 -07:00
parent f6e80c0178
commit dad6e3e49e
10 changed files with 2424 additions and 0 deletions

View file

@ -0,0 +1,302 @@
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.SerialPortCommunication.FrostedSerial;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using MatterHackers.MatterControl.PrinterCommunication;
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;
PrinterConnection printerConnection;
public TcpipSerialPort(PrinterConnection printerConnection, string name)
{
this.printerConnection = printerConnection;
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
if (int.TryParse(ActiveSliceSettings.Instance.GetValue("ip_port"), out port)
&& IPAddress.TryParse(ActiveSliceSettings.Instance.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
{//ADD Attempt to connect Message to just the console
printerConnection.TerminalLog.WriteLine("Attempting to connect to: " + ipEndPoint.Address + " on port " + ipEndPoint.Port);
socket.Connect(ipEndPoint);
stream = new NetworkStream(socket);
printerConnection.TerminalLog.WriteLine("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)
{//ADD Error Message to just the console
printerConnection.TerminalLog.WriteLine("Exception:" + e.Message);
}
//These were set before and are now set in the stream
// if (stream != null)
{
stream.WriteTimeout = tempWriteTimeout;
stream.ReadTimeout = tempReadTimeout;
}
}
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)
{
printerConnection.TerminalLog.WriteLine("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)
{
StringBuilder 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
{//ADD Attempt to connect Message to just the console
printerConnection.TerminalLog.WriteLine("Attempting to connect to: " + ipEndPoint.Address + " on port " + ipEndPoint.Port);
socket.Connect(ipEndPoint);
stream = new NetworkStream(socket);
printerConnection.TerminalLog.WriteLine("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)
{//ADD Error Message to just the console
printerConnection.TerminalLog.WriteLine("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);
}
}
}

View file

@ -0,0 +1,25 @@
using System.Net;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.SerialPortCommunication.FrostedSerial;
namespace TcpipDriver
{
public class TcpipSerialPortFactory : FrostedSerialPortFactory
{
public override bool SerialPortAlreadyOpen(string portName) => false;
protected override string GetDriverType() => "TCPIP";
public override IFrostedSerialPort Create(string serialPortName)
{
return new TcpipSerialPort(ApplicationController.Instance.ActivePrinter.Connection, serialPortName);
}
public override bool SerialPortIsAvailable(string serialPortName)
{
return int.TryParse(ActiveSliceSettings.Instance.GetValue(SettingsKey.ip_port), out _)
&& IPAddress.TryParse(ActiveSliceSettings.Instance.GetValue(SettingsKey.ip_address), out _);
}
}
}

View file

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MatterHackers.MatterControl.Plugins.X3GDriver
{
class X3GCrc
{
private uint crc;
public X3GCrc()
{
crc = 0;
}
public void update(byte command)
{
crc = (crc ^ command) & 0xff;
for (int i = 0; i < 8; i++)
{
if ((crc & 0x01) != 0) // C# is strictly typed so you must make a comparison
{
crc = ((crc >> 1) ^ 0x8c) & 0xff;
}
else
{
crc = (crc >> 1) & 0xff;
}
}
}//end generateNewCRC
public byte getCrc()
{
return (byte)crc;
}
public void clear()
{
crc = 0;
}
}
}

View file

@ -0,0 +1,154 @@
/*
Copyright (c) 2017, Matt Moening, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MatterHackers.Agg.Image;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl.Library;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.Plugins.X3GDriver;
namespace MatterHackers.MatterControl.Plugins.X3GDriver
{
public class X3GExport : IExportPlugin
{
private PrinterConfig printer;
public string ButtonText { get; } = "Machine File (X3G)";
public string ExtensionFilter { get; } = "Export X3G|*.x3g";
public string FileExtension { get; } = ".x3g";
public ImageBuffer Icon { get; } = AggContext.StaticData.LoadIcon(Path.Combine("filetypes", "x3g.png"));
public void Initialize(PrinterConfig printer)
{
this.printer = printer;
}
public bool Enabled
{
get => printer.Settings.PrinterSelected
&& printer.Settings.GetValue<bool>("enable_sailfish_communication");
}
public bool ExportPossible(ILibraryAsset libraryItem) => true;
public async Task<bool> Generate(IEnumerable<ILibraryItem> libraryItems, string outputPath)
{
ILibraryAssetStream libraryContent = libraryItems.OfType<ILibraryAssetStream>().FirstOrDefault();
if (libraryContent == null)
{
return false;
}
var result = await libraryContent.GetStream(null);
StreamReader inputFile = new StreamReader(result.Stream);
FileStream binaryFileStream = new FileStream(outputPath, FileMode.OpenOrCreate);
BinaryWriter outputFile = new BinaryWriter(binaryFileStream);
X3GPrinterDetails printerDetails = new X3GPrinterDetails();
X3GWriter x3gConverter = new X3GWriter(printerDetails);
List<byte[]> x3gLines = new List<byte[]>();
byte[] emptyByteArray = { 0 };
string line;
//Makes sure steps per mm and bed offset is set
string splitString = "\\n";
string connectGCodeLines = printer.Settings.GetValue(SettingsKey.connect_gcode);
foreach (string connectLine in connectGCodeLines.Split(splitString.ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
{
bool sendToPrinter;
string prepedLine = connectLine.Split(';')[0];
if (prepedLine != String.Empty)
{
prepedLine += '\n';
x3gConverter.translate(connectLine, out sendToPrinter);
x3gConverter.GetAndClearOverflowPackets();
}
}//MakerBot Settings set
line = inputFile.ReadLine();
while (line != null)
{
string translationCommand = line.Split(';')[0];
translationCommand.Trim();
if (translationCommand != String.Empty)
{
translationCommand += '\n';
//Translate Lines
bool sendToPrinter;
x3gLines.Add(x3gConverter.translate(translationCommand, out sendToPrinter));
x3gLines.AddRange(x3gConverter.GetAndClearOverflowPackets());
x3gConverter.updateCurrentPosition();
if (sendToPrinter)//certain commands are for handling internal changes only
{
//Write lines to file if needed
foreach (byte[] x3gLine in x3gLines)
{
if (x3gLine != emptyByteArray)
{
byte[] trimmedX3gLine = TrimPacketStructure(x3gLine);
outputFile.Write(trimmedX3gLine, 0, trimmedX3gLine.Length);
}
}
}
x3gLines.Clear();
}
line = inputFile.ReadLine();
}
inputFile.Close();
outputFile.Close();
return true;
}
private static byte[] TrimPacketStructure(byte[] s3gPacket)
{
byte[] x3gCommand = new byte[s3gPacket.Length - 3];
for (int i = 0; i < x3gCommand.Length; i++)
{
x3gCommand[i] = s3gPacket[i + 2];
}
return x3gCommand;
}
}
}

View file

@ -0,0 +1,54 @@
using MatterHackers.VectorMath;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MatterHackers.Plugins.X3GDriver
{
public class X3GPrinterDetails
{
public Vector3 stepsPerMm;//Make a M command to receive steps and save into these static values (will be sent after successful connect)
public long extruderStepsPerMm;
public Vector3 currentPosition;
public float activeExtruderPosition;
public float inactiveExtruderPosition;
public Vector3 targetMovePosition;
public float targetExtruderPosition;
public Vector3 positionalOffset;//Used in absolute to simulate 0,0 being at the bottom left of the print bed
public bool extruderRelativePos;
public Vector3 homingFeedRate;
public byte activeExtruderIndex;
public Vector2 extruderOffset;
public bool heatingLockout;//boolean that is used to mimic M109, suppresses oks and sends M105s to the printer until target temp is reached
public int[] targetExtruderTemps;
public int targetBedTemp;
public int targetTempForMakerbotStyleCommands;
public int requiredTemperatureResponseCount;//The number of responses from the printer that corresponds to one M105 (adjusts to extruders & bed heating as required)
public int teperatureResponseCount;//number of responses for temperature currently received from the printer. resets after hitting target count
public long dwellTime; //this is set during a dwell command and is reset to zero after the dwell has completed
public X3GPrinterDetails()
{
currentPosition = new Vector3(/*285,150,0*/);//defaults to "far corner" (where the machine thinks it is at 0,0) inverted positional offset
activeExtruderPosition = 0;
targetMovePosition = new Vector3();
targetExtruderPosition = 0;
positionalOffset = new Vector3(285, 150, 0);
stepsPerMm = new Vector3(88.8, 88.8, 400); //Default steps per mm in case they are not set
extruderStepsPerMm = 101;//repG says 96
homingFeedRate = new Vector3(300, 300, 400);
activeExtruderIndex = 0;
targetExtruderTemps = new int[2];
targetBedTemp = 0;
inactiveExtruderPosition = 0;
extruderOffset = new Vector2();
extruderRelativePos = false;
}
}
}

View file

@ -0,0 +1,229 @@
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.VectorMath;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MatterHackers.MatterControl.Plugins.X3GDriver
{
/********************************************************
* S3G Response Packet Structure:
* Index 0:StartBit
* Index 1:packet length
* Index 2+: Payload-
* PayLoad Index 0: Response Code(values 0x80 - 0x8C
* PayLoad Index 1+: Optional Response Arguments
* Index (2+N): crc
*******************************************************/
public class X3GReader
{
private MatterHackers.Plugins.X3GDriver.X3GPrinterDetails printerDetails;
private X3GPacketAnalyzer analyzer;
public X3GReader(MatterHackers.Plugins.X3GDriver.X3GPrinterDetails PtrDetails)
{
this.printerDetails = PtrDetails;
analyzer = new X3GPacketAnalyzer(PtrDetails);
}
public string translate(byte[] x3gResponse, string relatedGCommand, out bool commandOK)
{
//X3GPacketAnalyzer analyzer = new X3GPacketAnalyzer(,writerPtr);
return analyzer.analyze(x3gResponse, relatedGCommand, out commandOK);
}
private class X3GPacketAnalyzer
{
private byte[] response;
private X3GCrc crc;
private string gCommandForResponse; //Figure out better name. this is the gCommand that was sent to the printer that caused this response
private MatterHackers.Plugins.X3GDriver.X3GPrinterDetails printerDetails; //used to get location information and other needed response data
private StringBuilder temperatureResponseStrBuilder; //Saves extruder temp when we have a heated bed to send back temps together
public X3GPacketAnalyzer(MatterHackers.Plugins.X3GDriver.X3GPrinterDetails PtrDetails)
{
crc = new X3GCrc();
printerDetails = PtrDetails;
temperatureResponseStrBuilder = new StringBuilder();
}
public X3GPacketAnalyzer(byte[] x3gResponse, string relatedGCommand, MatterHackers.Plugins.X3GDriver.X3GPrinterDetails PtrDetails)
{
response = x3gResponse;
crc = new X3GCrc();
gCommandForResponse = relatedGCommand;
printerDetails = PtrDetails;
temperatureResponseStrBuilder = new StringBuilder();
}
public string analyze(byte[] x3gResponse, string relatedGcommand, out bool commandOK)
{
response = x3gResponse;
gCommandForResponse = relatedGcommand;
StringBuilder gCodeResponse = new StringBuilder();
int payloadLength;
commandOK = false;
if (response[0] == 0xD5)
{
payloadLength = response[1];
gCodeResponse.Append(analyzePayload(payloadLength, out commandOK));
checkCrc(payloadLength + 2);
}
gCodeResponse.Append("\n");
return gCodeResponse.ToString();
}
private string analyzePayload(int payloadLength, out bool commandOK)
{
commandOK = false;
StringBuilder payloadStrBuilder = new StringBuilder();
switch (response[2])
{
case 0x81:
payloadStrBuilder.Append("ok");
commandOK = true;
break;
case 0x80:
payloadStrBuilder.Append("Generic Packet Error, packet discarded");
break;
case 0x83:
payloadStrBuilder.Append("CRC mismatch, packet discarded\n");
payloadStrBuilder.Append("RS:" + X3GWriter.lineNumber + "\n");
payloadStrBuilder.Append("ok");
break;
case 0x88:
payloadStrBuilder.Append("Tool lock Timeout");
break;
case 0x89:
payloadStrBuilder.Append("Cancel Build");
break;
case 0x8C:
payloadStrBuilder.Append("Packet timeout error, packet discarded");
payloadStrBuilder.Append("RS:" + X3GWriter.lineNumber + "\n");
payloadStrBuilder.Append("ok");
break;
case 0x82://Action Buffer overflow, Packet Discarded (currently will request resend of line, later should be avoided by checking buffer size before send)
payloadStrBuilder.Append("Action Buffer overflow, Packet Discarded\n");
payloadStrBuilder.Append("RS:" + X3GWriter.lineNumber + "\n");
payloadStrBuilder.Append("ok");
break;
case 0x84:
payloadStrBuilder.Append("Query Packet too big, packet discarded");
break;
case 0x85:
payloadStrBuilder.Append("Command not supported/recognized");
break;
case 0x87:
payloadStrBuilder.Append("Downstream timeout");
break;
case 0x8A:
payloadStrBuilder.Append("Bot is Building from SD");
break;
case 0x8B:
payloadStrBuilder.Append("Bot is Shutdown due to Overheat");
break;
default:
payloadStrBuilder.Append("Command Failed: " + response[2]);
break;
}
switch (payloadLength)
{
case 23: //22 is the length of the get position response + 1 for response code
if (printerDetails.currentPosition.Length != 0)//if we are not connecting just now to the printer we will report back the target move position
{
Vector3 printerPos = printerDetails.targetMovePosition;
payloadStrBuilder.Append(String.Format(" C: X:{0} Y:{1} Z:{2} E:{3}", printerPos.X, printerPos.Y, printerPos.Z, 0));
}
else//if we have not told the printer to move yet we get the location the printer actually thinks it is at
{
Vector3 posFromPrinter = new Vector3();
posFromPrinter.X = translateInt32(3);
posFromPrinter.Y = translateInt32(7);
posFromPrinter.Z = translateInt32(11);
posFromPrinter.X = posFromPrinter.X / printerDetails.stepsPerMm.X;
posFromPrinter.Y = posFromPrinter.Y / printerDetails.stepsPerMm.Y;
posFromPrinter.Z = posFromPrinter.Z / printerDetails.stepsPerMm.Z;
payloadStrBuilder.Append(String.Format(" C: X:{0} Y:{1} Z:{2} E:{3}", posFromPrinter.X, posFromPrinter.Y, posFromPrinter.Z, 0));
}
break;
case 3: //Length of temperature response, temperature is requested individually for each extruder and bed separately. This collects the information and condenses it into one response to be sent to the printer
if (!gCommandForResponse.Contains("M115"))
{
int temperature = translateInt16(3);
printerDetails.teperatureResponseCount++;
if (printerDetails.teperatureResponseCount == 1)
{
if (ActiveSliceSettings.Instance.GetValue<int>(SettingsKey.extruder_count) > 1)
{
temperatureResponseStrBuilder.Append(String.Format(" T0:{0}", temperature));
}
else
{
temperatureResponseStrBuilder.Append(String.Format(" T:{0}", temperature));
}
}
else if (printerDetails.teperatureResponseCount == 2 && ActiveSliceSettings.Instance.GetValue<bool>(SettingsKey.has_heated_bed))
{
temperatureResponseStrBuilder.Append(String.Format(" B:{0}", temperature));
}
else
{
temperatureResponseStrBuilder.Append(String.Format(" T1:{0}", temperature));
}
if (printerDetails.teperatureResponseCount == printerDetails.requiredTemperatureResponseCount)
{
payloadStrBuilder.Append(temperatureResponseStrBuilder.ToString());
temperatureResponseStrBuilder.Clear();
printerDetails.teperatureResponseCount = 0;
}
}
break;
}
for (int i = 2; i < payloadLength + 2; i++)
{
crc.update(response[i]);
}
return payloadStrBuilder.ToString();
}
private int translateInt16(int startingIndex)
{
return (response[startingIndex] + (response[startingIndex + 1] * 256));
}
private long translateInt32(int startingIndex)
{
return (long)(response[startingIndex] + (response[startingIndex + 1] * 256) + (response[startingIndex + 2] * 256 ^ 2) + (response[startingIndex + 3] * (256 ^ 3)));
}
private bool checkCrc(int crcIndex)
{
return crc.getCrc() == response[crcIndex];
}
}
}
}

View file

@ -0,0 +1,15 @@
using MatterHackers.Plugins.X3GDriver;
using MatterHackers.SerialPortCommunication.FrostedSerial;
namespace MatterHackers.MatterControl.Plugins.X3GDriver
{
public class X3GFrostedSerialPortFactory : FrostedSerialPortFactory
{
override protected string GetDriverType() => "X3G";
public override IFrostedSerialPort Create(string serialPortName)
{
return new X3GSerialPortWrapper(serialPortName);
}
}
}

View file

@ -0,0 +1,416 @@
using MatterHackers.MatterControl.Plugins.X3GDriver;
using MatterHackers.SerialPortCommunication.FrostedSerial;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MatterHackers.MatterControl;
/*****************************************************
* Initialization Requirements:
* Steps per mm
* Initialize Firmware to boot state
* Get current position
*****************************************************/
namespace MatterHackers.Plugins.X3GDriver
{
class X3GSerialPortWrapper : IFrostedSerialPort
{
private IFrostedSerialPort port;
private X3GPrinterDetails printerDetails;
private X3GWriter writer;
private X3GReader reader;
private Queue<string> sentCommandQueue; //Keeps track of commands sent to printer to be used in translation (only keeps strings until printer responses to command)
private string lastStr;
private Stopwatch timeSinceLastCommandSent;
private Stopwatch timeSinceLastOK;
private StringBuilder readBuffer;
private List<Byte> readPacket;
private Queue<byte[]> outboundOverflowPackets;
private bool waitForResponse;
private bool dtrEnable;
public X3GSerialPortWrapper(string serialPortName)
{
port = FrostedSerialPortFactory.GetAppropriateFactory("raw").Create(serialPortName);
printerDetails = new X3GPrinterDetails();
writer = new X3GWriter(printerDetails);
reader = new X3GReader(printerDetails);
timeSinceLastCommandSent = new Stopwatch();
timeSinceLastOK = new Stopwatch();
sentCommandQueue = new Queue<string>();
lastStr = "";
readBuffer = new StringBuilder();
readPacket = new List<byte>();
outboundOverflowPackets = new Queue<byte[]>();
waitForResponse = false;
}
public bool RtsEnable
{
get; set;
}
public bool DtrEnable
{
get { return dtrEnable; }
set
{
dtrEnable = value;
port.DtrEnable = value;
}
}
public int BaudRate
{
get
{
return port.BaudRate;
}
set
{
port.BaudRate = value;
}
}
public int BytesToRead
{
get
{
//To avoid spamming resends so fast that we lag mattercontrol we wait 20ms before checking response Also if there is an active dwell we wait the dwell duration before accepting the OK
if (timeSinceLastCommandSent.ElapsedMilliseconds > 20 && timeSinceLastCommandSent.ElapsedMilliseconds > printerDetails.dwellTime)
{
printerDetails.dwellTime = 0;
//Checks to see if the buffer has an entire packet before we try to translate it
if (hasFullPacket())
{
bool returnedOK;
string translatedReply = reader.translate(readPacket.ToArray(), lastStr, out returnedOK);
waitForResponse = false;
//After response is translated we check to see if the lockout of a M109 is active
if (printerDetails.heatingLockout)
{ //Check if the previous M105 has sent all of its associated packets (1-3 packets per M105)
if (QueueIsEmpty())
{
//check to see if we have reached target temperature(s) and either disable the lock out or send another M105
if (ExtruderIsReady(translatedReply) && BedIsReady(translatedReply) && secondExtruderIsReady(translatedReply))//Maker bot seems to stop the lockout when within 2 Degrees so we will match it
{
printerDetails.heatingLockout = false;
}
else
{
translatedReply = supressOk(translatedReply);//don't send an ok back until we are done heating
bool temp;//Normally we check if we need to send this command to the printer but if a RS is requested it has to be sent
byte[] output = writer.translate("M105\n", out temp);
Write(output, 0, output.Length);
foreach (byte[] packet in writer.GetAndClearOverflowPackets())
{
outboundOverflowPackets.Enqueue(packet);
}
}
if (translatedReply != string.Empty)
{
readBuffer.Append(translatedReply);
}
}
else
{
byte[] output = outboundOverflowPackets.Dequeue();
Write(output, 0, output.Length);
}
}
else
{ //This handles resending when the printer's action buffer is full
if (translatedReply.Contains("RS:"))
{
bool temp;
byte[] output = writer.translate(lastStr, out temp);
Write(output, 0, output.Length);
}
else
{
if (!QueueIsEmpty())//If there are overFlowPackets we need to send them to the printer before we continue by sending back an OK
{
byte[] output = outboundOverflowPackets.Dequeue();
Write(output, 0, output.Length);
}
else
{
readBuffer.Append(translatedReply);
}
if (returnedOK)
{
writer.updateCurrentPosition();
}
}
}
readPacket.Clear();
}
else if (timeSinceLastCommandSent.ElapsedMilliseconds > 3000)
{
if (outboundOverflowPackets.Count > 0)
{
//If there 3seconds has passed since a response and there are outbound packets waiting in the queue we will send one assuming a packet was dropped
byte[] output = outboundOverflowPackets.Dequeue();
Write(output, 0, output.Length);
}
else if (waitForResponse) //We haven't gotten a response in 3 seconds and MC is waiting on an ok - Send a resend
{
readBuffer.Append("RS:" + X3GWriter.lineNumber + "\n");
readBuffer.Append("ok");
}
}
}
return readBuffer.Length;
}//end get
}
private bool secondExtruderIsReady(string translatedReply)
{
int extruderTemp = 0;
int index = translatedReply.IndexOf("T1:");
if (index != -1)
{
char[] whitespace = { ' ', '\n', '\t' };
int i = translatedReply.IndexOfAny(whitespace, index);
string str = translatedReply.Substring(index + 3, i - (index + 3));
extruderTemp = int.Parse(str);
}
return (extruderTemp >= printerDetails.targetExtruderTemps[1] - 2);//Maker bot seems to stop the lockout when within 2 Degrees so we will match it
}
private bool ExtruderIsReady(string translatedReply)
{
int extruderTemp = 0;
int index = translatedReply.IndexOf("T:");
if (index != -1)
{
char[] whitespace = { ' ', '\n', '\t' };
int i = translatedReply.IndexOfAny(whitespace, index);
string str = translatedReply.Substring(index + 2, i - (index + 2));
extruderTemp = int.Parse(str);
}
else
{
index = translatedReply.IndexOf("T0:");
if (index != -1)
{
char[] whitespace = { ' ', '\n', '\t' };
int i = translatedReply.IndexOfAny(whitespace, index);
string str = translatedReply.Substring(index + 3, i - (index + 3));
extruderTemp = int.Parse(str);
}
}
return extruderTemp >= (printerDetails.targetExtruderTemps[0] - 2);//Maker bot seems to stop the lockout when within 2 Degrees so we will match it
}
private bool BedIsReady(string translatedReply)
{
int bedTemp = 0;
bool isReady = true;
int index = translatedReply.IndexOf("B:");
if (index != -1)
{
char[] whitespace = { ' ', '\n', '\t' };
int i = translatedReply.IndexOfAny(whitespace, index);
string str = translatedReply.Substring(index + 2, i - (index + 2));
bedTemp = int.Parse(str);
}
isReady = bedTemp >= (printerDetails.targetBedTemp - 2);
if (isReady)
{
printerDetails.targetBedTemp = 0; //Flashforges seem to lose the ability to maintain this temperature, rather than locking them out forever we remove the requirement after reached once
}
return isReady;
}
private string supressOk(string translatedReply)
{
if (translatedReply.Contains("ok"))
{
translatedReply = translatedReply.Replace("ok", "");
}
return translatedReply;
}
private bool hasFullPacket()
{
bool result = false;
int byteCount = port.BytesToRead;
byte[] bytesRead;
if (readPacket.Count > 0 && byteCount > 0)//if the readPacket already has values and the input buffer has bytes to read
{
if (byteCount > readPacket.ElementAt(1))
{
bytesRead = new byte[readPacket.ElementAt(1) + 1];
port.Read(bytesRead, 0, readPacket.ElementAt(1) + 1);
readPacket.AddRange(bytesRead);
result = true;
}
}
else if (byteCount > 2)//if the input buffer has bytes to read and you get here then we start filling the readPacket
{
bytesRead = new byte[2];
port.Read(bytesRead, 0, 2);
if (bytesRead[0] == 0xD5)//checks for start bit from printer
{
readPacket.AddRange(bytesRead);
if (readPacket.ElementAt(1) < byteCount)//checks packet size against how full the buffer is
{
bytesRead = new byte[readPacket.ElementAt(1) + 1];
port.Read(bytesRead, 0, readPacket.ElementAt(1) + 1);
readPacket.AddRange(bytesRead);
result = true;
}
}
else
{
if (bytesRead[1] == 0xD5)//checks for start bit in second spot in case we somehow got a stray bit in here somehow
{
readPacket.Add(bytesRead[1]);//Add the start bit and retrieve packet length from the buffer (may need to check buffer size before reading)
bytesRead = new byte[1];
port.Read(bytesRead, 0, 1);
readPacket.Add(bytesRead[0]);
if (readPacket.ElementAt(1) < byteCount)//checks packet size against how full the buffer is
{
bytesRead = new byte[readPacket.ElementAt(1) + 1];
port.Read(bytesRead, 0, readPacket.ElementAt(1) + 1);
readPacket.AddRange(bytesRead);
result = true;
}
}
}
}
return result;
}
public void Write(string str)
{
bool sendToPrinter;
sentCommandQueue.Enqueue(str.ToString());
lastStr = str.ToString();
byte[] output = writer.translate(str, out sendToPrinter);
if (QueueIsEmpty() && !waitForResponse)
{
if (sendToPrinter)
{
Write(output, 0, output.Length);
}
else
{
fakeOk();
}
}
else
{
if (sendToPrinter)
{
outboundOverflowPackets.Enqueue(output);
}
else
{
fakeOk();
}
}
//certain gcode commands are translated to multiple x3g commands and the excess are queued up here
foreach (byte[] packet in writer.GetAndClearOverflowPackets())
{
outboundOverflowPackets.Enqueue(packet);
}
}
private bool QueueIsEmpty()
{
return outboundOverflowPackets.Count < 1;
}
public void Write(byte[] buffer, int offset, int count)
{
port.Write(buffer, offset, count);
timeSinceLastCommandSent.Restart();
waitForResponse = true;
}
public int WriteTimeout
{
get
{ return port.WriteTimeout; }
set
{ port.WriteTimeout = value; }
}
public int ReadTimeout
{
get { return port.ReadTimeout; }
set { port.ReadTimeout = value; }
}
public string ReadExisting()//Translate via Reader
{
string tempString = readBuffer.ToString();
readBuffer.Clear();
return tempString;
}
public int Read(byte[] buffer, int offset, int count)
{
return port.Read(buffer, offset, count);
}
public bool IsOpen
{
get { return port.IsOpen; }
}
public void Open()
{
port.Open();
}
public void Close()
{
port.Close();
}
public void Dispose()
{
port.Dispose();
}
private void fakeOk()
{
readBuffer.Append("ok\n");
}
}
}

File diff suppressed because it is too large Load diff