GCodeSender.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. ///|/ Copyright (c) Prusa Research 2016 - 2021 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
  2. ///|/ Copyright (c) Slic3r 2014 - 2016 Alessandro Ranellucci @alranel
  3. ///|/ Copyright (c) 2016 Gregor Best
  4. ///|/
  5. ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
  6. ///|/
  7. #include "GCodeSender.hpp"
  8. #include <iostream>
  9. #include <istream>
  10. #include <string>
  11. #include <thread>
  12. #include <boost/algorithm/string/predicate.hpp>
  13. #include <boost/algorithm/string/trim.hpp>
  14. #include <boost/date_time/posix_time/posix_time.hpp>
  15. #include <boost/lexical_cast.hpp>
  16. #if defined(__APPLE__) || defined(__OpenBSD__)
  17. #include <termios.h>
  18. #endif
  19. #ifdef __APPLE__
  20. #include <sys/ioctl.h>
  21. #include <IOKit/serial/ioss.h>
  22. #endif
  23. #ifdef __linux__
  24. #include <sys/ioctl.h>
  25. #include <fcntl.h>
  26. #include "/usr/include/asm-generic/ioctls.h"
  27. /* The following definitions are kindly borrowed from:
  28. /usr/include/asm-generic/termbits.h
  29. Unfortunately we cannot just include that one because
  30. it would redefine the "struct termios" already defined
  31. the <termios.h> already included by Boost.ASIO. */
  32. #define K_NCCS 19
  33. struct termios2 {
  34. tcflag_t c_iflag;
  35. tcflag_t c_oflag;
  36. tcflag_t c_cflag;
  37. tcflag_t c_lflag;
  38. cc_t c_line;
  39. cc_t c_cc[K_NCCS];
  40. speed_t c_ispeed;
  41. speed_t c_ospeed;
  42. };
  43. #define BOTHER CBAUDEX
  44. #endif
  45. //#define DEBUG_SERIAL
  46. #ifdef DEBUG_SERIAL
  47. #include <cstdlib>
  48. #include <fstream>
  49. std::fstream fs;
  50. #endif
  51. #define KEEP_SENT 20
  52. namespace Slic3r {
  53. GCodeSender::GCodeSender()
  54. : io(), serial(io), can_send(false), sent(0), open(false), error(false),
  55. connected(false), queue_paused(false)
  56. {
  57. #ifdef DEBUG_SERIAL
  58. std::srand(std::time(nullptr));
  59. #endif
  60. }
  61. GCodeSender::~GCodeSender()
  62. {
  63. this->disconnect();
  64. }
  65. bool
  66. GCodeSender::connect(std::string devname, unsigned int baud_rate)
  67. {
  68. this->disconnect();
  69. this->set_error_status(false);
  70. try {
  71. this->serial.open(devname);
  72. this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd));
  73. this->serial.set_option(boost::asio::serial_port_base::character_size(boost::asio::serial_port_base::character_size(8)));
  74. this->serial.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
  75. this->serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
  76. this->set_baud_rate(baud_rate);
  77. this->serial.close();
  78. this->serial.open(devname);
  79. this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
  80. // set baud rate again because set_option overwrote it
  81. this->set_baud_rate(baud_rate);
  82. this->open = true;
  83. this->reset();
  84. } catch (boost::system::system_error &) {
  85. this->set_error_status(true);
  86. return false;
  87. }
  88. // a reset firmware expect line numbers to start again from 1
  89. this->sent = 0;
  90. this->last_sent.clear();
  91. /* Initialize debugger */
  92. #ifdef DEBUG_SERIAL
  93. fs.open("serial.txt", std::fstream::out | std::fstream::trunc);
  94. #endif
  95. // this gives some work to the io_service before it is started
  96. // (post() runs the supplied function in its thread)
  97. this->io.post(boost::bind(&GCodeSender::do_read, this));
  98. // start reading in the background thread
  99. boost::thread t(boost::bind(&boost::asio::io_service::run, &this->io));
  100. this->background_thread.swap(t);
  101. // always send a M105 to check for connection because firmware might be silent on connect
  102. //FIXME Vojtech: This is being sent too early, leading to line number synchronization issues,
  103. // from which the GCodeSender never recovers.
  104. // this->send("M105", true);
  105. return true;
  106. }
  107. void
  108. GCodeSender::set_baud_rate(unsigned int baud_rate)
  109. {
  110. try {
  111. // This does not support speeds > 115200
  112. this->serial.set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
  113. } catch (boost::system::system_error &) {
  114. boost::asio::serial_port::native_handle_type handle = this->serial.native_handle();
  115. #if __APPLE__
  116. termios ios;
  117. ::tcgetattr(handle, &ios);
  118. ::cfsetspeed(&ios, baud_rate);
  119. speed_t newSpeed = baud_rate;
  120. ioctl(handle, IOSSIOSPEED, &newSpeed);
  121. ::tcsetattr(handle, TCSANOW, &ios);
  122. #elif __linux__
  123. termios2 ios;
  124. if (ioctl(handle, TCGETS2, &ios))
  125. printf("Error in TCGETS2: %s\n", strerror(errno));
  126. ios.c_ispeed = ios.c_ospeed = baud_rate;
  127. ios.c_cflag &= ~CBAUD;
  128. ios.c_cflag |= BOTHER | CLOCAL | CREAD;
  129. ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
  130. ios.c_cc[VTIME] = 1;
  131. if (ioctl(handle, TCSETS2, &ios))
  132. printf("Error in TCSETS2: %s\n", strerror(errno));
  133. #elif __OpenBSD__
  134. struct termios ios;
  135. ::tcgetattr(handle, &ios);
  136. ::cfsetspeed(&ios, baud_rate);
  137. if (::tcsetattr(handle, TCSAFLUSH, &ios) != 0)
  138. printf("Failed to set baud rate: %s\n", strerror(errno));
  139. #else
  140. //throw Slic3r::InvalidArgument("OS does not currently support custom bauds");
  141. #endif
  142. }
  143. }
  144. void
  145. GCodeSender::disconnect()
  146. {
  147. if (!this->open) return;
  148. this->open = false;
  149. this->connected = false;
  150. this->io.post(boost::bind(&GCodeSender::do_close, this));
  151. this->background_thread.join();
  152. this->io.reset();
  153. /*
  154. if (this->error_status()) {
  155. throw(boost::system::system_error(boost::system::error_code(),
  156. "Error while closing the device"));
  157. }
  158. */
  159. #ifdef DEBUG_SERIAL
  160. fs << "DISCONNECTED" << std::endl << std::flush;
  161. fs.close();
  162. #endif
  163. }
  164. bool
  165. GCodeSender::is_connected() const
  166. {
  167. return this->connected;
  168. }
  169. bool
  170. GCodeSender::wait_connected(unsigned int timeout) const
  171. {
  172. using namespace boost::posix_time;
  173. ptime t0 = second_clock::local_time() + seconds(timeout);
  174. while (!this->connected) {
  175. if (second_clock::local_time() > t0) return false;
  176. boost::this_thread::sleep(boost::posix_time::milliseconds(100));
  177. }
  178. return true;
  179. }
  180. size_t
  181. GCodeSender::queue_size() const
  182. {
  183. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  184. return this->queue.size();
  185. }
  186. void
  187. GCodeSender::pause_queue()
  188. {
  189. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  190. this->queue_paused = true;
  191. }
  192. void
  193. GCodeSender::resume_queue()
  194. {
  195. {
  196. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  197. this->queue_paused = false;
  198. }
  199. this->send();
  200. }
  201. void
  202. GCodeSender::purge_queue(bool priority)
  203. {
  204. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  205. if (priority) {
  206. // clear priority queue
  207. std::list<std::string> empty;
  208. std::swap(this->priqueue, empty);
  209. } else {
  210. // clear queue
  211. std::queue<std::string> empty;
  212. std::swap(this->queue, empty);
  213. this->queue_paused = false;
  214. }
  215. }
  216. // purge log and return its contents
  217. std::vector<std::string>
  218. GCodeSender::purge_log()
  219. {
  220. boost::lock_guard<boost::mutex> l(this->log_mutex);
  221. std::vector<std::string> retval;
  222. retval.reserve(this->log.size());
  223. while (!this->log.empty()) {
  224. retval.push_back(this->log.front());
  225. this->log.pop();
  226. }
  227. return retval;
  228. }
  229. std::string
  230. GCodeSender::getT() const
  231. {
  232. boost::lock_guard<boost::mutex> l(this->log_mutex);
  233. return this->T;
  234. }
  235. std::string
  236. GCodeSender::getB() const
  237. {
  238. boost::lock_guard<boost::mutex> l(this->log_mutex);
  239. return this->B;
  240. }
  241. void
  242. GCodeSender::do_close()
  243. {
  244. this->set_error_status(false);
  245. boost::system::error_code ec;
  246. this->serial.cancel(ec);
  247. if (ec) this->set_error_status(true);
  248. this->serial.close(ec);
  249. if (ec) this->set_error_status(true);
  250. }
  251. void
  252. GCodeSender::set_error_status(bool e)
  253. {
  254. boost::lock_guard<boost::mutex> l(this->error_mutex);
  255. this->error = e;
  256. }
  257. bool
  258. GCodeSender::error_status() const
  259. {
  260. boost::lock_guard<boost::mutex> l(this->error_mutex);
  261. return this->error;
  262. }
  263. void
  264. GCodeSender::do_read()
  265. {
  266. // read one line
  267. boost::asio::async_read_until(
  268. this->serial,
  269. this->read_buffer,
  270. '\n',
  271. boost::bind(
  272. &GCodeSender::on_read,
  273. this,
  274. boost::asio::placeholders::error,
  275. boost::asio::placeholders::bytes_transferred
  276. )
  277. );
  278. }
  279. void
  280. GCodeSender::on_read(const boost::system::error_code& error,
  281. size_t bytes_transferred)
  282. {
  283. this->set_error_status(false);
  284. if (error) {
  285. #ifdef __APPLE__
  286. if (error.value() == 45) {
  287. // OS X bug: http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
  288. this->do_read();
  289. return;
  290. }
  291. #endif
  292. // printf("ERROR: [%d] %s\n", error.value(), error.message().c_str());
  293. // error can be true even because the serial port was closed.
  294. // In this case it is not a real error, so ignore.
  295. if (this->open) {
  296. this->do_close();
  297. this->set_error_status(true);
  298. }
  299. return;
  300. }
  301. std::istream is(&this->read_buffer);
  302. std::string line;
  303. std::getline(is, line);
  304. if (!line.empty()) {
  305. #ifdef DEBUG_SERIAL
  306. fs << "<< " << line << std::endl << std::flush;
  307. #endif
  308. // note that line might contain \r at its end
  309. // parse incoming line
  310. if (!this->connected
  311. && (boost::starts_with(line, "start")
  312. || boost::starts_with(line, "Grbl ")
  313. || boost::starts_with(line, "ok")
  314. || boost::contains(line, "T:"))) {
  315. this->connected = true;
  316. {
  317. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  318. this->can_send = true;
  319. }
  320. this->send();
  321. } else if (boost::starts_with(line, "ok")) {
  322. {
  323. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  324. this->can_send = true;
  325. }
  326. this->send();
  327. } else if (boost::istarts_with(line, "resend") // Marlin uses "Resend: "
  328. || boost::istarts_with(line, "rs")) {
  329. // extract the first number from line
  330. boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit());
  331. size_t toresend = boost::lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
  332. #ifdef DEBUG_SERIAL
  333. fs << "!! line num out of sync: toresend = " << toresend << ", sent = " << sent << ", last_sent.size = " << last_sent.size() << std::endl;
  334. #endif
  335. if (toresend > this->sent - this->last_sent.size() && toresend <= this->sent) {
  336. {
  337. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  338. const auto lines_to_resend = this->sent - toresend + 1;
  339. #ifdef DEBUG_SERIAL
  340. fs << "!! resending " << lines_to_resend << " lines" << std::endl;
  341. #endif
  342. // move the unsent lines to priqueue
  343. this->priqueue.insert(
  344. this->priqueue.begin(), // insert at the beginning
  345. this->last_sent.begin() + this->last_sent.size() - lines_to_resend,
  346. this->last_sent.end()
  347. );
  348. // we can empty last_sent because it's not useful anymore
  349. this->last_sent.clear();
  350. // start resending with the requested line number
  351. this->sent = toresend - 1;
  352. this->can_send = true;
  353. }
  354. this->send();
  355. } else {
  356. printf("Cannot resend %zu (oldest we have is %zu)\n", toresend, this->sent - this->last_sent.size());
  357. }
  358. } else if (boost::starts_with(line, "wait")) {
  359. // ignore
  360. } else {
  361. // push any other line into the log
  362. boost::lock_guard<boost::mutex> l(this->log_mutex);
  363. this->log.push(line);
  364. }
  365. // parse temperature info
  366. {
  367. size_t pos = line.find("T:");
  368. if (pos != std::string::npos && line.size() > pos + 2) {
  369. // we got temperature info
  370. boost::lock_guard<boost::mutex> l(this->log_mutex);
  371. this->T = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2));
  372. pos = line.find("B:");
  373. if (pos != std::string::npos && line.size() > pos + 2) {
  374. // we got bed temperature info
  375. this->B = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2));
  376. }
  377. }
  378. }
  379. }
  380. this->do_read();
  381. }
  382. void
  383. GCodeSender::send(const std::vector<std::string> &lines, bool priority)
  384. {
  385. // append lines to queue
  386. {
  387. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  388. for (std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line) {
  389. if (priority) {
  390. this->priqueue.push_back(*line);
  391. } else {
  392. this->queue.push(*line);
  393. }
  394. }
  395. }
  396. this->send();
  397. }
  398. void
  399. GCodeSender::send(const std::string &line, bool priority)
  400. {
  401. // append line to queue
  402. {
  403. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  404. if (priority) {
  405. this->priqueue.push_back(line);
  406. } else {
  407. this->queue.push(line);
  408. }
  409. }
  410. this->send();
  411. }
  412. void
  413. GCodeSender::send()
  414. {
  415. this->io.post(boost::bind(&GCodeSender::do_send, this));
  416. }
  417. void
  418. GCodeSender::do_send()
  419. {
  420. boost::lock_guard<boost::mutex> l(this->queue_mutex);
  421. // printer is not connected or we're still waiting for the previous ack
  422. if (!this->can_send) return;
  423. std::string line;
  424. while (!this->priqueue.empty() || (!this->queue.empty() && !this->queue_paused)) {
  425. if (!this->priqueue.empty()) {
  426. line = this->priqueue.front();
  427. this->priqueue.pop_front();
  428. } else {
  429. line = this->queue.front();
  430. this->queue.pop();
  431. }
  432. // strip comments
  433. size_t comment_pos = line.find_first_of(';');
  434. if (comment_pos != std::string::npos)
  435. line.erase(comment_pos, std::string::npos);
  436. boost::algorithm::trim(line);
  437. // if line is not empty, send it
  438. if (!line.empty()) break;
  439. // if line is empty, process next item in queue
  440. }
  441. if (line.empty()) return;
  442. // compute full line
  443. ++ this->sent;
  444. #ifndef DEBUG_SERIAL
  445. const auto line_num = this->sent;
  446. #else
  447. // In DEBUG_SERIAL mode, test line re-synchronization by sending bad line number 1/4 of the time
  448. const auto line_num = std::rand() < RAND_MAX/4 ? 0 : this->sent;
  449. #endif
  450. std::string full_line = "N" + boost::lexical_cast<std::string>(line_num) + " " + line;
  451. // calculate checksum
  452. int cs = 0;
  453. for (std::string::const_iterator it = full_line.begin(); it != full_line.end(); ++it)
  454. cs = cs ^ *it;
  455. // write line to device
  456. full_line += "*";
  457. full_line += boost::lexical_cast<std::string>(cs);
  458. full_line += "\n";
  459. #ifdef DEBUG_SERIAL
  460. fs << ">> " << full_line << std::flush;
  461. #endif
  462. this->last_sent.push_back(line);
  463. this->can_send = false;
  464. while (this->last_sent.size() > KEEP_SENT) {
  465. this->last_sent.pop_front();
  466. }
  467. // we can't supply boost::asio::buffer(full_line) to async_write() because full_line is on the
  468. // stack and the buffer would lose its underlying storage causing memory corruption
  469. std::ostream os(&this->write_buffer);
  470. os << full_line;
  471. boost::asio::async_write(this->serial, this->write_buffer, boost::bind(&GCodeSender::on_write, this, boost::asio::placeholders::error,
  472. boost::asio::placeholders::bytes_transferred));
  473. }
  474. void
  475. GCodeSender::on_write(const boost::system::error_code& error,
  476. size_t bytes_transferred)
  477. {
  478. this->set_error_status(false);
  479. if (error) {
  480. if (this->open) {
  481. this->do_close();
  482. this->set_error_status(true);
  483. }
  484. return;
  485. }
  486. this->do_send();
  487. }
  488. void
  489. GCodeSender::set_DTR(bool on)
  490. {
  491. #if defined(_WIN32) && !defined(__SYMBIAN32__)
  492. boost::asio::serial_port_service::native_handle_type handle = this->serial.native_handle();
  493. if (on)
  494. EscapeCommFunction(handle, SETDTR);
  495. else
  496. EscapeCommFunction(handle, CLRDTR);
  497. #else
  498. int fd = this->serial.native_handle();
  499. int status;
  500. ioctl(fd, TIOCMGET, &status);
  501. if (on)
  502. status |= TIOCM_DTR;
  503. else
  504. status &= ~TIOCM_DTR;
  505. ioctl(fd, TIOCMSET, &status);
  506. #endif
  507. }
  508. void
  509. GCodeSender::reset()
  510. {
  511. set_DTR(false);
  512. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  513. set_DTR(true);
  514. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  515. set_DTR(false);
  516. std::this_thread::sleep_for(std::chrono::milliseconds(500));
  517. }
  518. } // namespace Slic3r