Finished GCodeSender
This commit is contained in:
parent
8b438dc0de
commit
b126f92f41
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ xs/buildtmp
|
||||
*.o
|
||||
MANIFEST.bak
|
||||
xs/MANIFEST.bak
|
||||
xs/assertlib*
|
||||
|
@ -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__
|
||||
|
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
|
||||
include/boost/aligned_storage.hpp
|
||||
include/boost/array.hpp
|
||||
|
@ -71,3 +71,5 @@
|
||||
|
||||
# Avoid archives of this distribution
|
||||
\bSlic3r-XS-[\d\.\_]+
|
||||
|
||||
^assertlib
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user