/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */ #include #include #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) { extrusion_amount = 0; retraction_extrusion_window = 0.0; extruderSwitchRetraction = 14.5; current_extruder = 0; currentFanSpeed = -1; totalPrintTime = 0.0; for(unsigned int e=0; epreSwitchExtruderCode[id] = preSwitchExtruderCode; this->postSwitchExtruderCode[id] = postSwitchExtruderCode; } void GCodeExport::setFlavor(EGCodeFlavor flavor) { this->flavor = flavor; if (flavor == GCODE_FLAVOR_MACH3) for(int n=0; nflavor; } void GCodeExport::setRetractionSettings(int extruderSwitchRetraction, int extruderSwitchRetractionSpeed, int extruderSwitchPrimeSpeed, int retraction_extrusion_window, int retraction_count_max) { this->extruderSwitchRetraction = INT2MM(extruderSwitchRetraction); this->extruderSwitchRetractionSpeed = extruderSwitchRetractionSpeed; this->extruderSwitchPrimeSpeed = extruderSwitchPrimeSpeed; this->retraction_extrusion_window = INT2MM(retraction_extrusion_window); this->retraction_count_max = INT2MM(retraction_count_max); } 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; } double GCodeExport::getFilamentArea(unsigned int extruder) { double r = INT2MM(filament_diameter[extruder]) / 2.0; double filament_area = M_PI * r * r; return filament_area; } void GCodeExport::setFilamentDiameter(unsigned int n, int diameter) { filament_diameter[n] = diameter; } 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 totalFilament[e] + getExtrusionAmountMM3(e); return totalFilament[e]; } double GCodeExport::getTotalPrintTime() { return totalPrintTime; } void GCodeExport::resetTotalPrintTimeAndFilament() { totalPrintTime = 0; for(unsigned int e=0; e 0) { if (isRetracted) { if (currentSpeed != int(rpm * 10)) { //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 = int(rpm * 10); } //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(x - extruderOffset[current_extruder].X) << " Y" << INT2MM(y - extruderOffset[current_extruder].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 = false; } if (isRetracted) { if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC) { *output_stream << "G11\n"; //Assume default UM2 retraction settings. estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0); }else{ *output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruderCharacter[current_extruder] << 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; } extrusion_amount += extrusion_per_mm * diff.vSizeMM(); *output_stream << "G1"; }else{ *output_stream << "G0"; } if (currentSpeed != speed) { *output_stream << " F" << (speed * 60); currentSpeed = speed; } *output_stream << std::setprecision(3) << " X" << INT2MM(x - extruderOffset[current_extruder].X) << " Y" << INT2MM(y - extruderOffset[current_extruder].Y); if (z != currentPosition.z) *output_stream << " Z" << INT2MM(z); if (extrusion_mm3_per_mm > 0.000001) *output_stream << " " << extruderCharacter[current_extruder] << 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 == GCODE_FLAVOR_BFB)//BitsFromBytes does automatic retraction. return; if (isRetracted) return; if (config->amount <= 0) return; if (!force && retraction_count_max > 0 && extrusion_amount_at_previous_n_retractions.size() == retraction_count_max - 1 && extrusion_amount < extrusion_amount_at_previous_n_retractions.back() + retraction_extrusion_window) return; if (config->primeAmount > 0) extrusion_amount += config->primeAmount; retractionPrimeSpeed = config->primeSpeed; if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_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) << " " << extruderCharacter[current_extruder] << 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) { *output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + config->zHop) << "\n"; isZHopped = true; } extrusion_amount_at_previous_n_retractions.push_front(extrusion_amount); if (extrusion_amount_at_previous_n_retractions.size() == retraction_count_max) { extrusion_amount_at_previous_n_retractions.pop_back(); } isRetracted = true; } void GCodeExport::switchExtruder(int newExtruder) { if (current_extruder == newExtruder) return; if (flavor == GCODE_FLAVOR_BFB) { if (!isRetracted) *output_stream << "M103\r\n"; isRetracted = true; return; } resetExtrusionValue(); if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC) { *output_stream << "G10 S1\n"; }else{ *output_stream << "G1 F" << (extruderSwitchRetractionSpeed * 60) << " " << extruderCharacter[current_extruder] << std::setprecision(5) << (extrusion_amount - extruderSwitchRetraction) << "\n"; currentSpeed = extruderSwitchRetractionSpeed; } current_extruder = newExtruder; if (flavor == GCODE_FLAVOR_MACH3) resetExtrusionValue(); isRetracted = true; writeCode(preSwitchExtruderCode[current_extruder].c_str()); if (flavor == GCODE_FLAVOR_MAKERBOT) *output_stream << "M135 T" << current_extruder << "\n"; else *output_stream << "T" << current_extruder << "\n"; writeCode(postSwitchExtruderCode[current_extruder].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 == GCODE_FLAVOR_BFB) *output_stream << "\r\n"; else *output_stream << "\n"; } void GCodeExport::writeFanCommand(int speed) { if (currentFanSpeed == speed) return; if (speed > 0) { if (flavor == GCODE_FLAVOR_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 == GCODE_FLAVOR_MAKERBOT) *output_stream << "M127 T0\n"; else *output_stream << "M107\n"; } currentFanSpeed = speed; } void GCodeExport::writeTemperatureCommand(int extruder, int temperature, bool wait) { if (!wait && currentTemperature[extruder] == temperature) return; if (wait) *output_stream << "M109"; else *output_stream << "M104"; if (extruder != current_extruder) *output_stream << " T" << extruder; *output_stream << " S" << temperature << "\n"; currentTemperature[extruder] = temperature; } void GCodeExport::writeBedTemperatureCommand(int temperature, bool wait) { if (wait) *output_stream << "M190 S"; else *output_stream << "M140 S"; *output_stream << temperature << "\n"; } void GCodeExport::finalize(int maxObjectHeight, int moveSpeed, const char* endCode) { std::cerr << "maxObjectHeight : " << maxObjectHeight << std::endl; 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 0) log("Filament%d: %d\n", n + 1, int(getTotalFilamentUsed(n))); output_stream->flush(); } }//namespace cura