Initial ElegooLink Support (Only Centauri Carbon Tested)

This commit is contained in:
Dark98
2026-01-22 07:42:14 +00:00
parent 261ba81e06
commit f352a02b9f
13 changed files with 613 additions and 12 deletions
+1
View File
@@ -110,6 +110,7 @@ dependencies {
implementation 'com.google.android.material:material:1.12.0'
implementation 'com.loopj.android:android-async-http:1.4.11'
implementation 'androidx.activity:activity:1.10.1'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}
def loadLocalProperties() {
@@ -56,6 +56,7 @@ import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.print_host.ElegooLinkClient;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
import ru.ytkab0bp.slicebeam.slic3r.GCodeViewer;
@@ -76,7 +77,7 @@ public class SliceMenu extends ListBedMenu {
client.setMaxRetriesAndTimeout(0, 10000);
}
private final static List<String> SUPPORTED_SEND = Collections.singletonList("octoprint");
private final static List<String> SUPPORTED_SEND = Arrays.asList("octoprint", "elegoolink");
private int lastUid;
@Override
@@ -121,13 +122,14 @@ public class SliceMenu extends ListBedMenu {
String apiKey = obj.get("printhost_apikey");
if (SUPPORTED_SEND.contains(type) && !TextUtils.isEmpty(host)) {
String finalType = type;
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinter, R.drawable.send_outline_28).onClick(v -> upload(finalType, host, apiKey, false)));
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinterAndPrint, R.drawable.send_28).onClick(v -> upload(finalType, host, apiKey, true)));
ConfigObject finalObj = obj;
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinter, R.drawable.send_outline_28).onClick(v -> upload(finalType, host, apiKey, false, finalObj)));
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinterAndPrint, R.drawable.send_28).onClick(v -> upload(finalType, host, apiKey, true, finalObj)));
}
return items;
}
private void upload(String type, String host, String apiKey, boolean print) {
private void upload(String type, String host, String apiKey, boolean print, ConfigObject config) {
String name = fragment.getGlView().getRenderer().getGcodeResult().getRecommendedName();
switch (type) {
default:
@@ -172,7 +174,41 @@ public class SliceMenu extends ListBedMenu {
.show());
}
});
break;
case "elegoolink": {
if (!host.startsWith("http://") && !host.startsWith("https://")) {
host = "http://" + host;
}
String elegooTag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuSliceSendToPrinterLoading).tag(elegooTag));
String finalHost = host;
final boolean timelapse = config != null && "1".equals(config.get("elegoolink_timelapse"));
final boolean bedLeveling = config != null && "1".equals(config.get("elegoolink_bed_leveling"));
int bedTypeVal = 0;
if (config != null) {
String bedTypeValue = config.get("elegoolink_bed_type");
if ("1".equals(bedTypeValue) || "pc".equalsIgnoreCase(bedTypeValue)) {
bedTypeVal = 1;
}
}
final int bedType = bedTypeVal;
new Thread(() -> {
ElegooLinkClient.Result result = ElegooLinkClient.upload(BedFragment.getTempGCodePath(), finalHost, name, print, timelapse, bedLeveling, bedType);
ViewUtils.postOnMainThread(() -> {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(elegooTag));
if (result.ok) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(print ? SnackbarsLayout.Type.INFO : SnackbarsLayout.Type.DONE, print ? R.string.MenuSliceSendToPrinterPrintStarted : R.string.MenuSliceSendToPrinterOK));
} else {
new BeamAlertDialogBuilder(fragment.getContext())
.setTitle(R.string.MenuSliceSendToPrinterFailed)
.setMessage(result.error)
.setPositiveButton(android.R.string.ok, null)
.show();
}
});
}).start();
break;
}
}
}
@@ -124,6 +124,10 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
custom.put("machine_min_extruding_rate", "0");
custom.put("machine_min_travel_rate", "0");
custom.put("elegoolink_timelapse", "0");
custom.put("elegoolink_bed_leveling", "0");
custom.put("elegoolink_bed_type", "pte");
custom.put("start_gcode", "G90 ; use absolute coordinates\\nM83 ; extruder relative mode\\nM104 S{is_nil(idle_temperature[0]) ? 150 : idle_temperature[0]} ; set temporary nozzle temp to prevent oozing during homing\\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\\nG4 S30 ; allow partial nozzle warmup\\nG28 ; home all axis\\nG1 Z50 F240\\nG1 X2.0 Y10 F3000\\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\\nG1 Z0.28 F240\\nG92 E0\\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\\nG1 X2.3 Y140 F5000\\nG92 E0\\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\\nG92 E0");
custom.put("end_gcode", "{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\\nG1 X5 Y{print_bed_max[1]*0.85} F{travel_speed*60} ; present print\\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\\nM140 S0 ; turn off heatbed\\nM104 S0 ; turn off temperature\\nM107 ; turn off fan\\nM84 X Y E ; disable motors");
@@ -9,6 +9,7 @@ import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import ru.ytkab0bp.slicebeam.slic3r.ConfigOptionDef;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
@@ -175,6 +176,22 @@ public class PrinterConfigFragment extends ProfileListFragment {
new OptionElement(def.options.get("printhost_apikey"))
));
String hostType = null;
if (diffObject != null && diffObject.has("host_type")) {
hostType = diffObject.get("host_type");
}
if (hostType == null) {
hostType = currentConfig.get("host_type");
}
if ("elegoolink".equalsIgnoreCase(hostType)) {
list.addAll(Arrays.asList(
new OptionElement(new SubHeader("ElegooLink")),
new OptionElement(def.options.get("elegoolink_timelapse")),
new OptionElement(def.options.get("elegoolink_bed_leveling")),
new OptionElement(def.options.get("elegoolink_bed_type"))
));
}
return list;
}
@@ -237,4 +254,18 @@ public class PrinterConfigFragment extends ProfileListFragment {
// TODO: Reset print/filament profiles, maybe physical profiles?
SliceBeam.saveConfig();
}
@Override
protected void updateConfigField(ConfigOptionDef def, int i, String value) {
super.updateConfigField(def, i, value);
if ("host_type".equals(def.key)) {
onUpdateConfigItems();
}
}
@Override
protected void onUpdateConfigItems() {
setConfigItems(getConfigItems());
super.onUpdateConfigItems();
}
}
@@ -519,6 +519,7 @@ public abstract class ProfileListFragment extends Fragment {
@SuppressLint("NotifyDataSetChanged")
protected void setConfigItems(List<OptionElement> items) {
categoryElements.clear();
List<OptionWrapper> list = new ArrayList<>();
int j = 0;
for (int i = 0; i < items.size(); i++) {
@@ -539,7 +540,21 @@ public abstract class ProfileListFragment extends Fragment {
categoryElements.get(j - 1).add(w);
}
}
currentList = list;
List<OptionWrapper> expanded = new ArrayList<>();
int categoryIndex = 0;
for (OptionWrapper w : list) {
expanded.add(w);
if (w.categoryIndex == categoryIndex && unfolded.get(categoryIndex)) {
List<OptionWrapper> extra = categoryElements.get(categoryIndex);
if (extra != null) {
expanded.addAll(extra);
}
}
if (w.categoryIndex == categoryIndex) {
categoryIndex++;
}
}
currentList = expanded;
recyclerView.getAdapter().notifyDataSetChanged();
}
@@ -676,8 +691,8 @@ public abstract class ProfileListFragment extends Fragment {
String[] labels;
String[] values;
if (Objects.equals("host_type", def.key)) {
labels = new String[]{"OctoPrint"};
values = new String[]{"octoprint"};
labels = new String[]{"OctoPrint", "ElegooLink"};
values = new String[]{"octoprint", "elegoolink"};
} else {
labels = new String[def.enumLabels.length];
values = def.enumValues;
@@ -0,0 +1,441 @@
package ru.ytkab0bp.slicebeam.print_host;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;
public final class ElegooLinkClient {
private static final int MAX_UPLOAD_PACKAGE_LENGTH = 1024 * 1024;
private static final MediaType OCTET_STREAM = MediaType.parse("application/octet-stream");
private ElegooLinkClient() {}
public static Result upload(File gcode, String host, String uploadName, boolean startPrint, boolean timelapse, boolean bedLeveling, int bedType) {
if (gcode == null || !gcode.exists()) {
return Result.error("G-code file not found.");
}
String finalName = (uploadName == null || uploadName.isEmpty()) ? gcode.getName() : uploadName;
String baseUrl = normalizeBaseUrl(host);
String uploadUrl = baseUrl + "/uploadFile/upload";
OkHttpClient client = new OkHttpClient.Builder()
.callTimeout(60, TimeUnit.SECONDS)
.build();
String md5;
try {
md5 = md5File(gcode);
} catch (Exception e) {
return Result.error("Failed to compute MD5: " + e.getMessage());
}
long size = gcode.length();
String uuid = UUID.randomUUID().toString().replace("-", "");
int packageCount = (int) ((size + MAX_UPLOAD_PACKAGE_LENGTH - 1) / MAX_UPLOAD_PACKAGE_LENGTH);
for (int i = 0; i < packageCount; i++) {
long offset = (long) MAX_UPLOAD_PACKAGE_LENGTH * i;
long length = Math.min(MAX_UPLOAD_PACKAGE_LENGTH, size - offset);
Result partRes = uploadPart(client, uploadUrl, gcode, finalName, md5, uuid, size, offset, length);
if (!partRes.ok) {
return partRes;
}
}
if (!startPrint) {
return Result.ok();
}
return startPrint(client, host, finalName, timelapse, bedLeveling, bedType);
}
private static Result uploadPart(OkHttpClient client, String url, File file, String uploadName, String md5, String uuid, long totalSize, long offset, long length) {
RequestBody fileBody = new FileSliceRequestBody(file, offset, length);
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("Check", "1")
.addFormDataPart("S-File-MD5", md5)
.addFormDataPart("Offset", String.valueOf(offset))
.addFormDataPart("Uuid", uuid)
.addFormDataPart("TotalSize", String.valueOf(totalSize))
.addFormDataPart("File", uploadName, fileBody)
.build();
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
String body = response.body() != null ? response.body().string() : "";
if (!response.isSuccessful()) {
return Result.error("Upload failed: HTTP " + response.code());
}
if (!isElegooOk(body)) {
return Result.error(parseElegooError(body));
}
return Result.ok();
} catch (IOException e) {
return Result.error("Upload failed: " + e.getMessage());
}
}
private static Result startPrint(OkHttpClient client, String host, String filename, boolean timelapse, boolean bedLeveling, int bedType) {
WebSocketSession session = null;
String lastError = null;
for (String wsUrl : buildWebSocketCandidates(host)) {
WebSocketSession attempt = new WebSocketSession(client, wsUrl);
if (attempt.awaitOpen(10, TimeUnit.SECONDS)) {
session = attempt;
break;
}
lastError = attempt.failureSummary();
if (attempt.isTooManyClients()) {
return Result.error("Printer reports too many connected clients. Close the ElegooLink web UI and other apps, then try again.");
}
attempt.close();
}
if (session == null) {
if (lastError != null && !lastError.isEmpty()) {
return Result.error("Failed to connect to ElegooLink websocket. " + lastError);
}
return Result.error("Failed to connect to ElegooLink websocket.");
}
String requestId = UUID.randomUUID().toString().replace("-", "");
long timestamp = System.currentTimeMillis();
String json = "{"
+ "\"Id\":\"\","
+ "\"Data\":{"
+ "\"Cmd\":128,"
+ "\"Data\":{"
+ "\"Filename\":\"/local/" + filename + "\","
+ "\"StartLayer\":0,"
+ "\"Calibration_switch\":" + (bedLeveling ? 1 : 0) + ","
+ "\"PrintPlatformType\":" + (bedType != 0 ? 1 : 0) + ","
+ "\"Tlp_Switch\":" + (timelapse ? 1 : 0)
+ "},"
+ "\"RequestID\":\"" + requestId + "\","
+ "\"MainboardID\":\"\","
+ "\"TimeStamp\":" + timestamp + ","
+ "\"From\":1"
+ "}"
+ "}";
session.sendText(json);
String response = session.receiveText(30, TimeUnit.SECONDS);
if (response == null) {
session.close();
return Result.error("Start print timeout.");
}
try {
JSONObject root = new JSONObject(response);
JSONObject data = root.optJSONObject("Data");
if (data == null) {
session.close();
return Result.error("Invalid response from printer.");
}
int cmd = data.optInt("Cmd", -1);
if (cmd != 128) {
session.close();
return Result.error("Unexpected response from printer.");
}
JSONObject ackData = data.optJSONObject("Data");
int ack = ackData != null ? ackData.optInt("Ack", -1) : -1;
if (ack == 0) {
session.close();
return Result.ok();
}
String error = mapAckError(ack);
session.close();
return Result.error(error);
} catch (JSONException e) {
session.close();
return Result.error("Invalid response from printer.");
}
}
private static boolean isElegooOk(String body) {
try {
JSONObject root = new JSONObject(body);
String code = root.optString("code", "");
return "000000".equals(code);
} catch (JSONException e) {
return false;
}
}
private static String parseElegooError(String body) {
try {
JSONObject root = new JSONObject(body);
String code = root.optString("code", "unknown");
StringBuilder sb = new StringBuilder();
sb.append("ErrorCode: ").append(code);
JSONArray messages = root.optJSONArray("messages");
if (messages != null) {
for (int i = 0; i < messages.length(); i++) {
JSONObject msg = messages.optJSONObject(i);
if (msg != null) {
sb.append("\n").append(msg.optString("field", ""))
.append(":").append(msg.optString("message", ""));
}
}
}
return sb.toString();
} catch (JSONException e) {
return "Upload failed.";
}
}
private static String mapAckError(int ack) {
switch (ack) {
case 1:
return "The printer is busy.";
case 2:
return "The file is missing.";
case 3:
return "MD5 check failed.";
case 4:
return "File I/O error.";
case 5:
case 6:
return "File format or resolution is invalid.";
case 7:
return "File does not match the printer.";
default:
return "Unknown error. Error code: " + ack;
}
}
private static String normalizeBaseUrl(String host) {
String value = host.trim();
if (!value.contains("://")) {
value = "http://" + value;
}
if (value.endsWith("/")) {
value = value.substring(0, value.length() - 1);
}
return value;
}
private static List<String> buildWebSocketCandidates(String host) {
String h = sanitizeWebSocketHost(host);
List<String> urls = new ArrayList<>(1);
urls.add(String.format(Locale.US, "ws://%s:3030/websocket", h));
return urls;
}
private static String sanitizeWebSocketHost(String host) {
String base = normalizeBaseUrl(host);
try {
URI uri = new URI(base);
String h = uri.getHost() != null ? uri.getHost() : uri.getAuthority();
if (h != null && h.contains(":")) {
h = h.substring(0, h.indexOf(':'));
}
if (h == null || h.isEmpty()) {
h = host;
}
return wrapIpv6Host(h);
} catch (URISyntaxException e) {
String h = host;
int schemeIdx = h.indexOf("://");
if (schemeIdx != -1) {
h = h.substring(schemeIdx + 3);
}
int slashIdx = h.indexOf('/');
if (slashIdx != -1) {
h = h.substring(0, slashIdx);
}
int portIdx = h.indexOf(':');
if (portIdx != -1) {
h = h.substring(0, portIdx);
}
return wrapIpv6Host(h);
}
}
private static String wrapIpv6Host(String host) {
if (host == null || host.isEmpty()) {
return host;
}
if (host.indexOf(':') != -1 && !host.startsWith("[") && !host.endsWith("]")) {
return "[" + host + "]";
}
return host;
}
private static String md5File(File file) throws IOException, NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("MD5");
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) {
byte[] buffer = new byte[8192];
int read;
while ((read = in.read(buffer)) != -1) {
digest.update(buffer, 0, read);
}
}
StringBuilder sb = new StringBuilder();
for (byte b : digest.digest()) {
sb.append(String.format(Locale.US, "%02x", b));
}
return sb.toString();
}
private static final class FileSliceRequestBody extends RequestBody {
private final File file;
private final long offset;
private final long length;
FileSliceRequestBody(File file, long offset, long length) {
this.file = file;
this.offset = offset;
this.length = length;
}
@Override
public MediaType contentType() {
return OCTET_STREAM;
}
@Override
public long contentLength() {
return length;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
raf.seek(offset);
byte[] buffer = new byte[8192];
long remaining = length;
while (remaining > 0) {
int read = raf.read(buffer, 0, (int) Math.min(buffer.length, remaining));
if (read == -1) {
break;
}
sink.write(buffer, 0, read);
remaining -= read;
}
}
}
}
private static final class WebSocketSession extends okhttp3.WebSocketListener {
private final java.util.concurrent.BlockingQueue<String> messages = new java.util.concurrent.LinkedBlockingQueue<>();
private final okhttp3.WebSocket socket;
private volatile boolean opened;
private volatile String failureBody;
private volatile okhttp3.Response failureResponse;
private volatile Throwable failure;
WebSocketSession(OkHttpClient client, String url) {
Request request = new Request.Builder().url(url).build();
socket = client.newWebSocket(request, this);
}
boolean awaitOpen(long timeout, TimeUnit unit) {
try {
long deadline = System.nanoTime() + unit.toNanos(timeout);
while (!opened && System.nanoTime() < deadline) {
Thread.sleep(50);
}
return opened;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
void sendText(String message) {
socket.send(message);
}
String receiveText(long timeout, TimeUnit unit) {
try {
return messages.poll(timeout, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
void close() {
socket.close(1000, "done");
}
String failureSummary() {
if (failureResponse != null) {
return "HTTP " + failureResponse.code();
}
if (failure != null) {
return failure.getMessage();
}
return "";
}
boolean isTooManyClients() {
return failureBody != null && failureBody.toLowerCase(Locale.US).contains("too many client");
}
@Override
public void onOpen(okhttp3.WebSocket webSocket, okhttp3.Response response) {
opened = true;
}
@Override
public void onMessage(okhttp3.WebSocket webSocket, String text) {
messages.offer(text);
}
@Override
public void onFailure(okhttp3.WebSocket webSocket, Throwable t, okhttp3.Response response) {
failure = t;
failureResponse = response;
if (response != null && response.body() != null) {
try {
failureBody = response.body().string();
} catch (IOException ignored) {
}
}
}
}
public static final class Result {
public final boolean ok;
public final String error;
private Result(boolean ok, String error) {
this.ok = ok;
this.error = error;
}
public static Result ok() {
return new Result(true, null);
}
public static Result error(String error) {
return new Result(false, error);
}
}
}
@@ -95,7 +95,8 @@ public class Slic3rConfigWrapper {
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
"machine_min_extruding_rate", "machine_min_travel_rate",
"machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e"
"machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e",
"elegoolink_timelapse", "elegoolink_bed_leveling", "elegoolink_bed_type"
);
public final static List<String> PHYSICAL_PRINTER_CONFIG_KEYS = Arrays.asList(
"preset_name", // temporary option to compatibility with older Slicer
+23
View File
@@ -1173,6 +1173,29 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
this->placeholder_parser().set("has_wipe_tower", has_wipe_tower);
this->placeholder_parser().set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
this->placeholder_parser().set("total_toolchanges", tool_ordering.toolchanges_count());
{
const char *bed_type_label = (print.config().elegoolink_bed_type.value == ElegooBedType::PTE) ? "Side A" : "Side B";
this->placeholder_parser().set("curr_bed_type", new ConfigOptionString(bed_type_label));
}
{
this->placeholder_parser().set("printable_height", new ConfigOptionFloat(print.config().max_print_height.value));
this->placeholder_parser().set("nozzle_temperature_initial_layer", new ConfigOptionInt(print.config().first_layer_temperature.get_at(initial_extruder_id)));
this->placeholder_parser().set("bed_temperature_initial_layer_single", new ConfigOptionInt(print.config().first_layer_bed_temperature.get_at(initial_extruder_id)));
this->placeholder_parser().set("initial_no_support_extruder", new ConfigOptionInt(int(initial_extruder_id)));
this->placeholder_parser().set("outer_wall_acceleration", new ConfigOptionFloat(print.config().external_perimeter_acceleration.value));
if (const ConfigOption *opt = print.config().optptr("enable_pressure_advance"); opt != nullptr) {
this->placeholder_parser().set("enable_pressure_advance", opt->clone());
} else {
this->placeholder_parser().set("enable_pressure_advance", new ConfigOptionBools(std::max<size_t>(1, print.config().nozzle_diameter.values.size()), false));
}
if (const ConfigOption *opt = print.config().optptr("pressure_advance"); opt != nullptr) {
this->placeholder_parser().set("pressure_advance", opt->clone());
} else {
this->placeholder_parser().set("pressure_advance", new ConfigOptionFloat(0.0));
}
}
{
BoundingBoxf bbox(print.config().bed_shape.values);
assert(bbox.defined);
+6 -2
View File
@@ -525,7 +525,8 @@ static std::vector<std::string> s_Preset_printer_options {
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "multimaterial_purging",
"max_print_height", "default_print_profile", "inherits",
"remaining_times", "silent_mode",
"machine_limits_usage", "thumbnails", "thumbnails_format"
"machine_limits_usage", "thumbnails", "thumbnails_format",
"elegoolink_timelapse", "elegoolink_bed_leveling", "elegoolink_bed_type"
};
static std::vector<std::string> s_Preset_sla_print_options {
@@ -1702,7 +1703,10 @@ static std::vector<std::string> s_PhysicalPrinter_opts {
// HTTP digest authentization (RFC 2617)
"printhost_user",
"printhost_password",
"printhost_ssl_ignore_revoke"
"printhost_ssl_ignore_revoke",
"elegoolink_timelapse",
"elegoolink_bed_leveling",
"elegoolink_bed_type"
};
const std::vector<std::string>& PhysicalPrinter::printer_options()
+3
View File
@@ -1622,6 +1622,9 @@ std::string Print::output_filename(const std::string &filename_base) const
DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
config.set_key_value("num_extruders", new ConfigOptionInt((int)m_config.nozzle_diameter.size()));
config.set_key_value("default_output_extension", new ConfigOptionString(".gcode"));
config.set_key_value("nozzle_diameter", new ConfigOptionFloats(m_config.nozzle_diameter.values));
config.set_key_value("filament_type", new ConfigOptionStrings(m_config.filament_type.values));
config.set_key_value("layer_height", new ConfigOptionFloat(m_default_object_config.layer_height.value));
// Handle output_filename_format. There is a hack related to binary G-codes: gcode / bgcode substitution.
std::string output_filename_format = m_config.output_filename_format.value;
+2
View File
@@ -56,6 +56,7 @@ void PrintBase::update_object_placeholders(DynamicConfig &config, const std::str
const std::string input_filename_base = input_filename.substr(0, input_filename.find_last_of("."));
// config.set_key_value("input_filename", new ConfigOptionString(input_filename_base + default_output_ext));
config.set_key_value("input_filename_base", new ConfigOptionString(input_filename_base));
config.set_key_value("_input_filename_base", new ConfigOptionString(input_filename_base));
}
}
@@ -72,6 +73,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
if (! filename_base.empty()) {
// cfg.set_key_value("input_filename", new ConfigOptionString(filename_base + default_ext));
cfg.set_key_value("input_filename_base", new ConfigOptionString(filename_base));
cfg.set_key_value("_input_filename_base", new ConfigOptionString(filename_base));
}
try {
boost::filesystem::path filename = format.empty() ?
+34 -1
View File
@@ -104,6 +104,7 @@ static const t_config_enum_values s_keys_map_PrintHostType {
{ "repetier", htRepetier },
{ "mks", htMKS },
{ "prusaconnectnew", htPrusaConnectNew },
{ "elegoolink", htElegooLink },
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintHostType)
@@ -244,6 +245,12 @@ static const t_config_enum_values s_keys_map_GCodeThumbnailsFormat = {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(GCodeThumbnailsFormat)
static const t_config_enum_values s_keys_map_ElegooBedType = {
{ "pte", int(ElegooBedType::PTE) },
{ "pc", int(ElegooBedType::PC) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ElegooBedType)
static const t_config_enum_values s_keys_map_ForwardCompatibilitySubstitutionRule = {
{ "disable", ForwardCompatibilitySubstitutionRule::Disable },
{ "enable", ForwardCompatibilitySubstitutionRule::Enable },
@@ -2223,12 +2230,38 @@ void PrintConfigDef::init_fff_params()
{ "flashair", "FlashAir" },
{ "astrobox", "AstroBox" },
{ "repetier", "Repetier" },
{ "mks", "MKS" }
{ "mks", "MKS" },
{ "elegoolink", "ElegooLink" }
});
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htPrusaLink));
def = this->add("elegoolink_timelapse", coBool);
def->label = L("ElegooLink timelapse");
def->tooltip = L("Enable timelapse recording when starting a print via ElegooLink.");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("elegoolink_bed_leveling", coBool);
def->label = L("ElegooLink bed leveling");
def->tooltip = L("Enable heated bed leveling when starting a print via ElegooLink.");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("elegoolink_bed_type", coEnum);
def->label = L("ElegooLink bed type");
def->tooltip = L("Select bed type for ElegooLink printing.");
def->set_enum<ElegooBedType>({
{ "pte", L("Side A") },
{ "pc", L("Side B") }
});
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionEnum<ElegooBedType>(ElegooBedType::PTE));
def = this->add("only_retract_when_crossing_perimeters", coBool);
def->label = L("Only retract when crossing perimeters");
def->tooltip = L("Disables retraction when the travel path does not exceed the upper layer's perimeters "
+8 -1
View File
@@ -65,7 +65,7 @@ enum class MachineLimitsUsage {
};
enum PrintHostType {
htPrusaLink, htPrusaConnect, htOctoPrint, htMoonraker, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htPrusaConnectNew
htPrusaLink, htPrusaConnect, htOctoPrint, htMoonraker, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htPrusaConnectNew, htElegooLink
};
enum AuthorizationType {
@@ -78,6 +78,11 @@ enum class FuzzySkinType {
All,
};
enum class ElegooBedType {
PTE = 0,
PC = 1
};
enum InfillPattern : int {
ipRectilinear, ipMonotonic, ipMonotonicLines, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase,
@@ -212,6 +217,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(MachineLimitsUsage)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrintHostType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(AuthorizationType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ElegooBedType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(FuzzySkinType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(InfillPattern)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(IroningType)
@@ -883,6 +889,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloatOrPercent, first_layer_height))
((ConfigOptionFloatOrPercent, first_layer_speed))
((ConfigOptionInts, first_layer_temperature))
((ConfigOptionEnum<ElegooBedType>, elegoolink_bed_type))
((ConfigOptionIntsNullable, idle_temperature))
((ConfigOptionInts, full_fan_speed_layer))
((ConfigOptionFloat, infill_acceleration))