Fixed errors after cherry picking Prusalink related commits

Add Prusa MK4 profile (working, but more work to do)
This commit is contained in:
SoftFever 2023-08-28 00:00:09 +08:00
commit 15d3aa646e
32 changed files with 676 additions and 257 deletions

View file

@ -1,9 +1,13 @@
{ {
"name": "Prusa", "name": "Prusa",
"version": "01.06.04.00", "version": "01.06.06.00",
"force_update": "0", "force_update": "0",
"description": "Prusa configurations", "description": "Prusa configurations",
"machine_model_list": [ "machine_model_list": [
{
"name": "Prusa MK4",
"sub_path": "machine/Prusa MK4.json"
},
{ {
"name": "Prusa MK3S", "name": "Prusa MK3S",
"sub_path": "machine/Prusa MK3S.json" "sub_path": "machine/Prusa MK3S.json"
@ -22,6 +26,10 @@
"name": "0.20mm Standard @MK3S", "name": "0.20mm Standard @MK3S",
"sub_path": "process/0.20mm Standard @MK3S.json" "sub_path": "process/0.20mm Standard @MK3S.json"
}, },
{
"name": "0.20mm Standard @MK4",
"sub_path": "process/0.20mm Standard @MK4.json"
},
{ {
"name": "0.20mm Standard @MINI", "name": "0.20mm Standard @MINI",
"sub_path": "process/0.20mm Standard @MINI.json" "sub_path": "process/0.20mm Standard @MINI.json"
@ -114,6 +122,10 @@
"name": "Prusa MK3S 0.4 nozzle", "name": "Prusa MK3S 0.4 nozzle",
"sub_path": "machine/Prusa MK3S 0.4 nozzle.json" "sub_path": "machine/Prusa MK3S 0.4 nozzle.json"
}, },
{
"name": "Prusa MK4 0.4 nozzle",
"sub_path": "machine/Prusa MK4 0.4 nozzle.json"
},
{ {
"name": "Prusa MINI 0.4 nozzle", "name": "Prusa MINI 0.4 nozzle",
"sub_path": "machine/Prusa MINI 0.4 nozzle.json" "sub_path": "machine/Prusa MINI 0.4 nozzle.json"

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View file

@ -14,6 +14,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -14,6 +14,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -20,6 +20,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -17,6 +17,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -14,6 +14,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -44,6 +44,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -20,6 +20,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -17,6 +17,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -20,6 +20,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -11,6 +11,7 @@
], ],
"compatible_printers": [ "compatible_printers": [
"Prusa MK3S 0.4 nozzle", "Prusa MK3S 0.4 nozzle",
"Prusa MK4 0.4 nozzle",
"Prusa MINI 0.4 nozzle" "Prusa MINI 0.4 nozzle"
] ]
} }

View file

@ -0,0 +1,35 @@
{
"type": "machine",
"setting_id": "GM003",
"name": "Prusa MK4 0.4 nozzle",
"from": "system",
"instantiation": "true",
"inherits": "fdm_machine_common",
"printer_model": "Prusa MK4",
"default_filament_profile": [
"Prusa Generic PLA"
],
"default_print_profile": "0.20mm Standard @MK4",
"nozzle_diameter": [
"0.4"
],
"bed_exclude_area": [
"0x0"
],
"printable_area": [
"0x0",
"250x0",
"250x210",
"0x210"
],
"printable_height": "220",
"machine_start_gcode": "G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[bed_temperature_initial_layer_single] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[bed_temperature_initial_layer_single] ; wait for bed temp\nM204 T1250 ; set travel acceleration\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM204 T[machine_max_acceleration_travel] ; restore travel acceleration\nM104 S[nozzle_temperature_initial_layer] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[nozzle_temperature_initial_layer] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow",
"machine_end_gcode": "G1 E-1 F2100 ; retract\n{if max_layer_z < 210}G1 Z{min(max_layer_z+2, 210)} F720 ; Move print head up{endif}\nG1 X178 Y178 F4200 ; park print head\n{if max_layer_z < 210}G1 Z{min(max_layer_z+30, 210)} F720 ; Move print head further up{endif}\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM900 K0 ; reset LA\nM84 ; disable motors",
"layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]",
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n",
"scan_first_layer": "0",
"machine_load_filament_time": "17",
"machine_unload_filament_time": "16",
"nozzle_type": "hardened_steel",
"auxiliary_fan": "0"
}

View file

@ -0,0 +1,12 @@
{
"type": "machine_model",
"name": "Prusa MK4",
"model_id": "MK4",
"nozzle_diameter": "0.4",
"machine_tech": "FFF",
"family": "Prusa",
"bed_model": "mk4_bed.stl",
"bed_texture": "mk4.svg",
"hotend_model": "",
"default_materials": "Prusa Generic ABS;Prusa Generic PLA;Prusa Generic PLA-CF;Prusa Generic PETG;Prusa Generic TPU;Prusa Generic ASA;Prusa Generic PC;Prusa Generic PVA;Prusa Generic PA;Prusa Generic PA-CF"
}

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="svg3128" xmlns="http://www.w3.org/2000/svg" width="710.1" height="596.7" viewBox="0 0 710.1 596.7">
<path id="path3098" d="m548.6,532h2.1v4.8h0c.1-.2.3-.4.5-.6.2-.2.4-.3.7-.5.2-.1.5-.2.8-.3.3,0,.5-.1.8,0,.7,0,1.3.1,1.9.4.5.2,1,.6,1.4,1.1.4.5.6,1,.8,1.6.2.6.3,1.3.3,1.9,0,.6,0,1.2-.2,1.8-.2.6-.4,1.1-.7,1.6-.7,1-1.8,1.5-3,1.5-.3,0-.6,0-1,0-.3,0-.6-.1-.9-.2-.3-.1-.5-.3-.8-.5-.2-.2-.4-.5-.6-.8h0v1.3h-2v-12.9Zm7.2,8.2c0-.4,0-.8-.2-1.2-.1-.4-.3-.7-.5-1-.2-.3-.5-.6-.8-.7-.3-.2-.7-.3-1.1-.3-.8,0-1.5.3-2,.9-.5.7-.7,1.6-.7,2.4,0,.4,0,.9.2,1.3.1.4.3.7.5,1,.2.3.5.5.8.7.3.2.7.3,1.1.2.4,0,.8,0,1.2-.3.3-.2.6-.4.8-.7.2-.3.4-.7.5-1,0-.4.1-.8.1-1.2Z" style="fill: #fff;"/>
<path id="path3100" d="m558.5,535.6h2.2l2.4,7h0l2.4-7h2.1l-3.6,9.8c-.2.4-.3.8-.5,1.2-.2.4-.4.7-.6,1-.2.3-.5.5-.9.7-.4.2-.9.3-1.3.3-.5,0-1,0-1.4-.1v-1.7h.5c.2,0,.3,0,.5,0,.2,0,.4,0,.6,0,.1,0,.3-.1.4-.3.1-.1.2-.3.3-.4,0-.2.1-.4.2-.5l.2-.7-3.5-9.2Z" style="fill: #fff;"/>
<path id="path3102" d="m581.1,540.8c0,.5,0,1.1-.1,1.6,0,.5-.3,1-.6,1.4-.3.4-.8.8-1.3,1-.7.3-1.5.4-2.2.4-.6,0-1.2,0-1.7-.3-.5-.2-.9-.5-1.2-.9-.3-.4-.6-.8-.7-1.3-.1-.5-.2-1.1-.2-1.6v-.7h2.2v.7c0,.6,0,1.2.4,1.7.3.4.9.7,1.4.6.3,0,.6,0,.9-.2.2-.1.4-.3.6-.5.1-.2.2-.5.3-.8,0-.4,0-.7,0-1.1v-8.8h2.2v8.7Z" style="fill: #fff;"/>
<path id="path3104" d="m587.8,545.1c-.7,0-1.4-.1-2-.4-.6-.2-1.1-.6-1.5-1-.4-.4-.7-1-.9-1.5-.2-.6-.3-1.3-.3-2,0-.7,0-1.4.3-2,.2-.6.5-1.1.9-1.5.4-.4.9-.8,1.5-1,1.3-.5,2.7-.5,4,0,.6.2,1.1.6,1.5,1,.4.4.7,1,.9,1.5.2.6.3,1.3.3,2,0,.7,0,1.4-.3,2-.2.6-.5,1.1-.9,1.5-.4.4-.9.8-1.5,1-.6.3-1.3.4-2,.4Zm0-1.6c.4,0,.8,0,1.2-.3.3-.2.6-.4.8-.8.2-.3.4-.7.5-1.1.1-.4.2-.8.2-1.2,0-.4,0-.8-.2-1.2,0-.4-.3-.7-.5-1-.2-.3-.5-.6-.8-.8-.7-.4-1.6-.4-2.4,0-.3.2-.6.4-.8.8-.2.3-.4.6-.5,1,0,.4-.1.8-.2,1.2,0,.4,0,.8.2,1.2,0,.4.3.7.5,1.1.2.3.5.6.8.8.4.2.8.3,1.2.3h0Z" style="fill: #fff;"/>
<path id="path3106" d="m595.7,541.9c0,.5.3,1,.7,1.3.4.2.9.4,1.4.4.2,0,.4,0,.7,0,.2,0,.5,0,.7-.2.2,0,.4-.2.5-.4.3-.4.2-.9,0-1.3-.2-.2-.4-.3-.7-.4-.3-.1-.7-.2-1-.3l-1.1-.2c-.4,0-.7-.2-1.1-.3-.4-.1-.7-.3-1-.5-.3-.2-.5-.5-.6-.9-.2-.4-.3-.8-.2-1.2,0-.5.1-.9.4-1.3.2-.3.6-.6.9-.8.4-.2.8-.4,1.3-.4.4,0,.9-.1,1.3-.1.5,0,.9,0,1.4.2.4,0,.8.3,1.2.5.4.2.7.5.9.9.2.4.4.9.4,1.3h-2.1c0-.4-.3-.8-.7-1-.4-.2-.8-.3-1.2-.2-.2,0-.3,0-.5,0-.2,0-.4,0-.6.1-.2,0-.3.2-.4.3-.1.1-.2.3-.2.5,0,.2,0,.5.3.6.2.2.4.3.7.4.3.1.7.2,1,.3l1.1.2c.4,0,.7.2,1.1.3.4.1.7.3,1,.5.3.2.5.5.7.8.2.4.3.8.3,1.2,0,.5-.1,1-.4,1.4-.3.4-.6.7-1,.9-.4.2-.9.4-1.3.5-.5.1-1,.2-1.5.2-.5,0-1.1,0-1.6-.2-.5-.1-.9-.3-1.3-.6-.4-.3-.7-.6-.9-1-.2-.4-.3-.9-.3-1.4h2Z" style="fill: #fff;"/>
<path id="path3108" d="m605.1,540.8c0,.3,0,.7.2,1,0,.3.3.6.5.9.2.3.5.5.8.6.4.2.7.2,1.1.2.5,0,1.1,0,1.5-.3.4-.3.7-.7.8-1.2h1.9c0,.5-.3.9-.6,1.3-.3.4-.6.7-1,1-.4.3-.8.5-1.2.6-.5.1-1,.2-1.5.2-.7,0-1.3-.1-1.9-.4-.5-.2-1-.6-1.4-1-.4-.4-.7-1-.9-1.5-.2-.6-.3-1.3-.3-2,0-.6.1-1.3.3-1.9.2-.6.5-1.1.9-1.6.8-1,2-1.5,3.3-1.5.7,0,1.4.1,2,.5.6.3,1.1.7,1.5,1.2.4.5.7,1.1.8,1.7.2.7.2,1.3.1,2h-6.9Zm4.8-1.3c0-.3,0-.6-.2-.9-.1-.3-.3-.6-.5-.8-.2-.2-.4-.4-.7-.5-.3-.1-.6-.2-.9-.2-.3,0-.7,0-1,.2-.3.1-.5.3-.8.5-.2.2-.4.5-.5.8-.1.3-.2.7-.2,1h4.8Z" style="fill: #fff;"/>
<path id="path3110" d="m612.6,535.6h1.5v-.8c0-.5,0-1,.2-1.4.1-.3.3-.6.6-.8.2-.2.5-.3.8-.4.3,0,.7-.1,1,0,.5,0,.9,0,1.4,0v1.6c-.1,0-.3,0-.4,0h-.5c-.3,0-.5,0-.7.2-.2.2-.3.5-.3.8v1h1.7v1.5h-1.7v7.8h-2v-7.8h-1.5v-1.5Z" style="fill: #fff;"/>
<path id="path3112" d="m624.5,532h5.7c.8,0,1.5.1,2.2.4.5.2,1,.6,1.3,1,.3.4.5.8.6,1.3.1.4.2.9.2,1.3,0,.4,0,.9-.2,1.3-.1.5-.3.9-.6,1.3-.4.4-.8.8-1.3,1-.7.3-1.5.4-2.2.4h-3.4v4.9h-2.2v-12.9Zm2.2,6.1h3.3c.3,0,.5,0,.8-.1.3,0,.5-.2.7-.3.2-.2.4-.4.5-.7.1-.3.2-.7.2-1,0-.3,0-.7-.2-1-.2-.5-.7-.9-1.2-1-.3,0-.6,0-.8,0h-3.3v4.2Z" style="fill: #fff;"/>
<path id="path3114" d="m636.2,535.6h1.9v1.8h0c0-.3.2-.5.4-.7.2-.2.4-.5.6-.7.2-.2.5-.4.8-.5.3-.1.6-.2.9-.2h.8v2h-.4c-.1,0-.3,0-.5,0-.7,0-1.3.3-1.8.8-.2.3-.4.6-.5,1-.1.4-.2.9-.2,1.4v4.4h-2v-9.3Z" style="fill: #fff;"/>
<path id="path3116" d="m650.7,544.9h-2v-1.3h0c-.3.5-.7.9-1.1,1.1-.5.3-1,.4-1.5.4-1,0-1.9-.2-2.7-.9-.6-.8-.9-1.8-.8-2.7v-5.9h2v5.7c0,.6.1,1.2.5,1.7.3.3.8.5,1.3.5.4,0,.7,0,1.1-.2.3-.1.5-.3.7-.5.2-.2.3-.5.4-.8,0-.3.1-.7.1-1v-5.4h2v9.3Z" style="fill: #fff;"/>
<path id="path3118" d="m654.4,541.9c0,.5.3,1,.7,1.2.4.2.9.4,1.4.4.2,0,.4,0,.7,0,.2,0,.5,0,.7-.2.2,0,.4-.2.5-.4.1-.2.2-.4.2-.6,0-.2-.1-.5-.3-.7-.2-.2-.4-.3-.7-.4-.3-.1-.7-.2-1-.3l-1.1-.2c-.4,0-.7-.2-1.1-.3-.4-.1-.7-.3-1-.5-.3-.2-.5-.5-.7-.8-.2-.4-.3-.8-.3-1.2,0-.5.1-.9.4-1.3.2-.3.6-.6.9-.8.4-.2.8-.4,1.3-.4.4,0,.9-.1,1.3-.1.5,0,.9,0,1.4.2.4.1.8.3,1.2.5.4.2.7.5.9.9.2.4.4.9.4,1.3h-2.1c0-.4-.3-.8-.7-1-.4-.2-.8-.3-1.2-.2-.2,0-.3,0-.5,0-.2,0-.4,0-.5.1-.2,0-.3.2-.4.3-.1.1-.2.3-.2.5,0,.2,0,.5.3.6.2.2.4.3.7.4.3.1.7.2,1,.3l1.1.2c.4,0,.7.2,1.1.3.4.1.7.3,1,.5.3.2.5.5.7.8.2.4.3.8.3,1.2,0,.5-.1,1-.4,1.4-.3.4-.6.7-1,.9-.4.2-.9.4-1.3.5-.5.1-1,.2-1.5.2-.6,0-1.1,0-1.6-.2-.5-.1-.9-.3-1.3-.6-.4-.3-.7-.6-.9-1-.2-.4-.3-.9-.3-1.4h2.1Z" style="fill: #fff;"/>
<path id="path3120" d="m670,542.8c0,.2,0,.4,0,.5,0,.1.2.2.4.2h.5v1.4h-.3c0,0-.3.2-.3.2h-.4c-.1,0-.2,0-.3,0-.3,0-.7,0-1-.2-.3-.2-.5-.5-.5-.9-.4.4-.9.7-1.5.9-.6.2-1.1.3-1.7.3-.4,0-.8,0-1.2-.2-.4-.1-.7-.3-1-.5-.3-.2-.5-.5-.7-.8-.2-.4-.3-.8-.3-1.2,0-.5,0-1,.3-1.4.2-.3.5-.6.8-.8.4-.2.7-.4,1.2-.4.4,0,.9-.2,1.3-.2.3,0,.7-.1,1.1-.2.3,0,.6,0,.9-.2.2,0,.4-.2.6-.3.2-.2.2-.4.2-.7,0-.2,0-.5-.2-.7-.1-.2-.3-.3-.5-.4-.2,0-.4-.2-.6-.2-.2,0-.4,0-.7,0-.5,0-1,.1-1.4.4-.4.3-.6.7-.6,1.1h-2c0-.5.2-1,.4-1.5.3-.4.6-.7,1-1,.4-.2.9-.4,1.3-.5.5-.1,1-.2,1.5-.2.5,0,.9,0,1.3.2.4,0,.8.2,1.2.5.3.2.6.5.8.8.2.4.3.8.3,1.2v4.8Zm-2.2-2.6c-.3.2-.7.3-1.2.4-.5,0-.9.1-1.4.2-.2,0-.4,0-.6.2-.2,0-.4.1-.5.3-.2.1-.3.3-.4.5,0,.2-.1.4-.1.7,0,.2,0,.4.2.6.1.2.3.3.5.4.2,0,.4.2.6.2.2,0,.4,0,.6,0,.2,0,.5,0,.7,0,.3,0,.5-.2.8-.3.2-.1.4-.3.6-.5.2-.2.2-.5.2-.8v-1.5Z" style="fill: #fff;"/>
<g>
<path d="m385.6,511.3c0-1.3.2-2.6.6-3.7s1-2.1,1.7-3c.7-.9,1.7-1.5,2.8-2,1.1-.5,2.3-.7,3.7-.7s2.6.2,3.7.7c1.1.5,2,1.2,2.8,2s1.3,1.8,1.7,3,.6,2.4.6,3.7-.2,2.5-.6,3.6-1,2.1-1.7,2.9c-.7.8-1.7,1.5-2.8,2-1.1.5-2.3.7-3.7.7s-2.6-.2-3.7-.7c-1.1-.5-2-1.1-2.8-2-.7-.8-1.3-1.8-1.7-2.9-.4-1.1-.6-2.3-.6-3.6Zm3.9,0c0,.7.1,1.5.3,2.2.2.7.4,1.3.9,1.9.4.6.9,1,1.5,1.4s1.4.5,2.2.5,1.6-.2,2.2-.5,1.1-.8,1.5-1.4c.4-.6.6-1.2.9-1.9.2-.7.3-1.4.3-2.2s-.1-1.5-.3-2.3c-.2-.7-.4-1.4-.9-1.9-.4-.6-.9-1-1.5-1.4-.6-.3-1.4-.5-2.2-.5s-1.6.2-2.2.5c-.6.3-1.1.8-1.5,1.4s-.6,1.2-.9,1.9c-.2.7-.3,1.5-.3,2.3Z" style="fill: #959998;"/>
<path d="m404.8,502.3h9.6c.8,0,1.5.1,2.1.4.6.3,1.2.6,1.6,1.1.4.4.8,1,1.1,1.6.2.6.4,1.2.4,1.9,0,1.1-.2,1.9-.6,2.7-.4.7-1.2,1.4-2.1,1.7h0c.5.2.9.4,1.2.6s.6.6.8,1c.2.4.3.8.4,1.2s.2.9.2,1.3,0,.6,0,1,0,.8.1,1.2c0,.4.1.8.2,1.1.1.4.2.7.4.9h-3.9c-.1-.3-.2-.6-.3-.9,0-.3-.1-.7-.1-1.1s0-.7-.1-1.2c0-.4,0-.7-.1-1.1-.1-.9-.4-1.6-.9-2s-1.1-.6-2.1-.6h-3.9v6.9h-3.9v-17.7Zm3.9,8.1h4.3c.9,0,1.6-.2,2-.6.4-.4.7-1.1.7-1.9s-.2-1.5-.7-1.9-1.1-.6-2-.6h-4.3v5Z" style="fill: #959998;"/>
<path d="m421.8,502.3h3.9v17.8h-3.9v-17.8Z" style="fill: #959998;"/>
<path d="m441,518.1c-.7.9-1.5,1.5-2.3,1.9s-1.7.5-2.6.5c-1.4,0-2.6-.2-3.7-.7-1.1-.5-2-1.1-2.8-2-.7-.8-1.3-1.8-1.7-2.9-.4-1.1-.6-2.3-.6-3.6s.2-2.6.6-3.7,1-2.1,1.7-3c.7-.9,1.7-1.5,2.8-2,1.1-.5,2.3-.7,3.7-.7s1.8.1,2.7.4c.9.3,1.6.7,2.3,1.2s1.3,1.2,1.7,2,.7,1.7.9,2.7h-3.7c-.2-1-.7-1.7-1.4-2.2s-1.5-.7-2.4-.7-1.6.2-2.2.5-1.1.8-1.5,1.4c-.4.6-.6,1.2-.9,1.9-.2.7-.3,1.5-.3,2.3s.1,1.5.3,2.2c.2.7.4,1.3.9,1.9.4.6.9,1,1.5,1.4.6.3,1.4.5,2.2.5,1.3,0,2.3-.3,3-1s1.1-1.6,1.3-2.9h-3.9v-2.9h7.5v9.6h-2.5l-.4-2Z" style="fill: #959998;"/>
<path d="m446.3,502.3h3.9v17.8h-3.9v-17.8Z" style="fill: #959998;"/>
<path d="m452.7,502.3h3.9l7.4,11.9h0v-11.9h3.7v17.8h-3.9l-7.4-11.9h0v11.9h-3.7v-17.8Z" style="fill: #959998;"/>
<path d="m475.1,502.3h4l6.6,17.8h-4l-1.4-3.9h-6.6l-1.4,3.9h-3.9l6.7-17.8Zm-.4,10.9h4.6l-2.2-6.5h0l-2.3,6.5Z" style="fill: #959998;"/>
<path d="m486.5,502.3h3.9v14.5h8.6v3.3h-12.6v-17.8h0Z" style="fill: #959998;"/>
</g>
<g>
<path d="m506.9,502.3h8c1.1,0,2.1.2,2.8.5s1.4.7,1.9,1.3c.5.5.9,1.2,1.1,1.8.2.7.3,1.4.3,2.1s-.1,1.4-.3,2.1-.6,1.3-1.1,1.8c-.5.5-1.1,1-1.9,1.3s-1.7.5-2.8.5h-4.1v6.4h-3.9v-17.8Zm3.9,8.4h3c.4,0,.9,0,1.3-.1.4,0,.8-.2,1.1-.4.3-.2.6-.5.7-.8.2-.3.3-.8.3-1.4s-.1-1-.3-1.4c-.2-.3-.4-.6-.7-.8-.3-.2-.7-.3-1.1-.4s-.9-.1-1.3-.1h-3v5.3Z" style="fill: #fff;"/>
<path d="m522.2,502.3h9.6c.8,0,1.5.1,2.1.4.6.3,1.2.6,1.6,1.1.4.4.8,1,1.1,1.6.2.6.4,1.2.4,1.9,0,1.1-.2,1.9-.6,2.7-.4.7-1.2,1.4-2.1,1.7h0c.5.2.9.4,1.2.6s.6.6.8,1c.2.4.3.8.4,1.2s.2.9.2,1.3,0,.6,0,1,0,.8.1,1.2c0,.4.1.8.2,1.1.1.4.2.7.4.9h-3.9c-.1-.3-.2-.6-.3-.9,0-.3-.1-.7-.1-1.1s0-.7-.1-1.2c0-.4,0-.7-.1-1.1-.1-.9-.4-1.6-.9-2s-1.1-.6-2.1-.6h-3.9v6.9h-3.9v-17.7Zm3.9,8.1h4.3c.9,0,1.5-.2,2-.6.4-.4.7-1.1.7-1.9s-.2-1.5-.7-1.9-1.1-.6-2-.6h-4.3v5Z" style="fill: #fff;"/>
<path d="m554.1,513.3c0,2.4-.7,4.2-2,5.4-1.4,1.2-3.2,1.8-5.6,1.8s-4.3-.6-5.6-1.8c-1.3-1.2-2-3-2-5.4v-11.1h3.9v11.1c0,.5,0,1,.1,1.4,0,.5.3.9.5,1.3.3.4.6.6,1.1.9.5.2,1.1.3,1.9.3,1.4,0,2.3-.3,2.9-.9s.8-1.6.8-2.9v-11.1h3.9v11h0Z" style="fill: #fff;"/>
<path d="m559.1,514.2c0,.6.1,1.1.3,1.5.2.4.5.7.9,1s.8.4,1.3.6,1,.2,1.5.2.7,0,1.1-.1.8-.2,1.1-.3.6-.4.9-.7c.2-.3.3-.6.3-1.1s-.2-.9-.5-1.2-.7-.5-1.2-.7c-.5-.2-1.1-.4-1.7-.5s-1.3-.3-1.9-.5c-.7-.2-1.3-.4-1.9-.6-.6-.2-1.2-.5-1.7-.9s-.9-.9-1.2-1.4c-.3-.6-.5-1.3-.5-2.1s.2-1.7.6-2.4.9-1.2,1.5-1.7,1.4-.8,2.1-1,1.6-.3,2.4-.3,1.8.1,2.7.3,1.6.5,2.3,1,1.2,1.1,1.6,1.8.6,1.6.6,2.6h-3.8c0-.5-.1-1-.3-1.3s-.4-.6-.7-.8-.7-.3-1.1-.4-.9-.1-1.3-.1-.6,0-1,.1c-.3,0-.6.2-.9.3-.3.2-.5.4-.6.6s-.2.6-.2.9,0,.6.2.9c.1.2.4.4.8.6s.9.4,1.6.5c.7.2,1.6.4,2.7.7.2,0,.5.1.9.2.3.1.7.2,1.1.4.4.2.8.4,1.2.6.4.2.7.5,1.1.9.3.4.6.8.8,1.3.2.5.3,1.1.3,1.8s-.2,1.6-.5,2.3c-.3.7-.8,1.3-1.4,1.8s-1.4.9-2.3,1.2c-.9.3-2,.4-3.2.4s-1.9-.1-2.9-.4-1.7-.6-2.4-1.2-1.3-1.2-1.7-2c-.4-.8-.6-1.7-.6-2.8h3.8Z" style="fill: #fff;"/>
<path d="m576.5,502.3h4l6.6,17.8h-4l-1.4-3.9h-6.6l-1.4,3.9h-3.9l6.7-17.8Zm-.4,10.9h4.6l-2.2-6.5h0l-2.3,6.5Z" style="fill: #fff;"/>
</g>
<g>
<path d="m594.3,502.3h5.5l4.1,12.2h0l3.9-12.2h5.5v17.8h-3.7v-12.6h0l-4.4,12.6h-3l-4.4-12.5h0v12.5h-3.7v-17.8h0Z" style="fill: #ed6b21;"/>
<path d="m616,502.3h3.9v7.4l6.9-7.4h4.9l-6.9,7,7.6,10.7h-4.9l-5.3-8-2.2,2.3v5.7h-3.9v-17.8Z" style="fill: #ed6b21;"/>
<path d="m638.4,516.1h-7.4v-3.2l7.6-10.2h3.2v10.5h2.3v2.9h-2.3v4h-3.4v-4h0Zm0-9h0l-4.5,6.1h4.6v-6.1Z" style="fill: #ed6b21;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

View file

@ -0,0 +1,26 @@
{
"type": "process",
"setting_id": "GP004",
"name": "0.20mm Standard @MK4",
"from": "system",
"instantiation": "true",
"inherits": "fdm_process_common",
"initial_layer_speed": "45",
"initial_layer_infill_speed": "80",
"outer_wall_speed": "170",
"inner_wall_speed": "170",
"sparse_infill_speed": "200",
"internal_solid_infill_speed": "200",
"top_surface_speed": "100",
"gap_infill_speed": "120",
"travel_speed": "300",
"default_acceleration": "4000",
"initial_layer_acceleration": "700",
"top_surface_acceleration": "1000",
"travel_acceleration": "4000",
"inner_wall_acceleration": "4000",
"outer_wall_acceleration": "3000",
"compatible_printers": [
"Prusa MK4 0.4 nozzle"
]
}

View file

@ -335,6 +335,9 @@ void AppConfig::set_defaults()
// } // }
// #endif // #endif
if (get("allow_ip_resolve").empty())
set("allow_ip_resolve", "1");
if (get("presets", "filament_colors").empty()) { if (get("presets", "filament_colors").empty()) {
set_str("presets", "filament_colors", "#F2754E"); set_str("presets", "filament_colors", "#F2754E");
} }

View file

@ -80,6 +80,8 @@ public:
{ std::string value; this->get(section, key, value); return value; } { std::string value; this->get(section, key, value); return value; }
std::string get(const std::string &key) const std::string get(const std::string &key) const
{ std::string value; this->get("app", key, value); return value; } { std::string value; this->get("app", key, value); return value; }
bool get_bool(const std::string &key) const
{ return this->get(key) == "true"; }
void set(const std::string &section, const std::string &key, const std::string &value) void set(const std::string &section, const std::string &key, const std::string &value)
{ {
#ifndef NDEBUG #ifndef NDEBUG

View file

@ -2489,6 +2489,12 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print)
file.write_format("M204 P%d T%d ; sets acceleration (P, T), mm/sec^2\n", file.write_format("M204 P%d T%d ; sets acceleration (P, T), mm/sec^2\n",
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
travel_acc); travel_acc);
else if (flavor == gcfMarlinFirmware)
// New Marlin uses M204 P[print] R[retract] T[travel]
file.write_format("M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
int(print.config().machine_max_acceleration_travel.values.front() + 0.5));
else else
file.write_format("M204 P%d R%d T%d\n", file.write_format("M204 P%d R%d T%d\n",
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),

View file

@ -18,16 +18,20 @@ namespace Slic3r {
bool GCodeWriter::full_gcode_comment = true; bool GCodeWriter::full_gcode_comment = true;
const double GCodeWriter::slope_threshold = 3 * PI / 180; const double GCodeWriter::slope_threshold = 3 * PI / 180;
bool supports_separate_travel_acceleration(GCodeFlavor flavor)
{
return (flavor == gcfRepetier || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware);
}
void GCodeWriter::apply_print_config(const PrintConfig &print_config) void GCodeWriter::apply_print_config(const PrintConfig &print_config)
{ {
this->config.apply(print_config, true); this->config.apply(print_config, true);
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
bool use_mach_limits = print_config.gcode_flavor.value == gcfMarlinLegacy || bool use_mach_limits = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware ||
print_config.gcode_flavor.value == gcfMarlinFirmware || print_config.gcode_flavor.value == gcfKlipper || print_config.gcode_flavor.value == gcfRepRapFirmware;
print_config.gcode_flavor.value == gcfKlipper ||
print_config.gcode_flavor.value == gcfRepRapFirmware;
m_max_acceleration = std::lrint(use_mach_limits ? print_config.machine_max_acceleration_extruding.values.front() : 0); m_max_acceleration = std::lrint(use_mach_limits ? print_config.machine_max_acceleration_extruding.values.front() : 0);
m_max_jerk = std::lrint(use_mach_limits ? std::min(print_config.machine_max_jerk_x.values.front(), print_config.machine_max_jerk_y.values.front()) : 0); m_max_jerk = std::lrint(
use_mach_limits ? std::min(print_config.machine_max_jerk_x.values.front(), print_config.machine_max_jerk_y.values.front()) : 0);
m_max_jerk_z = print_config.machine_max_jerk_z.values.front(); m_max_jerk_z = print_config.machine_max_jerk_z.values.front();
m_max_jerk_e = print_config.machine_max_jerk_e.values.front(); m_max_jerk_e = print_config.machine_max_jerk_e.values.front();
} }

View file

@ -1972,7 +1972,7 @@ void PrintConfigDef::init_fff_params()
//def->enum_values.push_back("repetier"); //def->enum_values.push_back("repetier");
//def->enum_values.push_back("teacup"); //def->enum_values.push_back("teacup");
//def->enum_values.push_back("makerware"); //def->enum_values.push_back("makerware");
//def->enum_values.push_back("marlin2"); def->enum_values.push_back("marlin2");
//def->enum_values.push_back("sailfish"); //def->enum_values.push_back("sailfish");
//def->enum_values.push_back("mach3"); //def->enum_values.push_back("mach3");
//def->enum_values.push_back("machinekit"); //def->enum_values.push_back("machinekit");
@ -1985,7 +1985,7 @@ void PrintConfigDef::init_fff_params()
//def->enum_labels.push_back("Repetier"); //def->enum_labels.push_back("Repetier");
//def->enum_labels.push_back("Teacup"); //def->enum_labels.push_back("Teacup");
//def->enum_labels.push_back("MakerWare (MakerBot)"); //def->enum_labels.push_back("MakerWare (MakerBot)");
//def->enum_labels.push_back("Marlin 2"); def->enum_labels.push_back("Marlin 2");
//def->enum_labels.push_back("Sailfish (MakerBot)"); //def->enum_labels.push_back("Sailfish (MakerBot)");
//def->enum_labels.push_back("Mach3/LinuxCNC"); //def->enum_labels.push_back("Mach3/LinuxCNC");
//def->enum_labels.push_back("Machinekit"); //def->enum_labels.push_back("Machinekit");

View file

@ -5,6 +5,8 @@
#include <set> #include <set>
#include <mutex> #include <mutex>
#include <boost/nowide/convert.hpp>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/button.h> #include <wx/button.h>
#include <wx/listctrl.h> #include <wx/listctrl.h>
@ -15,8 +17,8 @@
#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/format.hpp"
#include "slic3r/Utils/Bonjour.hpp" #include "slic3r/Utils/Bonjour.hpp"
#include "Widgets/Button.hpp"
namespace Slic3r { namespace Slic3r {
@ -61,8 +63,6 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
, timer_state(0) , timer_state(0)
, tech(tech) , tech(tech)
{ {
SetBackgroundColour(*wxWHITE);
const int em = GUI::wxGetApp().em_unit(); const int em = GUI::wxGetApp().em_unit();
list->SetMinSize(wxSize(80 * em, 30 * em)); list->SetMinSize(wxSize(80 * em, 30 * em));
@ -81,39 +81,10 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
vsizer->Add(list, 1, wxEXPAND | wxALL, em); vsizer->Add(list, 1, wxEXPAND | wxALL, em);
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
auto button_sizer = new wxBoxSizer(wxHORIZONTAL); button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em);
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em);
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed), std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered), // ^ Note: The Ok/Cancel labels are translated by wxWidgets
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
StateColor btn_bg_white(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed), std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
auto m_button_ok = new Button(this, _L("OK"));
m_button_ok->SetBackgroundColor(btn_bg_green);
m_button_ok->SetBorderColor(*wxWHITE);
m_button_ok->SetTextColor(*wxWHITE);
m_button_ok->SetFont(Label::Body_12);
m_button_ok->SetSize(wxSize(FromDIP(58), FromDIP(24)));
m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24)));
m_button_ok->SetCornerRadius(FromDIP(12));
m_button_ok->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { this->EndModal(wxID_OK); });
auto m_button_cancel = new Button(this, _L("Cancel"));
m_button_cancel->SetBackgroundColor(btn_bg_white);
m_button_cancel->SetBorderColor(wxColour(38, 46, 48));
m_button_cancel->SetFont(Label::Body_12);
m_button_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24)));
m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24)));
m_button_cancel->SetCornerRadius(FromDIP(12));
m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { this->EndModal(wxID_CANCEL); });
button_sizer->AddStretchSpacer();
button_sizer->Add(m_button_ok, 0, wxALL, FromDIP(5));
button_sizer->Add(m_button_cancel, 0, wxALL, FromDIP(5));
vsizer->Add(button_sizer, 0, wxALIGN_CENTER); vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
SetSizerAndFit(vsizer); SetSizerAndFit(vsizer);
@ -253,19 +224,61 @@ void BonjourDialog::on_timer(wxTimerEvent &)
// explicitly (wxTimerEvent should not be created by user code). // explicitly (wxTimerEvent should not be created by user code).
void BonjourDialog::on_timer_process() void BonjourDialog::on_timer_process()
{ {
const auto search_str = _utf8(L("Searching for devices")); const auto search_str = _L("Searching for devices");
if (timer_state > 0) { if (timer_state > 0) {
const std::string dots(timer_state, '.'); const std::string dots(timer_state, '.');
label->SetLabel(GUI::from_u8((boost::format("%1% %2%") % search_str % dots).str())); label->SetLabel(search_str + dots);
timer_state = (timer_state) % 3 + 1; timer_state = (timer_state) % 3 + 1;
} else { } else {
label->SetLabel(GUI::from_u8((boost::format("%1%: %2%") % search_str % (_utf8(L("Finished"))+".")).str())); label->SetLabel(search_str + ": " + _L("Finished") + ".");
timer->Stop(); timer->Stop();
} }
} }
IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std::vector<boost::asio::ip::address>& ips, size_t& selected_index)
: wxDialog(parent, wxID_ANY, _(L("Multiple resolved IP addresses")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, m_list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxSIMPLE_BORDER))
, m_selected_index (selected_index)
{
const int em = GUI::wxGetApp().em_unit();
m_list->SetMinSize(wxSize(40 * em, 30 * em));
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
auto* label = new wxStaticText(this, wxID_ANY, GUI::format_wxstr(_L("There are several IP addresses resolving to hostname %1%.\nPlease select one that should be used."), hostname));
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em);
m_list->SetSingleStyle(wxLC_SINGLE_SEL);
m_list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 40 * em);
for (size_t i = 0; i < ips.size(); i++)
m_list->InsertItem(i, boost::nowide::widen(ips[i].to_string()));
m_list->Select(0);
vsizer->Add(m_list, 1, wxEXPAND | wxALL, em);
wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL);
button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em);
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em);
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
SetSizerAndFit(vsizer);
GUI::wxGetApp().UpdateDlgDarkUI(this);
}
IPListDialog::~IPListDialog()
{
}
void IPListDialog::EndModal(int retCode)
{
if (retCode == wxID_OK) {
m_selected_index = (size_t)m_list->GetFirstSelected();
}
wxDialog::EndModal(retCode);
}
} }

View file

@ -1,9 +1,13 @@
#ifndef slic3r_BonjourDialog_hpp_ #ifndef slic3r_BonjourDialog_hpp_
#define slic3r_BonjourDialog_hpp_ #define slic3r_BonjourDialog_hpp_
#include <cstddef>
#include <memory> #include <memory>
#include <boost/asio/ip/address.hpp>
#include <wx/dialog.h> #include <wx/dialog.h>
#include <wx/string.h>
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"
@ -11,7 +15,7 @@ class wxListView;
class wxStaticText; class wxStaticText;
class wxTimer; class wxTimer;
class wxTimerEvent; class wxTimerEvent;
class address;
namespace Slic3r { namespace Slic3r {
@ -41,12 +45,26 @@ private:
unsigned timer_state; unsigned timer_state;
Slic3r::PrinterTechnology tech; Slic3r::PrinterTechnology tech;
void on_reply(BonjourReplyEvent &); virtual void on_reply(BonjourReplyEvent &);
void on_timer(wxTimerEvent &); void on_timer(wxTimerEvent &);
void on_timer_process(); void on_timer_process();
}; };
class IPListDialog : public wxDialog
{
public:
IPListDialog(wxWindow* parent, const wxString& hostname, const std::vector<boost::asio::ip::address>& ips, size_t& selected_index);
IPListDialog(IPListDialog&&) = delete;
IPListDialog(const IPListDialog&) = delete;
IPListDialog& operator=(IPListDialog&&) = delete;
IPListDialog& operator=(const IPListDialog&) = delete;
~IPListDialog();
virtual void EndModal(int retCode) wxOVERRIDE;
private:
wxListView* m_list;
size_t& m_selected_index;
};
} }

View file

@ -409,8 +409,14 @@ void PhysicalPrinterDialog::update(bool printer_change)
update_host_type(printer_change); update_host_type(printer_change);
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type"); const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
m_optgroup->show_field("host_type"); m_optgroup->show_field("host_type");
if (opt->value == htPrusaLink)
{ // hide PrusaConnect address
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
if (wxTextCtrl* temp = dynamic_cast<wxTextCtrl*>(printhost_field->getWindow()); temp && temp->GetValue() == L"https://connect.prusa3d.com") {
temp->SetValue(wxString());
}
}
if (opt->value == htPrusaLink) { // PrusaConnect does NOT allow http digest
m_optgroup->show_field("printhost_authorization_type"); m_optgroup->show_field("printhost_authorization_type");
AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value; AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value;
m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword); m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword);
@ -422,8 +428,14 @@ void PhysicalPrinterDialog::update(bool printer_change)
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" }) for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" })
m_optgroup->hide_field(opt_key); m_optgroup->hide_field(opt_key);
supports_multiple_printers = opt && opt->value == htRepetier; supports_multiple_printers = opt && opt->value == htRepetier;
if (opt->value == htPrusaConnect) { // automatically show default prusaconnect address
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
if (wxTextCtrl* temp = dynamic_cast<wxTextCtrl*>(printhost_field->getWindow()); temp && temp->GetValue().IsEmpty()) {
temp->SetValue(L"https://connect.prusa3d.com");
}
}
}
} }
} }
else { else {
m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false); m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false);
@ -453,30 +465,23 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
{ {
if (m_config == nullptr) if (m_config == nullptr)
return; return;
bool all_presets_are_from_mk3_family = false;
Field* ht = m_optgroup->get_field("host_type"); Field* ht = m_optgroup->get_field("host_type");
wxArrayString types; wxArrayString types;
// Append localized enum_labels int last_in_conf = m_config->option("host_type")->getInt(); // this is real position in last choice
assert(ht->m_opt.enum_labels.size() == ht->m_opt.enum_values.size());
for (size_t i = 0; i < ht->m_opt.enum_labels.size(); i++) {
if (ht->m_opt.enum_values[i] == "prusalink" && !all_presets_are_from_mk3_family)
continue;
types.Add(_(ht->m_opt.enum_labels[i]));
}
Choice* choice = dynamic_cast<Choice*>(ht); Choice* choice = dynamic_cast<Choice*>(ht);
choice->set_values(types); choice->set_values(types);
auto set_to_choice_and_config = [this, choice](PrintHostType type) { int index_in_choice = (printer_change ? std::clamp(last_in_conf - ((int)ht->m_opt.enum_values.size() - (int)types.size()), 0, (int)ht->m_opt.enum_values.size() - 1) : last_in_conf);
choice->set_value(static_cast<int>(type)); choice->set_value(index_in_choice);
if ("prusalink" == ht->m_opt.enum_values.at(index_in_choice))
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaLink));
else if ("prusaconnect" == ht->m_opt.enum_values.at(index_in_choice))
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaConnect));
else {
int host_type = std::clamp(index_in_choice + ((int)ht->m_opt.enum_values.size() - (int)types.size()), 0, (int)ht->m_opt.enum_values.size() - 1);
PrintHostType type = static_cast<PrintHostType>(host_type);
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(type)); m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(type));
}; }
if ((printer_change && all_presets_are_from_mk3_family) || all_presets_are_from_mk3_family)
set_to_choice_and_config(htPrusaLink);
else if ((printer_change && !all_presets_are_from_mk3_family) || (!all_presets_are_from_mk3_family && m_config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value == htPrusaLink))
set_to_choice_and_config(htOctoPrint);
else
choice->set_value(m_config->option("host_type")->getInt());
} }
void PhysicalPrinterDialog::update_printers() void PhysicalPrinterDialog::update_printers()

View file

@ -10966,42 +10966,34 @@ void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn)
upload_job.printhost->get_groups(groups); upload_job.printhost->get_groups(groups);
} }
// orca merge todo // PrusaLink specific: Query the server for the list of file groups.
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups); wxArrayString storage_paths;
wxArrayString storage_names;
{
wxBusyCursor wait;
try {
upload_job.printhost->get_storage(storage_paths, storage_names);
} catch (const Slic3r::IOError& ex) {
show_error(this, ex.what(), false);
return;
}
}
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups, storage_paths, storage_names);
if (dlg.ShowModal() == wxID_OK) { if (dlg.ShowModal() == wxID_OK) {
upload_job.upload_data.upload_path = dlg.filename(); upload_job.upload_data.upload_path = dlg.filename();
upload_job.upload_data.post_action = dlg.post_action(); upload_job.upload_data.post_action = dlg.post_action();
upload_job.upload_data.group = dlg.group(); upload_job.upload_data.group = dlg.group();
upload_job.upload_data.storage = dlg.storage();
p->export_gcode(fs::path(), false, std::move(upload_job)); // Show "Is printer clean" dialog for PrusaConnect - Upload and print.
if (std::string(upload_job.printhost->get_name()) == "PrusaConnect" && upload_job.upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
try { GUI::MessageDialog dlg(nullptr, _L("Is the printer ready? Is the print sheet in place, empty and clean?"), _L("Upload and Print"), wxOK | wxCANCEL);
json j; if (dlg.ShowModal() != wxID_OK)
switch (dlg.post_action()) {
case PrintHostPostUploadAction::None:
j["post_action"] = "Upload";
break;
case PrintHostPostUploadAction::StartPrint:
j["post_action"] = "StartPrint";
break;
case PrintHostPostUploadAction::StartSimulation:
j["post_action"] = "StartSimulation";
break;
}
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
if (preset_bundle) {
j["gcode_printer_model"] = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
}
if (physical_printer_config) {
j["printer_preset"] = physical_printer_config->opt_string("inherits");
}
NetworkAgent *agent = wxGetApp().getAgent();
} catch (...) {
return; return;
} }
p->export_gcode(fs::path(), false, std::move(upload_job));
} }
} }
int Plater::send_gcode(int plate_idx, Export3mfProgressFn proFn) int Plater::send_gcode(int plate_idx, Export3mfProgressFn proFn)

View file

@ -38,13 +38,14 @@ static const char *CONFIG_KEY_PATH = "printhost_path";
static const char *CONFIG_KEY_GROUP = "printhost_group"; static const char *CONFIG_KEY_GROUP = "printhost_group";
static const char* CONFIG_KEY_STORAGE = "printhost_storage"; static const char* CONFIG_KEY_STORAGE = "printhost_storage";
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups, const wxArrayString& storage) PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups, const wxArrayString& storage_paths, const wxArrayString& storage_names)
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:"), 0) // Set style = 0 to avoid default creation of the "OK" button. : MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:"), 0) // Set style = 0 to avoid default creation of the "OK" button.
// All buttons will be added later in this constructor // All buttons will be added later in this constructor
, txt_filename(new wxTextCtrl(this, wxID_ANY)) , txt_filename(new wxTextCtrl(this, wxID_ANY))
, combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr) , combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr)
, combo_storage(storage.GetCount() > 1 ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, storage, wxCB_READONLY) : nullptr) , combo_storage(storage_names.GetCount() > 1 ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, storage_names, wxCB_READONLY) : nullptr)
, post_upload_action(PrintHostPostUploadAction::None) , post_upload_action(PrintHostPostUploadAction::None)
, m_paths(storage_paths)
{ {
#ifdef __APPLE__ #ifdef __APPLE__
txt_filename->OSXDisableAllSmartSubstitutions(); txt_filename->OSXDisableAllSmartSubstitutions();
@ -70,18 +71,18 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
if (combo_storage != nullptr) { if (combo_storage != nullptr) {
// PrusaLink specific: User needs to choose a storage // PrusaLink specific: User needs to choose a storage
auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage:")); auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage") + ":");
content_sizer->Add(label_group); content_sizer->Add(label_group);
content_sizer->Add(combo_storage, 0, wxBOTTOM, 2 * VERT_SPACING); content_sizer->Add(combo_storage, 0, wxBOTTOM, 2 * VERT_SPACING);
combo_storage->SetValue(storage.front()); combo_storage->SetValue(storage_names.front());
wxString recent_storage = from_u8(app_config->get("recent", CONFIG_KEY_STORAGE)); wxString recent_storage = from_u8(app_config->get("recent", CONFIG_KEY_STORAGE));
if (!recent_storage.empty()) if (!recent_storage.empty())
combo_storage->SetValue(recent_storage); combo_storage->SetValue(recent_storage);
} else if (storage.GetCount() == 1){ } else if (storage_names.GetCount() == 1){
// PrusaLink specific: Show which storage has been detected. // PrusaLink specific: Show which storage has been detected.
auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage: ") + storage.front()); auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage") + ": " + storage_names.front());
content_sizer->Add(label_group); content_sizer->Add(label_group);
m_preselected_storage = storage.front(); m_preselected_storage = storage_paths.front();
} }
@ -196,7 +197,9 @@ std::string PrintHostSendDialog::storage() const
{ {
if (!combo_storage) if (!combo_storage)
return GUI::format("%1%", m_preselected_storage); return GUI::format("%1%", m_preselected_storage);
return boost::nowide::narrow(combo_storage->GetValue()); if (combo_storage->GetSelection() < 0 || combo_storage->GetSelection() >= int(m_paths.size()))
return {};
return boost::nowide::narrow(m_paths[combo_storage->GetSelection()]);
} }
void PrintHostSendDialog::EndModal(int ret) void PrintHostSendDialog::EndModal(int ret)
@ -226,8 +229,6 @@ void PrintHostSendDialog::EndModal(int ret)
MsgDialog::EndModal(ret); MsgDialog::EndModal(ret);
} }
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event); wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
@ -355,8 +356,6 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
if (selected == wxNOT_FOUND) { return; } if (selected == wxNOT_FOUND) { return; }
GUI::show_error(nullptr, job_list->GetTextValue(selected, COL_ERRORMSG)); GUI::show_error(nullptr, job_list->GetTextValue(selected, COL_ERRORMSG));
}); });
wxGetApp().UpdateDlgDarkUI(this);
} }
void PrintHostQueueDialog::append_job(const PrintHostJob &job) void PrintHostQueueDialog::append_job(const PrintHostJob &job)
@ -474,7 +473,7 @@ void PrintHostQueueDialog::on_error(Event &evt)
set_state(evt.job_id, ST_ERROR); set_state(evt.job_id, ST_ERROR);
auto errormsg = from_u8((boost::format("%1%\n%2%") % _utf8(L("Error uploading to print host:")) % std::string(evt.status.ToUTF8())).str()); auto errormsg = format_wxstr("%1%\n%2%", _L("Error uploading to print host") + ":", evt.status);
job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS);
job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later
@ -505,6 +504,7 @@ void PrintHostQueueDialog::on_cancel(Event &evt)
void PrintHostQueueDialog::on_info(Event& evt) void PrintHostQueueDialog::on_info(Event& evt)
{ {
/*
wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list"); wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list");
if (evt.tag == L"resolve") { if (evt.tag == L"resolve") {
@ -524,6 +524,7 @@ void PrintHostQueueDialog::on_info(Event& evt)
} else if (evt.tag == L"set_complete_off") { } else if (evt.tag == L"set_complete_off") {
wxGetApp().notification_manager()->set_upload_job_notification_comp_on_100(evt.job_id + 1, false); wxGetApp().notification_manager()->set_upload_job_notification_comp_on_100(evt.job_id + 1, false);
} }
*/
} }
void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, std::string>>& ret) void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, std::string>>& ret)
@ -565,9 +566,12 @@ bool PrintHostQueueDialog::load_user_data(int udt, std::vector<int>& vector)
auto* app_config = wxGetApp().app_config; auto* app_config = wxGetApp().app_config;
auto hasget = [app_config](const std::string& name, std::vector<int>& vector)->bool { auto hasget = [app_config](const std::string& name, std::vector<int>& vector)->bool {
if (app_config->has(name)) { if (app_config->has(name)) {
vector.push_back(std::stoi(app_config->get(name))); std::string val = app_config->get(name);
if (!val.empty() || val[0]!='\0') {
vector.push_back(std::stoi(val));
return true; return true;
} }
}
return false; return false;
}; };
if (udt & UserDataType::UDT_SIZE) { if (udt & UserDataType::UDT_SIZE) {

View file

@ -26,7 +26,7 @@ namespace GUI {
class PrintHostSendDialog : public GUI::MsgDialog class PrintHostSendDialog : public GUI::MsgDialog
{ {
public: public:
PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups, const wxArrayString& storage); PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups, const wxArrayString& storage_paths, const wxArrayString& storage_names);
boost::filesystem::path filename() const; boost::filesystem::path filename() const;
PrintHostPostUploadAction post_action() const; PrintHostPostUploadAction post_action() const;
std::string group() const; std::string group() const;
@ -40,6 +40,7 @@ private:
PrintHostPostUploadAction post_upload_action; PrintHostPostUploadAction post_upload_action;
wxString m_valid_suffix; wxString m_valid_suffix;
wxString m_preselected_storage; wxString m_preselected_storage;
wxArrayString m_paths;
}; };

View file

@ -7,12 +7,17 @@
#include <set> #include <set>
#include <unordered_map> #include <unordered_map>
#include <functional> #include <functional>
#include <boost/asio/ip/address.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/optional.hpp>
#include <boost/system/error_code.hpp>
#include <boost/shared_ptr.hpp>
namespace Slic3r { namespace Slic3r {
struct BonjourReply struct BonjourReply
{ {
typedef std::unordered_map<std::string, std::string> TxtData; typedef std::unordered_map<std::string, std::string> TxtData;
@ -40,7 +45,6 @@ struct BonjourReply
std::ostream& operator<<(std::ostream &, const BonjourReply &); std::ostream& operator<<(std::ostream &, const BonjourReply &);
/// Bonjour lookup performer /// Bonjour lookup performer
class Bonjour : public std::enable_shared_from_this<Bonjour> { class Bonjour : public std::enable_shared_from_this<Bonjour> {
private: private:
@ -49,6 +53,7 @@ public:
typedef std::shared_ptr<Bonjour> Ptr; typedef std::shared_ptr<Bonjour> Ptr;
typedef std::function<void(BonjourReply &&)> ReplyFn; typedef std::function<void(BonjourReply &&)> ReplyFn;
typedef std::function<void()> CompleteFn; typedef std::function<void()> CompleteFn;
typedef std::function<void(const std::vector<BonjourReply>&)> ResolveFn;
typedef std::set<std::string> TxtKeys; typedef std::set<std::string> TxtKeys;
Bonjour(std::string service); Bonjour(std::string service);
@ -66,14 +71,216 @@ public:
// Timeout is per one retry, ie. total time spent listening = retries * timeout. // Timeout is per one retry, ie. total time spent listening = retries * timeout.
// If retries > 1, then care needs to be taken as more than one reply from the same service may be received. // If retries > 1, then care needs to be taken as more than one reply from the same service may be received.
// sets hostname queried by resolve()
Bonjour& set_hostname(const std::string& hostname);
Bonjour& on_reply(ReplyFn fn); Bonjour& on_reply(ReplyFn fn);
Bonjour& on_complete(CompleteFn fn); Bonjour& on_complete(CompleteFn fn);
Bonjour& on_resolve(ResolveFn fn);
// lookup all devices by given TxtKeys
// each correct reply is passed back in ReplyFn, finishes with CompleteFn
Ptr lookup(); Ptr lookup();
// performs resolving of hostname into vector of ip adresses passed back by ResolveFn
// needs set_hostname and on_resolve to be called before.
Ptr resolve();
// resolve on the current thread
void resolve_sync();
private: private:
std::unique_ptr<priv> p; std::unique_ptr<priv> p;
}; };
struct BonjourRequest
{
static const boost::asio::ip::address_v4 MCAST_IP4;
static const boost::asio::ip::address_v6 MCAST_IP6;
static const uint16_t MCAST_PORT;
std::vector<char> m_data;
static boost::optional<BonjourRequest> make_PTR(const std::string& service, const std::string& protocol);
static boost::optional<BonjourRequest> make_A(const std::string& hostname);
static boost::optional<BonjourRequest> make_AAAA(const std::string& hostname);
private:
BonjourRequest(std::vector<char>&& data) : m_data(std::move(data)) {}
};
class LookupSocket;
class ResolveSocket;
// Session is created for each async_receive of socket. On receive, its handle_receive method is called (Thru io_service->post).
// ReplyFn is called if correct datagram was received.
class UdpSession
{
public:
UdpSession(Bonjour::ReplyFn rfn);
virtual void handle_receive(const boost::system::error_code& error, size_t bytes) = 0;
std::vector<char> buffer;
boost::asio::ip::udp::endpoint remote_endpoint;
protected:
Bonjour::ReplyFn replyfn;
};
typedef std::shared_ptr<UdpSession> SharedSession;
// Session for LookupSocket
class LookupSession : public UdpSession
{
public:
LookupSession(const LookupSocket* sckt, Bonjour::ReplyFn rfn) : UdpSession(rfn), socket(sckt) {}
void handle_receive(const boost::system::error_code& error, size_t bytes) override;
protected:
// const pointer to socket to get needed data as txt_keys etc.
const LookupSocket* socket;
};
// Session for ResolveSocket
class ResolveSession : public UdpSession
{
public:
ResolveSession(const ResolveSocket* sckt, Bonjour::ReplyFn rfn) : UdpSession(rfn), socket(sckt) {}
void handle_receive(const boost::system::error_code& error, size_t bytes) override;
protected:
// const pointer to seocket to get hostname during handle_receive
const ResolveSocket* socket;
};
// Udp socket, starts receiving answers after first send() call until io_service is stopped.
class UdpSocket
{
public:
// Two constructors: 1st is with interface which must be resolved before calling this
UdpSocket(Bonjour::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, const boost::asio::ip::address& interface_address
, std::shared_ptr< boost::asio::io_service > io_service);
UdpSocket(Bonjour::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, std::shared_ptr< boost::asio::io_service > io_service);
void send();
void async_receive();
void cancel() { socket.cancel(); }
protected:
void receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes);
virtual SharedSession create_session() const = 0;
Bonjour::ReplyFn replyfn;
boost::asio::ip::address multicast_address;
boost::asio::ip::udp::socket socket;
boost::asio::ip::udp::endpoint mcast_endpoint;
std::shared_ptr< boost::asio::io_service > io_service;
std::vector<BonjourRequest> requests;
};
class LookupSocket : public UdpSocket
{
public:
LookupSocket(Bonjour::TxtKeys txt_keys
, std::string service
, std::string service_dn
, std::string protocol
, Bonjour::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, const boost::asio::ip::address& interface_address
, std::shared_ptr< boost::asio::io_service > io_service)
: UdpSocket(replyfn, multicast_address, interface_address, io_service)
, txt_keys(txt_keys)
, service(service)
, service_dn(service_dn)
, protocol(protocol)
{
assert(!service.empty() && replyfn);
create_request();
}
LookupSocket(Bonjour::TxtKeys txt_keys
, std::string service
, std::string service_dn
, std::string protocol
, Bonjour::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, std::shared_ptr< boost::asio::io_service > io_service)
: UdpSocket(replyfn, multicast_address, io_service)
, txt_keys(txt_keys)
, service(service)
, service_dn(service_dn)
, protocol(protocol)
{
assert(!service.empty() && replyfn);
create_request();
}
const Bonjour::TxtKeys get_txt_keys() const { return txt_keys; }
const std::string get_service() const { return service; }
const std::string get_service_dn() const { return service_dn; }
protected:
SharedSession create_session() const override;
void create_request()
{
requests.clear();
// create PTR request
if (auto rqst = BonjourRequest::make_PTR(service, protocol); rqst)
requests.push_back(std::move(rqst.get()));
}
boost::optional<BonjourRequest> request;
Bonjour::TxtKeys txt_keys;
std::string service;
std::string service_dn;
std::string protocol;
};
class ResolveSocket : public UdpSocket
{
public:
ResolveSocket(const std::string& hostname
, Bonjour::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, const boost::asio::ip::address& interface_address
, std::shared_ptr< boost::asio::io_service > io_service)
: UdpSocket(replyfn, multicast_address, interface_address, io_service)
, hostname(hostname)
{
assert(!hostname.empty() && replyfn);
create_requests();
}
ResolveSocket(const std::string& hostname
, Bonjour::ReplyFn replyfn
, const boost::asio::ip::address& multicast_address
, std::shared_ptr< boost::asio::io_service > io_service)
: UdpSocket(replyfn, multicast_address, io_service)
, hostname(hostname)
{
assert(!hostname.empty() && replyfn);
create_requests();
}
std::string get_hostname() const { return hostname; }
protected:
SharedSession create_session() const override;
void create_requests()
{
requests.clear();
// BonjourRequest::make_A / AAAA is now implemented to add .local correctly after the hostname.
// If that is unsufficient, we need to change make_A / AAAA and pass full hostname.
std::string trimmed_hostname = hostname;
if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos)
trimmed_hostname = trimmed_hostname.substr(0, dot_pos);
if (auto rqst = BonjourRequest::make_A(trimmed_hostname); rqst)
requests.push_back(std::move(rqst.get()));
trimmed_hostname = hostname;
if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos)
trimmed_hostname = trimmed_hostname.substr(0, dot_pos);
if (auto rqst = BonjourRequest::make_AAAA(trimmed_hostname); rqst)
requests.push_back(std::move(rqst.get()));
}
std::string hostname;
};
} }

View file

@ -33,6 +33,38 @@ namespace Slic3r {
namespace { namespace {
#ifdef WIN32 #ifdef WIN32
std::string get_host_from_url(const std::string& url_in)
{
std::string url = url_in;
// add http:// if there is no scheme
size_t double_slash = url.find("//");
if (double_slash == std::string::npos)
url = "http://" + url;
std::string out = url;
CURLU* hurl = curl_url();
if (hurl) {
// Parse the input URL.
CURLUcode rc = curl_url_set(hurl, CURLUPART_URL, url.c_str(), 0);
if (rc == CURLUE_OK) {
// Replace the address.
char* host;
rc = curl_url_get(hurl, CURLUPART_HOST, &host, 0);
if (rc == CURLUE_OK) {
out = host;
curl_free(host);
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to get host form URL " << url;
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to parse URL " << url;
curl_url_cleanup(hurl);
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to allocate curl_url";
return out;
}
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail. // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
std::string substitute_host(const std::string& orig_addr, std::string sub_addr) std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
{ {
@ -96,38 +128,6 @@ std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
return out; return out;
#endif #endif
} }
std::string get_host_from_url(const std::string& url_in)
{
std::string url = url_in;
// add http:// if there is no scheme
size_t double_slash = url.find("//");
if (double_slash == std::string::npos)
url = "http://" + url;
std::string out = url;
CURLU* hurl = curl_url();
if (hurl) {
// Parse the input URL.
CURLUcode rc = curl_url_set(hurl, CURLUPART_URL, url.c_str(), 0);
if (rc == CURLUE_OK) {
// Replace the address.
char* host;
rc = curl_url_get(hurl, CURLUPART_HOST, &host, 0);
if (rc == CURLUE_OK) {
out = host;
curl_free(host);
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to get host form URL " << url;
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to parse URL " << url;
curl_url_cleanup(hurl);
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to allocate curl_url";
return out;
}
#endif // WIN32 #endif // WIN32
std::string escape_string(const std::string& unescaped) std::string escape_string(const std::string& unescaped)
{ {
@ -179,7 +179,15 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
std::string host = get_host_from_url(m_host);
auto http = Http::get(url);//std::move(url)); auto http = Http::get(url);//std::move(url));
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
// Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
http.header("Host", host);
set_auth(http); set_auth(http);
http http
.on_error([&](std::string body, std::string error, unsigned status) { .on_error([&](std::string body, std::string error, unsigned status) {
@ -203,7 +211,7 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const
const auto text = ptree.get_optional<std::string>("text"); const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text); res = validate_version_text(text);
if (!res) { if (!res) {
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : name));
} }
} }
catch (const std::exception&) { catch (const std::exception&) {
@ -228,7 +236,7 @@ bool OctoPrint::test(wxString& msg) const
auto url = make_url("api/version"); auto url = make_url("api/version");
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
// Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself.
auto http = Http::get(std::move(url)); auto http = Http::get(std::move(url));
set_auth(http); set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) { http.on_error([&](std::string body, std::string error, unsigned status) {
@ -252,7 +260,7 @@ bool OctoPrint::test(wxString& msg) const
const auto text = ptree.get_optional<std::string>("text"); const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text); res = validate_version_text(text);
if (! res) { if (! res) {
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : name));
} }
} }
catch (const std::exception &) { catch (const std::exception &) {
@ -273,7 +281,6 @@ bool OctoPrint::test(wxString& msg) const
return res; return res;
} }
wxString OctoPrint::get_test_ok_msg () const wxString OctoPrint::get_test_ok_msg () const
{ {
return _(L("Connection to OctoPrint works correctly.")); return _(L("Connection to OctoPrint works correctly."));
@ -281,10 +288,10 @@ wxString OctoPrint::get_test_ok_msg () const
wxString OctoPrint::get_test_failed_msg (wxString &msg) const wxString OctoPrint::get_test_failed_msg (wxString &msg) const
{ {
return GUI::from_u8((boost::format("%s: %s\n\n%s") return GUI::format_wxstr("%s: %s\n\n%s"
% _utf8(L("Could not connect to OctoPrint")) , _L("Could not connect to OctoPrint")
% std::string(msg.ToUTF8()) , msg
% _utf8(L("Note: OctoPrint version at least 1.1.0 is required."))).str()); , _L("Note: OctoPrint version at least 1.1.0 is required."));
} }
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
@ -300,7 +307,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
boost::asio::ip::address host_ip = boost::asio::ip::make_address(host, ec); boost::asio::ip::address host_ip = boost::asio::ip::make_address(host, ec);
if (!ec) { if (!ec) {
resolved_addr.push_back(host_ip); resolved_addr.push_back(host_ip);
} else if ( GUI::get_app_config()->get("allow_ip_resolve") == "1" && boost::algorithm::ends_with(host, ".local")){ } else if ( GUI::get_app_config()->get_bool("allow_ip_resolve") && boost::algorithm::ends_with(host, ".local")){
Bonjour("octoprint") Bonjour("octoprint")
.set_hostname(host) .set_hostname(host)
.set_retries(5) // number of rounds of queries send .set_retries(5) // number of rounds of queries send
@ -379,7 +386,14 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
% upload_parent_path.string() % upload_parent_path.string()
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false"); % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
std::string host = get_host_from_url(m_host);
auto http = Http::post(url);//std::move(url)); auto http = Http::post(url);//std::move(url));
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
http.header("Host", host);
set_auth(http); set_auth(http);
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false") http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
@ -397,7 +411,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
prorgess_fn(std::move(progress), cancel); prorgess_fn(std::move(progress), cancel);
if (cancel) { if (cancel) {
// Upload was canceled // Upload was canceled
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled"; BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
result = false; result = false;
} }
}) })
@ -428,7 +442,7 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
#ifdef WIN32 #ifdef WIN32
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail. // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || GUI::get_app_config()->get("allow_ip_resolve") != "1") if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || !GUI::get_app_config()->get_bool("allow_ip_resolve"))
#endif // _WIN32 #endif // _WIN32
{ {
// If https is entered we assume signed ceritificate is being used // If https is entered we assume signed ceritificate is being used
@ -458,6 +472,16 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false"); % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
auto http = Http::post(std::move(url)); auto http = Http::post(std::move(url));
#ifdef WIN32
// "Host" header is necessary here. In the workaround above (two mDNS..) we have got IP address from test connection and subsituted it into "url" variable.
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
// Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
std::string host = get_host_from_url(m_host);
http.header("Host", host);
#endif // _WIN32
set_auth(http); set_auth(http);
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false") http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
@ -474,7 +498,7 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
prorgess_fn(std::move(progress), cancel); prorgess_fn(std::move(progress), cancel);
if (cancel) { if (cancel) {
// Upload was canceled // Upload was canceled
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled"; BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
res = false; res = false;
} }
}) })
@ -513,11 +537,8 @@ std::string OctoPrint::make_url(const std::string &path) const
} }
} }
SL1Host::SL1Host(DynamicPrintConfig *config) : SL1Host::SL1Host(DynamicPrintConfig *config)
OctoPrint(config), : PrusaLink(config)
m_authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
m_username(config->opt_string("printhost_user")),
m_password(config->opt_string("printhost_password"))
{ {
} }
@ -531,9 +552,7 @@ wxString SL1Host::get_test_ok_msg () const
wxString SL1Host::get_test_failed_msg (wxString &msg) const wxString SL1Host::get_test_failed_msg (wxString &msg) const
{ {
return GUI::from_u8((boost::format("%s: %s") return GUI::format_wxstr("%s: %s", _L("Could not connect to Prusa SLA"), msg);
% _utf8(L("Could not connect to Prusa SLA"))
% std::string(msg.ToUTF8())).str());
} }
bool SL1Host::validate_version_text(const boost::optional<std::string> &version_text) const bool SL1Host::validate_version_text(const boost::optional<std::string> &version_text) const
@ -541,26 +560,10 @@ bool SL1Host::validate_version_text(const boost::optional<std::string> &version_
return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false; return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false;
} }
void SL1Host::set_auth(Http &http) const
{
switch (m_authorization_type) {
case atKeyPassword:
http.header("X-Api-Key", get_apikey());
break;
case atUserPassword:
http.auth_digest(m_username, m_password);
break;
}
if (! get_cafile().empty()) {
http.ca_file(get_cafile());
}
}
// PrusaLink // PrusaLink
PrusaLink::PrusaLink(DynamicPrintConfig* config, bool show_after_message) : PrusaLink::PrusaLink(DynamicPrintConfig* config, bool show_after_message) :
OctoPrint(config), OctoPrint(config),
m_authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value), m_authorization_type(config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value),
m_username(config->opt_string("printhost_user")), m_username(config->opt_string("printhost_user")),
m_password(config->opt_string("printhost_password")), m_password(config->opt_string("printhost_password")),
m_show_after_message(show_after_message) m_show_after_message(show_after_message)
@ -576,9 +579,7 @@ wxString PrusaLink::get_test_ok_msg() const
wxString PrusaLink::get_test_failed_msg(wxString& msg) const wxString PrusaLink::get_test_failed_msg(wxString& msg) const
{ {
return GUI::from_u8((boost::format("%s: %s") return GUI::format_wxstr("%s: %s", _L("Could not connect to PrusaLink"), msg);
% _utf8(L("Could not connect to PrusaLink"))
% std::string(msg.ToUTF8())).str());
} }
bool PrusaLink::validate_version_text(const boost::optional<std::string>& version_text) const bool PrusaLink::validate_version_text(const boost::optional<std::string>& version_text) const
@ -664,7 +665,7 @@ bool PrusaLink::test(wxString& msg) const
const auto text = ptree.get_optional<std::string>("text"); const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text); res = validate_version_text(text);
if (!res) { if (!res) {
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
} }
} }
catch (const std::exception&) { catch (const std::exception&) {
@ -685,7 +686,7 @@ bool PrusaLink::test(wxString& msg) const
return res; return res;
} }
bool PrusaLink::get_storage(wxArrayString& output) const bool PrusaLink::get_storage(wxArrayString& storage_path, wxArrayString& storage_name) const
{ {
const char* name = get_name(); const char* name = get_name();
@ -694,16 +695,21 @@ bool PrusaLink::get_storage(wxArrayString& output) const
wxString error_msg; wxString error_msg;
struct StorageInfo { struct StorageInfo {
wxString path;
wxString name; wxString name;
bool read_only; bool read_only = false;
long long free_space; long long free_space = -1;
}; };
std::vector<StorageInfo> storage; std::vector<StorageInfo> storage;
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get storage at: %2%") % name % url; BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get storage at: %2%") % name % url;
wxString wlang = GUI::wxGetApp().current_language_code();
std::string lang = GUI::format(wlang.SubString(0, 1));
auto http = Http::get(std::move(url)); auto http = Http::get(std::move(url));
set_auth(http); set_auth(http);
http.header("Accept-Language", lang);
http.on_error([&](std::string body, std::string error, unsigned status) { http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting storage: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting storage: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
error_msg = L"\n\n" + boost::nowide::widen(error); error_msg = L"\n\n" + boost::nowide::widen(error);
@ -716,7 +722,7 @@ bool PrusaLink::get_storage(wxArrayString& output) const
res = true; res = true;
}) })
.on_complete([&, this](std::string body, unsigned) { .on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got storage: %2%") % name % body; BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got storage: %2%") % name % body;
try try
{ {
@ -731,14 +737,19 @@ bool PrusaLink::get_storage(wxArrayString& output) const
} }
// each storage has own subtree of storage_list // each storage has own subtree of storage_list
for (const auto& section : ptree.front().second) { for (const auto& section : ptree.front().second) {
const auto name = section.second.get_optional<std::string>("name");
const auto path = section.second.get_optional<std::string>("path"); const auto path = section.second.get_optional<std::string>("path");
const auto space = section.second.get_optional<std::string>("free_space"); const auto space = section.second.get_optional<std::string>("free_space");
const auto read_only = section.second.get_optional<bool>("read_only"); const auto read_only = section.second.get_optional<bool>("read_only");
const auto ro = section.second.get_optional<bool>("ro"); // In PrusaLink 0.7.0RC2 "read_only" value is stored under "ro".
const auto available = section.second.get_optional<bool>("available"); const auto available = section.second.get_optional<bool>("available");
if (path && (!available || *available)) { if (path && (!available || *available)) {
StorageInfo si; StorageInfo si;
si.name = boost::nowide::widen(*path); si.path = boost::nowide::widen(*path);
si.read_only = read_only ? *read_only : false; // If read_only is missing, assume it is NOT read only. si.name = name ? boost::nowide::widen(*name) : wxString();
// If read_only is missing, assume it is NOT read only.
// si.read_only = read_only ? *read_only : false; // version without "ro"
si.read_only = (read_only ? *read_only : (ro ? *ro : false));
si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space. si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space.
storage.emplace_back(std::move(si)); storage.emplace_back(std::move(si));
} }
@ -756,19 +767,25 @@ bool PrusaLink::get_storage(wxArrayString& output) const
.perform_sync(); .perform_sync();
for (const auto& si : storage) { for (const auto& si : storage) {
if (!si.read_only && si.free_space > 0) if (!si.read_only && si.free_space > 0) {
output.push_back(si.name); storage_path.push_back(si.path);
storage_name.push_back(si.name);
}
} }
if (res && output.empty()) if (res && storage_path.empty()) {
{
if (!storage.empty()) { // otherwise error_msg is already filled if (!storage.empty()) { // otherwise error_msg is already filled
error_msg = L"\n\n" + _L("Storages found:") + L" \n"; error_msg = L"\n\n" + _L("Storages found") + L": \n";
for (const auto& si : storage) { for (const auto& si : storage) {
error_msg += si.name + L" : " + (si.read_only ? _L("read only") : _L("no free space")) + L"\n"; error_msg += GUI::format_wxstr(si.read_only ?
// TRN %1% = storage path
_L("%1% : read only") :
// TRN %1% = storage path
_L("%1% : no free space"), si.path) + L"\n";
} }
} }
std::string message = GUI::format(_L("Upload has failed. There is no suitable storage found at %1%.%2%"), m_host, error_msg); // TRN %1% = host
std::string message = GUI::format(_L("Upload has failed. There is no suitable storage found at %1%."), m_host) + GUI::into_u8(error_msg);
BOOST_LOG_TRIVIAL(error) << message; BOOST_LOG_TRIVIAL(error) << message;
throw Slic3r::IOError(message); throw Slic3r::IOError(message);
} }
@ -787,7 +804,7 @@ bool PrusaLink::test_with_method_check(wxString& msg, bool& use_put) const
auto url = make_url("api/version"); auto url = make_url("api/version");
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
// Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself.
auto http = Http::get(std::move(url)); auto http = Http::get(std::move(url));
set_auth(http); set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) { http.on_error([&](std::string body, std::string error, unsigned status) {
@ -811,7 +828,7 @@ bool PrusaLink::test_with_method_check(wxString& msg, bool& use_put) const
const auto text = ptree.get_optional<std::string>("text"); const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text); res = validate_version_text(text);
if (!res) { if (!res) {
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
use_put = false; use_put = false;
return; return;
} }
@ -860,7 +877,15 @@ bool PrusaLink::test_with_resolved_ip_and_method_check(wxString& msg, bool& use_
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
std::string host = get_host_from_url(m_host);
auto http = Http::get(url);//std::move(url)); auto http = Http::get(url);//std::move(url));
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
// Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
http.header("Host", host);
set_auth(http); set_auth(http);
http http
.on_error([&](std::string body, std::string error, unsigned status) { .on_error([&](std::string body, std::string error, unsigned status) {
@ -884,7 +909,7 @@ bool PrusaLink::test_with_resolved_ip_and_method_check(wxString& msg, bool& use_
const auto text = ptree.get_optional<std::string>("text"); const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text); res = validate_version_text(text);
if (!res) { if (!res) {
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
use_put = false; use_put = false;
return; return;
} }
@ -968,12 +993,11 @@ bool PrusaLink::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
} }
std::string url; std::string url;
bool res = true;
std::string storage_path = (use_put ? "api/v1/files" : "api/files"); std::string storage_path = (use_put ? "api/v1/files" : "api/files");
storage_path += (upload_data.storage.empty() ? "/local" : upload_data.storage); storage_path += (upload_data.storage.empty() ? "/local" : upload_data.storage);
#ifdef WIN32 #ifdef WIN32
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail. // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || GUI::get_app_config()->get("allow_ip_resolve") != "1") if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || !GUI::get_app_config()->get_bool("allow_ip_resolve"))
#endif // _WIN32 #endif // _WIN32
{ {
// If https is entered we assume signed ceritificate is being used // If https is entered we assume signed ceritificate is being used
@ -1015,13 +1039,20 @@ bool PrusaLink::put_inner(PrintHostUpload upload_data, std::string url, const st
bool res = true; bool res = true;
// Percent escape all filenames in on path and add it to the url. This is different from POST. // Percent escape all filenames in on path and add it to the url. This is different from POST.
url += "/" + escape_path_by_element(upload_data.upload_path); url += "/" + escape_path_by_element(upload_data.upload_path);
Http http = Http::put(std::move(url)); Http http = Http::put(std::move(url));
#ifdef WIN32
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
std::string host = get_host_from_url(m_host);
http.header("Host", host);
#endif // _WIN32
set_auth(http); set_auth(http);
// This is ugly, but works. There was an error at PrusaLink side that accepts any string at Print-After-Upload as true, thus False was also triggering print after upload. // This is ugly, but works. There was an error at PrusaLink side that accepts any string at Print-After-Upload as true, thus False was also triggering print after upload.
if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) if (upload_data.post_action == PrintHostPostUploadAction::StartPrint)
http.header("Print-After-Upload", "True"); http.header("Print-After-Upload", "?1");
http.set_put_body(upload_data.source_path) http.set_put_body(upload_data.source_path)
.header("Content-Type", "text/x.gcode") .header("Content-Type", "text/x.gcode")
.header("Overwrite", "?1") .header("Overwrite", "?1")
@ -1057,7 +1088,17 @@ bool PrusaLink::post_inner(PrintHostUpload upload_data, std::string url, const s
bool res = true; bool res = true;
const auto upload_filename = upload_data.upload_path.filename(); const auto upload_filename = upload_data.upload_path.filename();
const auto upload_parent_path = upload_data.upload_path.parent_path(); const auto upload_parent_path = upload_data.upload_path.parent_path();
Http http = Http::post(std::move(url)); Http http = Http::post(std::move(url));
#ifdef WIN32
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
std::string host = get_host_from_url(m_host);
http.header("Host", host);
#endif // _WIN32
set_auth(http); set_auth(http);
set_http_post_header_args(http, upload_data.post_action); set_http_post_header_args(http, upload_data.post_action);
http.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? http.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
@ -1133,9 +1174,6 @@ wxString PrusaConnect::get_test_ok_msg() const
wxString PrusaConnect::get_test_failed_msg(wxString& msg) const wxString PrusaConnect::get_test_failed_msg(wxString& msg) const
{ {
return GUI::from_u8((boost::format("%s: %s") return GUI::format_wxstr("%s: %s", _L("Could not connect to Prusa Connect"), msg);
% _utf8(L("Could not connect to PrusaConnect"))
% std::string(msg.ToUTF8())).str());
} }
} }

View file

@ -55,30 +55,6 @@ private:
#endif #endif
}; };
class SL1Host: public OctoPrint
{
public:
SL1Host(DynamicPrintConfig *config);
~SL1Host() override = default;
const char* get_name() const override;
wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString &msg) const override;
PrintHostPostUploadActions get_post_upload_actions() const override { return {}; }
protected:
bool validate_version_text(const boost::optional<std::string> &version_text) const override;
private:
void set_auth(Http &http) const override;
// Host authorization type.
AuthorizationType m_authorization_type;
// username and password for HTTP Digest Authentization (RFC RFC2617)
std::string m_username;
std::string m_password;
};
class PrusaLink : public OctoPrint class PrusaLink : public OctoPrint
{ {
@ -94,7 +70,7 @@ public:
virtual PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; } virtual PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
// gets possible storage to be uploaded to. This allows different printer to have different storage. F.e. local vs sdcard vs usb. // gets possible storage to be uploaded to. This allows different printer to have different storage. F.e. local vs sdcard vs usb.
bool get_storage(wxArrayString& /* storage */) const override; bool get_storage(wxArrayString& storage_path, wxArrayString& storage_name) const override;
protected: protected:
bool test(wxString& curl_msg) const override; bool test(wxString& curl_msg) const override;
bool validate_version_text(const boost::optional<std::string>& version_text) const override; bool validate_version_text(const boost::optional<std::string>& version_text) const override;
@ -106,6 +82,12 @@ protected:
bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const override; bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const override;
#endif #endif
// Host authorization type.
AuthorizationType m_authorization_type;
// username and password for HTTP Digest Authentization (RFC RFC2617)
std::string m_username;
std::string m_password;
private: private:
bool test_with_method_check(wxString& curl_msg, bool& use_put) const; bool test_with_method_check(wxString& curl_msg, bool& use_put) const;
bool put_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const; bool put_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const;
@ -113,11 +95,7 @@ private:
#ifdef WIN32 #ifdef WIN32
bool test_with_resolved_ip_and_method_check(wxString& curl_msg, bool& use_put) const; bool test_with_resolved_ip_and_method_check(wxString& curl_msg, bool& use_put) const;
#endif #endif
// Host authorization type.
AuthorizationType m_authorization_type;
// username and password for HTTP Digest Authentization (RFC RFC2617)
std::string m_username;
std::string m_password;
bool m_show_after_message; bool m_show_after_message;
#if 0 #if 0
@ -139,6 +117,22 @@ protected:
void set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const override; void set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const override;
}; };
class SL1Host : public PrusaLink
{
public:
SL1Host(DynamicPrintConfig* config);
~SL1Host() override = default;
const char* get_name() const override;
wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString& msg) const override;
PrintHostPostUploadActions get_post_upload_actions() const override { return {}; }
protected:
bool validate_version_text(const boost::optional<std::string>& version_text) const override;
};
} }
#endif #endif

View file

@ -66,7 +66,7 @@ public:
virtual bool get_printers(wxArrayString & /* printers */) const { return false; } virtual bool get_printers(wxArrayString & /* printers */) const { return false; }
// Support for PrusaLink uploading to different storage. Not supported by other print hosts. // Support for PrusaLink uploading to different storage. Not supported by other print hosts.
// Returns false if not supported or fail. // Returns false if not supported or fail.
virtual bool get_storage(wxArrayString& /* storage */) const { return false; } virtual bool get_storage(wxArrayString& /*storage_path*/, wxArrayString& /*storage_name*/) const { return false; }
static PrintHost* get_print_host(DynamicPrintConfig *config); static PrintHost* get_print_host(DynamicPrintConfig *config);