Finished GCodeSender

This commit is contained in:
Alessandro Ranellucci 2014-12-27 22:57:20 +01:00
parent 8b438dc0de
commit b126f92f41
7 changed files with 222 additions and 68 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ xs/buildtmp
*.o
MANIFEST.bak
xs/MANIFEST.bak
xs/assertlib*

View File

@ -10,17 +10,27 @@ BEGIN {
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]);
1 until $serial->is_connected;
print "Connected to printer\n";
$serial->send($ARGV[2]);
exit;
while (1) {
$serial->send("1");
sleep 1;
$serial->send("0");
sleep 1;
{
local $/ = "\n";
Slic3r::open(\my $fh, '<', $ARGV[2])
or die "Unable to open $ARGV[2]: $!\n";
binmode $fh, ':utf8';
while (<$fh>) {
$serial->send($_);
}
close $fh;
}
while ((my $queue_size = $serial->queue_size) > 0) {
printf "Queue size: %d\n", $queue_size;
}
$serial->disconnect;
__END__

View File

@ -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
include/boost/aligned_storage.hpp
include/boost/array.hpp

View File

@ -71,3 +71,5 @@
# Avoid archives of this distribution
\bSlic3r-XS-[\d\.\_]+
^assertlib

View File

@ -1,13 +1,18 @@
#ifdef BOOST_LIBS
#include "GCodeSender.hpp"
#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 asio = boost::asio;
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.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.open(devname);
this->serial.set_option(asio::serial_port_base::parity(asio::serial_port_base::parity::none));
this->open = true;
std::string greeting;
this->read_line(&greeting);
// this gives some work to the io_service before it is started
// (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
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
GCodeSender::send(const std::string &s)
GCodeSender::send(const std::string &line)
{
// append line to queue
{
std::stringstream ss(s);
std::string 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);
boost::lock_guard<boost::mutex> l(this->queue_mutex);
this->queue.push(line);
}
this->send();
}
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;
std::ostream os(&b);
os << line << "\n";
os << "N" << sent << " " << line
<< "*" << cs << "\n";
asio::write(this->serial, b);
}
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;
}
}
this->can_send = false;
}
#ifdef SLIC3RXS

View File

@ -3,28 +3,47 @@
#ifdef BOOST_LIBS
#include <myinit.h>
#include <queue>
#include <string>
#include <vector>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
namespace Slic3r {
namespace asio = boost::asio;
class GCodeSender {
class GCodeSender : private boost::noncopyable {
public:
GCodeSender(std::string devname, unsigned int baud_rate);
void send(const std::vector<std::string> &lines);
void send(const std::string &s);
void disconnect();
bool error_status() const;
bool is_connected() const;
size_t queue_size() const;
private:
asio::io_service io;
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);
void read_line(std::string* line);
mutable boost::mutex queue_mutex;
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();
};
}

View File

@ -11,6 +11,9 @@
GCodeSender(std::string port, unsigned int baud_rate);
~GCodeSender();
bool is_connected() const;
int queue_size() const;
void disconnect();
void send(std::string s);
};