Arquivos
CuraEngine/src/gcodeExport.cpp
T
2015-11-06 10:42:59 +01:00

535 linhas
18 KiB
C++

/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdarg.h>
#include <iomanip>
#include "gcodeExport.h"
#include "utils/logoutput.h"
namespace cura {
GCodeExport::GCodeExport()
: output_stream(&std::cout),currentPosition(0,0,0),startPosition(INT32_MIN,INT32_MIN,0),commandSocket(nullptr),layer_nr(0)
{
extrusion_amount = 0;
current_extruder = 0;
currentFanSpeed = -1;
totalPrintTime = 0.0;
currentSpeed = 1;
retractionPrimeSpeed = 1;
isRetracted = false;
isZHopped = 0;
last_coasted_amount_mm3 = 0;
setFlavor(EGCodeFlavor::REPRAP);
}
GCodeExport::~GCodeExport()
{
}
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
commandSocket = commandSocket_;
layer_nr = layer_nr_;
}
void GCodeExport::setOutputStream(std::ostream* stream)
{
output_stream = stream;
*output_stream << std::fixed;
}
Point GCodeExport::getExtruderOffset(int id)
{
return extruder_attr[id].nozzle_offset;
}
Point GCodeExport::getGcodePos(int64_t x, int64_t y, int extruder_train)
{
if (use_extruder_offset_to_offset_coords) { return Point(x,y) - getExtruderOffset(extruder_train); }
else { return Point(x,y); }
}
void GCodeExport::setFlavor(EGCodeFlavor flavor)
{
this->flavor = flavor;
if (flavor == EGCodeFlavor::MACH3)
for(int n=0; n<MAX_EXTRUDERS; n++)
extruder_attr[n].extruderCharacter = 'A' + n;
else
for(int n=0; n<MAX_EXTRUDERS; n++)
extruder_attr[n].extruderCharacter = 'E';
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
{
is_volumatric = true;
}
else
{
is_volumatric = false;
}
}
EGCodeFlavor GCodeExport::getFlavor()
{
return this->flavor;
}
void GCodeExport::setZ(int z)
{
this->zPos = z;
}
Point3 GCodeExport::getPosition()
{
return currentPosition;
}
Point GCodeExport::getPositionXY()
{
return Point(currentPosition.x, currentPosition.y);
}
int GCodeExport::getPositionZ()
{
return currentPosition.z;
}
void GCodeExport::resetStartPosition()
{
startPosition.x = INT32_MIN;
startPosition.y = INT32_MIN;
}
Point GCodeExport::getStartPositionXY()
{
return Point(startPosition.x, startPosition.y);
}
int GCodeExport::getExtruderNr()
{
return current_extruder;
}
void GCodeExport::setFilamentDiameter(unsigned int extruder, int diameter)
{
double r = INT2MM(diameter) / 2.0;
double area = M_PI * r * r;
extruder_attr[extruder].filament_area = area;
}
double GCodeExport::getFilamentArea(unsigned int extruder)
{
return extruder_attr[extruder].filament_area;
}
double GCodeExport::getExtrusionAmountMM3(unsigned int extruder)
{
if (is_volumatric)
{
return extrusion_amount;
}
else
{
return extrusion_amount * getFilamentArea(extruder);
}
}
double GCodeExport::getTotalFilamentUsed(int e)
{
if (e == current_extruder)
return extruder_attr[e].totalFilament + getExtrusionAmountMM3(e);
return extruder_attr[e].totalFilament;
}
double GCodeExport::getTotalPrintTime()
{
return totalPrintTime;
}
void GCodeExport::resetTotalPrintTimeAndFilament()
{
totalPrintTime = 0;
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
{
extruder_attr[e].totalFilament = 0.0;
extruder_attr[e].currentTemperature = 0;
}
extrusion_amount = 0.0;
estimateCalculator.reset();
}
void GCodeExport::updateTotalPrintTime()
{
totalPrintTime += estimateCalculator.calculate();
estimateCalculator.reset();
}
void GCodeExport::writeComment(std::string comment)
{
*output_stream << ";";
for (unsigned int i = 0; i < comment.length(); i++)
{
if (comment[i] == '\n')
{
*output_stream << "\\n";
}else{
*output_stream << comment[i];
}
}
*output_stream << "\n";
}
void GCodeExport::writeTypeComment(const char* type)
{
*output_stream << ";TYPE:" << type << "\n";
}
void GCodeExport::writeLayerComment(int layer_nr)
{
*output_stream << ";LAYER:" << layer_nr << "\n";
}
void GCodeExport::writeLayerCountComment(int layer_count)
{
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
}
void GCodeExport::writeLine(const char* line)
{
*output_stream << line << "\n";
}
void GCodeExport::resetExtrusionValue()
{
if (extrusion_amount != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
{
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0\n";
extruder_attr[current_extruder].totalFilament += getExtrusionAmountMM3(current_extruder);
for (unsigned int i = 0; i < extrusion_amount_at_previous_n_retractions.size(); i++)
extrusion_amount_at_previous_n_retractions[i] -= extrusion_amount;
extrusion_amount = 0.0;
}
}
void GCodeExport::writeDelay(double timeAmount)
{
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
estimateCalculator.addTime(timeAmount);
}
void GCodeExport::writeMove(Point p, double speed, double extrusion_mm3_per_mm)
{
writeMove(p.X, p.Y, zPos, speed, extrusion_mm3_per_mm);
}
void GCodeExport::writeMove(Point3 p, double speed, double extrusion_mm3_per_mm)
{
writeMove(p.x, p.y, p.z, speed, extrusion_mm3_per_mm);
}
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
{
if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z)
return;
#ifdef ASSERT_INSANE_OUTPUT
assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release)
#endif //ASSERT_INSANE_OUTPUT
if (extrusion_mm3_per_mm < 0)
logWarning("Warning! Negative extrusion move!");
double extrusion_per_mm = extrusion_mm3_per_mm;
if (!is_volumatric)
{
extrusion_per_mm = extrusion_mm3_per_mm / getFilamentArea(current_extruder);
}
Point gcode_pos = getGcodePos(x,y, current_extruder);
if (flavor == EGCodeFlavor::BFB)
{
//For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values.
float fspeed = speed * 60;
float rpm = extrusion_per_mm * speed * 60;
const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion.
rpm /= mm_per_rpm;
if (rpm > 0)
{
if (isRetracted)
{
if (currentSpeed != double(rpm))
{
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
//fprintf(f, "M108 S%0.1f\r\n", rpm);
*output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n";
currentSpeed = double(rpm);
}
//Add M101 or M201 to enable the proper extruder.
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n";
isRetracted = false;
}
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
// (Trick copied from KISSlicer, thanks Jonathan)
fspeed *= (rpm / (roundf(rpm * 100) / 100));
//Increase the extrusion amount to calculate the amount of filament used.
Point3 diff = Point3(x,y,z) - getPosition();
extrusion_amount += extrusion_per_mm * diff.vSizeMM();
}else{
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
if (!isRetracted)
{
*output_stream << "M103\r\n";
isRetracted = true;
}
}
*output_stream << std::setprecision(3) <<
"G1 X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y) <<
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n";
}
else
{
//Normal E handling.
if (extrusion_mm3_per_mm > 0.000001)
{
Point3 diff = Point3(x,y,z) - getPosition();
if (isZHopped > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
isZHopped = 0;
}
extrusion_amount += (is_volumatric) ? last_coasted_amount_mm3 : last_coasted_amount_mm3 / getFilamentArea(current_extruder);
if (isRetracted)
{
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
{
*output_stream << "G11\n";
//Assume default UM2 retraction settings.
if (last_coasted_amount_mm3 > 0)
{
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
currentSpeed = retractionPrimeSpeed;
}
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0);
}else{
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
currentSpeed = retractionPrimeSpeed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed);
}
if (getExtrusionAmountMM3(current_extruder) > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
resetExtrusionValue();
isRetracted = false;
}
else
{
if (last_coasted_amount_mm3 > 0)
{
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
currentSpeed = retractionPrimeSpeed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed);
}
}
last_coasted_amount_mm3 = 0;
extrusion_amount += extrusion_per_mm * diff.vSizeMM();
*output_stream << "G1";
}else{
*output_stream << "G0";
if (commandSocket) {
// we should send this travel as a non-retraction move
cura::Polygons travelPoly;
PolygonRef travel = travelPoly.newPoly();
travel.add(Point(currentPosition.x, currentPosition.y));
travel.add(Point(x, y));
commandSocket->sendPolygons(isRetracted ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, isRetracted ? MM2INT(0.2) : MM2INT(0.1));
}
}
if (currentSpeed != speed)
{
*output_stream << " F" << (speed * 60);
currentSpeed = speed;
}
*output_stream << std::setprecision(3) <<
" X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y);
if (z != currentPosition.z)
*output_stream << " Z" << INT2MM(z + isZHopped);
if (extrusion_mm3_per_mm > 0.000001)
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount;
*output_stream << "\n";
}
currentPosition = Point3(x, y, z);
startPosition = currentPosition;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), speed);
}
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
{
if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction.
return;
if (isRetracted)
return;
if (config->amount <= 0)
return;
if (!force && config->retraction_count_max > 0 && int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max - 1
&& extrusion_amount < extrusion_amount_at_previous_n_retractions.back() + config->retraction_extrusion_window)
return;
if (config->primeAmount > 0)
{
extrusion_amount += config->primeAmount;
}
retractionPrimeSpeed = config->primeSpeed;
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
{
*output_stream << "G10\n";
//Assume default UM2 retraction settings.
double retraction_distance = 4.5;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - retraction_distance), 25); // TODO: hardcoded values!
}else{
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount - config->amount << "\n";
currentSpeed = config->speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - config->amount), currentSpeed);
}
if (config->zHop > 0)
{
isZHopped = config->zHop;
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n";
}
extrusion_amount_at_previous_n_retractions.push_front(extrusion_amount);
if (int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max)
{
extrusion_amount_at_previous_n_retractions.pop_back();
}
isRetracted = true;
}
void GCodeExport::writeRetraction_extruderSwitch()
{
if (isRetracted) { return; }
if (flavor == EGCodeFlavor::BFB)
{
if (!isRetracted)
*output_stream << "M103\r\n";
isRetracted = true;
return;
}
resetExtrusionValue();
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
{
*output_stream << "G10 S1\n";
}else{
*output_stream << "G1 F" << (extruder_attr[current_extruder].extruderSwitchRetractionSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << (extrusion_amount - extruder_attr[current_extruder].extruderSwitchRetraction) << "\n";
currentSpeed = extruder_attr[current_extruder].extruderSwitchRetractionSpeed;
}
isRetracted = true;
}
void GCodeExport::switchExtruder(int new_extruder)
{
if (current_extruder == new_extruder)
return;
if (!isRetracted) // assumes the last retraction already was an extruder switch retraction
{
writeRetraction_extruderSwitch();
}
int old_extruder = current_extruder;
current_extruder = new_extruder;
if (flavor == EGCodeFlavor::MACH3)
resetExtrusionValue();
isRetracted = true;
writeCode(extruder_attr[old_extruder].end_code.c_str());
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M135 T" << current_extruder << "\n";
else
*output_stream << "T" << current_extruder << "\n";
writeCode(extruder_attr[new_extruder].start_code.c_str());
//Change the Z position so it gets re-writting again. We do not know if the switch code modified the Z position.
currentPosition.z += 1;
}
void GCodeExport::writeCode(const char* str)
{
*output_stream << str;
if (flavor == EGCodeFlavor::BFB)
*output_stream << "\r\n";
else
*output_stream << "\n";
}
void GCodeExport::writeFanCommand(double speed)
{
if (currentFanSpeed == speed)
return;
if (speed > 0)
{
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M126 T0\n"; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
else
*output_stream << "M106 S" << (speed * 255 / 100) << "\n";
}
else
{
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M127 T0\n";
else
*output_stream << "M107\n";
}
currentFanSpeed = speed;
}
void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool wait)
{
if (!wait && extruder_attr[extruder].currentTemperature == temperature)
return;
if (wait)
*output_stream << "M109";
else
*output_stream << "M104";
if (extruder != current_extruder)
*output_stream << " T" << extruder;
*output_stream << " S" << temperature << "\n";
extruder_attr[extruder].currentTemperature = temperature;
}
void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
{
if (wait)
*output_stream << "M190 S";
else
*output_stream << "M140 S";
*output_stream << temperature << "\n";
}
void GCodeExport::finalize(int maxObjectHeight, double moveSpeed, const char* endCode)
{
writeFanCommand(0);
setZ(maxObjectHeight + 5000);
writeMove(Point3(0,0,maxObjectHeight + 5000) + getPositionXY(), moveSpeed, 0);
writeCode(endCode);
log("Print time: %d\n", int(getTotalPrintTime()));
log("Filament: %d\n", int(getTotalFilamentUsed(0)));
for(int n=1; n<MAX_EXTRUDERS; n++)
if (getTotalFilamentUsed(n) > 0)
log("Filament%d: %d\n", n + 1, int(getTotalFilamentUsed(n)));
output_stream->flush();
}
}//namespace cura