Finished GCodeSender
This commit is contained in:
parent
8b438dc0de
commit
b126f92f41
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ xs/buildtmp
|
|||||||
*.o
|
*.o
|
||||||
MANIFEST.bak
|
MANIFEST.bak
|
||||||
xs/MANIFEST.bak
|
xs/MANIFEST.bak
|
||||||
|
xs/assertlib*
|
||||||
|
@ -10,17 +10,27 @@ BEGIN {
|
|||||||
|
|
||||||
use Slic3r;
|
use Slic3r;
|
||||||
|
|
||||||
|
die "Usage: send-gcode.pl SERIALPORT BAUDRATE GCODE_FILE\n"
|
||||||
|
if @ARGV != 3;
|
||||||
|
|
||||||
my $serial = Slic3r::GCode::Sender->new($ARGV[0], $ARGV[1]);
|
my $serial = Slic3r::GCode::Sender->new($ARGV[0], $ARGV[1]);
|
||||||
|
1 until $serial->is_connected;
|
||||||
|
print "Connected to printer\n";
|
||||||
|
|
||||||
$serial->send($ARGV[2]);
|
{
|
||||||
|
local $/ = "\n";
|
||||||
exit;
|
Slic3r::open(\my $fh, '<', $ARGV[2])
|
||||||
|
or die "Unable to open $ARGV[2]: $!\n";
|
||||||
while (1) {
|
binmode $fh, ':utf8';
|
||||||
$serial->send("1");
|
while (<$fh>) {
|
||||||
sleep 1;
|
$serial->send($_);
|
||||||
$serial->send("0");
|
}
|
||||||
sleep 1;
|
close $fh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while ((my $queue_size = $serial->queue_size) > 0) {
|
||||||
|
printf "Queue size: %d\n", $queue_size;
|
||||||
|
}
|
||||||
|
$serial->disconnect;
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
18
xs/MANIFEST
18
xs/MANIFEST
@ -1,21 +1,3 @@
|
|||||||
assertlib2WNuZugY.dSYM/Contents/Info.plist
|
|
||||||
assertlib2WNuZugY.dSYM/Contents/Resources/DWARF/assertlib2WNuZugY
|
|
||||||
assertlib_1AsN89M.dSYM/Contents/Info.plist
|
|
||||||
assertlib_1AsN89M.dSYM/Contents/Resources/DWARF/assertlib_1AsN89M
|
|
||||||
assertlibCHIyOgiI.dSYM/Contents/Info.plist
|
|
||||||
assertlibCHIyOgiI.dSYM/Contents/Resources/DWARF/assertlibCHIyOgiI
|
|
||||||
assertlibePik23eL.dSYM/Contents/Info.plist
|
|
||||||
assertlibePik23eL.dSYM/Contents/Resources/DWARF/assertlibePik23eL
|
|
||||||
assertlibk8tKRIAD.dSYM/Contents/Info.plist
|
|
||||||
assertlibk8tKRIAD.dSYM/Contents/Resources/DWARF/assertlibk8tKRIAD
|
|
||||||
assertlibNTySykmH.dSYM/Contents/Info.plist
|
|
||||||
assertlibNTySykmH.dSYM/Contents/Resources/DWARF/assertlibNTySykmH
|
|
||||||
assertlibpBddNlll.dSYM/Contents/Info.plist
|
|
||||||
assertlibpBddNlll.dSYM/Contents/Resources/DWARF/assertlibpBddNlll
|
|
||||||
assertlibtdgvvqTu.dSYM/Contents/Info.plist
|
|
||||||
assertlibtdgvvqTu.dSYM/Contents/Resources/DWARF/assertlibtdgvvqTu
|
|
||||||
assertlibxg2L4e5_.dSYM/Contents/Info.plist
|
|
||||||
assertlibxg2L4e5_.dSYM/Contents/Resources/DWARF/assertlibxg2L4e5_
|
|
||||||
Build.PL
|
Build.PL
|
||||||
include/boost/aligned_storage.hpp
|
include/boost/aligned_storage.hpp
|
||||||
include/boost/array.hpp
|
include/boost/array.hpp
|
||||||
|
@ -71,3 +71,5 @@
|
|||||||
|
|
||||||
# Avoid archives of this distribution
|
# Avoid archives of this distribution
|
||||||
\bSlic3r-XS-[\d\.\_]+
|
\bSlic3r-XS-[\d\.\_]+
|
||||||
|
|
||||||
|
^assertlib
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
#ifdef BOOST_LIBS
|
#ifdef BOOST_LIBS
|
||||||
#include "GCodeSender.hpp"
|
#include "GCodeSender.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <istream>
|
||||||
|
#include <string>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
namespace asio = boost::asio;
|
namespace asio = boost::asio;
|
||||||
|
|
||||||
GCodeSender::GCodeSender(std::string devname, unsigned int baud_rate)
|
GCodeSender::GCodeSender(std::string devname, unsigned int baud_rate)
|
||||||
: io(), serial(io)
|
: io(), serial(io), can_send(false), sent(0), error(false), connected(false)
|
||||||
{
|
{
|
||||||
this->serial.open(devname);
|
this->serial.open(devname);
|
||||||
this->serial.set_option(asio::serial_port_base::baud_rate(baud_rate));
|
this->serial.set_option(asio::serial_port_base::baud_rate(baud_rate));
|
||||||
@ -18,56 +23,188 @@ GCodeSender::GCodeSender(std::string devname, unsigned int baud_rate)
|
|||||||
this->serial.close();
|
this->serial.close();
|
||||||
this->serial.open(devname);
|
this->serial.open(devname);
|
||||||
this->serial.set_option(asio::serial_port_base::parity(asio::serial_port_base::parity::none));
|
this->serial.set_option(asio::serial_port_base::parity(asio::serial_port_base::parity::none));
|
||||||
|
this->open = true;
|
||||||
|
|
||||||
std::string greeting;
|
// this gives some work to the io_service before it is started
|
||||||
this->read_line(&greeting);
|
// (post() runs the supplied function in its thread)
|
||||||
|
this->io.post(boost::bind(&GCodeSender::do_read, this));
|
||||||
|
|
||||||
|
// start reading in the background thread
|
||||||
|
boost::thread t(boost::bind(&asio::io_service::run, &this->io));
|
||||||
|
this->background_thread.swap(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCodeSender::disconnect()
|
||||||
|
{
|
||||||
|
if (!this->open) return;
|
||||||
|
|
||||||
|
this->open = false;
|
||||||
|
this->connected = false;
|
||||||
|
this->io.post(boost::bind(&GCodeSender::do_close, this));
|
||||||
|
this->background_thread.join();
|
||||||
|
this->io.reset();
|
||||||
|
if (this->error_status()) {
|
||||||
|
throw(boost::system::system_error(boost::system::error_code(),
|
||||||
|
"Error while closing the device"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
GCodeSender::is_connected() const
|
||||||
|
{
|
||||||
|
return this->connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
GCodeSender::queue_size() const
|
||||||
|
{
|
||||||
|
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||||
|
return this->queue.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCodeSender::do_close()
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
this->serial.cancel(ec);
|
||||||
|
if (ec) this->set_error_status(true);
|
||||||
|
this->serial.close(ec);
|
||||||
|
if (ec) this->set_error_status(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCodeSender::set_error_status(bool e)
|
||||||
|
{
|
||||||
|
boost::lock_guard<boost::mutex> l(this->error_mutex);
|
||||||
|
this->error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
GCodeSender::error_status() const
|
||||||
|
{
|
||||||
|
boost::lock_guard<boost::mutex> l(this->error_mutex);
|
||||||
|
return this->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCodeSender::do_read()
|
||||||
|
{
|
||||||
|
// read one line
|
||||||
|
asio::async_read_until(
|
||||||
|
this->serial,
|
||||||
|
this->read_buffer,
|
||||||
|
'\n',
|
||||||
|
boost::bind(
|
||||||
|
&GCodeSender::on_read,
|
||||||
|
this,
|
||||||
|
asio::placeholders::error,
|
||||||
|
asio::placeholders::bytes_transferred
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCodeSender::on_read(const boost::system::error_code& error,
|
||||||
|
size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
if (error) {
|
||||||
|
// error can be true even because the serial port was closed.
|
||||||
|
// In this case it is not a real error, so ignore.
|
||||||
|
if (this->open) {
|
||||||
|
this->do_close();
|
||||||
|
this->set_error_status(true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the read buffer into string
|
||||||
|
std::string line((std::istreambuf_iterator<char>(&this->read_buffer)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
|
// parse incoming line
|
||||||
|
if (!this->connected
|
||||||
|
&& (boost::starts_with(line, "start")
|
||||||
|
|| boost::starts_with(line, "Grbl "))) {
|
||||||
|
this->connected = true;
|
||||||
|
this->can_send = true;
|
||||||
|
this->send();
|
||||||
|
} else if (boost::starts_with(line, "ok")) {
|
||||||
|
{
|
||||||
|
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||||
|
this->queue.pop();
|
||||||
|
}
|
||||||
|
this->can_send = true;
|
||||||
|
this->send();
|
||||||
|
} else if (boost::istarts_with(line, "resend")
|
||||||
|
|| boost::istarts_with(line, "rs")) {
|
||||||
|
// extract the first number from line
|
||||||
|
using boost::lexical_cast;
|
||||||
|
using boost::bad_lexical_cast;
|
||||||
|
boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit());
|
||||||
|
size_t toresend = lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
|
||||||
|
if (toresend == this->sent) {
|
||||||
|
this->sent--;
|
||||||
|
this->can_send = true;
|
||||||
|
this->send();
|
||||||
|
} else {
|
||||||
|
printf("Cannot resend %lu (last was %lu)\n", toresend, this->sent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->do_read();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCodeSender::send(const std::vector<std::string> &lines)
|
GCodeSender::send(const std::vector<std::string> &lines)
|
||||||
{
|
{
|
||||||
this->lines = lines;
|
// append lines to queue
|
||||||
|
{
|
||||||
|
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||||
|
for (std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line)
|
||||||
|
this->queue.push(*line);
|
||||||
|
}
|
||||||
|
this->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCodeSender::send(const std::string &s)
|
GCodeSender::send(const std::string &line)
|
||||||
{
|
{
|
||||||
|
// append line to queue
|
||||||
{
|
{
|
||||||
std::stringstream ss(s);
|
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||||
std::string line;
|
this->queue.push(line);
|
||||||
while (std::getline(ss, line, '\n'))
|
|
||||||
this->lines.push_back(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::vector<std::string>::const_iterator line = this->lines.begin(); line != this->lines.end(); ++line) {
|
|
||||||
this->send_line(*line);
|
|
||||||
}
|
}
|
||||||
|
this->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCodeSender::send_line(const std::string &line)
|
GCodeSender::send()
|
||||||
{
|
{
|
||||||
|
// printer is not connected or we're still waiting for the previous ack
|
||||||
|
if (!this->can_send) return;
|
||||||
|
|
||||||
|
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||||
|
if (this->queue.empty()) return;
|
||||||
|
|
||||||
|
// get line and strip any comment
|
||||||
|
std::string line = this->queue.front();
|
||||||
|
if (size_t comment_pos = line.find_first_of(';') != std::string::npos)
|
||||||
|
line.erase(comment_pos, std::string::npos);
|
||||||
|
boost::algorithm::trim(line);
|
||||||
|
|
||||||
|
// calculate checksum
|
||||||
|
int cs = 0;
|
||||||
|
for (std::string::const_iterator it = line.begin(); it != line.end(); ++it)
|
||||||
|
cs = cs ^ *it;
|
||||||
|
|
||||||
|
sent++;
|
||||||
asio::streambuf b;
|
asio::streambuf b;
|
||||||
std::ostream os(&b);
|
std::ostream os(&b);
|
||||||
os << line << "\n";
|
os << "N" << sent << " " << line
|
||||||
|
<< "*" << cs << "\n";
|
||||||
asio::write(this->serial, b);
|
asio::write(this->serial, b);
|
||||||
}
|
this->can_send = false;
|
||||||
|
|
||||||
void
|
|
||||||
GCodeSender::read_line(std::string* line)
|
|
||||||
{
|
|
||||||
for (;;) {
|
|
||||||
char c;
|
|
||||||
asio::read(this->serial, asio::buffer(&c, 1));
|
|
||||||
switch (c) {
|
|
||||||
case '\r':
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
*line += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
@ -3,28 +3,47 @@
|
|||||||
#ifdef BOOST_LIBS
|
#ifdef BOOST_LIBS
|
||||||
|
|
||||||
#include <myinit.h>
|
#include <myinit.h>
|
||||||
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
namespace asio = boost::asio;
|
namespace asio = boost::asio;
|
||||||
|
|
||||||
class GCodeSender {
|
class GCodeSender : private boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
GCodeSender(std::string devname, unsigned int baud_rate);
|
GCodeSender(std::string devname, unsigned int baud_rate);
|
||||||
void send(const std::vector<std::string> &lines);
|
void send(const std::vector<std::string> &lines);
|
||||||
void send(const std::string &s);
|
void send(const std::string &s);
|
||||||
|
void disconnect();
|
||||||
|
bool error_status() const;
|
||||||
|
bool is_connected() const;
|
||||||
|
size_t queue_size() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
asio::io_service io;
|
asio::io_service io;
|
||||||
asio::serial_port serial;
|
asio::serial_port serial;
|
||||||
std::vector<std::string> lines;
|
boost::thread background_thread;
|
||||||
|
boost::asio::streambuf read_buffer;
|
||||||
|
bool open; // whether the serial socket is connected
|
||||||
|
bool connected; // whether the printer is online
|
||||||
|
bool error;
|
||||||
|
mutable boost::mutex error_mutex;
|
||||||
|
|
||||||
void send_line(const std::string &line);
|
mutable boost::mutex queue_mutex;
|
||||||
void read_line(std::string* line);
|
std::queue<std::string> queue;
|
||||||
|
bool can_send;
|
||||||
|
size_t sent;
|
||||||
|
|
||||||
|
void set_error_status(bool e);
|
||||||
|
void do_close();
|
||||||
|
void do_read();
|
||||||
|
void on_read(const boost::system::error_code& error, size_t bytes_transferred);
|
||||||
|
void send();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
GCodeSender(std::string port, unsigned int baud_rate);
|
GCodeSender(std::string port, unsigned int baud_rate);
|
||||||
~GCodeSender();
|
~GCodeSender();
|
||||||
|
|
||||||
|
bool is_connected() const;
|
||||||
|
int queue_size() const;
|
||||||
|
void disconnect();
|
||||||
void send(std::string s);
|
void send(std::string s);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user