Merge branch 'master' of https://github.com/prusa3d/Slic3r into opengl_to_cpp
This commit is contained in:
commit
39c326ebae
@ -26,7 +26,7 @@ include_directories(${LIBDIR}/libslic3r)
|
|||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
|
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
|
||||||
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB)
|
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601)
|
||||||
# -D_ITERATOR_DEBUG_LEVEL)
|
# -D_ITERATOR_DEBUG_LEVEL)
|
||||||
if(WIN10SDK_PATH)
|
if(WIN10SDK_PATH)
|
||||||
message("Building with Win10 Netfabb STL fixing service support")
|
message("Building with Win10 Netfabb STL fixing service support")
|
||||||
@ -260,6 +260,8 @@ add_library(libslic3r_gui STATIC
|
|||||||
${LIBDIR}/slic3r/Utils/PresetUpdater.hpp
|
${LIBDIR}/slic3r/Utils/PresetUpdater.hpp
|
||||||
${LIBDIR}/slic3r/Utils/Time.cpp
|
${LIBDIR}/slic3r/Utils/Time.cpp
|
||||||
${LIBDIR}/slic3r/Utils/Time.hpp
|
${LIBDIR}/slic3r/Utils/Time.hpp
|
||||||
|
${LIBDIR}/slic3r/Utils/HexFile.cpp
|
||||||
|
${LIBDIR}/slic3r/Utils/HexFile.hpp
|
||||||
${LIBDIR}/slic3r/IProgressIndicator.hpp
|
${LIBDIR}/slic3r/IProgressIndicator.hpp
|
||||||
${LIBDIR}/slic3r/AppController.hpp
|
${LIBDIR}/slic3r/AppController.hpp
|
||||||
${LIBDIR}/slic3r/AppController.cpp
|
${LIBDIR}/slic3r/AppController.cpp
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
|
||||||
|
|
||||||
add_definitions(-D_BSD_SOURCE -D_DEFAULT_SOURCE) # To enable various useful macros and functions on Unices
|
add_definitions(-D_BSD_SOURCE -D_DEFAULT_SOURCE) # To enable various useful macros and functions on Unices
|
||||||
@ -13,67 +14,74 @@ endif()
|
|||||||
|
|
||||||
|
|
||||||
set(AVRDUDE_SOURCES
|
set(AVRDUDE_SOURCES
|
||||||
${LIBDIR}/avrdude/arduino.c
|
arduino.c
|
||||||
${LIBDIR}/avrdude/avr.c
|
avr.c
|
||||||
# ${LIBDIR}/avrdude/avrftdi.c
|
# avrftdi.c
|
||||||
# ${LIBDIR}/avrdude/avrftdi_tpi.c
|
# avrftdi_tpi.c
|
||||||
${LIBDIR}/avrdude/avrpart.c
|
avrpart.c
|
||||||
${LIBDIR}/avrdude/avr910.c
|
avr910.c
|
||||||
${LIBDIR}/avrdude/bitbang.c
|
bitbang.c
|
||||||
${LIBDIR}/avrdude/buspirate.c
|
buspirate.c
|
||||||
${LIBDIR}/avrdude/butterfly.c
|
butterfly.c
|
||||||
${LIBDIR}/avrdude/config.c
|
config.c
|
||||||
${LIBDIR}/avrdude/config_gram.c
|
config_gram.c
|
||||||
# ${LIBDIR}/avrdude/confwin.c
|
# confwin.c
|
||||||
${LIBDIR}/avrdude/crc16.c
|
crc16.c
|
||||||
# ${LIBDIR}/avrdude/dfu.c
|
# dfu.c
|
||||||
${LIBDIR}/avrdude/fileio.c
|
fileio.c
|
||||||
# ${LIBDIR}/avrdude/flip1.c
|
# flip1.c
|
||||||
# ${LIBDIR}/avrdude/flip2.c
|
# flip2.c
|
||||||
# ${LIBDIR}/avrdude/ft245r.c
|
# ft245r.c
|
||||||
# ${LIBDIR}/avrdude/jtagmkI.c
|
# jtagmkI.c
|
||||||
# ${LIBDIR}/avrdude/jtagmkII.c
|
# jtagmkII.c
|
||||||
# ${LIBDIR}/avrdude/jtag3.c
|
# jtag3.c
|
||||||
${LIBDIR}/avrdude/lexer.c
|
lexer.c
|
||||||
${LIBDIR}/avrdude/linuxgpio.c
|
linuxgpio.c
|
||||||
${LIBDIR}/avrdude/lists.c
|
lists.c
|
||||||
# ${LIBDIR}/avrdude/par.c
|
# par.c
|
||||||
${LIBDIR}/avrdude/pgm.c
|
pgm.c
|
||||||
${LIBDIR}/avrdude/pgm_type.c
|
pgm_type.c
|
||||||
${LIBDIR}/avrdude/pickit2.c
|
pickit2.c
|
||||||
${LIBDIR}/avrdude/pindefs.c
|
pindefs.c
|
||||||
# ${LIBDIR}/avrdude/ppi.c
|
# ppi.c
|
||||||
# ${LIBDIR}/avrdude/ppiwin.c
|
# ppiwin.c
|
||||||
${LIBDIR}/avrdude/safemode.c
|
safemode.c
|
||||||
${LIBDIR}/avrdude/ser_avrdoper.c
|
ser_avrdoper.c
|
||||||
${LIBDIR}/avrdude/serbb_posix.c
|
serbb_posix.c
|
||||||
${LIBDIR}/avrdude/serbb_win32.c
|
serbb_win32.c
|
||||||
${LIBDIR}/avrdude/ser_posix.c
|
ser_posix.c
|
||||||
${LIBDIR}/avrdude/ser_win32.c
|
ser_win32.c
|
||||||
${LIBDIR}/avrdude/stk500.c
|
stk500.c
|
||||||
${LIBDIR}/avrdude/stk500generic.c
|
stk500generic.c
|
||||||
${LIBDIR}/avrdude/stk500v2.c
|
stk500v2.c
|
||||||
${LIBDIR}/avrdude/term.c
|
term.c
|
||||||
${LIBDIR}/avrdude/update.c
|
update.c
|
||||||
# ${LIBDIR}/avrdude/usbasp.c
|
# usbasp.c
|
||||||
# ${LIBDIR}/avrdude/usb_hidapi.c
|
# usb_hidapi.c
|
||||||
# ${LIBDIR}/avrdude/usb_libusb.c
|
# usb_libusb.c
|
||||||
# ${LIBDIR}/avrdude/usbtiny.c
|
# usbtiny.c
|
||||||
${LIBDIR}/avrdude/wiring.c
|
wiring.c
|
||||||
|
|
||||||
${LIBDIR}/avrdude/main.c
|
main.c
|
||||||
${LIBDIR}/avrdude/avrdude-slic3r.hpp
|
avrdude-slic3r.hpp
|
||||||
${LIBDIR}/avrdude/avrdude-slic3r.cpp
|
avrdude-slic3r.cpp
|
||||||
)
|
)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES}
|
set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES}
|
||||||
${LIBDIR}/avrdude/windows/unistd.cpp
|
windows/unistd.cpp
|
||||||
${LIBDIR}/avrdude/windows/getopt.c
|
windows/getopt.c
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
|
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
|
||||||
|
|
||||||
|
set(STANDALONE_SOURCES
|
||||||
|
main-standalone.c
|
||||||
|
)
|
||||||
|
add_executable(avrdude-slic3r ${STANDALONE_SOURCES})
|
||||||
|
target_link_libraries(avrdude-slic3r avrdude)
|
||||||
|
set_target_properties(avrdude-slic3r PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_compile_definitions(avrdude PRIVATE WIN32NATIVE=1)
|
target_compile_definitions(avrdude PRIVATE WIN32NATIVE=1)
|
||||||
target_include_directories(avrdude SYSTEM PRIVATE ${LIBDIR}/avrdude/windows) # So that sources find the getopt.h windows drop-in
|
target_include_directories(avrdude SYSTEM PRIVATE windows) # So that sources find the getopt.h windows drop-in
|
||||||
endif()
|
endif()
|
||||||
|
54
xs/src/avrdude/Makefile.standalone
Normal file
54
xs/src/avrdude/Makefile.standalone
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
TARGET = avrdude-slic3r
|
||||||
|
|
||||||
|
SOURCES = \
|
||||||
|
arduino.c \
|
||||||
|
avr.c \
|
||||||
|
avrpart.c \
|
||||||
|
avr910.c \
|
||||||
|
bitbang.c \
|
||||||
|
buspirate.c \
|
||||||
|
butterfly.c \
|
||||||
|
config.c \
|
||||||
|
config_gram.c \
|
||||||
|
crc16.c \
|
||||||
|
fileio.c \
|
||||||
|
lexer.c \
|
||||||
|
linuxgpio.c \
|
||||||
|
lists.c \
|
||||||
|
pgm.c \
|
||||||
|
pgm_type.c \
|
||||||
|
pickit2.c \
|
||||||
|
pindefs.c \
|
||||||
|
safemode.c \
|
||||||
|
ser_avrdoper.c \
|
||||||
|
serbb_posix.c \
|
||||||
|
serbb_win32.c \
|
||||||
|
ser_posix.c \
|
||||||
|
ser_win32.c \
|
||||||
|
stk500.c \
|
||||||
|
stk500generic.c \
|
||||||
|
stk500v2.c \
|
||||||
|
term.c \
|
||||||
|
update.c \
|
||||||
|
wiring.c \
|
||||||
|
main.c \
|
||||||
|
main-standalone.c
|
||||||
|
|
||||||
|
OBJECTS = $(SOURCES:.c=.o)
|
||||||
|
CFLAGS = -std=c99 -Wall -D_BSD_SOURCE -D_DEFAULT_SOURCE -O3 -DNDEBUG -fPIC
|
||||||
|
LDFLAGS = -lm
|
||||||
|
|
||||||
|
CC = gcc
|
||||||
|
RM = rm
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJECTS)
|
||||||
|
$(CC) -o ./$@ $(OBJECTS) $(LDFLAGS)
|
||||||
|
|
||||||
|
$(OBJECTS): %.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -f $(OBJECTS) $(TARGET)
|
@ -36,6 +36,7 @@ struct AvrDude::priv
|
|||||||
std::string sys_config;
|
std::string sys_config;
|
||||||
std::deque<std::vector<std::string>> args;
|
std::deque<std::vector<std::string>> args;
|
||||||
size_t current_args_set = 0;
|
size_t current_args_set = 0;
|
||||||
|
bool cancelled = false;
|
||||||
RunFn run_fn;
|
RunFn run_fn;
|
||||||
MessageFn message_fn;
|
MessageFn message_fn;
|
||||||
ProgressFn progress_fn;
|
ProgressFn progress_fn;
|
||||||
@ -141,11 +142,16 @@ AvrDude::Ptr AvrDude::run()
|
|||||||
|
|
||||||
if (self->p) {
|
if (self->p) {
|
||||||
auto avrdude_thread = std::thread([self]() {
|
auto avrdude_thread = std::thread([self]() {
|
||||||
|
bool cancel = false;
|
||||||
|
int res = -1;
|
||||||
|
|
||||||
if (self->p->run_fn) {
|
if (self->p->run_fn) {
|
||||||
self->p->run_fn();
|
self->p->run_fn(*self);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = self->p->run();
|
if (! self->p->cancelled) {
|
||||||
|
res = self->p->run();
|
||||||
|
}
|
||||||
|
|
||||||
if (self->p->complete_fn) {
|
if (self->p->complete_fn) {
|
||||||
self->p->complete_fn(res, self->p->current_args_set);
|
self->p->complete_fn(res, self->p->current_args_set);
|
||||||
@ -160,7 +166,10 @@ AvrDude::Ptr AvrDude::run()
|
|||||||
|
|
||||||
void AvrDude::cancel()
|
void AvrDude::cancel()
|
||||||
{
|
{
|
||||||
::avrdude_cancel();
|
if (p) {
|
||||||
|
p->cancelled = true;
|
||||||
|
::avrdude_cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvrDude::join()
|
void AvrDude::join()
|
||||||
|
@ -12,7 +12,7 @@ class AvrDude
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<AvrDude> Ptr;
|
typedef std::shared_ptr<AvrDude> Ptr;
|
||||||
typedef std::function<void()> RunFn;
|
typedef std::function<void(AvrDude&)> RunFn;
|
||||||
typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
|
typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
|
||||||
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
|
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
|
||||||
typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn;
|
typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn;
|
||||||
@ -31,7 +31,8 @@ public:
|
|||||||
AvrDude& push_args(std::vector<std::string> args);
|
AvrDude& push_args(std::vector<std::string> args);
|
||||||
|
|
||||||
// Set a callback to be called just after run() before avrdude is ran
|
// Set a callback to be called just after run() before avrdude is ran
|
||||||
// This can be used to perform any needed setup tasks from the background thread.
|
// This can be used to perform any needed setup tasks from the background thread,
|
||||||
|
// and, optionally, to cancel by writing true to the `cancel` argument.
|
||||||
// This has no effect when using run_sync().
|
// This has no effect when using run_sync().
|
||||||
AvrDude& on_run(RunFn fn);
|
AvrDude& on_run(RunFn fn);
|
||||||
|
|
||||||
|
@ -98,11 +98,11 @@ static int fileio_num(struct fioparms * fio,
|
|||||||
char * filename, FILE * f, AVRMEM * mem, int size,
|
char * filename, FILE * f, AVRMEM * mem, int size,
|
||||||
FILEFMT fmt);
|
FILEFMT fmt);
|
||||||
|
|
||||||
static int fmt_autodetect(char * fname, size_t offset);
|
static int fmt_autodetect(char * fname, unsigned section);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offset)
|
static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section)
|
||||||
{
|
{
|
||||||
FILE *file;
|
FILE *file;
|
||||||
// On Windows we need to convert the filename to UTF-16
|
// On Windows we need to convert the filename to UTF-16
|
||||||
@ -118,16 +118,38 @@ static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offse
|
|||||||
file = fopen(filename, mode);
|
file = fopen(filename, mode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (file != NULL) {
|
if (file == NULL) {
|
||||||
// Some systems allow seeking past the end of file, so we need check for that first and disallow
|
return NULL;
|
||||||
if (fseek(file, 0, SEEK_END) != 0
|
}
|
||||||
|| offset >= ftell(file)
|
|
||||||
|| fseek(file, offset, SEEK_SET) != 0
|
// Seek to the specified 'section'
|
||||||
) {
|
static const char *hex_terminator = ":00000001FF\r";
|
||||||
fclose(file);
|
unsigned terms_seen = 0;
|
||||||
file = NULL;
|
char buffer[MAX_LINE_LEN + 1];
|
||||||
errno = EINVAL;
|
|
||||||
|
while (terms_seen < section && fgets(buffer, MAX_LINE_LEN, file) != NULL) {
|
||||||
|
size_t len = strlen(buffer);
|
||||||
|
|
||||||
|
if (buffer[len - 1] == '\n') {
|
||||||
|
len--;
|
||||||
|
buffer[len] = 0;
|
||||||
}
|
}
|
||||||
|
if (buffer[len - 1] != '\r') {
|
||||||
|
buffer[len] = '\r';
|
||||||
|
len++;
|
||||||
|
buffer[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(buffer, hex_terminator) == 0) {
|
||||||
|
// Found a section terminator
|
||||||
|
terms_seen++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feof(file)) {
|
||||||
|
// Section not found
|
||||||
|
fclose(file);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
@ -1392,7 +1414,7 @@ int fileio_setparms(int op, struct fioparms * fp,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int fmt_autodetect(char * fname, size_t offset)
|
static int fmt_autodetect(char * fname, unsigned section)
|
||||||
{
|
{
|
||||||
FILE * f;
|
FILE * f;
|
||||||
unsigned char buf[MAX_LINE_LEN];
|
unsigned char buf[MAX_LINE_LEN];
|
||||||
@ -1402,9 +1424,9 @@ static int fmt_autodetect(char * fname, size_t offset)
|
|||||||
int first = 1;
|
int first = 1;
|
||||||
|
|
||||||
#if defined(WIN32NATIVE)
|
#if defined(WIN32NATIVE)
|
||||||
f = fopen_and_seek(fname, "r", offset);
|
f = fopen_and_seek(fname, "r", section);
|
||||||
#else
|
#else
|
||||||
f = fopen_and_seek(fname, "rb", offset);
|
f = fopen_and_seek(fname, "rb", section);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
@ -1480,7 +1502,7 @@ static int fmt_autodetect(char * fname, size_t offset)
|
|||||||
|
|
||||||
|
|
||||||
int fileio(int op, char * filename, FILEFMT format,
|
int fileio(int op, char * filename, FILEFMT format,
|
||||||
struct avrpart * p, char * memtype, int size, size_t offset)
|
struct avrpart * p, char * memtype, int size, unsigned section)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
FILE * f;
|
FILE * f;
|
||||||
@ -1539,7 +1561,7 @@ int fileio(int op, char * filename, FILEFMT format,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
format_detect = fmt_autodetect(fname, offset);
|
format_detect = fmt_autodetect(fname, section);
|
||||||
if (format_detect < 0) {
|
if (format_detect < 0) {
|
||||||
avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n",
|
avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n",
|
||||||
progname, fname);
|
progname, fname);
|
||||||
@ -1570,7 +1592,7 @@ int fileio(int op, char * filename, FILEFMT format,
|
|||||||
|
|
||||||
if (format != FMT_IMM) {
|
if (format != FMT_IMM) {
|
||||||
if (!using_stdio) {
|
if (!using_stdio) {
|
||||||
f = fopen_and_seek(fname, fio.mode, offset);
|
f = fopen_and_seek(fname, fio.mode, section);
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n",
|
avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n",
|
||||||
progname, fio.iodesc, fname, strerror(errno));
|
progname, fio.iodesc, fname, strerror(errno));
|
||||||
|
@ -821,7 +821,7 @@ extern "C" {
|
|||||||
char * fmtstr(FILEFMT format);
|
char * fmtstr(FILEFMT format);
|
||||||
|
|
||||||
int fileio(int op, char * filename, FILEFMT format,
|
int fileio(int op, char * filename, FILEFMT format,
|
||||||
struct avrpart * p, char * memtype, int size, size_t offset);
|
struct avrpart * p, char * memtype, int size, unsigned section);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
@ -870,7 +870,7 @@ enum updateflags {
|
|||||||
typedef struct update_t {
|
typedef struct update_t {
|
||||||
char * memtype;
|
char * memtype;
|
||||||
int op;
|
int op;
|
||||||
size_t offset;
|
unsigned section;
|
||||||
char * filename;
|
char * filename;
|
||||||
int format;
|
int format;
|
||||||
} UPDATE;
|
} UPDATE;
|
||||||
@ -882,7 +882,7 @@ extern "C" {
|
|||||||
extern UPDATE * parse_op(char * s);
|
extern UPDATE * parse_op(char * s);
|
||||||
extern UPDATE * dup_update(UPDATE * upd);
|
extern UPDATE * dup_update(UPDATE * upd);
|
||||||
extern UPDATE * new_update(int op, char * memtype, int filefmt,
|
extern UPDATE * new_update(int op, char * memtype, int filefmt,
|
||||||
char * filename, size_t offset);
|
char * filename, unsigned section);
|
||||||
extern void free_update(UPDATE * upd);
|
extern void free_update(UPDATE * upd);
|
||||||
extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd,
|
extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd,
|
||||||
enum updateflags flags);
|
enum updateflags flags);
|
||||||
|
9
xs/src/avrdude/main-standalone.c
Normal file
9
xs/src/avrdude/main-standalone.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "avrdude.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const char* SYS_CONFIG = "/etc/avrdude-slic3r.conf";
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return avrdude_main(argc, argv, SYS_CONFIG);
|
||||||
|
}
|
@ -194,7 +194,7 @@ static void usage(void)
|
|||||||
" -F Override invalid signature check.\n"
|
" -F Override invalid signature check.\n"
|
||||||
" -e Perform a chip erase.\n"
|
" -e Perform a chip erase.\n"
|
||||||
" -O Perform RC oscillator calibration (see AVR053). \n"
|
" -O Perform RC oscillator calibration (see AVR053). \n"
|
||||||
" -U <memtype>:r|w|v:<offset>:<filename>[:format]\n"
|
" -U <memtype>:r|w|v:<section>:<filename>[:format]\n"
|
||||||
" Memory operation specification.\n"
|
" Memory operation specification.\n"
|
||||||
" Multiple -U options are allowed, each request\n"
|
" Multiple -U options are allowed, each request\n"
|
||||||
" is performed in the order specified.\n"
|
" is performed in the order specified.\n"
|
||||||
|
@ -101,22 +101,22 @@ UPDATE * parse_op(char * s)
|
|||||||
|
|
||||||
p++;
|
p++;
|
||||||
|
|
||||||
// Extension: Parse file contents offset
|
// Extension: Parse file section number
|
||||||
size_t offset = 0;
|
unsigned section = 0;
|
||||||
|
|
||||||
for (; *p != ':'; p++) {
|
for (; *p != ':'; p++) {
|
||||||
if (*p >= '0' && *p <= '9') {
|
if (*p >= '0' && *p <= '9') {
|
||||||
offset *= 10;
|
section *= 10;
|
||||||
offset += *p - 0x30;
|
section += *p - 0x30;
|
||||||
} else {
|
} else {
|
||||||
avrdude_message(MSG_INFO, "%s: invalid update specification: offset is not a number\n", progname);
|
avrdude_message(MSG_INFO, "%s: invalid update specification: <section> is not a number\n", progname);
|
||||||
free(upd->memtype);
|
free(upd->memtype);
|
||||||
free(upd);
|
free(upd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upd->offset = offset;
|
upd->section = section;
|
||||||
p++;
|
p++;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -194,7 +194,7 @@ UPDATE * dup_update(UPDATE * upd)
|
|||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t offset)
|
UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, unsigned section)
|
||||||
{
|
{
|
||||||
UPDATE * u;
|
UPDATE * u;
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t
|
|||||||
u->filename = strdup(filename);
|
u->filename = strdup(filename);
|
||||||
u->op = op;
|
u->op = op;
|
||||||
u->format = filefmt;
|
u->format = filefmt;
|
||||||
u->offset = offset;
|
u->section = section;
|
||||||
|
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
@ -286,7 +286,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
|
|||||||
progname,
|
progname,
|
||||||
strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
|
strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
|
||||||
}
|
}
|
||||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
|
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
|
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
|
||||||
progname, upd->filename);
|
progname, upd->filename);
|
||||||
@ -351,7 +351,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
|
|||||||
progname, mem->desc, upd->filename);
|
progname, mem->desc, upd->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
|
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
|
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
|
||||||
progname, upd->filename);
|
progname, upd->filename);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <istream>
|
#include <istream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
@ -568,16 +569,12 @@ GCodeSender::set_DTR(bool on)
|
|||||||
void
|
void
|
||||||
GCodeSender::reset()
|
GCodeSender::reset()
|
||||||
{
|
{
|
||||||
this->set_DTR(false);
|
set_DTR(false);
|
||||||
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
this->set_DTR(true);
|
set_DTR(true);
|
||||||
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
this->set_DTR(false);
|
set_DTR(false);
|
||||||
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
{
|
|
||||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
|
||||||
this->can_send = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -1,12 +1,23 @@
|
|||||||
#include "FirmwareDialog.hpp"
|
|
||||||
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <thread>
|
||||||
|
#include <stdexcept>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
#include "libslic3r/Utils.hpp"
|
||||||
|
#include "avrdude/avrdude-slic3r.hpp"
|
||||||
|
#include "GUI.hpp"
|
||||||
|
#include "MsgDialog.hpp"
|
||||||
|
#include "../Utils/HexFile.hpp"
|
||||||
|
#include "../Utils/Serial.hpp"
|
||||||
|
|
||||||
|
// wx includes need to come after asio because of the WinSock.h problem
|
||||||
|
#include "FirmwareDialog.hpp"
|
||||||
|
|
||||||
#include <wx/app.h>
|
#include <wx/app.h>
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
@ -22,16 +33,18 @@
|
|||||||
#include <wx/collpane.h>
|
#include <wx/collpane.h>
|
||||||
#include <wx/msgdlg.h>
|
#include <wx/msgdlg.h>
|
||||||
|
|
||||||
#include "libslic3r/Utils.hpp"
|
|
||||||
#include "avrdude/avrdude-slic3r.hpp"
|
|
||||||
#include "GUI.hpp"
|
|
||||||
#include "../Utils/Serial.hpp"
|
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
namespace asio = boost::asio;
|
||||||
|
using boost::system::error_code;
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
using Utils::HexFile;
|
||||||
|
using Utils::SerialPortInfo;
|
||||||
|
using Utils::Serial;
|
||||||
|
|
||||||
|
|
||||||
// This enum discriminates the kind of information in EVT_AVRDUDE,
|
// This enum discriminates the kind of information in EVT_AVRDUDE,
|
||||||
// it's stored in the ExtraLong field of wxCommandEvent.
|
// it's stored in the ExtraLong field of wxCommandEvent.
|
||||||
@ -40,6 +53,7 @@ enum AvrdudeEvent
|
|||||||
AE_MESSAGE,
|
AE_MESSAGE,
|
||||||
AE_PROGRESS,
|
AE_PROGRESS,
|
||||||
AE_EXIT,
|
AE_EXIT,
|
||||||
|
AE_ERROR,
|
||||||
};
|
};
|
||||||
|
|
||||||
wxDECLARE_EVENT(EVT_AVRDUDE, wxCommandEvent);
|
wxDECLARE_EVENT(EVT_AVRDUDE, wxCommandEvent);
|
||||||
@ -52,7 +66,6 @@ struct FirmwareDialog::priv
|
|||||||
{
|
{
|
||||||
enum AvrDudeComplete
|
enum AvrDudeComplete
|
||||||
{
|
{
|
||||||
AC_NONE,
|
|
||||||
AC_SUCCESS,
|
AC_SUCCESS,
|
||||||
AC_FAILURE,
|
AC_FAILURE,
|
||||||
AC_CANCEL,
|
AC_CANCEL,
|
||||||
@ -61,7 +74,7 @@ struct FirmwareDialog::priv
|
|||||||
FirmwareDialog *q; // PIMPL back pointer ("Q-Pointer")
|
FirmwareDialog *q; // PIMPL back pointer ("Q-Pointer")
|
||||||
|
|
||||||
wxComboBox *port_picker;
|
wxComboBox *port_picker;
|
||||||
std::vector<Utils::SerialPortInfo> ports;
|
std::vector<SerialPortInfo> ports;
|
||||||
wxFilePickerCtrl *hex_picker;
|
wxFilePickerCtrl *hex_picker;
|
||||||
wxStaticText *txt_status;
|
wxStaticText *txt_status;
|
||||||
wxGauge *progressbar;
|
wxGauge *progressbar;
|
||||||
@ -80,7 +93,9 @@ struct FirmwareDialog::priv
|
|||||||
AvrDude::Ptr avrdude;
|
AvrDude::Ptr avrdude;
|
||||||
std::string avrdude_config;
|
std::string avrdude_config;
|
||||||
unsigned progress_tasks_done;
|
unsigned progress_tasks_done;
|
||||||
|
unsigned progress_tasks_bar;
|
||||||
bool cancelled;
|
bool cancelled;
|
||||||
|
const bool extra_verbose; // For debugging
|
||||||
|
|
||||||
priv(FirmwareDialog *q) :
|
priv(FirmwareDialog *q) :
|
||||||
q(q),
|
q(q),
|
||||||
@ -89,16 +104,25 @@ struct FirmwareDialog::priv
|
|||||||
timer_pulse(q),
|
timer_pulse(q),
|
||||||
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
|
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
|
||||||
progress_tasks_done(0),
|
progress_tasks_done(0),
|
||||||
cancelled(false)
|
progress_tasks_bar(0),
|
||||||
|
cancelled(false),
|
||||||
|
extra_verbose(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void find_serial_ports();
|
void find_serial_ports();
|
||||||
void flashing_start(bool flashing_l10n);
|
void flashing_start(unsigned tasks);
|
||||||
void flashing_done(AvrDudeComplete complete);
|
void flashing_done(AvrDudeComplete complete);
|
||||||
size_t hex_lang_offset(const wxString &path);
|
void check_model_id(const HexFile &metadata, const SerialPortInfo &port);
|
||||||
|
|
||||||
|
void prepare_common(AvrDude &, const SerialPortInfo &port, const std::string &filename);
|
||||||
|
void prepare_mk2(AvrDude &, const SerialPortInfo &port, const std::string &filename);
|
||||||
|
void prepare_mk3(AvrDude &, const SerialPortInfo &port, const std::string &filename);
|
||||||
|
void prepare_mm_control(AvrDude &, const SerialPortInfo &port, const std::string &filename);
|
||||||
void perform_upload();
|
void perform_upload();
|
||||||
|
|
||||||
void cancel();
|
void cancel();
|
||||||
void on_avrdude(const wxCommandEvent &evt);
|
void on_avrdude(const wxCommandEvent &evt);
|
||||||
|
void ensure_joined();
|
||||||
};
|
};
|
||||||
|
|
||||||
void FirmwareDialog::priv::find_serial_ports()
|
void FirmwareDialog::priv::find_serial_ports()
|
||||||
@ -108,7 +132,7 @@ void FirmwareDialog::priv::find_serial_ports()
|
|||||||
this->ports = new_ports;
|
this->ports = new_ports;
|
||||||
port_picker->Clear();
|
port_picker->Clear();
|
||||||
for (const auto &port : this->ports)
|
for (const auto &port : this->ports)
|
||||||
port_picker->Append(port.friendly_name);
|
port_picker->Append(wxString::FromUTF8(port.friendly_name.data()));
|
||||||
if (ports.size() > 0) {
|
if (ports.size() > 0) {
|
||||||
int idx = port_picker->GetValue().IsEmpty() ? 0 : -1;
|
int idx = port_picker->GetValue().IsEmpty() ? 0 : -1;
|
||||||
for (int i = 0; i < (int)this->ports.size(); ++ i)
|
for (int i = 0; i < (int)this->ports.size(); ++ i)
|
||||||
@ -122,7 +146,7 @@ void FirmwareDialog::priv::find_serial_ports()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FirmwareDialog::priv::flashing_start(bool flashing_l10n)
|
void FirmwareDialog::priv::flashing_start(unsigned tasks)
|
||||||
{
|
{
|
||||||
txt_stdout->Clear();
|
txt_stdout->Clear();
|
||||||
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
|
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
|
||||||
@ -132,9 +156,10 @@ void FirmwareDialog::priv::flashing_start(bool flashing_l10n)
|
|||||||
hex_picker->Disable();
|
hex_picker->Disable();
|
||||||
btn_close->Disable();
|
btn_close->Disable();
|
||||||
btn_flash->SetLabel(btn_flash_label_flashing);
|
btn_flash->SetLabel(btn_flash_label_flashing);
|
||||||
progressbar->SetRange(flashing_l10n ? 500 : 200); // See progress callback below
|
progressbar->SetRange(200 * tasks); // See progress callback below
|
||||||
progressbar->SetValue(0);
|
progressbar->SetValue(0);
|
||||||
progress_tasks_done = 0;
|
progress_tasks_done = 0;
|
||||||
|
progress_tasks_bar = 0;
|
||||||
cancelled = false;
|
cancelled = false;
|
||||||
timer_pulse.Start(50);
|
timer_pulse.Start(50);
|
||||||
}
|
}
|
||||||
@ -158,63 +183,49 @@ void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FirmwareDialog::priv::hex_lang_offset(const wxString &path)
|
void FirmwareDialog::priv::check_model_id(const HexFile &metadata, const SerialPortInfo &port)
|
||||||
{
|
{
|
||||||
fs::ifstream file(fs::path(path.wx_str()));
|
if (metadata.model_id.empty()) {
|
||||||
if (! file.good()) {
|
// No data to check against
|
||||||
return 0;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asio::io_service io;
|
||||||
|
Serial serial(io, port.port, 115200);
|
||||||
|
serial.printer_setup();
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TIMEOUT = 1000,
|
||||||
|
RETREIES = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (! serial.printer_ready_wait(RETREIES, TIMEOUT)) {
|
||||||
|
throw wxString::Format(_(L("Could not connect to the printer at %s")), port.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *hex_terminator = ":00000001FF\r";
|
|
||||||
size_t res = 0;
|
|
||||||
std::string line;
|
std::string line;
|
||||||
while (getline(file, line, '\n').good()) {
|
error_code ec;
|
||||||
// Account for LF vs CRLF
|
serial.printer_write_line("PRUSA Rev");
|
||||||
if (!line.empty() && line.back() != '\r') {
|
while (serial.read_line(TIMEOUT, line, ec)) {
|
||||||
line.push_back('\r');
|
if (ec) { throw wxString::Format(_(L("Could not connect to the printer at %s")), port.port); }
|
||||||
|
if (line == "ok") { continue; }
|
||||||
|
|
||||||
|
if (line == metadata.model_id) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
throw wxString::Format(_(L(
|
||||||
|
"The firmware hex file does not match the printer model.\n"
|
||||||
|
"The hex file is intended for:\n %s\n"
|
||||||
|
"Printer reports:\n %s"
|
||||||
|
)), metadata.model_id, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line == hex_terminator) {
|
line.clear();
|
||||||
if (res == 0) {
|
|
||||||
// This is the first terminator seen, save the position
|
|
||||||
res = file.tellg();
|
|
||||||
} else {
|
|
||||||
// We've found another terminator, return the offset just after the first one
|
|
||||||
// which is the start of the second 'section'.
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FirmwareDialog::priv::perform_upload()
|
void FirmwareDialog::priv::prepare_common(AvrDude &avrdude, const SerialPortInfo &port, const std::string &filename)
|
||||||
{
|
{
|
||||||
auto filename = hex_picker->GetPath();
|
|
||||||
std::string port = port_picker->GetValue().ToStdString();
|
|
||||||
int selection = port_picker->GetSelection();
|
|
||||||
if (selection != -1) {
|
|
||||||
// Verify whether the combo box list selection equals to the combo box edit value.
|
|
||||||
if (this->ports[selection].friendly_name == port)
|
|
||||||
port = this->ports[selection].port;
|
|
||||||
}
|
|
||||||
if (filename.IsEmpty() || port.empty()) { return; }
|
|
||||||
|
|
||||||
const bool extra_verbose = false; // For debugging
|
|
||||||
const auto lang_offset = hex_lang_offset(filename);
|
|
||||||
const auto filename_utf8 = filename.utf8_str();
|
|
||||||
|
|
||||||
flashing_start(lang_offset > 0);
|
|
||||||
|
|
||||||
// It is ok here to use the q-pointer to the FirmwareDialog
|
|
||||||
// because the dialog ensures it doesn't exit before the background thread is done.
|
|
||||||
auto q = this->q;
|
|
||||||
|
|
||||||
// Init the avrdude object
|
|
||||||
AvrDude avrdude(avrdude_config);
|
|
||||||
|
|
||||||
// Build argument list(s)
|
|
||||||
std::vector<std::string> args {{
|
std::vector<std::string> args {{
|
||||||
extra_verbose ? "-vvvvv" : "-v",
|
extra_verbose ? "-vvvvv" : "-v",
|
||||||
"-p", "atmega2560",
|
"-p", "atmega2560",
|
||||||
@ -222,11 +233,10 @@ void FirmwareDialog::priv::perform_upload()
|
|||||||
// The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
|
// The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
|
||||||
// is flashed with a buggy firmware.
|
// is flashed with a buggy firmware.
|
||||||
"-c", "wiring",
|
"-c", "wiring",
|
||||||
"-P", port,
|
"-P", port.port,
|
||||||
"-b", "115200", // TODO: Allow other rates? Ditto below.
|
"-b", "115200", // TODO: Allow other rates? Ditto below.
|
||||||
"-D",
|
"-D",
|
||||||
// XXX: Safe mode?
|
"-U", (boost::format("flash:w:0:%1%:i") % filename).str(),
|
||||||
"-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(),
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
|
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
|
||||||
@ -235,32 +245,167 @@ void FirmwareDialog::priv::perform_upload()
|
|||||||
});
|
});
|
||||||
|
|
||||||
avrdude.push_args(std::move(args));
|
avrdude.push_args(std::move(args));
|
||||||
|
}
|
||||||
if (lang_offset > 0) {
|
|
||||||
// The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy)
|
|
||||||
// This is done via another avrdude invocation, here we build arg list for that:
|
|
||||||
std::vector<std::string> args_l10n {{
|
|
||||||
extra_verbose ? "-vvvvv" : "-v",
|
|
||||||
"-p", "atmega2560",
|
|
||||||
// Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
|
|
||||||
// The Prusa's avrdude is patched again to never send semicolons inside the data packets.
|
|
||||||
"-c", "arduino",
|
|
||||||
"-P", port,
|
|
||||||
"-b", "115200",
|
|
||||||
"-D",
|
|
||||||
"-u", // disable safe mode
|
|
||||||
"-U", (boost::format("flash:w:%1%:%2%:i") % lang_offset % filename_utf8.data()).str(),
|
|
||||||
}};
|
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
|
void FirmwareDialog::priv::prepare_mk2(AvrDude &avrdude, const SerialPortInfo &port, const std::string &filename)
|
||||||
<< std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) {
|
{
|
||||||
return a + ' ' + b;
|
prepare_common(avrdude, port, filename);
|
||||||
});
|
}
|
||||||
|
|
||||||
avrdude.push_args(std::move(args_l10n));
|
void FirmwareDialog::priv::prepare_mk3(AvrDude &avrdude, const SerialPortInfo &port, const std::string &filename)
|
||||||
|
{
|
||||||
|
prepare_common(avrdude, port, filename);
|
||||||
|
|
||||||
|
// The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy)
|
||||||
|
// This is done via another avrdude invocation, here we build arg list for that:
|
||||||
|
std::vector<std::string> args_l10n {{
|
||||||
|
extra_verbose ? "-vvvvv" : "-v",
|
||||||
|
"-p", "atmega2560",
|
||||||
|
// Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
|
||||||
|
// The Prusa's avrdude is patched again to never send semicolons inside the data packets.
|
||||||
|
"-c", "arduino",
|
||||||
|
"-P", port.port,
|
||||||
|
"-b", "115200",
|
||||||
|
"-D",
|
||||||
|
"-u", // disable safe mode
|
||||||
|
"-U", (boost::format("flash:w:1:%1%:i") % filename).str(),
|
||||||
|
}};
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
|
||||||
|
<< std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) {
|
||||||
|
return a + ' ' + b;
|
||||||
|
});
|
||||||
|
|
||||||
|
avrdude.push_args(std::move(args_l10n));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FirmwareDialog::priv::prepare_mm_control(AvrDude &avrdude, const SerialPortInfo &port_in, const std::string &filename)
|
||||||
|
{
|
||||||
|
// Check if the port has the PID/VID of 0x2c99/3
|
||||||
|
// If not, check if it is the MMU (0x2c99/4) and reboot the by opening @ 1200 bauds
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
|
||||||
|
SerialPortInfo port = port_in;
|
||||||
|
if (! port.id_match(0x2c99, 3)) {
|
||||||
|
if (! port.id_match(0x2c99, 4)) {
|
||||||
|
// This is not a Prusa MMU 2.0 device
|
||||||
|
BOOST_LOG_TRIVIAL(error) << boost::format("Not a Prusa MMU 2.0 device: `%1%`") % port.port;
|
||||||
|
throw wxString::Format(_(L("The device at `%s` is not am Original Prusa i3 MMU 2.0 device")), port.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % port.port;
|
||||||
|
|
||||||
|
{
|
||||||
|
asio::io_service io;
|
||||||
|
Serial serial(io, port.port, 1200);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the bootloader to show up
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||||
|
|
||||||
|
// Look for the rebooted device
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "... looking for VID/PID 0x2c99/3 ...";
|
||||||
|
auto new_ports = Utils::scan_serial_ports_extended();
|
||||||
|
unsigned hits = 0;
|
||||||
|
for (auto &&new_port : new_ports) {
|
||||||
|
if (new_port.id_match(0x2c99, 3)) {
|
||||||
|
hits++;
|
||||||
|
port = std::move(new_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits == 0) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "No VID/PID 0x2c99/3 device found after rebooting the MMU 2.0";
|
||||||
|
throw wxString::Format(_(L("Failed to reboot the device at `%s` for programming")), port.port);
|
||||||
|
} else if (hits > 1) {
|
||||||
|
// We found multiple 0x2c99/3 devices, this is bad, because there's no way to find out
|
||||||
|
// which one is the one user wants to flash.
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found after rebooting the MMU 2.0";
|
||||||
|
throw wxString::Format(_(L("Multiple Original Prusa i3 MMU 2.0 devices found. Please only connect one at a time for flashing.")), port.port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/3 at `%1%`, flashing ...") % port.port;
|
||||||
|
|
||||||
|
std::vector<std::string> args {{
|
||||||
|
extra_verbose ? "-vvvvv" : "-v",
|
||||||
|
"-p", "atmega32u4",
|
||||||
|
"-c", "avr109",
|
||||||
|
"-P", port.port,
|
||||||
|
"-b", "57600",
|
||||||
|
"-D",
|
||||||
|
"-U", (boost::format("flash:w:0:%1%:i") % filename).str(),
|
||||||
|
}};
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
|
||||||
|
<< std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) {
|
||||||
|
return a + ' ' + b;
|
||||||
|
});
|
||||||
|
|
||||||
|
avrdude.push_args(std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FirmwareDialog::priv::perform_upload()
|
||||||
|
{
|
||||||
|
auto filename = hex_picker->GetPath();
|
||||||
|
if (filename.IsEmpty()) { return; }
|
||||||
|
|
||||||
|
int selection = port_picker->GetSelection();
|
||||||
|
if (selection == wxNOT_FOUND) { return; }
|
||||||
|
|
||||||
|
const SerialPortInfo &port = this->ports[selection];
|
||||||
|
// Verify whether the combo box list selection equals to the combo box edit value.
|
||||||
|
if (wxString::FromUTF8(this->ports[selection].friendly_name.data()) != port_picker->GetValue()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool extra_verbose = false; // For debugging
|
||||||
|
HexFile metadata(filename.wx_str());
|
||||||
|
const std::string filename_utf8(filename.utf8_str().data());
|
||||||
|
|
||||||
|
flashing_start(metadata.device == HexFile::DEV_MK3 ? 2 : 1);
|
||||||
|
|
||||||
|
// Init the avrdude object
|
||||||
|
AvrDude avrdude(avrdude_config);
|
||||||
|
|
||||||
|
// It is ok here to use the q-pointer to the FirmwareDialog
|
||||||
|
// because the dialog ensures it doesn't exit before the background thread is done.
|
||||||
|
auto q = this->q;
|
||||||
|
|
||||||
this->avrdude = avrdude
|
this->avrdude = avrdude
|
||||||
|
.on_run([=](AvrDude &avrdude) {
|
||||||
|
auto queue_error = [&](wxString message) {
|
||||||
|
avrdude.cancel();
|
||||||
|
|
||||||
|
auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId());
|
||||||
|
evt->SetExtraLong(AE_ERROR);
|
||||||
|
evt->SetString(std::move(message));
|
||||||
|
wxQueueEvent(this->q, evt);
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (metadata.device) {
|
||||||
|
case HexFile::DEV_MK3:
|
||||||
|
this->check_model_id(metadata, port);
|
||||||
|
this->prepare_mk3(avrdude, port, filename_utf8);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HexFile::DEV_MM_CONTROL:
|
||||||
|
this->check_model_id(metadata, port);
|
||||||
|
this->prepare_mm_control(avrdude, port, filename_utf8);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this->prepare_mk2(avrdude, port, filename_utf8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (const wxString &message) {
|
||||||
|
queue_error(message);
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
queue_error(wxString::Format(_(L("Error accessing port at %s: %s")), port.port, ex.what()));
|
||||||
|
}
|
||||||
|
})
|
||||||
.on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) {
|
.on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) {
|
||||||
if (extra_verbose) {
|
if (extra_verbose) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg;
|
BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg;
|
||||||
@ -313,12 +458,15 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
|
|||||||
// and then display overall progress during the latter tasks.
|
// and then display overall progress during the latter tasks.
|
||||||
|
|
||||||
if (progress_tasks_done > 0) {
|
if (progress_tasks_done > 0) {
|
||||||
progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt());
|
progressbar->SetValue(progress_tasks_bar + evt.GetInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt.GetInt() == 100) {
|
if (evt.GetInt() == 100) {
|
||||||
timer_pulse.Stop();
|
timer_pulse.Stop();
|
||||||
progress_tasks_done += 100;
|
if (progress_tasks_done % 3 != 0) {
|
||||||
|
progress_tasks_bar += 100;
|
||||||
|
}
|
||||||
|
progress_tasks_done++;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -328,11 +476,17 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
|
|||||||
|
|
||||||
complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
|
complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
|
||||||
flashing_done(complete_kind);
|
flashing_done(complete_kind);
|
||||||
|
ensure_joined();
|
||||||
|
break;
|
||||||
|
|
||||||
// Make sure the background thread is collected and the AvrDude object reset
|
case AE_ERROR:
|
||||||
if (avrdude) { avrdude->join(); }
|
txt_stdout->AppendText(evt.GetString());
|
||||||
avrdude.reset();
|
flashing_done(AC_FAILURE);
|
||||||
|
ensure_joined();
|
||||||
|
{
|
||||||
|
GUI::ErrorDialog dlg(this->q, evt.GetString());
|
||||||
|
dlg.ShowModal();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -340,6 +494,13 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FirmwareDialog::priv::ensure_joined()
|
||||||
|
{
|
||||||
|
// Make sure the background thread is collected and the AvrDude object reset
|
||||||
|
if (avrdude) { avrdude->join(); }
|
||||||
|
avrdude.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
|
|
||||||
|
107
xs/src/slic3r/Utils/HexFile.cpp
Normal file
107
xs/src/slic3r/Utils/HexFile.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include "HexFile.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
#include <boost/property_tree/ini_parser.hpp>
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
namespace pt = boost::property_tree;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
|
||||||
|
static HexFile::DeviceKind parse_device_kind(const std::string &str)
|
||||||
|
{
|
||||||
|
if (str == "mk2") { return HexFile::DEV_MK2; }
|
||||||
|
else if (str == "mk3") { return HexFile::DEV_MK3; }
|
||||||
|
else if (str == "mm-control") { return HexFile::DEV_MM_CONTROL; }
|
||||||
|
else { return HexFile::DEV_GENERIC; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t hex_num_sections(fs::ifstream &file)
|
||||||
|
{
|
||||||
|
file.seekg(0);
|
||||||
|
if (! file.good()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *hex_terminator = ":00000001FF\r";
|
||||||
|
size_t res = 0;
|
||||||
|
std::string line;
|
||||||
|
while (getline(file, line, '\n').good()) {
|
||||||
|
// Account for LF vs CRLF
|
||||||
|
if (!line.empty() && line.back() != '\r') {
|
||||||
|
line.push_back('\r');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line == hex_terminator) {
|
||||||
|
res++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
HexFile::HexFile(fs::path path) :
|
||||||
|
path(std::move(path)),
|
||||||
|
device(DEV_GENERIC)
|
||||||
|
{
|
||||||
|
fs::ifstream file(this->path);
|
||||||
|
if (! file.good()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
std::stringstream header_ini;
|
||||||
|
while (std::getline(file, line, '\n').good()) {
|
||||||
|
if (line.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account for LF vs CRLF
|
||||||
|
if (!line.empty() && line.back() == '\r') {
|
||||||
|
line.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.front() == ';') {
|
||||||
|
line.front() = ' ';
|
||||||
|
header_ini << line << std::endl;
|
||||||
|
} else if (line.front() == ':') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pt::ptree ptree;
|
||||||
|
try {
|
||||||
|
pt::read_ini(header_ini, ptree);
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_device_meta = false;
|
||||||
|
const auto device = ptree.find("device");
|
||||||
|
if (device != ptree.not_found()) {
|
||||||
|
this->device = parse_device_kind(device->second.data());
|
||||||
|
has_device_meta = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto model_id = ptree.find("model_id");
|
||||||
|
if (model_id != ptree.not_found()) {
|
||||||
|
this->model_id = model_id->second.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! has_device_meta) {
|
||||||
|
// No device metadata, look at the number of 'sections'
|
||||||
|
if (hex_num_sections(file) == 2) {
|
||||||
|
// Looks like a pre-metadata l10n firmware for the MK3, assume that's the case
|
||||||
|
this->device = DEV_MK3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
32
xs/src/slic3r/Utils/HexFile.hpp
Normal file
32
xs/src/slic3r/Utils/HexFile.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef slic3r_Hex_hpp_
|
||||||
|
#define slic3r_Hex_hpp_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
|
||||||
|
struct HexFile
|
||||||
|
{
|
||||||
|
enum DeviceKind {
|
||||||
|
DEV_GENERIC,
|
||||||
|
DEV_MK2,
|
||||||
|
DEV_MK3,
|
||||||
|
DEV_MM_CONTROL,
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::filesystem::path path;
|
||||||
|
DeviceKind device;
|
||||||
|
std::string model_id;
|
||||||
|
|
||||||
|
HexFile(boost::filesystem::path path);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -3,17 +3,22 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <Setupapi.h>
|
#include <Setupapi.h>
|
||||||
#include <initguid.h>
|
#include <initguid.h>
|
||||||
#include <devguid.h>
|
#include <devguid.h>
|
||||||
|
#include <regex>
|
||||||
// Undefine min/max macros incompatible with the standard library
|
// Undefine min/max macros incompatible with the standard library
|
||||||
// For example, std::numeric_limits<std::streamsize>::max()
|
// For example, std::numeric_limits<std::streamsize>::max()
|
||||||
// produces some weird errors
|
// produces some weird errors
|
||||||
@ -34,6 +39,23 @@
|
|||||||
#include <sys/syslimits.h>
|
#include <sys/syslimits.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__) || defined(__OpenBSD__)
|
||||||
|
#include <termios.h>
|
||||||
|
#elif defined __linux__
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <asm-generic/ioctls.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using boost::optional;
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
|
|
||||||
@ -42,15 +64,43 @@ static bool looks_like_printer(const std::string &friendly_name)
|
|||||||
return friendly_name.find("Original Prusa") != std::string::npos;
|
return friendly_name.find("Original Prusa") != std::string::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#if _WIN32
|
||||||
static std::string get_tty_friendly_name(const std::string &path, const std::string &name)
|
void parse_hardware_id(const std::string &hardware_id, SerialPortInfo &spi)
|
||||||
{
|
{
|
||||||
const auto sysfs_product = (boost::format("/sys/class/tty/%1%/device/../product") % name).str();
|
unsigned vid, pid;
|
||||||
std::ifstream file(sysfs_product);
|
std::regex pattern("USB\\\\.*VID_([[:xdigit:]]+)&PID_([[:xdigit:]]+).*");
|
||||||
std::string product;
|
std::smatch matches;
|
||||||
|
if (std::regex_match(hardware_id, matches, pattern)) {
|
||||||
|
try {
|
||||||
|
vid = std::stoul(matches[1].str(), 0, 16);
|
||||||
|
pid = std::stoul(matches[2].str(), 0, 16);
|
||||||
|
spi.id_vendor = vid;
|
||||||
|
spi.id_product = pid;
|
||||||
|
}
|
||||||
|
catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::getline(file, product);
|
#ifdef __linux__
|
||||||
return file.good() ? (boost::format("%1% (%2%)") % product % path).str() : path;
|
optional<std::string> sysfs_tty_prop(const std::string &tty_dev, const std::string &name)
|
||||||
|
{
|
||||||
|
const auto prop_path = (boost::format("/sys/class/tty/%1%/device/../%2%") % tty_dev % name).str();
|
||||||
|
std::ifstream file(prop_path);
|
||||||
|
std::string res;
|
||||||
|
|
||||||
|
std::getline(file, res);
|
||||||
|
if (file.good()) { return res; }
|
||||||
|
else { return boost::none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<unsigned long> sysfs_tty_prop_hex(const std::string &tty_dev, const std::string &name)
|
||||||
|
{
|
||||||
|
auto prop = sysfs_tty_prop(tty_dev, name);
|
||||||
|
if (!prop) { return boost::none; }
|
||||||
|
|
||||||
|
try { return std::stoul(*prop, 0, 16); }
|
||||||
|
catch (...) { return boost::none; }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -80,6 +130,7 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
|
|||||||
if (port_info.port.empty())
|
if (port_info.port.empty())
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the size required to hold the device info.
|
// Find the size required to hold the device info.
|
||||||
DWORD regDataType;
|
DWORD regDataType;
|
||||||
DWORD reqSize = 0;
|
DWORD reqSize = 0;
|
||||||
@ -88,7 +139,8 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
|
|||||||
// Now store it in a buffer.
|
// Now store it in a buffer.
|
||||||
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, ®DataType, (BYTE*)hardware_id.data(), reqSize, nullptr))
|
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, ®DataType, (BYTE*)hardware_id.data(), reqSize, nullptr))
|
||||||
continue;
|
continue;
|
||||||
port_info.hardware_id = boost::nowide::narrow(hardware_id.data());
|
parse_hardware_id(boost::nowide::narrow(hardware_id.data()), port_info);
|
||||||
|
|
||||||
// Find the size required to hold the friendly name.
|
// Find the size required to hold the friendly name.
|
||||||
reqSize = 0;
|
reqSize = 0;
|
||||||
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, nullptr, 0, &reqSize);
|
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, nullptr, 0, &reqSize);
|
||||||
@ -120,6 +172,8 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
|
|||||||
if (result) {
|
if (result) {
|
||||||
SerialPortInfo port_info;
|
SerialPortInfo port_info;
|
||||||
port_info.port = path;
|
port_info.port = path;
|
||||||
|
|
||||||
|
// Attempt to read out the device friendly name
|
||||||
if ((cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
|
if ((cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
|
||||||
CFSTR("USB Interface Name"), kCFAllocatorDefault,
|
CFSTR("USB Interface Name"), kCFAllocatorDefault,
|
||||||
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
|
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
|
||||||
@ -141,6 +195,23 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
|
|||||||
}
|
}
|
||||||
if (port_info.friendly_name.empty())
|
if (port_info.friendly_name.empty())
|
||||||
port_info.friendly_name = port_info.port;
|
port_info.friendly_name = port_info.port;
|
||||||
|
|
||||||
|
// Attempt to read out the VID & PID
|
||||||
|
int vid, pid;
|
||||||
|
auto cf_vendor = IORegistryEntrySearchCFProperty(port, kIOServicePlane, CFSTR("idVendor"),
|
||||||
|
kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents);
|
||||||
|
auto cf_product = IORegistryEntrySearchCFProperty(port, kIOServicePlane, CFSTR("idProduct"),
|
||||||
|
kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents);
|
||||||
|
if (cf_vendor && cf_product) {
|
||||||
|
if (CFNumberGetValue((CFNumberRef)cf_vendor, kCFNumberIntType, &vid) &&
|
||||||
|
CFNumberGetValue((CFNumberRef)cf_product, kCFNumberIntType, &pid)) {
|
||||||
|
port_info.id_vendor = vid;
|
||||||
|
port_info.id_product = pid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cf_vendor) { CFRelease(cf_vendor); }
|
||||||
|
if (cf_product) { CFRelease(cf_product); }
|
||||||
|
|
||||||
output.emplace_back(std::move(port_info));
|
output.emplace_back(std::move(port_info));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,9 +229,15 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
|
|||||||
const auto path = dir_entry.path().string();
|
const auto path = dir_entry.path().string();
|
||||||
SerialPortInfo spi;
|
SerialPortInfo spi;
|
||||||
spi.port = path;
|
spi.port = path;
|
||||||
spi.hardware_id = path;
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
spi.friendly_name = get_tty_friendly_name(path, name);
|
auto friendly_name = sysfs_tty_prop(name, "product");
|
||||||
|
spi.friendly_name = friendly_name ? (boost::format("%1% (%2%)") % *friendly_name % path).str() : path;
|
||||||
|
auto vid = sysfs_tty_prop_hex(name, "idVendor");
|
||||||
|
auto pid = sysfs_tty_prop_hex(name, "idProduct");
|
||||||
|
if (vid && pid) {
|
||||||
|
spi.id_vendor = *vid;
|
||||||
|
spi.id_product = *pid;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
spi.friendly_name = path;
|
spi.friendly_name = path;
|
||||||
#endif
|
#endif
|
||||||
@ -189,5 +266,224 @@ std::vector<std::string> scan_serial_ports()
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Class Serial
|
||||||
|
|
||||||
|
namespace asio = boost::asio;
|
||||||
|
using boost::system::error_code;
|
||||||
|
|
||||||
|
Serial::Serial(asio::io_service& io_service) :
|
||||||
|
asio::serial_port(io_service)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Serial::Serial(asio::io_service& io_service, const std::string &name, unsigned baud_rate) :
|
||||||
|
asio::serial_port(io_service, name)
|
||||||
|
{
|
||||||
|
set_baud_rate(baud_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial::~Serial() {}
|
||||||
|
|
||||||
|
void Serial::set_baud_rate(unsigned baud_rate)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// This does not support speeds > 115200
|
||||||
|
set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
|
||||||
|
} catch (boost::system::system_error &) {
|
||||||
|
auto handle = native_handle();
|
||||||
|
|
||||||
|
auto handle_errno = [](int retval) {
|
||||||
|
if (retval != 0) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
(boost::format("Could not set baud rate: %1%") % strerror(errno)).str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if __APPLE__
|
||||||
|
termios ios;
|
||||||
|
handle_errno(::tcgetattr(handle, &ios));
|
||||||
|
handle_errno(::cfsetspeed(&ios, baud_rate));
|
||||||
|
speed_t newSpeed = baud_rate;
|
||||||
|
handle_errno(::ioctl(handle, IOSSIOSPEED, &newSpeed));
|
||||||
|
handle_errno(::tcsetattr(handle, TCSANOW, &ios));
|
||||||
|
#elif __linux
|
||||||
|
|
||||||
|
/* The following definitions are kindly borrowed from:
|
||||||
|
/usr/include/asm-generic/termbits.h
|
||||||
|
Unfortunately we cannot just include that one because
|
||||||
|
it would redefine the "struct termios" already defined
|
||||||
|
the <termios.h> already included by Boost.ASIO. */
|
||||||
|
#define K_NCCS 19
|
||||||
|
struct termios2 {
|
||||||
|
tcflag_t c_iflag;
|
||||||
|
tcflag_t c_oflag;
|
||||||
|
tcflag_t c_cflag;
|
||||||
|
tcflag_t c_lflag;
|
||||||
|
cc_t c_line;
|
||||||
|
cc_t c_cc[K_NCCS];
|
||||||
|
speed_t c_ispeed;
|
||||||
|
speed_t c_ospeed;
|
||||||
|
};
|
||||||
|
#define BOTHER CBAUDEX
|
||||||
|
|
||||||
|
termios2 ios;
|
||||||
|
handle_errno(::ioctl(handle, TCGETS2, &ios));
|
||||||
|
ios.c_ispeed = ios.c_ospeed = baud_rate;
|
||||||
|
ios.c_cflag &= ~CBAUD;
|
||||||
|
ios.c_cflag |= BOTHER | CLOCAL | CREAD;
|
||||||
|
ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
|
||||||
|
ios.c_cc[VTIME] = 1;
|
||||||
|
handle_errno(::ioctl(handle, TCSETS2, &ios));
|
||||||
|
|
||||||
|
#elif __OpenBSD__
|
||||||
|
struct termios ios;
|
||||||
|
handle_errno(::tcgetattr(handle, &ios));
|
||||||
|
handle_errno(::cfsetspeed(&ios, baud_rate));
|
||||||
|
handle_errno(::tcsetattr(handle, TCSAFLUSH, &ios));
|
||||||
|
#else
|
||||||
|
throw std::runtime_error("Custom baud rates are not currently supported on this OS");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Serial::set_DTR(bool on)
|
||||||
|
{
|
||||||
|
auto handle = native_handle();
|
||||||
|
#if defined(_WIN32) && !defined(__SYMBIAN32__)
|
||||||
|
if (! EscapeCommFunction(handle, on ? SETDTR : CLRDTR)) {
|
||||||
|
throw std::runtime_error("Could not set serial port DTR");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int status;
|
||||||
|
if (::ioctl(handle, TIOCMGET, &status) == 0) {
|
||||||
|
on ? status |= TIOCM_DTR : status &= ~TIOCM_DTR;
|
||||||
|
if (::ioctl(handle, TIOCMSET, &status) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error(
|
||||||
|
(boost::format("Could not set serial port DTR: %1%") % strerror(errno)).str()
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Serial::reset_line_num()
|
||||||
|
{
|
||||||
|
// See https://github.com/MarlinFirmware/Marlin/wiki/M110
|
||||||
|
printer_write_line("M110 N0", 0);
|
||||||
|
m_line_num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Serial::read_line(unsigned timeout, std::string &line, error_code &ec)
|
||||||
|
{
|
||||||
|
auto &io_service = get_io_service();
|
||||||
|
asio::deadline_timer timer(io_service);
|
||||||
|
char c = 0;
|
||||||
|
bool fail = false;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
io_service.reset();
|
||||||
|
|
||||||
|
asio::async_read(*this, boost::asio::buffer(&c, 1), [&](const error_code &read_ec, size_t size) {
|
||||||
|
if (ec || size == 0) {
|
||||||
|
fail = true;
|
||||||
|
ec = read_ec;
|
||||||
|
}
|
||||||
|
timer.cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (timeout > 0) {
|
||||||
|
timer.expires_from_now(boost::posix_time::milliseconds(timeout));
|
||||||
|
timer.async_wait([&](const error_code &ec) {
|
||||||
|
// Ignore timer aborts
|
||||||
|
if (!ec) {
|
||||||
|
fail = true;
|
||||||
|
this->cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
|
|
||||||
|
if (fail) {
|
||||||
|
return false;
|
||||||
|
} else if (c != '\n') {
|
||||||
|
line += c;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Serial::printer_setup()
|
||||||
|
{
|
||||||
|
printer_reset();
|
||||||
|
write_string("\r\r\r\r\r\r\r\r\r\r"); // Gets rid of line noise, if any
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Serial::write_string(const std::string &str)
|
||||||
|
{
|
||||||
|
// TODO: might be wise to timeout here as well
|
||||||
|
return asio::write(*this, asio::buffer(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Serial::printer_ready_wait(unsigned retries, unsigned timeout)
|
||||||
|
{
|
||||||
|
std::string line;
|
||||||
|
error_code ec;
|
||||||
|
|
||||||
|
for (; retries > 0; retries--) {
|
||||||
|
reset_line_num();
|
||||||
|
|
||||||
|
while (read_line(timeout, line, ec)) {
|
||||||
|
if (line == "ok") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
line.clear();
|
||||||
|
}
|
||||||
|
line.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Serial::printer_write_line(const std::string &line, unsigned line_num)
|
||||||
|
{
|
||||||
|
const auto formatted_line = Utils::Serial::printer_format_line(line, line_num);
|
||||||
|
return write_string(formatted_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Serial::printer_write_line(const std::string &line)
|
||||||
|
{
|
||||||
|
m_line_num++;
|
||||||
|
return printer_write_line(line, m_line_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Serial::printer_reset()
|
||||||
|
{
|
||||||
|
this->set_DTR(false);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
this->set_DTR(true);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
this->set_DTR(false);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Serial::printer_format_line(const std::string &line, unsigned line_num)
|
||||||
|
{
|
||||||
|
const auto line_num_str = std::to_string(line_num);
|
||||||
|
|
||||||
|
unsigned checksum = 'N';
|
||||||
|
for (auto c : line_num_str) { checksum ^= c; }
|
||||||
|
checksum ^= ' ';
|
||||||
|
for (auto c : line) { checksum ^= c; }
|
||||||
|
|
||||||
|
return (boost::format("N%1% %2%*%3%\n") % line_num_str % line % checksum).str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -1,31 +1,80 @@
|
|||||||
#ifndef slic3r_GUI_Utils_Serial_hpp_
|
#ifndef slic3r_GUI_Utils_Serial_hpp_
|
||||||
#define slic3r_GUI_Utils_Serial_hpp_
|
#define slic3r_GUI_Utils_Serial_hpp_
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <boost/system/error_code.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
|
|
||||||
struct SerialPortInfo {
|
struct SerialPortInfo {
|
||||||
std::string port;
|
std::string port;
|
||||||
std::string hardware_id;
|
unsigned id_vendor = -1;
|
||||||
|
unsigned id_product = -1;
|
||||||
std::string friendly_name;
|
std::string friendly_name;
|
||||||
bool is_printer = false;
|
bool is_printer = false;
|
||||||
|
|
||||||
|
bool id_match(unsigned id_vendor, unsigned id_product) const { return id_vendor == this->id_vendor && id_product == this->id_product; }
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2)
|
inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2)
|
||||||
{
|
{
|
||||||
return sp1.port == sp2.port &&
|
return
|
||||||
sp1.hardware_id == sp2.hardware_id &&
|
sp1.port == sp2.port &&
|
||||||
sp1.is_printer == sp2.is_printer;
|
sp1.id_vendor == sp2.id_vendor &&
|
||||||
|
sp1.id_product == sp2.id_product &&
|
||||||
|
sp1.is_printer == sp2.is_printer;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern std::vector<std::string> scan_serial_ports();
|
extern std::vector<std::string> scan_serial_ports();
|
||||||
extern std::vector<SerialPortInfo> scan_serial_ports_extended();
|
extern std::vector<SerialPortInfo> scan_serial_ports_extended();
|
||||||
|
|
||||||
|
|
||||||
|
class Serial : public boost::asio::serial_port
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Serial(boost::asio::io_service &io_service);
|
||||||
|
Serial(boost::asio::io_service &io_service, const std::string &name, unsigned baud_rate);
|
||||||
|
Serial(const Serial &) = delete;
|
||||||
|
Serial &operator=(const Serial &) = delete;
|
||||||
|
~Serial();
|
||||||
|
|
||||||
|
void set_baud_rate(unsigned baud_rate);
|
||||||
|
void set_DTR(bool on);
|
||||||
|
|
||||||
|
// Resets the line number both internally as well as with the firmware using M110
|
||||||
|
void reset_line_num();
|
||||||
|
|
||||||
|
// Reads a line or times out, the timeout is in milliseconds
|
||||||
|
bool read_line(unsigned timeout, std::string &line, boost::system::error_code &ec);
|
||||||
|
|
||||||
|
// Perform setup for communicating with a printer
|
||||||
|
void printer_setup();
|
||||||
|
|
||||||
|
// Write data from a string
|
||||||
|
size_t write_string(const std::string &str);
|
||||||
|
|
||||||
|
bool printer_ready_wait(unsigned retries, unsigned timeout);
|
||||||
|
|
||||||
|
// Write Marlin-formatted line, with a line number and a checksum
|
||||||
|
size_t printer_write_line(const std::string &line, unsigned line_num);
|
||||||
|
|
||||||
|
// Same as above, but with internally-managed line number
|
||||||
|
size_t printer_write_line(const std::string &line);
|
||||||
|
|
||||||
|
// Toggles DTR to reset the printer
|
||||||
|
void printer_reset();
|
||||||
|
|
||||||
|
// Formats a line Marlin-style, ie. with a sequential number and a checksum
|
||||||
|
static std::string printer_format_line(const std::string &line, unsigned line_num);
|
||||||
|
private:
|
||||||
|
unsigned m_line_num = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} // Utils
|
} // Utils
|
||||||
} // Slic3r
|
} // Slic3r
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user