Merge branch 'master' of https://github.com/prusa3d/Slic3r into gcode_preview
This commit is contained in:
commit
6dd241921f
@ -42,6 +42,21 @@ message("PATH: $ENV{PATH}")
|
||||
message("PERL5LIB: $ENV{PERL5LIB}")
|
||||
find_package(Perl REQUIRED)
|
||||
|
||||
# CMAKE_PREFIX_PATH is used to point CMake to the remaining dependencies (Boost, TBB, ...)
|
||||
# We pick it from environment if it is not defined in another way
|
||||
if(NOT DEFINED CMAKE_PREFIX_PATH)
|
||||
if(DEFINED ENV{CMAKE_PREFIX_PATH})
|
||||
set(CMAKE_PREFIX_PATH "$ENV{CMAKE_PREFIX_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
# By default the startup project in MSVC is the 'ALL_BUILD' cmake-created project,
|
||||
# but we want 'slic3r' as the startup one because debugging run command is associated with it.
|
||||
# (Unfortunatelly it cannot be associated with ALL_BUILD using CMake.)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT slic3r)
|
||||
endif ()
|
||||
|
||||
add_subdirectory(xs)
|
||||
|
||||
enable_testing ()
|
||||
|
59
cmake/modules/FindCURL.cmake
Normal file
59
cmake/modules/FindCURL.cmake
Normal file
@ -0,0 +1,59 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#.rst:
|
||||
# FindCURL
|
||||
# --------
|
||||
#
|
||||
# Find curl
|
||||
#
|
||||
# Find the native CURL headers and libraries.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# CURL_INCLUDE_DIRS - where to find curl/curl.h, etc.
|
||||
# CURL_LIBRARIES - List of libraries when using curl.
|
||||
# CURL_FOUND - True if curl found.
|
||||
# CURL_VERSION_STRING - the version of curl found (since CMake 2.8.8)
|
||||
|
||||
# Look for the header file.
|
||||
find_path(CURL_INCLUDE_DIR NAMES curl/curl.h)
|
||||
mark_as_advanced(CURL_INCLUDE_DIR)
|
||||
|
||||
# Look for the library (sorted from most current/relevant entry to least).
|
||||
find_library(CURL_LIBRARY NAMES
|
||||
curl
|
||||
# Windows MSVC Makefile:
|
||||
libcurl_a
|
||||
# Windows MSVC prebuilts:
|
||||
curllib
|
||||
libcurl_imp
|
||||
curllib_static
|
||||
# Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip):
|
||||
libcurl
|
||||
)
|
||||
mark_as_advanced(CURL_LIBRARY)
|
||||
|
||||
if(CURL_INCLUDE_DIR)
|
||||
foreach(_curl_version_header curlver.h curl.h)
|
||||
if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}")
|
||||
file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"")
|
||||
|
||||
string(REGEX REPLACE "^#define[\t ]+LIBCURL_VERSION[\t ]+\"([^\"]*)\".*" "\\1" CURL_VERSION_STRING "${curl_version_str}")
|
||||
unset(curl_version_str)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(CURL
|
||||
REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR
|
||||
VERSION_VAR CURL_VERSION_STRING)
|
||||
|
||||
if(CURL_FOUND)
|
||||
set(CURL_LIBRARIES ${CURL_LIBRARY})
|
||||
set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
|
||||
|
||||
message(STATUS " Curl libraries: = ${CURL_LIBRARIES}")
|
||||
message(STATUS " Curl include dirs: = ${CURL_INCLUDE_DIRS}")
|
||||
endif()
|
9
cmake/msvc/slic3r.wperl64d.props
Normal file
9
cmake/msvc/slic3r.wperl64d.props
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<LocalDebuggerCommand>C:\wperl64d\bin\perl.exe</LocalDebuggerCommand>
|
||||
<LocalDebuggerCommandArguments>slic3r.pl</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
<LocalDebuggerWorkingDirectory>..\..</LocalDebuggerWorkingDirectory>
|
||||
</PropertyGroup>
|
||||
</Project>
|
11
cmake/msvc/xs.wperl64d.props
Normal file
11
cmake/msvc/xs.wperl64d.props
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets">
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<ExecutablePath>$(VC_ExecutablePath_x64);$(WindowsSDK_ExecutablePath);$(VS_ExecutablePath);$(MSBuild_ExecutablePath);$(FxCopDir);$(PATH);c:\wperl64d\bin\;</ExecutablePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup />
|
||||
<ItemGroup />
|
||||
</Project>
|
2
doc/How to build - UNIX.md
Normal file
2
doc/How to build - UNIX.md
Normal file
@ -0,0 +1,2 @@
|
||||
# Building Slic3r PE on Linux/UNIX
|
||||
|
86
doc/How to build - Windows.md
Normal file
86
doc/How to build - Windows.md
Normal file
@ -0,0 +1,86 @@
|
||||
# Building Slic3r PE on Microsoft Windows
|
||||
|
||||
The currently supported way of building Slic3r PE on Windows is with MS Visual Studio 2013
|
||||
using our Perl binary distribution (compiled from official Perl sources).
|
||||
You can use the free [Visual Studio 2013 Community Edition](https://www.visualstudio.com/vs/older-downloads/).
|
||||
|
||||
Other setups (such as mingw + Strawberry Perl) _may_ work, but we cannot guarantee this will work
|
||||
and cannot provide guidance.
|
||||
|
||||
|
||||
### Geting the dependencies
|
||||
|
||||
First, download and upnack our Perl + wxWidgets binary distribution:
|
||||
|
||||
- 32 bit, release mode: [wperl32-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl32-5.24.0-2018-03-02.7z)
|
||||
- 64 bit, release mode: [wperl64-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl64-5.24.0-2018-03-02.7z)
|
||||
- 64 bit, release mode + debug symbols: [wperl64d-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl64d-5.24.0-2018-03-02.7z)
|
||||
|
||||
It is recommended to unpack this package into `C:\`.
|
||||
|
||||
Apart from wxWidgets and Perl, you will also need additional dependencies:
|
||||
|
||||
- Boost
|
||||
- Intel TBB
|
||||
- libcurl
|
||||
|
||||
We have prepared a binary package of the listed libraries:
|
||||
|
||||
- 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-32.7z)
|
||||
- 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-64.7z)
|
||||
|
||||
It is recommended you unpack this package into `C:\local\` as the environment
|
||||
setup script expects it there.
|
||||
|
||||
Alternatively you can also compile the additional dependencies yourself.
|
||||
There is a [powershell script](./deps-build/windows/slic3r-makedeps.ps1) which automates this process.
|
||||
|
||||
### Building Slic3r PE
|
||||
|
||||
Once the dependencies are set up in their respective locations,
|
||||
go to the `wperl*` directory extracted earlier and launch the `cmdline.lnk` file
|
||||
which opens a command line prompt with appropriate environment variables set up.
|
||||
|
||||
In this command line, `cd` into the directory with Slic3r sources
|
||||
and use these commands to build the Slic3r from the command line:
|
||||
|
||||
perl Build.PL
|
||||
perl Build.PL --gui
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release
|
||||
nmake
|
||||
ctest --verbose # TODO: ???
|
||||
cd ..
|
||||
perl slic3r.pl
|
||||
|
||||
The above commands use `nmake` Makefiles.
|
||||
You may also build Slic3r PE with other build tools:
|
||||
|
||||
|
||||
### Building with Visual Studio
|
||||
|
||||
To build, lanuch and/or debug Slic3r PE with Visual Studio (64 bits), replace the `cmake` command with:
|
||||
|
||||
cmake .. -G "Visual Studio 12 Win64" -DCMAKE_CONFIGURATION_TYPES=Release;RelWithDebInfo || exit /b
|
||||
|
||||
For the 32-bit variant, use:
|
||||
|
||||
cmake .. -G "Visual Studio 12" -DCMAKE_CONFIGURATION_TYPES=Release;RelWithDebInfo || exit /b
|
||||
|
||||
After `cmake` has finished, go to the `Slic3r\build` directory and open the `Slic3r.sln` solution file.
|
||||
This should open Visual Studio and load all the Slic3r solution containing all the projects.
|
||||
Make sure you use Visual Studio 2013 to open the solution.
|
||||
|
||||
You can then use the usual Visual Studio controls to build Slic3r.
|
||||
If you want to run or debug Slic3r from within Visual Studio, make sure the `slic3r` project is activated.
|
||||
There are multiple projects in the Slic3r solution, but only the `slic3r` project is configured with the right
|
||||
commands to run Slic3r.
|
||||
|
||||
|
||||
### Building with ninja
|
||||
|
||||
To use [Ninja](TODO), replace the `cmake` and `nmake` commands with:
|
||||
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||
ninja
|
132
doc/deps-build/unix-static/Makefile
Normal file
132
doc/deps-build/unix-static/Makefile
Normal file
@ -0,0 +1,132 @@
|
||||
|
||||
#
|
||||
# This makefile downloads, configures and builds Slic3r PE dependencies for Unix.
|
||||
# (That is, all dependencies except perl + wxWidgets.)
|
||||
# The libraries are installed in DESTDIR, which you can customize like so:
|
||||
#
|
||||
# DESTDIR=foo/bar make
|
||||
#
|
||||
# The default DESTDIR is ~/slic3r-destdir
|
||||
# If the DESTDIR doesn't exits, the makefile tries to create it
|
||||
#
|
||||
# To pass the DESTDIR path along to cmake, set the use CMAKE_PREFIX_PATH variable
|
||||
# and set it to $DESTDIR/usr/local
|
||||
#
|
||||
# You can also customize the NPROC variable in the same way to configure the number
|
||||
# of cores the build process uses. By default this is set to what the `nproc` command says.
|
||||
#
|
||||
|
||||
|
||||
DESTDIR ?= $(HOME)/slic3r-destdir
|
||||
NPROC ?= $(shell nproc)
|
||||
|
||||
|
||||
BOOST = boost_1_66_0
|
||||
TBB_SHA = a0dc9bf76d0120f917b641ed095360448cabc85b
|
||||
TBB = tbb-$(TBB_SHA)
|
||||
OPENSSL = openssl-OpenSSL_1_1_0g
|
||||
CURL = curl-7.58.0
|
||||
|
||||
.PHONY: all destdir boost libcurl libopenssl libtbb
|
||||
|
||||
all: destdir boost libtbb libcurl
|
||||
@echo
|
||||
@echo "All done!"
|
||||
@echo
|
||||
|
||||
destdir:
|
||||
mkdir -p $(DESTDIR)
|
||||
|
||||
|
||||
|
||||
boost: $(BOOST).tar.gz
|
||||
tar -zxvf $(BOOST).tar.gz
|
||||
cd $(BOOST) && ./bootstrap.sh --with-libraries=system,filesystem,thread,log,locale,regex --prefix=$(DESTDIR)/usr/local
|
||||
cd $(BOOST) && ./b2 \
|
||||
-j $(NPROC) \
|
||||
link=static \
|
||||
variant=release \
|
||||
threading=multi \
|
||||
boost.locale.icu=off \
|
||||
cxxflags=-fPIC cflags=-fPIC \
|
||||
install
|
||||
|
||||
$(BOOST).tar.gz:
|
||||
curl -L -o $@ https://dl.bintray.com/boostorg/release/1.66.0/source/$@
|
||||
|
||||
|
||||
|
||||
libtbb: $(TBB).tar.gz
|
||||
tar -zxvf $(TBB).tar.gz
|
||||
mkdir -p $(TBB)/mybuild
|
||||
cd $(TBB)/mybuild && cmake .. -DTBB_BUILD_SHARED=OFF -DTBB_BUILD_TESTS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
$(MAKE) -C $(TBB)/mybuild -j$(NPROC)
|
||||
$(MAKE) -C $(TBB)/mybuild install DESTDIR=$(DESTDIR)
|
||||
|
||||
$(TBB).tar.gz:
|
||||
curl -L -o $@ https://github.com/wjakob/tbb/archive/$(TBB_SHA).tar.gz
|
||||
|
||||
|
||||
|
||||
libopenssl: $(OPENSSL).tar.gz
|
||||
tar -zxvf $(OPENSSL).tar.gz
|
||||
cd $(OPENSSL) && ./config --openssldir=/etc/ssl shared no-ssl3-method no-dynamic-engine '-Wa,--noexecstack'
|
||||
make -C $(OPENSSL) depend
|
||||
make -C $(OPENSSL) -j$(NPROC)
|
||||
make -C $(OPENSSL) install DESTDIR=$(DESTDIR)
|
||||
|
||||
$(OPENSSL).tar.gz:
|
||||
curl -L -o $@ 'https://github.com/openssl/openssl/archive/OpenSSL_1_1_0g.tar.gz'
|
||||
|
||||
|
||||
|
||||
libcurl: libopenssl $(CURL).tar.gz
|
||||
tar -zxvf $(CURL).tar.gz
|
||||
# XXX: disable shared?
|
||||
# Setting PKG_CONFIG_PATH should make libcurl find our previously built openssl
|
||||
cd $(CURL) && PKG_CONFIG_PATH=$(DESTDIR)/usr/local/lib/pkgconfig ./configure \
|
||||
--enable-static \
|
||||
--enable-shared \
|
||||
--with-pic \
|
||||
--enable-ipv6 \
|
||||
--enable-versioned-symbols \
|
||||
--enable-threaded-resolver \
|
||||
--with-random=/dev/urandom \
|
||||
--with-ca-bundle=/etc/ssl/certs/ca-certificates.crt \
|
||||
--disable-ldap \
|
||||
--disable-ldaps \
|
||||
--disable-manual \
|
||||
--disable-rtsp \
|
||||
--disable-dict \
|
||||
--disable-telnet \
|
||||
--disable-pop3 \
|
||||
--disable-imap \
|
||||
--disable-smb \
|
||||
--disable-smtp \
|
||||
--disable-gopher \
|
||||
--disable-crypto-auth \
|
||||
--without-gssapi \
|
||||
--without-libpsl \
|
||||
--without-libidn2 \
|
||||
--without-gnutls \
|
||||
--without-polarssl \
|
||||
--without-mbedtls \
|
||||
--without-cyassl \
|
||||
--without-nss \
|
||||
--without-axtls \
|
||||
--without-brotli \
|
||||
--without-libmetalink \
|
||||
--without-libssh \
|
||||
--without-libssh2 \
|
||||
--without-librtmp \
|
||||
--without-nghttp2 \
|
||||
--without-zsh-functions-dir
|
||||
$(MAKE) -C $(CURL) -j$(NPROC)
|
||||
$(MAKE) -C $(CURL) install DESTDIR=$(DESTDIR)
|
||||
|
||||
$(CURL).tar.gz:
|
||||
curl -L -o $@ https://curl.haxx.se/download/$@
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf $(BOOST) $(BOOST).tar.gz $(TBB) $(TBB).tar.gz $(OPENSSL) $(OPENSSL).tar.gz $(CURL) $(CURL).tar.gz
|
141
doc/deps-build/windows/slic3r-makedeps.ps1
Normal file
141
doc/deps-build/windows/slic3r-makedeps.ps1
Normal file
@ -0,0 +1,141 @@
|
||||
#!powershell
|
||||
#
|
||||
# This script downloads, configures and builds Slic3r PE dependencies for Unix.
|
||||
# (That is, all dependencies except perl + wxWidgets.)
|
||||
#
|
||||
# To use this script, launch the Visual Studio command line,
|
||||
# `cd` into the directory containing this script and use this command:
|
||||
#
|
||||
# powershell .\slic3r-makedeps.ps1
|
||||
#
|
||||
# The dependencies will be downloaded and unpacked into the current dir.
|
||||
# This script WILL NOT try to guess the build architecture (64 vs 32 bits),
|
||||
# it will by default build the 64-bit variant. To build the 32-bit variant, use:
|
||||
#
|
||||
# powershell .\slic3r-makedeps.ps1 -b32
|
||||
#
|
||||
# Built libraries are installed into $destdir,
|
||||
# which by default is C:\local\slic3r-destdir-$bits
|
||||
# You can customize the $destdir using:
|
||||
#
|
||||
# powershell .\slic3r-makedeps.ps1 -destdir C:\foo\bar
|
||||
#
|
||||
# To pass the $destdir path along to cmake, set the use CMAKE_PREFIX_PATH variable
|
||||
# and set it to $destdir\usr\local
|
||||
#
|
||||
# Script requirements: PowerShell 3.0, .NET 4.5
|
||||
#
|
||||
|
||||
|
||||
param(
|
||||
[switch]$b32 = $false,
|
||||
[string]$destdir = ""
|
||||
)
|
||||
|
||||
if ($destdir -eq "") {
|
||||
$destdir = "C:\local\slic3r-destdir-" + ('32', '64')[!$b32]
|
||||
}
|
||||
|
||||
$BOOST = 'boost_1_63_0'
|
||||
$CURL = 'curl-7.28.0'
|
||||
$TBB_SHA = 'a0dc9bf76d0120f917b641ed095360448cabc85b'
|
||||
$TBB = "tbb-$TBB_SHA"
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
# Set up various settings and utilities:
|
||||
[Environment]::CurrentDirectory = Get-Location
|
||||
$NPROC = (Get-WmiObject -class Win32_processor).NumberOfLogicalProcessors
|
||||
Add-Type -A System.IO.Compression.FileSystem
|
||||
# This fxies SSL/TLS errors, credit goes to Ansible; see their `win_get_url.ps1` file
|
||||
$security_protcols = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::SystemDefault
|
||||
if ([Net.SecurityProtocolType].GetMember('Tls11').Count -gt 0) {
|
||||
$security_protcols = $security_protcols -bor [Net.SecurityProtocolType]::Tls11
|
||||
}
|
||||
if ([Net.SecurityProtocolType].GetMember('Tls12').Count -gt 0) {
|
||||
$security_protcols = $security_protcols -bor [Net.SecurityProtocolType]::Tls12
|
||||
}
|
||||
[Net.ServicePointManager]::SecurityProtocol = $security_protcols
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
|
||||
|
||||
# Ensure DESTDIR exists:
|
||||
mkdir $destdir -ea 0
|
||||
mkdir "$destdir\usr\local" -ea 0
|
||||
|
||||
|
||||
# Download sources:
|
||||
echo 'Downloading sources ...'
|
||||
if (!(Test-Path "$BOOST.zip")) { $webclient.DownloadFile("https://dl.bintray.com/boostorg/release/1.63.0/source/$BOOST.zip", "$BOOST.zip") }
|
||||
if (!(Test-Path "$TBB.zip")) { $webclient.DownloadFile("https://github.com/wjakob/tbb/archive/$TBB_SHA.zip", "$TBB.zip") }
|
||||
if (!(Test-Path "$CURL.zip")) { $webclient.DownloadFile("https://curl.haxx.se/download/$CURL.zip", ".\$CURL.zip") }
|
||||
|
||||
|
||||
# Unpack sources:
|
||||
echo 'Unpacking ...'
|
||||
if (!(Test-Path $BOOST)) { [IO.Compression.ZipFile]::ExtractToDirectory("$BOOST.zip", '.') }
|
||||
if (!(Test-Path $TBB)) { [IO.Compression.ZipFile]::ExtractToDirectory("$TBB.zip", '.') }
|
||||
if (!(Test-Path $CURL)) { [IO.Compression.ZipFile]::ExtractToDirectory("$CURL.zip", '.') }
|
||||
|
||||
|
||||
# Build libraries:
|
||||
echo 'Building ...'
|
||||
|
||||
# Build boost
|
||||
pushd "$BOOST"
|
||||
.\bootstrap
|
||||
$adr_mode = ('32', '64')[!$b32]
|
||||
.\b2 `
|
||||
-j "$NPROC" `
|
||||
--with-system `
|
||||
--with-filesystem `
|
||||
--with-thread `
|
||||
--with-log `
|
||||
--with-locale `
|
||||
--with-regex `
|
||||
"--prefix=$destdir/usr/local" `
|
||||
"address-model=$adr_mode" `
|
||||
toolset=msvc-12.0 `
|
||||
link=static `
|
||||
variant=release `
|
||||
threading=multi `
|
||||
boost.locale.icu=off `
|
||||
install
|
||||
popd
|
||||
|
||||
# Build TBB
|
||||
pushd "$TBB"
|
||||
mkdir 'mybuild' -ea 0
|
||||
cd 'mybuild'
|
||||
$generator = ('Visual Studio 12', 'Visual Studio 12 Win64')[!$b32]
|
||||
cmake .. `
|
||||
-G "$generator" `
|
||||
-DCMAKE_CONFIGURATION_TYPES=Release `
|
||||
-DTBB_BUILD_SHARED=OFF `
|
||||
-DTBB_BUILD_TESTS=OFF "-DCMAKE_INSTALL_PREFIX:PATH=$destdir\usr\local"
|
||||
msbuild /P:Configuration=Release INSTALL.vcxproj
|
||||
popd
|
||||
|
||||
# Build libcurl:
|
||||
pushd "$CURL\winbuild"
|
||||
$machine = ("x86", "x64")[!$b32]
|
||||
nmake /f Makefile.vc mode=static VC=12 GEN_PDB=yes DEBUG=no "MACHINE=$machine"
|
||||
Copy-Item -R -Force ..\builds\libcurl-*-winssl\include\* "$destdir\usr\local\include\"
|
||||
Copy-Item -R -Force ..\builds\libcurl-*-winssl\lib\* "$destdir\usr\local\lib\"
|
||||
popd
|
||||
|
||||
|
||||
echo ""
|
||||
echo "All done!"
|
||||
echo ""
|
||||
|
||||
|
||||
}
|
||||
catch [Exception]
|
||||
{
|
||||
# This prints errors in a verbose manner
|
||||
echo $_.Exception|format-list -force
|
||||
}
|
@ -174,7 +174,6 @@ sub _init_tabpanel {
|
||||
EVT_COMMAND($self, -1, $BUTTON_BROWSE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $msg = $event->GetString;
|
||||
print "BUTTON_BROWSE_EVENT: ", $msg, "\n";
|
||||
|
||||
# look for devices
|
||||
my $entries;
|
||||
@ -197,7 +196,6 @@ sub _init_tabpanel {
|
||||
EVT_COMMAND($self, -1, $BUTTON_TEST_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $msg = $event->GetString;
|
||||
print "BUTTON_TEST_EVENT: ", $msg, "\n";
|
||||
|
||||
my $ua = LWP::UserAgent->new;
|
||||
$ua->timeout(10);
|
||||
@ -424,6 +422,7 @@ sub _init_menubar {
|
||||
# (Select application language from the list of installed languages)
|
||||
Slic3r::GUI::add_debug_menu($menubar, $self->{lang_ch_event});
|
||||
$menubar->Append($helpMenu, L("&Help"));
|
||||
# Add an optional debug menu. In production code, the add_debug_menu() call should do nothing.
|
||||
$self->SetMenuBar($menubar);
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ sub new {
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
$self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
|
||||
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
|
||||
serial_port serial_speed octoprint_host octoprint_apikey
|
||||
serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
|
||||
nozzle_diameter single_extruder_multi_material
|
||||
wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour
|
||||
)]);
|
||||
@ -1458,8 +1458,13 @@ sub on_export_completed {
|
||||
wxTheApp->notify($message);
|
||||
|
||||
$self->do_print if $do_print;
|
||||
|
||||
# Send $self->{send_gcode_file} to OctoPrint.
|
||||
$self->send_gcode if $send_gcode;
|
||||
if ($send_gcode) {
|
||||
my $op = Slic3r::OctoPrint->new($self->{config});
|
||||
$op->send_gcode($self->GetId(), $PROGRESS_BAR_EVENT, $ERROR_EVENT, $self->{send_gcode_file});
|
||||
}
|
||||
|
||||
$self->{print_file} = undef;
|
||||
$self->{send_gcode_file} = undef;
|
||||
$self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost));
|
||||
@ -1488,45 +1493,6 @@ sub do_print {
|
||||
my $filament_names = wxTheApp->{preset_bundle}->filament_presets;
|
||||
$filament_stats = { map { $filament_names->[$_] => $filament_stats->{$_} } keys %$filament_stats };
|
||||
$printer_panel->load_print_job($self->{print_file}, $filament_stats);
|
||||
|
||||
$self->GetFrame->select_tab(1);
|
||||
}
|
||||
|
||||
# Send $self->{send_gcode_file} to OctoPrint.
|
||||
#FIXME Currently this call blocks the UI. Make it asynchronous.
|
||||
sub send_gcode {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->statusbar->StartBusy;
|
||||
|
||||
my $ua = LWP::UserAgent->new;
|
||||
$ua->timeout(180);
|
||||
|
||||
my $res = $ua->post(
|
||||
"http://" . $self->{config}->octoprint_host . "/api/files/local",
|
||||
Content_Type => 'form-data',
|
||||
'X-Api-Key' => $self->{config}->octoprint_apikey,
|
||||
Content => [
|
||||
file => [
|
||||
# On Windows, the path has to be encoded in local code page for perl to be able to open it.
|
||||
Slic3r::encode_path($self->{send_gcode_file}),
|
||||
# Remove the UTF-8 flag from the perl string, so the LWP::UserAgent can insert
|
||||
# the UTF-8 encoded string into the request as a byte stream.
|
||||
Slic3r::path_to_filename_raw($self->{send_gcode_file})
|
||||
],
|
||||
print => $self->{send_gcode_file_print} ? 1 : 0,
|
||||
],
|
||||
);
|
||||
|
||||
$self->statusbar->StopBusy;
|
||||
|
||||
if ($res->is_success) {
|
||||
$self->statusbar->SetStatusText(L("G-code file successfully uploaded to the OctoPrint server"));
|
||||
} else {
|
||||
my $message = L("Error while uploading to the OctoPrint server: ") . $res->status_line;
|
||||
Slic3r::GUI::show_error($self, $message);
|
||||
$self->statusbar->SetStatusText($message);
|
||||
}
|
||||
}
|
||||
|
||||
sub export_stl {
|
||||
|
@ -199,6 +199,12 @@ add_library(libslic3r_gui STATIC
|
||||
${LIBDIR}/slic3r/GUI/2DBed.hpp
|
||||
${LIBDIR}/slic3r/GUI/wxExtensions.cpp
|
||||
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
|
||||
${LIBDIR}/slic3r/Utils/Http.cpp
|
||||
${LIBDIR}/slic3r/Utils/Http.hpp
|
||||
${LIBDIR}/slic3r/Utils/OctoPrint.cpp
|
||||
${LIBDIR}/slic3r/Utils/OctoPrint.hpp
|
||||
${LIBDIR}/slic3r/Utils/Bonjour.cpp
|
||||
${LIBDIR}/slic3r/Utils/Bonjour.hpp
|
||||
)
|
||||
|
||||
add_library(admesh STATIC
|
||||
@ -338,6 +344,7 @@ set(XS_XSP_FILES
|
||||
${XSP_DIR}/Surface.xsp
|
||||
${XSP_DIR}/SurfaceCollection.xsp
|
||||
${XSP_DIR}/TriangleMesh.xsp
|
||||
${XSP_DIR}/Utils_OctoPrint.xsp
|
||||
${XSP_DIR}/XS.xsp
|
||||
)
|
||||
foreach (file ${XS_XSP_FILES})
|
||||
@ -523,6 +530,27 @@ if (SLIC3R_PRUSACONTROL)
|
||||
target_link_libraries(XS ${wxWidgets_LIBRARIES})
|
||||
endif()
|
||||
|
||||
find_package(CURL REQUIRED)
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
target_link_libraries(XS ${CURL_LIBRARIES})
|
||||
|
||||
if (SLIC3R_STATIC)
|
||||
if (NOT APPLE)
|
||||
# libcurl is always linked dynamically to the system libcurl on OSX.
|
||||
# On other systems, libcurl is linked statically if SLIC3R_STATIC is set.
|
||||
add_definitions(-DCURL_STATICLIB)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# As of now, our build system produces a statically linked libcurl,
|
||||
# which links the OpenSSL library dynamically.
|
||||
find_package(OpenSSL REQUIRED)
|
||||
message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
||||
message("OpenSSL libraries: ${OPENSSL_LIBRARIES}")
|
||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||
target_link_libraries(XS ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
## OPTIONAL packages
|
||||
|
||||
# Find eigen3 or use bundled version
|
||||
@ -597,6 +625,17 @@ elseif (NOT MSVC)
|
||||
target_link_libraries(slic3r -lstdc++)
|
||||
endif ()
|
||||
|
||||
if (MSVC)
|
||||
# Here we associate some additional properties with the MSVC projects to enable compilation and debugging out of the box.
|
||||
# It seems a props file needs to be copied to the same dir as the proj file, otherwise MSVC doesn't load it up.
|
||||
# For copying, the configure_file() function seems to work much better than the file() function.
|
||||
configure_file("${PROJECT_SOURCE_DIR}/cmake/msvc/xs.wperl64d.props" ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
|
||||
set_target_properties(XS PROPERTIES VS_USER_PROPS "xs.wperl64d.props")
|
||||
configure_file("${PROJECT_SOURCE_DIR}/cmake/msvc/slic3r.wperl64d.props" ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
|
||||
set_target_properties(slic3r PROPERTIES VS_USER_PROPS "slic3r.wperl64d.props")
|
||||
endif ()
|
||||
|
||||
|
||||
# Installation
|
||||
install(TARGETS XS DESTINATION lib/slic3r-prusa3d/auto/Slic3r/XS)
|
||||
install(FILES lib/Slic3r/XS.pm DESTINATION lib/slic3r-prusa3d/Slic3r)
|
||||
install(TARGETS XS DESTINATION ${PERL_VENDORARCH}/auto/Slic3r/XS)
|
||||
install(FILES lib/Slic3r/XS.pm DESTINATION ${PERL_VENDORLIB}/Slic3r)
|
||||
|
@ -904,10 +904,17 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->cli = "octoprint-apikey=s";
|
||||
def->default_value = new ConfigOptionString("");
|
||||
|
||||
def = this->add("octoprint_cafile", coString);
|
||||
def->label = "HTTPS CA file";
|
||||
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
|
||||
"If left blank, the default OS CA certificate repository is used.";
|
||||
def->cli = "octoprint-cafile=s";
|
||||
def->default_value = new ConfigOptionString("");
|
||||
|
||||
def = this->add("octoprint_host", coString);
|
||||
def->label = L("Host or IP");
|
||||
def->label = L("Hostname, IP or URL");
|
||||
def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain "
|
||||
"the hostname or IP address of the OctoPrint instance.");
|
||||
"the hostname, IP address or URL of the OctoPrint instance.");
|
||||
def->cli = "octoprint-host=s";
|
||||
def->default_value = new ConfigOptionString("");
|
||||
|
||||
|
@ -684,6 +684,7 @@ class HostConfig : public StaticPrintConfig
|
||||
public:
|
||||
ConfigOptionString octoprint_host;
|
||||
ConfigOptionString octoprint_apikey;
|
||||
ConfigOptionString octoprint_cafile;
|
||||
ConfigOptionString serial_port;
|
||||
ConfigOptionInt serial_speed;
|
||||
|
||||
@ -692,6 +693,7 @@ protected:
|
||||
{
|
||||
OPT_PTR(octoprint_host);
|
||||
OPT_PTR(octoprint_apikey);
|
||||
OPT_PTR(octoprint_cafile);
|
||||
OPT_PTR(serial_port);
|
||||
OPT_PTR(serial_speed);
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
|
||||
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
|
||||
REGISTER_CLASS(PresetHints, "GUI::PresetHints");
|
||||
REGISTER_CLASS(TabIface, "GUI::Tab");
|
||||
REGISTER_CLASS(OctoPrint, "OctoPrint");
|
||||
|
||||
SV* ConfigBase__as_hash(ConfigBase* THIS)
|
||||
{
|
||||
|
@ -5,9 +5,9 @@
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#if __APPLE__
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
@ -573,4 +573,18 @@ wxString from_u8(std::string str)
|
||||
return wxString::FromUTF8(str.c_str());
|
||||
}
|
||||
|
||||
wxWindow *get_widget_by_id(int id)
|
||||
{
|
||||
if (g_wxMainFrame == nullptr) {
|
||||
throw std::runtime_error("Main frame not set");
|
||||
}
|
||||
|
||||
wxWindow *window = g_wxMainFrame->FindWindow(id);
|
||||
if (window == nullptr) {
|
||||
throw std::runtime_error((boost::format("Could not find widget by ID: %1%") % id).str());
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
} }
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Config.hpp"
|
||||
|
||||
class wxApp;
|
||||
class wxWindow;
|
||||
class wxFrame;
|
||||
class wxWindow;
|
||||
class wxMenuBar;
|
||||
@ -121,6 +122,8 @@ wxString L_str(std::string str);
|
||||
// Return wxString from std::string in UTF8
|
||||
wxString from_u8(std::string str);
|
||||
|
||||
wxWindow *get_widget_by_id(int id);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +224,7 @@ const std::vector<std::string>& Preset::printer_options()
|
||||
if (s_opts.empty()) {
|
||||
s_opts = {
|
||||
"bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
|
||||
"octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
|
||||
"octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
|
||||
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
|
||||
"between_objects_gcode", "printer_notes"
|
||||
};
|
||||
|
704
xs/src/slic3r/Utils/Bonjour.cpp
Normal file
704
xs/src/slic3r/Utils/Bonjour.cpp
Normal file
@ -0,0 +1,704 @@
|
||||
#include "Bonjour.hpp"
|
||||
|
||||
#include <iostream> // XXX
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_duration.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
using boost::optional;
|
||||
using boost::system::error_code;
|
||||
namespace endian = boost::endian;
|
||||
namespace asio = boost::asio;
|
||||
using boost::asio::ip::udp;
|
||||
|
||||
|
||||
// TODO: Fuzzing test (done without TXT)
|
||||
// FIXME: check char retype to unsigned
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
// Minimal implementation of a MDNS/DNS-SD client
|
||||
// This implementation is extremely simple, only the bits that are useful
|
||||
// for very basic MDNS discovery are present.
|
||||
|
||||
struct DnsName: public std::string
|
||||
{
|
||||
enum
|
||||
{
|
||||
MAX_RECURSION = 10, // Keep this low
|
||||
};
|
||||
|
||||
static optional<DnsName> decode(const std::vector<char> &buffer, size_t &offset, unsigned depth = 0)
|
||||
{
|
||||
// Check offset sanity:
|
||||
if (offset + 1 >= buffer.size()) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Check for recursion depth to prevent parsing names that are nested too deeply
|
||||
// or end up cyclic:
|
||||
if (depth >= MAX_RECURSION) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
DnsName res;
|
||||
const size_t bsize = buffer.size();
|
||||
|
||||
while (true) {
|
||||
const char* ptr = buffer.data() + offset;
|
||||
unsigned len = static_cast<unsigned char>(*ptr);
|
||||
if (len & 0xc0) {
|
||||
// This is a recursive label
|
||||
unsigned len_2 = static_cast<unsigned char>(ptr[1]);
|
||||
size_t pointer = (len & 0x3f) << 8 | len_2;
|
||||
const auto nested = decode(buffer, pointer, depth + 1);
|
||||
if (!nested) {
|
||||
return boost::none;
|
||||
} else {
|
||||
if (res.size() > 0) {
|
||||
res.push_back('.');
|
||||
}
|
||||
res.append(*nested);
|
||||
offset += 2;
|
||||
return std::move(res);
|
||||
}
|
||||
} else if (len == 0) {
|
||||
// This is a name terminator
|
||||
offset++;
|
||||
break;
|
||||
} else {
|
||||
// This is a regular label
|
||||
len &= 0x3f;
|
||||
if (len + offset + 1 >= bsize) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
res.reserve(len);
|
||||
if (res.size() > 0) {
|
||||
res.push_back('.');
|
||||
}
|
||||
|
||||
ptr++;
|
||||
for (const auto end = ptr + len; ptr < end; ptr++) {
|
||||
char c = *ptr;
|
||||
if (c >= 0x20 && c <= 0x7f) {
|
||||
res.push_back(c);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
|
||||
offset += len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (res.size() > 0) {
|
||||
return std::move(res);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DnsHeader
|
||||
{
|
||||
uint16_t id;
|
||||
uint16_t flags;
|
||||
uint16_t qdcount;
|
||||
uint16_t ancount;
|
||||
uint16_t nscount;
|
||||
uint16_t arcount;
|
||||
|
||||
enum
|
||||
{
|
||||
SIZE = 12,
|
||||
};
|
||||
|
||||
static DnsHeader decode(const std::vector<char> &buffer) {
|
||||
DnsHeader res;
|
||||
const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(buffer.data());
|
||||
res.id = endian::big_to_native(data_16[0]);
|
||||
res.flags = endian::big_to_native(data_16[1]);
|
||||
res.qdcount = endian::big_to_native(data_16[2]);
|
||||
res.ancount = endian::big_to_native(data_16[3]);
|
||||
res.nscount = endian::big_to_native(data_16[4]);
|
||||
res.arcount = endian::big_to_native(data_16[5]);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t rrcount() const {
|
||||
return ancount + nscount + arcount;
|
||||
}
|
||||
};
|
||||
|
||||
struct DnsQuestion
|
||||
{
|
||||
enum
|
||||
{
|
||||
MIN_SIZE = 5,
|
||||
};
|
||||
|
||||
DnsName name;
|
||||
uint16_t type;
|
||||
uint16_t qclass;
|
||||
|
||||
DnsQuestion() :
|
||||
type(0),
|
||||
qclass(0)
|
||||
{}
|
||||
|
||||
static optional<DnsQuestion> decode(const std::vector<char> &buffer, size_t &offset)
|
||||
{
|
||||
auto qname = DnsName::decode(buffer, offset);
|
||||
if (!qname) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
DnsQuestion res;
|
||||
res.name = std::move(*qname);
|
||||
const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(buffer.data() + offset);
|
||||
res.type = endian::big_to_native(data_16[0]);
|
||||
res.qclass = endian::big_to_native(data_16[1]);
|
||||
|
||||
offset += 4;
|
||||
return std::move(res);
|
||||
}
|
||||
};
|
||||
|
||||
struct DnsResource
|
||||
{
|
||||
DnsName name;
|
||||
uint16_t type;
|
||||
uint16_t rclass;
|
||||
uint32_t ttl;
|
||||
std::vector<char> data;
|
||||
|
||||
DnsResource() :
|
||||
type(0),
|
||||
rclass(0),
|
||||
ttl(0)
|
||||
{}
|
||||
|
||||
static optional<DnsResource> decode(const std::vector<char> &buffer, size_t &offset, size_t &dataoffset)
|
||||
{
|
||||
const size_t bsize = buffer.size();
|
||||
if (offset + 1 >= bsize) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
auto rname = DnsName::decode(buffer, offset);
|
||||
if (!rname) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (offset + 10 >= bsize) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
DnsResource res;
|
||||
res.name = std::move(*rname);
|
||||
const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(buffer.data() + offset);
|
||||
res.type = endian::big_to_native(data_16[0]);
|
||||
res.rclass = endian::big_to_native(data_16[1]);
|
||||
res.ttl = endian::big_to_native(*reinterpret_cast<const uint32_t*>(data_16 + 2));
|
||||
uint16_t rdlength = endian::big_to_native(data_16[4]);
|
||||
|
||||
offset += 10;
|
||||
if (offset + rdlength > bsize) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
dataoffset = offset;
|
||||
res.data = std::move(std::vector<char>(buffer.begin() + offset, buffer.begin() + offset + rdlength));
|
||||
offset += rdlength;
|
||||
|
||||
return std::move(res);
|
||||
}
|
||||
};
|
||||
|
||||
struct DnsRR_A
|
||||
{
|
||||
enum { TAG = 0x1 };
|
||||
|
||||
asio::ip::address_v4 ip;
|
||||
|
||||
static void decode(optional<DnsRR_A> &result, const DnsResource &rr)
|
||||
{
|
||||
if (rr.data.size() == 4) {
|
||||
DnsRR_A res;
|
||||
const uint32_t ip = endian::big_to_native(*reinterpret_cast<const uint32_t*>(rr.data.data()));
|
||||
res.ip = asio::ip::address_v4(ip);
|
||||
result = std::move(res);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DnsRR_AAAA
|
||||
{
|
||||
enum { TAG = 0x1c };
|
||||
|
||||
asio::ip::address_v6 ip;
|
||||
|
||||
static void decode(optional<DnsRR_AAAA> &result, const DnsResource &rr)
|
||||
{
|
||||
if (rr.data.size() == 16) {
|
||||
DnsRR_AAAA res;
|
||||
std::array<unsigned char, 16> ip;
|
||||
std::copy_n(rr.data.begin(), 16, ip.begin());
|
||||
res.ip = asio::ip::address_v6(ip);
|
||||
result = std::move(res);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DnsRR_SRV
|
||||
{
|
||||
enum
|
||||
{
|
||||
TAG = 0x21,
|
||||
MIN_SIZE = 8,
|
||||
};
|
||||
|
||||
uint16_t priority;
|
||||
uint16_t weight;
|
||||
uint16_t port;
|
||||
DnsName hostname;
|
||||
|
||||
static optional<DnsRR_SRV> decode(const std::vector<char> &buffer, const DnsResource &rr, size_t dataoffset)
|
||||
{
|
||||
if (rr.data.size() < MIN_SIZE) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
DnsRR_SRV res;
|
||||
|
||||
const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(rr.data.data());
|
||||
res.priority = endian::big_to_native(data_16[0]);
|
||||
res.weight = endian::big_to_native(data_16[1]);
|
||||
res.port = endian::big_to_native(data_16[2]);
|
||||
|
||||
size_t offset = dataoffset + 6;
|
||||
auto hostname = DnsName::decode(buffer, offset);
|
||||
|
||||
if (hostname) {
|
||||
res.hostname = std::move(*hostname);
|
||||
return std::move(res);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DnsRR_TXT
|
||||
{
|
||||
enum
|
||||
{
|
||||
TAG = 0x10,
|
||||
};
|
||||
|
||||
std::vector<std::string> values;
|
||||
|
||||
static optional<DnsRR_TXT> decode(const DnsResource &rr)
|
||||
{
|
||||
const size_t size = rr.data.size();
|
||||
if (size < 2) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
DnsRR_TXT res;
|
||||
|
||||
for (auto it = rr.data.begin(); it != rr.data.end(); ) {
|
||||
unsigned val_size = static_cast<unsigned char>(*it);
|
||||
if (val_size == 0 || it + val_size >= rr.data.end()) {
|
||||
return boost::none;
|
||||
}
|
||||
++it;
|
||||
|
||||
std::string value(val_size, ' ');
|
||||
std::copy(it, it + val_size, value.begin());
|
||||
res.values.push_back(std::move(value));
|
||||
|
||||
it += val_size;
|
||||
}
|
||||
|
||||
return std::move(res);
|
||||
}
|
||||
};
|
||||
|
||||
struct DnsSDPair
|
||||
{
|
||||
optional<DnsRR_SRV> srv;
|
||||
optional<DnsRR_TXT> txt;
|
||||
};
|
||||
|
||||
struct DnsSDMap : public std::map<std::string, DnsSDPair>
|
||||
{
|
||||
void insert_srv(std::string &&name, DnsRR_SRV &&srv)
|
||||
{
|
||||
auto hit = this->find(name);
|
||||
if (hit != this->end()) {
|
||||
hit->second.srv = std::move(srv);
|
||||
} else {
|
||||
DnsSDPair pair;
|
||||
pair.srv = std::move(srv);
|
||||
this->insert(std::make_pair(std::move(name), std::move(pair)));
|
||||
}
|
||||
}
|
||||
|
||||
void insert_txt(std::string &&name, DnsRR_TXT &&txt)
|
||||
{
|
||||
auto hit = this->find(name);
|
||||
if (hit != this->end()) {
|
||||
hit->second.txt = std::move(txt);
|
||||
} else {
|
||||
DnsSDPair pair;
|
||||
pair.txt = std::move(txt);
|
||||
this->insert(std::make_pair(std::move(name), std::move(pair)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DnsMessage
|
||||
{
|
||||
enum
|
||||
{
|
||||
MAX_SIZE = 4096,
|
||||
MAX_ANS = 30,
|
||||
};
|
||||
|
||||
DnsHeader header;
|
||||
optional<DnsQuestion> question;
|
||||
|
||||
optional<DnsRR_A> rr_a;
|
||||
optional<DnsRR_AAAA> rr_aaaa;
|
||||
std::vector<DnsRR_SRV> rr_srv;
|
||||
|
||||
DnsSDMap sdmap;
|
||||
|
||||
static optional<DnsMessage> decode(const std::vector<char> &buffer, optional<uint16_t> id_wanted = boost::none)
|
||||
{
|
||||
const auto size = buffer.size();
|
||||
if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
DnsMessage res;
|
||||
res.header = DnsHeader::decode(buffer);
|
||||
|
||||
if (id_wanted && *id_wanted != res.header.id) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (res.header.qdcount > 1 || res.header.ancount > MAX_ANS) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
size_t offset = DnsHeader::SIZE;
|
||||
if (res.header.qdcount == 1) {
|
||||
res.question = DnsQuestion::decode(buffer, offset);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < res.header.rrcount(); i++) {
|
||||
size_t dataoffset = 0;
|
||||
auto rr = DnsResource::decode(buffer, offset, dataoffset);
|
||||
if (!rr) {
|
||||
return boost::none;
|
||||
} else {
|
||||
res.parse_rr(buffer, std::move(*rr), dataoffset);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(res);
|
||||
}
|
||||
private:
|
||||
void parse_rr(const std::vector<char> &buffer, DnsResource &&rr, size_t dataoffset)
|
||||
{
|
||||
switch (rr.type) {
|
||||
case DnsRR_A::TAG: DnsRR_A::decode(this->rr_a, rr); break;
|
||||
case DnsRR_AAAA::TAG: DnsRR_AAAA::decode(this->rr_aaaa, rr); break;
|
||||
case DnsRR_SRV::TAG: {
|
||||
auto srv = DnsRR_SRV::decode(buffer, rr, dataoffset);
|
||||
if (srv) { this->sdmap.insert_srv(std::move(rr.name), std::move(*srv)); }
|
||||
break;
|
||||
}
|
||||
case DnsRR_TXT::TAG: {
|
||||
auto txt = DnsRR_TXT::decode(rr);
|
||||
if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct BonjourRequest
|
||||
{
|
||||
static const asio::ip::address_v4 MCAST_IP4;
|
||||
static const uint16_t MCAST_PORT;
|
||||
|
||||
uint16_t id;
|
||||
std::vector<char> data;
|
||||
|
||||
static optional<BonjourRequest> make(const std::string &service, const std::string &protocol);
|
||||
|
||||
private:
|
||||
BonjourRequest(uint16_t id, std::vector<char> &&data) :
|
||||
id(id),
|
||||
data(std::move(data))
|
||||
{}
|
||||
};
|
||||
|
||||
const asio::ip::address_v4 BonjourRequest::MCAST_IP4{0xe00000fb};
|
||||
const uint16_t BonjourRequest::MCAST_PORT = 5353;
|
||||
|
||||
optional<BonjourRequest> BonjourRequest::make(const std::string &service, const std::string &protocol)
|
||||
{
|
||||
if (service.size() > 15 || protocol.size() > 15) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::random_device dev;
|
||||
std::uniform_int_distribution<uint16_t> dist;
|
||||
uint16_t id = dist(dev);
|
||||
uint16_t id_big = endian::native_to_big(id);
|
||||
const char *id_char = reinterpret_cast<char*>(&id_big);
|
||||
|
||||
std::vector<char> data;
|
||||
data.reserve(service.size() + 18);
|
||||
|
||||
// Add the transaction ID
|
||||
data.push_back(id_char[0]);
|
||||
data.push_back(id_char[1]);
|
||||
|
||||
// Add metadata
|
||||
static const unsigned char rq_meta[] = {
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
std::copy(rq_meta, rq_meta + sizeof(rq_meta), std::back_inserter(data));
|
||||
|
||||
// Add PTR query name
|
||||
data.push_back(service.size() + 1);
|
||||
data.push_back('_');
|
||||
data.insert(data.end(), service.begin(), service.end());
|
||||
data.push_back(protocol.size() + 1);
|
||||
data.push_back('_');
|
||||
data.insert(data.end(), protocol.begin(), protocol.end());
|
||||
|
||||
// Add the rest of PTR record
|
||||
static const unsigned char ptr_tail[] = {
|
||||
0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00, 0xff,
|
||||
};
|
||||
std::copy(ptr_tail, ptr_tail + sizeof(ptr_tail), std::back_inserter(data));
|
||||
|
||||
return BonjourRequest(id, std::move(data));
|
||||
}
|
||||
|
||||
|
||||
// API - private part
|
||||
|
||||
struct Bonjour::priv
|
||||
{
|
||||
const std::string service;
|
||||
const std::string protocol;
|
||||
const std::string service_dn;
|
||||
unsigned timeout;
|
||||
uint16_t rq_id;
|
||||
|
||||
std::vector<char> buffer;
|
||||
std::thread io_thread;
|
||||
Bonjour::ReplyFn replyfn;
|
||||
Bonjour::CompleteFn completefn;
|
||||
|
||||
priv(std::string service, std::string protocol);
|
||||
|
||||
void udp_receive(udp::endpoint from, size_t bytes);
|
||||
void lookup_perform();
|
||||
};
|
||||
|
||||
Bonjour::priv::priv(std::string service, std::string protocol) :
|
||||
service(std::move(service)),
|
||||
protocol(std::move(protocol)),
|
||||
service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()),
|
||||
timeout(10),
|
||||
rq_id(0)
|
||||
{
|
||||
buffer.resize(DnsMessage::MAX_SIZE);
|
||||
}
|
||||
|
||||
void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
|
||||
{
|
||||
if (bytes == 0 || !replyfn) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.resize(bytes);
|
||||
const auto dns_msg = DnsMessage::decode(buffer, rq_id);
|
||||
if (dns_msg) {
|
||||
asio::ip::address ip = from.address();
|
||||
if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; }
|
||||
else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; }
|
||||
|
||||
for (const auto &sdpair : dns_msg->sdmap) {
|
||||
if (! sdpair.second.srv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &srv = *sdpair.second.srv;
|
||||
BonjourReply reply(ip, sdpair.first, srv.hostname);
|
||||
|
||||
if (sdpair.second.txt) {
|
||||
static const std::string tag_path = "path=";
|
||||
static const std::string tag_version = "version=";
|
||||
|
||||
for (const auto &value : sdpair.second.txt->values) {
|
||||
if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) {
|
||||
reply.path = value.substr(tag_path.size());
|
||||
} else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) {
|
||||
reply.version = value.substr(tag_version.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
replyfn(std::move(reply));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bonjour::priv::lookup_perform()
|
||||
{
|
||||
const auto brq = BonjourRequest::make(service, protocol);
|
||||
if (!brq) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto self = this;
|
||||
rq_id = brq->id;
|
||||
|
||||
try {
|
||||
boost::asio::io_service io_service;
|
||||
udp::socket socket(io_service);
|
||||
socket.open(udp::v4());
|
||||
socket.set_option(udp::socket::reuse_address(true));
|
||||
udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT);
|
||||
socket.send_to(asio::buffer(brq->data), mcast);
|
||||
|
||||
bool timeout = false;
|
||||
asio::deadline_timer timer(io_service);
|
||||
timer.expires_from_now(boost::posix_time::seconds(10));
|
||||
timer.async_wait([=, &timeout](const error_code &error) {
|
||||
timeout = true;
|
||||
if (self->completefn) {
|
||||
self->completefn();
|
||||
}
|
||||
});
|
||||
|
||||
udp::endpoint recv_from;
|
||||
const auto recv_handler = [&](const error_code &error, size_t bytes) {
|
||||
if (!error) { self->udp_receive(recv_from, bytes); }
|
||||
};
|
||||
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
||||
|
||||
while (io_service.run_one()) {
|
||||
if (timeout) {
|
||||
socket.cancel();
|
||||
} else {
|
||||
buffer.resize(DnsMessage::MAX_SIZE);
|
||||
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
||||
}
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// API - public part
|
||||
|
||||
BonjourReply::BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname) :
|
||||
ip(std::move(ip)),
|
||||
service_name(std::move(service_name)),
|
||||
hostname(std::move(hostname)),
|
||||
path("/"),
|
||||
version("Unknown")
|
||||
{}
|
||||
|
||||
std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
|
||||
{
|
||||
os << "BonjourReply(" << reply.ip.to_string() << ", " << reply.service_name << ", "
|
||||
<< reply.hostname << ", " << reply.path << ", " << reply.version << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
Bonjour::Bonjour(std::string service, std::string protocol) :
|
||||
p(new priv(std::move(service), std::move(protocol)))
|
||||
{}
|
||||
|
||||
Bonjour::Bonjour(Bonjour &&other) : p(std::move(other.p)) {}
|
||||
|
||||
Bonjour::~Bonjour()
|
||||
{
|
||||
if (p && p->io_thread.joinable()) {
|
||||
p->io_thread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
Bonjour& Bonjour::set_timeout(unsigned timeout)
|
||||
{
|
||||
if (p) { p->timeout = timeout; }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bonjour& Bonjour::on_reply(ReplyFn fn)
|
||||
{
|
||||
if (p) { p->replyfn = std::move(fn); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bonjour& Bonjour::on_complete(CompleteFn fn)
|
||||
{
|
||||
if (p) { p->completefn = std::move(fn); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bonjour::Ptr Bonjour::lookup()
|
||||
{
|
||||
auto self = std::make_shared<Bonjour>(std::move(*this));
|
||||
|
||||
if (self->p) {
|
||||
auto io_thread = std::thread([self](){
|
||||
self->p->lookup_perform();
|
||||
});
|
||||
self->p->io_thread = std::move(io_thread);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
void Bonjour::pokus() // XXX
|
||||
{
|
||||
auto bonjour = Bonjour("octoprint")
|
||||
.set_timeout(15)
|
||||
.on_reply([](BonjourReply &&reply) {
|
||||
std::cerr << "BonjourReply: " << reply << std::endl;
|
||||
})
|
||||
.on_complete([](){
|
||||
std::cerr << "MDNS lookup complete" << std::endl;
|
||||
})
|
||||
.lookup();
|
||||
}
|
||||
|
||||
|
||||
}
|
56
xs/src/slic3r/Utils/Bonjour.hpp
Normal file
56
xs/src/slic3r/Utils/Bonjour.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef slic3r_Bonjour_hpp_
|
||||
#define slic3r_Bonjour_hpp_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
// #include <ostream>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
// TODO: reply data structure
|
||||
struct BonjourReply
|
||||
{
|
||||
boost::asio::ip::address ip;
|
||||
std::string service_name;
|
||||
std::string hostname;
|
||||
std::string path;
|
||||
std::string version;
|
||||
|
||||
BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname);
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &, const BonjourReply &);
|
||||
|
||||
|
||||
/// Bonjour lookup performer
|
||||
class Bonjour : public std::enable_shared_from_this<Bonjour> {
|
||||
private:
|
||||
struct priv;
|
||||
public:
|
||||
typedef std::shared_ptr<Bonjour> Ptr;
|
||||
typedef std::function<void(BonjourReply &&reply)> ReplyFn;
|
||||
typedef std::function<void()> CompleteFn;
|
||||
|
||||
Bonjour(std::string service, std::string protocol = "tcp");
|
||||
Bonjour(Bonjour &&other);
|
||||
~Bonjour();
|
||||
|
||||
Bonjour& set_timeout(unsigned timeout);
|
||||
Bonjour& on_reply(ReplyFn fn);
|
||||
Bonjour& on_complete(CompleteFn fn);
|
||||
|
||||
Ptr lookup();
|
||||
|
||||
static void pokus(); // XXX: remove
|
||||
private:
|
||||
std::unique_ptr<priv> p;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
261
xs/src/slic3r/Utils/Http.cpp
Normal file
261
xs/src/slic3r/Utils/Http.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
#include "Http.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "../../libslic3r/libslic3r.h"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
// Private
|
||||
|
||||
class CurlGlobalInit
|
||||
{
|
||||
static const CurlGlobalInit instance;
|
||||
|
||||
CurlGlobalInit() { ::curl_global_init(CURL_GLOBAL_DEFAULT); }
|
||||
~CurlGlobalInit() { ::curl_global_cleanup(); }
|
||||
};
|
||||
|
||||
struct Http::priv
|
||||
{
|
||||
enum {
|
||||
DEFAULT_SIZE_LIMIT = 5 * 1024 * 1024,
|
||||
};
|
||||
|
||||
::CURL *curl;
|
||||
::curl_httppost *form;
|
||||
::curl_httppost *form_end;
|
||||
::curl_slist *headerlist;
|
||||
std::string buffer;
|
||||
size_t limit;
|
||||
|
||||
std::thread io_thread;
|
||||
Http::CompleteFn completefn;
|
||||
Http::ErrorFn errorfn;
|
||||
|
||||
priv(const std::string &url);
|
||||
~priv();
|
||||
|
||||
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
|
||||
std::string body_size_error();
|
||||
void http_perform();
|
||||
};
|
||||
|
||||
Http::priv::priv(const std::string &url) :
|
||||
curl(::curl_easy_init()),
|
||||
form(nullptr),
|
||||
form_end(nullptr),
|
||||
headerlist(nullptr)
|
||||
{
|
||||
if (curl == nullptr) {
|
||||
throw std::runtime_error(std::string("Could not construct Curl object"));
|
||||
}
|
||||
|
||||
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally
|
||||
::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION);
|
||||
}
|
||||
|
||||
Http::priv::~priv()
|
||||
{
|
||||
::curl_easy_cleanup(curl);
|
||||
::curl_formfree(form);
|
||||
::curl_slist_free_all(headerlist);
|
||||
}
|
||||
|
||||
size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
auto self = static_cast<priv*>(userp);
|
||||
const char *cdata = static_cast<char*>(data);
|
||||
const size_t realsize = size * nmemb;
|
||||
|
||||
const size_t limit = self->limit > 0 ? self->limit : DEFAULT_SIZE_LIMIT;
|
||||
if (self->buffer.size() + realsize > limit) {
|
||||
// This makes curl_easy_perform return CURLE_WRITE_ERROR
|
||||
return 0;
|
||||
}
|
||||
|
||||
self->buffer.append(cdata, realsize);
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
std::string Http::priv::body_size_error()
|
||||
{
|
||||
return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str();
|
||||
}
|
||||
|
||||
void Http::priv::http_perform()
|
||||
{
|
||||
::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
|
||||
::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this));
|
||||
|
||||
#ifndef NDEBUG
|
||||
::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||
#endif
|
||||
|
||||
if (headerlist != nullptr) {
|
||||
::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||
}
|
||||
|
||||
if (form != nullptr) {
|
||||
::curl_easy_setopt(curl, CURLOPT_HTTPPOST, form);
|
||||
}
|
||||
|
||||
CURLcode res = ::curl_easy_perform(curl);
|
||||
long http_status = 0;
|
||||
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
std::string error;
|
||||
if (res == CURLE_WRITE_ERROR) {
|
||||
error = std::move(body_size_error());
|
||||
} else {
|
||||
error = ::curl_easy_strerror(res);
|
||||
};
|
||||
|
||||
if (errorfn) {
|
||||
errorfn(std::move(buffer), std::move(error), http_status);
|
||||
}
|
||||
} else {
|
||||
if (completefn) {
|
||||
completefn(std::move(buffer), http_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Http::Http(const std::string &url) : p(new priv(url)) {}
|
||||
|
||||
|
||||
// Public
|
||||
|
||||
Http::Http(Http &&other) : p(std::move(other.p)) {}
|
||||
|
||||
Http::~Http()
|
||||
{
|
||||
if (p && p->io_thread.joinable()) {
|
||||
p->io_thread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Http& Http::size_limit(size_t sizeLimit)
|
||||
{
|
||||
if (p) { p->limit = sizeLimit; }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::header(std::string name, const std::string &value)
|
||||
{
|
||||
if (!p) { return * this; }
|
||||
|
||||
if (name.size() > 0) {
|
||||
name.append(": ").append(value);
|
||||
} else {
|
||||
name.push_back(':');
|
||||
}
|
||||
p->headerlist = curl_slist_append(p->headerlist, name.c_str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::remove_header(std::string name)
|
||||
{
|
||||
if (p) {
|
||||
name.push_back(':');
|
||||
p->headerlist = curl_slist_append(p->headerlist, name.c_str());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::ca_file(const std::string &name)
|
||||
{
|
||||
if (p) {
|
||||
::curl_easy_setopt(p->curl, CURLOPT_CAINFO, name.c_str());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::form_add(const std::string &name, const std::string &contents)
|
||||
{
|
||||
if (p) {
|
||||
::curl_formadd(&p->form, &p->form_end,
|
||||
CURLFORM_COPYNAME, name.c_str(),
|
||||
CURLFORM_COPYCONTENTS, contents.c_str(),
|
||||
CURLFORM_END
|
||||
);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::form_add_file(const std::string &name, const std::string &filename)
|
||||
{
|
||||
if (p) {
|
||||
::curl_formadd(&p->form, &p->form_end,
|
||||
CURLFORM_COPYNAME, name.c_str(),
|
||||
CURLFORM_FILE, filename.c_str(),
|
||||
CURLFORM_CONTENTTYPE, "application/octet-stream",
|
||||
CURLFORM_END
|
||||
);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::on_complete(CompleteFn fn)
|
||||
{
|
||||
if (p) { p->completefn = std::move(fn); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::on_error(ErrorFn fn)
|
||||
{
|
||||
if (p) { p->errorfn = std::move(fn); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http::Ptr Http::perform()
|
||||
{
|
||||
auto self = std::make_shared<Http>(std::move(*this));
|
||||
|
||||
if (self->p) {
|
||||
auto io_thread = std::thread([self](){
|
||||
self->p->http_perform();
|
||||
});
|
||||
self->p->io_thread = std::move(io_thread);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void Http::perform_sync()
|
||||
{
|
||||
if (p) { p->http_perform(); }
|
||||
}
|
||||
|
||||
Http Http::get(std::string url)
|
||||
{
|
||||
return std::move(Http{std::move(url)});
|
||||
}
|
||||
|
||||
Http Http::post(std::string url)
|
||||
{
|
||||
Http http{std::move(url)};
|
||||
curl_easy_setopt(http.p->curl, CURLOPT_POST, 1L);
|
||||
return http;
|
||||
}
|
||||
|
||||
|
||||
}
|
53
xs/src/slic3r/Utils/Http.hpp
Normal file
53
xs/src/slic3r/Utils/Http.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef slic3r_Http_hpp_
|
||||
#define slic3r_Http_hpp_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
/// Represetns a Http request
|
||||
class Http : public std::enable_shared_from_this<Http> {
|
||||
private:
|
||||
struct priv;
|
||||
public:
|
||||
typedef std::shared_ptr<Http> Ptr;
|
||||
typedef std::function<void(std::string /* body */, unsigned /* http_status */)> CompleteFn;
|
||||
typedef std::function<void(std::string /* body */, std::string /* error */, unsigned /* http_status */)> ErrorFn;
|
||||
|
||||
Http(Http &&other);
|
||||
|
||||
static Http get(std::string url);
|
||||
static Http post(std::string url);
|
||||
~Http();
|
||||
|
||||
Http(const Http &) = delete;
|
||||
Http& operator=(const Http &) = delete;
|
||||
Http& operator=(Http &&) = delete;
|
||||
|
||||
Http& size_limit(size_t sizeLimit);
|
||||
Http& header(std::string name, const std::string &value);
|
||||
Http& remove_header(std::string name);
|
||||
Http& ca_file(const std::string &filename);
|
||||
Http& form_add(const std::string &name, const std::string &contents);
|
||||
Http& form_add_file(const std::string &name, const std::string &filename);
|
||||
|
||||
Http& on_complete(CompleteFn fn);
|
||||
Http& on_error(ErrorFn fn);
|
||||
|
||||
Ptr perform();
|
||||
void perform_sync();
|
||||
|
||||
private:
|
||||
Http(const std::string &url);
|
||||
|
||||
std::unique_ptr<priv> p;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
105
xs/src/slic3r/Utils/OctoPrint.cpp
Normal file
105
xs/src/slic3r/Utils/OctoPrint.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include "OctoPrint.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <wx/frame.h>
|
||||
#include <wx/event.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "Http.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
OctoPrint::OctoPrint(DynamicPrintConfig *config) :
|
||||
host(config->opt_string("octoprint_host")),
|
||||
apikey(config->opt_string("octoprint_apikey")),
|
||||
cafile(config->opt_string("octoprint_cafile"))
|
||||
{}
|
||||
|
||||
std::string OctoPrint::test() const
|
||||
{
|
||||
// Since the request is performed synchronously here,
|
||||
// it is ok to refer to `res` from within the closure
|
||||
std::string res;
|
||||
|
||||
auto http = Http::get(std::move(make_url("api/version")));
|
||||
set_auth(http);
|
||||
http.on_error([&](std::string, std::string error, unsigned status) {
|
||||
res = format_error(error, status);
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void OctoPrint::send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print) const
|
||||
{
|
||||
auto http = Http::post(std::move(make_url("api/files/local")));
|
||||
set_auth(http);
|
||||
http.form_add("print", print ? "true" : "false")
|
||||
.form_add_file("file", filename)
|
||||
.on_complete([=](std::string body, unsigned status) {
|
||||
wxWindow *window = GUI::get_widget_by_id(windowId);
|
||||
wxCommandEvent* evt = new wxCommandEvent(completeEvt);
|
||||
evt->SetString("G-code file successfully uploaded to the OctoPrint server");
|
||||
evt->SetInt(100);
|
||||
wxQueueEvent(window, evt);
|
||||
})
|
||||
.on_error([=](std::string body, std::string error, unsigned status) {
|
||||
wxWindow *window = GUI::get_widget_by_id(windowId);
|
||||
|
||||
wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt);
|
||||
evt_complete->SetInt(100);
|
||||
wxQueueEvent(window, evt_complete);
|
||||
|
||||
wxCommandEvent* evt_error = new wxCommandEvent(errorEvt);
|
||||
evt_error->SetString(wxString::Format("Error while uploading to the OctoPrint server: %s", format_error(error, status)));
|
||||
wxQueueEvent(window, evt_error);
|
||||
})
|
||||
.perform();
|
||||
}
|
||||
|
||||
void OctoPrint::set_auth(Http &http) const
|
||||
{
|
||||
http.header("X-Api-Key", apikey);
|
||||
|
||||
if (! cafile.empty()) {
|
||||
http.ca_file(cafile);
|
||||
}
|
||||
}
|
||||
|
||||
std::string OctoPrint::make_url(const std::string &path) const
|
||||
{
|
||||
if (host.find("http://") == 0 || host.find("https://") == 0) {
|
||||
if (host.back() == '/') {
|
||||
return std::move((boost::format("%1%%2%") % host % path).str());
|
||||
} else {
|
||||
return std::move((boost::format("%1%/%2%") % host % path).str());
|
||||
}
|
||||
} else {
|
||||
return std::move((boost::format("http://%1%/%2%") % host % path).str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string OctoPrint::format_error(std::string error, unsigned status)
|
||||
{
|
||||
if (status != 0) {
|
||||
std::string res{"HTTP "};
|
||||
res.append(std::to_string(status));
|
||||
|
||||
if (status == 401) {
|
||||
res.append(": Invalid API key");
|
||||
}
|
||||
|
||||
return std::move(res);
|
||||
} else {
|
||||
return std::move(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
35
xs/src/slic3r/Utils/OctoPrint.hpp
Normal file
35
xs/src/slic3r/Utils/OctoPrint.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef slic3r_OctoPrint_hpp_
|
||||
#define slic3r_OctoPrint_hpp_
|
||||
|
||||
#include <string>
|
||||
|
||||
// #include "Http.hpp" // XXX: ?
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
class DynamicPrintConfig;
|
||||
class Http;
|
||||
|
||||
class OctoPrint
|
||||
{
|
||||
public:
|
||||
OctoPrint(DynamicPrintConfig *config);
|
||||
|
||||
std::string test() const;
|
||||
// XXX: style
|
||||
void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const;
|
||||
private:
|
||||
std::string host;
|
||||
std::string apikey;
|
||||
std::string cafile;
|
||||
|
||||
void set_auth(Http &http) const;
|
||||
std::string make_url(const std::string &path) const;
|
||||
static std::string format_error(std::string error, unsigned status);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -59,6 +59,15 @@ extern "C" {
|
||||
#undef seek
|
||||
#undef send
|
||||
#undef write
|
||||
#undef open
|
||||
#undef close
|
||||
#undef seekdir
|
||||
#undef setbuf
|
||||
#undef fread
|
||||
#undef fseek
|
||||
#undef fputc
|
||||
#undef fwrite
|
||||
#undef fclose
|
||||
#endif /* _MSC_VER */
|
||||
}
|
||||
#endif
|
||||
|
14
xs/xsp/Utils_OctoPrint.xsp
Normal file
14
xs/xsp/Utils_OctoPrint.xsp
Normal file
@ -0,0 +1,14 @@
|
||||
%module{Slic3r::XS};
|
||||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "slic3r/Utils/OctoPrint.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::OctoPrint} class OctoPrint {
|
||||
OctoPrint(DynamicPrintConfig *config);
|
||||
~OctoPrint();
|
||||
|
||||
std::string test() const;
|
||||
void send_gcode(int windowId, int completeEvt, int errorEvt, std::string filename, bool print = false) const;
|
||||
};
|
@ -236,6 +236,10 @@ Ref<PresetHints> O_OBJECT_SLIC3R_T
|
||||
TabIface* O_OBJECT_SLIC3R
|
||||
Ref<TabIface> O_OBJECT_SLIC3R_T
|
||||
|
||||
OctoPrint* O_OBJECT_SLIC3R
|
||||
Ref<OctoPrint> O_OBJECT_SLIC3R_T
|
||||
Clone<OctoPrint> O_OBJECT_SLIC3R_T
|
||||
|
||||
Axis T_UV
|
||||
ExtrusionLoopRole T_UV
|
||||
ExtrusionRole T_UV
|
||||
|
Loading…
Reference in New Issue
Block a user