1 /*
2 This file is a part of the RepSnapper project.
3 Copyright (C) 2011-12 martin.dieringer@gmx.de
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "stdafx.h"
22 #else
23 #define _( t ) t
24 #endif
25
26 #include <iostream>
27 #include <sstream>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include "threaded_printer_serial.h"
32
33 const ntime_t ThreadedPrinterSerial::command_buffer_sleep = { 0, 100 * 1000 * 1000 };
34 const ntime_t ThreadedPrinterSerial::response_buffer_sleep = { 0, 10 * 1000 * 1000 };
35 const ntime_t ThreadedPrinterSerial::log_buffer_sleep = { 0, 10 * 1000 * 1000 };
36 const ntime_t ThreadedPrinterSerial::helper_thread_sleep = { 0, 100 * 1000 * 1000 };
37
ThreadedPrinterSerial()38 ThreadedPrinterSerial::ThreadedPrinterSerial() :
39 PrinterSerial( helper_thread_sleep.tv_nsec / 1000 / 1000 ),
40 command_buffer( command_buffer_size, command_buffer_sleep, "", false, true ),
41 response_buffer( response_buffer_size, true, response_buffer_sleep, "", true, false ),
42 log_buffer( log_buffer_size, false, log_buffer_sleep, _("\n*** Log overflow ***\n\n"), true, false ),
43 error_buffer( log_buffer_size, true, log_buffer_sleep, _("\n*** Error Log overflow ***\n\n"), true, false ) {
44 request_print = is_printing = printing_complete = false;
45 printer_commands = NULL;
46 pc_lines_printed = 0;
47 pc_bytes_printed = 0;
48 pc_stop_line = 0;
49 inhibit_count = 0;
50
51 mutex_init( &pc_mutex );
52 mutex_init( &pc_cond_mutex );
53 cond_init( &pc_cond );
54
55 helper_active = false;
56 helper_cancel = false;
57 return_data = NULL;
58 }
59
~ThreadedPrinterSerial()60 ThreadedPrinterSerial::~ThreadedPrinterSerial() {
61 if ( helper_active ) {
62 mutex_lock( &pc_cond_mutex );
63 helper_cancel = true;
64 mutex_unlock( &pc_cond_mutex );
65
66 thread_join( helper_thread );
67 helper_active = false;
68 }
69
70 mutex_destroy( &pc_mutex );
71 mutex_destroy( &pc_cond_mutex );
72 cond_destroy( &pc_cond );
73
74 if ( printer_commands != NULL )
75 delete [] printer_commands;
76 }
77
Connect(string device,int baudrate)78 bool ThreadedPrinterSerial::Connect( string device, int baudrate ) {
79 // Open Serial Port
80 if ( ! PrinterSerial::RawConnect( device, baudrate ) )
81 return false;
82
83 // Clear printer_commands
84 if ( printer_commands != NULL ) {
85 delete [] printer_commands;
86 printer_commands = NULL;
87 }
88
89 // Clear/Flush buffers
90 command_buffer.Flush();
91 response_buffer.Flush();
92
93 helper_cancel = false;
94
95 // Start thread
96 int rc;
97 if ( ( rc = thread_create( &helper_thread, HelperMainStatic, this ) ) != 0 ) {
98 PrinterSerial::Disconnect();
99 ostringstream os;
100 os << _("Error connecting to printer") << ": ";
101 os << _("Error creating serial helper thread") << ": ";
102 os << strerror( rc ) << endl;
103 LogError( os.str().c_str() );
104 return false;
105 }
106 helper_active = true;
107
108 return true;
109 }
110
Disconnect(void)111 void ThreadedPrinterSerial::Disconnect( void ) {
112 StopPrinting( true );
113
114 if ( helper_active ) {
115 mutex_lock( &pc_cond_mutex );
116 helper_cancel = true;
117 mutex_unlock( &pc_cond_mutex );
118
119 thread_join( helper_thread );
120 helper_active = false;
121 }
122
123 command_buffer.Flush();
124
125 PrinterSerial::Disconnect();
126 }
127
IsConnected(void)128 bool ThreadedPrinterSerial::IsConnected( void ) {
129 return helper_active && PrinterSerial::IsConnected();
130 }
131
Reset(void)132 bool ThreadedPrinterSerial::Reset( void ) {
133 StopPrinting( true );
134
135 if ( ! IsConnected() )
136 return false;
137
138 if ( helper_active ) {
139 mutex_lock( &pc_cond_mutex );
140 helper_cancel = true;
141 mutex_unlock( &pc_cond_mutex );
142
143 thread_join( helper_thread );
144 helper_active = false;
145 }
146
147 command_buffer.Flush();
148 response_buffer.Flush();
149
150 bool ret = PrinterSerial::RawReset();
151
152 helper_cancel = false;
153
154 // Start thread
155 int rc;
156 if ( ( rc = thread_create( &helper_thread, HelperMainStatic, this ) ) != 0 ) {
157 PrinterSerial::Disconnect();
158 ostringstream os;
159 os << _("Error reseting printer") << ": ";
160 os << _("Error creating serial helper thread") << ": ";
161 os << strerror( rc ) << endl;
162 LogError( os.str().c_str() );
163 return false;
164 }
165 helper_active = true;
166
167 return ret;
168 }
169
StartPrinting(string commands,unsigned long start_line,unsigned long stop_line)170 bool ThreadedPrinterSerial::StartPrinting( string commands, unsigned long start_line, unsigned long stop_line ) {
171 char *commands_copy;
172 int rc;
173 size_t pos;
174 unsigned long count;
175 unsigned long lines_printed;
176 unsigned long bytes_printed;
177
178 for ( pos = -1, count = 1; count < start_line; count++ ) {
179 if ( ( pos = commands.find( '\n', pos + 1 ) ) == string::npos ) {
180 char err_buf[ 1024 ];
181 snprintf( err_buf, 1024, _("Error: Cannot start print at line %lu since Gcode only contains %lu lines\n"), start_line, count );
182 if ( err_buf[ 1022 ] != '\0' )
183 err_buf[ 1022 ] = '\n';
184 err_buf[ 1023 ] = '\0';
185 LogError( err_buf );
186 return false;
187 }
188 }
189
190 bytes_printed = pos + 1;
191 lines_printed = start_line > 0 ? start_line - 1 : 0;
192
193 while ( count < stop_line ) {
194 if ( ( pos = commands.find( '\n', pos + 1 ) ) == string::npos ) {
195 if ( commands[ commands.length() - 1 ] != '\n' )
196 count++;
197 break;
198 }
199 count++;
200 }
201 stop_line = count;
202
203 // Allocate memory for commands
204 size_t len;
205 len = commands.length();
206 commands_copy = new char[ len + 10 ];
207 memcpy( commands_copy, commands.c_str(), len + 1 );
208
209 // Make sure we are connected to a printer
210 if ( ! IsConnected() ) {
211 delete [] commands_copy;
212 ostringstream os;
213 os << _("Error starting print") << ": " << _("Printer connection not established") << endl;
214 LogError( os.str().c_str() );
215 return false;
216 }
217
218 // Lock pc_mutex
219 if ( ( rc = mutex_lock( &pc_mutex ) ) != 0 ) {
220 delete [] commands_copy;
221 ostringstream os;
222 os << _("Error starting print") << ": pc_mutex: " << strerror( rc ) << endl;
223 LogError( os.str().c_str() );
224 return false;
225 }
226
227 // Lock the cond mutex
228 if ( ( rc = mutex_lock( &pc_cond_mutex ) ) != 0 ) {
229 delete [] commands_copy;
230 mutex_unlock( &pc_mutex );
231 ostringstream os;
232 os << _("Error starting print") << ": pc_cond_mutex: " << strerror( rc ) << endl;
233 LogError( os.str().c_str() );
234 return false;
235 }
236
237 if ( inhibit_count > 0 ) {
238 delete [] commands_copy;
239 mutex_unlock( &pc_cond_mutex );
240 mutex_unlock( &pc_mutex );
241 return false;
242 }
243
244 // Make sure we are not already printing
245 if ( is_printing ) {
246 request_print = false;
247
248 if ( ( rc = cond_wait( &pc_cond, &pc_cond_mutex ) ) !=0 ) {
249 delete [] commands_copy;
250 mutex_unlock( &pc_cond_mutex );
251 mutex_unlock( &pc_mutex );
252 ostringstream os;
253 os << _("Error starting print") << ": cond_wait: " << strerror( rc ) << endl;
254 LogError( os.str().c_str() );
255 return false;
256 }
257 }
258
259 // Ready to start printing, set the variables
260 if ( printer_commands != NULL )
261 delete [] printer_commands;
262
263 printer_commands = commands_copy;
264 pc_lines_printed = lines_printed;
265 pc_bytes_printed = bytes_printed;
266 pc_stop_line = stop_line;
267
268 // Request printing
269 request_print = true;
270
271 if ( ( rc = cond_wait( &pc_cond, &pc_cond_mutex ) ) !=0 ) {
272 delete [] commands_copy;
273 mutex_unlock( &pc_cond_mutex );
274 mutex_unlock( &pc_mutex );
275 ostringstream os;
276 os << _("Error starting print") << ": cond_wait: " << strerror( rc ) << endl;
277 LogError( os.str().c_str() );
278 return false;
279 }
280
281 // Unlock mutexes
282 mutex_unlock( &pc_cond_mutex );
283 mutex_unlock( &pc_mutex );
284
285 return true;
286 }
287
IsPrinting()288 bool ThreadedPrinterSerial::IsPrinting() {
289 return is_printing && ! printing_complete;
290 }
291
StopPrinting(bool wait)292 bool ThreadedPrinterSerial::StopPrinting( bool wait ) {
293 int rc;
294
295 if ( ! IsConnected() ) {
296 return true;
297 }
298
299 if ( ( rc = mutex_lock( &pc_mutex ) ) != 0 ) {
300 ostringstream os;
301 os << _("Error stopping print") << ": pc_mutex: " << strerror( rc ) << endl;
302 LogError( os.str().c_str() );
303 return false;
304 }
305
306 if ( ( rc = mutex_lock( &pc_cond_mutex ) ) != 0 ) {
307 mutex_unlock( &pc_mutex );
308 ostringstream os;
309 os << _("Error stopping print") << ": pc_cond_mutex: " << strerror( rc ) << endl;
310 LogError( os.str().c_str() );
311 return false;
312 }
313
314 request_print = false;
315
316 if ( wait && is_printing ) {
317 if ( ( rc = cond_wait( &pc_cond, &pc_cond_mutex ) ) !=0 ) {
318 mutex_unlock( &pc_cond_mutex );
319 mutex_unlock( &pc_mutex );
320 ostringstream os;
321 os << _("Error stopping print") << ": cond_wait: " << strerror( rc ) << endl;
322 LogError( os.str().c_str() );
323 return false;
324 }
325 }
326
327 mutex_unlock( &pc_cond_mutex );
328 mutex_unlock( &pc_mutex );
329 return true;
330 }
331
ContinuePrinting(bool wait)332 bool ThreadedPrinterSerial::ContinuePrinting( bool wait ) {
333 int rc;
334
335 if ( printer_commands == NULL ) {
336 ostringstream os;
337 os << _("Error continuing print") << ": ";
338 os << _("No stopped print to continue") << endl;
339 LogError( os.str().c_str() );
340 return false;
341 }
342
343 if ( ( rc = mutex_lock( &pc_mutex ) ) != 0 ) {
344 ostringstream os;
345 os << _("Error continuing print") << ": pc_mutex: " << strerror( rc ) << endl;
346 LogError( os.str().c_str() );
347 return false;
348 }
349
350 if ( ! IsConnected() ) {
351 mutex_unlock( &pc_mutex );
352 return false;
353 }
354
355 if ( ( rc = mutex_lock( &pc_cond_mutex ) ) != 0 ) {
356 mutex_unlock( &pc_mutex );
357 ostringstream os;
358 os << _("Error continuing print") << ": pc_cond_mutex: " << strerror( rc ) << endl;
359 LogError( os.str().c_str() );
360 return false;
361 }
362
363 if ( inhibit_count > 0 ) {
364 mutex_unlock( &pc_cond_mutex );
365 mutex_unlock( &pc_mutex );
366 return false;
367 }
368
369 request_print = true;
370
371 if ( wait && ! is_printing ) {
372 if ( ( rc = cond_wait( &pc_cond, &pc_cond_mutex ) ) !=0 ) {
373 mutex_unlock( &pc_cond_mutex );
374 mutex_unlock( &pc_mutex );
375 ostringstream os;
376 os << _("Error continuing print") << ": cond_wait: " << strerror( rc ) << endl;
377 LogError( os.str().c_str() );
378 return false;
379 }
380 }
381
382 mutex_unlock( &pc_cond_mutex );
383 mutex_unlock( &pc_mutex );
384 return true;
385 }
386
Inhibit(bool value)387 void ThreadedPrinterSerial::Inhibit( bool value ) {
388 mutex_lock( &pc_cond_mutex );
389
390 if ( value )
391 inhibit_count++;
392 else if ( inhibit_count > 0 )
393 inhibit_count--;
394
395 mutex_unlock( &pc_cond_mutex );
396 }
397
IsInhibited(void)398 bool ThreadedPrinterSerial::IsInhibited( void ) {
399 mutex_lock( &pc_cond_mutex );
400 bool inhib = inhibit_count > 0;
401 mutex_unlock( &pc_cond_mutex );
402 return inhib;
403 }
404
GetPrintingProgress(unsigned long * bytes_printed)405 unsigned long ThreadedPrinterSerial::GetPrintingProgress( unsigned long *bytes_printed ) {
406 unsigned long lines;
407 unsigned long bytes;
408
409 mutex_lock( &pc_cond_mutex );
410
411 lines = pc_lines_printed;
412 bytes = pc_bytes_printed;
413
414 mutex_unlock( &pc_cond_mutex );
415
416 if ( bytes_printed != NULL )
417 *bytes_printed = bytes;
418
419 return lines;
420 }
421
GetTotalPrintingLines(void)422 unsigned long ThreadedPrinterSerial::GetTotalPrintingLines( void ) {
423 unsigned long lines;
424
425 mutex_lock( &pc_cond_mutex );
426 lines = pc_stop_line;
427 mutex_unlock( &pc_cond_mutex );
428
429 return lines;
430 }
431
SendAsync(char const * command)432 bool ThreadedPrinterSerial::SendAsync( char const * command) {
433 return command_buffer.Write( command, true );
434 }
435
Send(string command)436 bool ThreadedPrinterSerial::Send( string command ) {
437 return command_buffer.Write( command.c_str(), true );
438 }
439
SendAndWaitResponse(string command)440 string ThreadedPrinterSerial::SendAndWaitResponse( string command ) {
441 ThreadBufferReturnData::ReturnData *ret_data = NULL;
442
443 if ( ! command_buffer.Write( command.c_str(), true, -1, &ret_data ) )
444 return "";
445
446 if ( ret_data == NULL )
447 return "";
448
449 if ( ! command_buffer.WaitForReturnData( *ret_data ) ) {
450 delete return_data;
451 return "";
452 }
453
454 string str = ret_data->GetData();
455 delete ret_data;
456
457 return str;
458 }
459
ReadResponse(bool wait)460 string ThreadedPrinterSerial::ReadResponse( bool wait ) {
461 return response_buffer.Read( wait );
462 }
463
ReadLog(bool wait)464 string ThreadedPrinterSerial::ReadLog( bool wait ) {
465 return log_buffer.Read( wait );
466 }
467
ReadErrorLog(bool wait)468 string ThreadedPrinterSerial::ReadErrorLog( bool wait ) {
469 return error_buffer.Read( wait );
470 }
471
472 ////////////////////////////////////////////////////////////////////////////
473 // Helper Thread Funcitons
474 ////////////////////////////////////////////////////////////////////////////
475
HelperMainStatic(void * arg)476 void *ThreadedPrinterSerial::HelperMainStatic( void *arg ) {
477 ThreadedPrinterSerial *serial = ( ThreadedPrinterSerial * ) arg;
478
479 return serial->HelperMain();
480 }
481
HelperMain(void)482 void *ThreadedPrinterSerial::HelperMain( void ) {
483 // Read start line before continuing
484 // The printer seems to lock up if it recvs a command before the start
485 // line has been sent
486 RecvLine();
487
488 // Sleep for 10 ms
489 ntime_t nts = { 0, 10 * 1000 * 1000 };
490 nsleep( &nts );
491
492 // Send version request command
493 strncpy( command_scratch, "M115\n", 6 );
494 SendCommand( false );
495
496 while ( true ) {
497 if ( return_data != NULL )
498 return_data->AddLine( _("**Internal Error\n") );
499 return_data = NULL;
500
501 CheckPrintingState();
502
503 if ( command_buffer.Read( command_scratch, max_command_size, false, &return_data ) > 0 ) {
504 SendCommand( true );
505 } else if ( IsPrinting() ) {
506 SendNextPrinterCommand();
507 } else {
508 nsleep( &helper_thread_sleep );
509 }
510 }
511
512 return NULL;
513 }
514
CheckPrintingState(void)515 void ThreadedPrinterSerial::CheckPrintingState( void ) {
516 mutex_lock( &pc_cond_mutex );
517
518 if ( helper_cancel ) {
519 helper_cancel = false;
520 mutex_unlock( &pc_cond_mutex );
521 if ( return_data != NULL )
522 return_data->AddLine( _("**Connection closed\n") );
523 return_data = NULL;
524 thread_exit();
525 }
526
527 if ( request_print != is_printing ) {
528 is_printing = request_print;
529 printing_complete = false;
530 cond_broadcast( &pc_cond );
531 }
532
533 mutex_unlock( &pc_cond_mutex );
534 }
535
SendNextPrinterCommand(void)536 void ThreadedPrinterSerial::SendNextPrinterCommand( void ) {
537 unsigned long datalen;
538 bool truncated = false;
539
540 mutex_lock( &pc_cond_mutex );
541
542 // Find the bounds of the next command
543 const char *start = printer_commands + pc_bytes_printed;
544
545 const char *stop;
546 for ( stop = start; *stop != '\n' && *stop != '\0'; stop++ )
547 ;
548
549 datalen = stop - start;
550 if ( datalen > max_command_size - 2 ) {
551 datalen = max_command_size - 2;
552 truncated = true;
553 }
554
555 // Copy command to scratch buffer. Always add a newline.
556 memcpy( command_scratch, start, datalen );
557 char *loc = command_scratch + datalen;
558 *loc++ = '\n';
559 *loc++ = '\0';
560
561 // Update status
562 pc_lines_printed++;
563 pc_bytes_printed = stop - printer_commands + ( ( *stop == '\n' ) ? 1 : 0 );
564
565 // Update printing complete
566 if ( *stop == '\0' || pc_lines_printed >= pc_stop_line )
567 printing_complete = true;
568
569 mutex_unlock( &pc_cond_mutex );
570
571 if ( truncated ) {
572 char warn[ 100 ];
573 snprintf( warn, 99, _("*** Warning: Truncated long printer command at line %lu\n"), pc_lines_printed );
574 if ( warn[ 98 ] != '\0' )
575 warn[ 98 ] = '\n';
576 warn[ 99 ] = '\0';
577 LogLine( warn );
578 LogError( warn );
579 }
580
581 // Send the command and wait for response
582 SendCommand( false );
583 }
584
SendCommand(bool buffer_response)585 void ThreadedPrinterSerial::SendCommand( bool buffer_response ) {
586 // Don't send blank lines
587 char *recvd = PrinterSerial::SendCommand();
588
589 if ( recvd == NULL ) {
590 if ( return_data != NULL )
591 return_data->AddLine( _("**Error sending line\n") );
592 return_data = NULL;
593 return;
594 }
595
596 if ( strncasecmp( recvd, "!!", 2 ) == 0 ) {
597 // !! Fatal Error
598 response_buffer.Write( recvd, true );
599 if ( return_data != NULL )
600 return_data->AddLine( _("**Fatal Error\n") );
601 return_data = NULL;
602 helper_active = false;
603 Disconnect(); // This is safe. With helper active false, no mutexes are needed and no threads are killed.
604 thread_exit();
605 }
606
607 if ( return_data != NULL ) {
608 return_data->AddLine( recvd );
609 return_data = NULL;
610 } else if ( buffer_response ) {
611 // buffer resposne if it is "interesting", that is if it is more than
612 // just the two letter "ok" reply followed by white space.
613 char *loc;
614 for ( loc = recvd + 2; *loc != ' '; loc++ )
615 ;
616
617 if ( *loc != '\n' )
618 response_buffer.Write( recvd, false );
619 }
620 }
621
RecvTimeout(void)622 void ThreadedPrinterSerial::RecvTimeout( void ) {
623 CheckPrintingState();
624 }
625
626 // Log the line. The provided line should end in a newline character.
LogLine(const char * line)627 void ThreadedPrinterSerial::LogLine( const char *line ) {
628 log_buffer.Write( line, false );
629 }
630
631 // Log error the line. The provided line should end in a newline character.
LogError(const char * error_line)632 void ThreadedPrinterSerial::LogError( const char *error_line ) {
633 error_buffer.Write( error_line, false );
634 }
635