/*
* controller.cpp - tinyg2 controller and top level parser
* Part of TinyG2 project
*
* Copyright (c) 2013 Alden S. Hart Jr.
* Copyright (c) 2013 Robert Giseburt
*
* This file ("the software") is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2 as published by the
* Free Software Foundation. You should have received a copy of the GNU General Public
* License, version 2 along with the software. If not, see .
*
* As a special exception, you may use this file as part of a software library without
* restriction. Specifically, if other files instantiate templates or use macros or
* inline functions from this file, or you compile this file and link it with other
* files to produce an executable, this file does not by itself cause the resulting
* executable to be covered by the GNU General Public License. This exception does not
* however invalidate any other reasons why the executable file might be covered by the
* GNU General Public License.
*
* THE SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
* SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* See the wiki for module details and additional information:
* https://github.com/synthetos/g2/wiki
*/
#include "tinyg2.h"
#include "controller.h"
#include "config.h"
#include "config_app.h"
#include "json_parser.h"
#include "text_parser.h"
//#include "gcode_parser.h"
#include "xio.h"
/*
#include // for parsing
#include "settings.h"
#include "json_parser.h"
#include "canonical_machine.h"
#include "plan_arc.h"
#include "planner.h"
#include "stepper.h"
#include "system.h"
#include "gpio.h"
#include "report.h"
#include "util.h"
#include "help.h"
*/
#ifdef __cplusplus
extern "C"{
#endif
/***********************************************************************************
**** STUCTURE ALLOCATIONS *********************************************************
***********************************************************************************/
controller_t cs; // controller state structure
/***********************************************************************************
**** STATICS AND LOCALS ***********************************************************
***********************************************************************************/
static void _controller_HSM(void);
static uint8_t _command_dispatch(void);
static uint8_t _normal_idler(void);
static uint8_t _alarm_idler(void);
/*
static uint8_t _reset_handler(void);
static uint8_t _bootloader_handler(void);
static uint8_t _limit_switch_handler(void);
static uint8_t _shutdown_idler(void);
static uint8_t _system_assertions(void);
static uint8_t _feedhold_handler(void);
static uint8_t _cycle_start_handler(void);
static uint8_t _sync_to_tx_buffer(void);
static uint8_t _sync_to_planner(void);
*/
/***********************************************************************************
**** CODE *************************************************************************
***********************************************************************************/
/*
* tg_init() - controller init
*/
void controller_init(uint8_t std_in, uint8_t std_out, uint8_t std_err)
{
cs.magic_start = MAGICNUM;
cs.magic_end = MAGICNUM;
cs.fw_build = TINYG_FIRMWARE_BUILD;
cs.fw_version = TINYG_FIRMWARE_VERSION;// NB: HW version is set from EEPROM
cs.linelen = 0; // initialize index for read_line()
// xio_set_stdin(std_in);
// xio_set_stdout(std_out);
// xio_set_stderr(std_err);
// cs.default_src = std_in;
// tg_set_active_source(cs.default_src); // set initial active source
}
/*
* tg_controller() - MAIN LOOP - top-level controller
*
* The order of the dispatched tasks is very important.
* Tasks are ordered by increasing dependency (blocking hierarchy).
* Tasks that are dependent on completion of lower-level tasks must be
* later in the list than the task(s) they are dependent upon.
*
* Tasks must be written as continuations as they will be called repeatedly,
* and are called even if they are not currently active.
*
* The DISPATCH macro calls the function and returns to the controller parent
* if not finished (STAT_EAGAIN), preventing later routines from running
* (they remain blocked). Any other condition - OK or ERR - drops through
* and runs the next routine in the list.
*
* A routine that had no action (i.e. is OFF or idle) should return STAT_NOOP
*
* Useful reference on state machines:
* http://johnsantic.com/comp/state.html, "Writing Efficient State Machines in C"
*/
void controller_run()
{
while (true) {
_controller_HSM();
}
}
#define DISPATCH(func) if (func == STAT_EAGAIN) return;
static void _controller_HSM()
{
//----- ISRs. These should be considered the highest priority scheduler functions ----//
/*
* HI Stepper DDA pulse generation // see stepper.h
* HI Stepper load routine SW interrupt // see stepper.h
* HI Dwell timer counter // see stepper.h
* MED GPIO1 switch port - limits / homing // see gpio.h
* MED Serial RX for USB // see xio_usart.h
* LO Segment execution SW interrupt // see stepper.h
* LO Serial TX for USB & RS-485 // see xio_usart.h
* LO Real time clock interrupt // see xmega_rtc.h
*/
//----- kernel level ISR handlers ----(flags are set in ISRs)-----------//
// Order is important:
// DISPATCH(_reset_handler()); // 1. software reset received
// DISPATCH(_bootloader_handler()); // 2. received ESC char to start bootloader
// DISPATCH(_limit_switch_handler()); // 3. limit switch has been thrown
// DISPATCH(_alarm_idler()); // 4. idle in shutdown state (alarmed)
// DISPATCH(_system_assertions()); // 5. system integrity assertions
// DISPATCH(_feedhold_handler()); // 6. feedhold requested
// DISPATCH(_cycle_start_handler()); // 7. cycle start requested
//----- planner hierarchy for gcode and cycles -------------------------//
// DISPATCH(rpt_status_report_callback()); // conditionally send status report
// DISPATCH(rpt_queue_report_callback()); // conditionally send queue report
// DISPATCH(mp_plan_hold_callback()); // plan a feedhold
// DISPATCH(mp_end_hold_callback()); // end a feedhold
// DISPATCH(ar_arc_callback()); // arc generation runs behind lines
// DISPATCH(cm_homing_callback()); // G28.2 continuation
//----- command readers and parsers ------------------------------------//
// DISPATCH(_sync_to_planner()); // ensure there is at least one free buffer in planning queue
// DISPATCH(_sync_to_tx_buffer()); // sync with TX buffer (pseudo-blocking)
// DISPATCH(cfg_baud_rate_callback()); // perform baud rate update (must be after TX sync)
DISPATCH(_command_dispatch()); // read and execute next command
DISPATCH(_normal_idler()); // blink LEDs slowly to show everything is OK
}
/*****************************************************************************
* _command_dispatch() - dispatch line received from active input device
*
* Reads next command line and dispatches to relevant parser or action
* Accepts commands if the move queue has room - EAGAINS if it doesn't
* Manages cutback to serial input from file devices (EOF)
* Also responsible for prompts and for flow control
*/
static status_t _command_dispatch()
{
// printf("printf test2 %d %f...\n", 10, 10.003);
status_t status;
strcpy(cs.in_buf, "$fb");
// read input line or return if not a completed line
// if ((status = read_line(cs.in_buf, &cs.linelen, sizeof(cs.in_buf))) != STAT_OK) {
// return (STAT_OK); // so the idler always runs
// }
write (cs.in_buf, cs.linelen);
cs.linelen = 0;
// return (STAT_OK);
// cmd_reset_list(); ++++ REMOVE THIS LINE
// cs.linelen = strlen(cs.in_buf)+1;
strncpy(cs.saved_buf, cs.in_buf, SAVED_BUFFER_LEN-1); // save input buffer for reporting
// dispatch the new text line
switch (toupper(cs.in_buf[0])) {
case NUL: { // blank line (just a CR)
if (cs.comm_mode != JSON_MODE) {
text_response(STAT_OK, cs.saved_buf);
}
break;
}
// case 'H': { // intercept help screens
// cs.comm_mode = TEXT_MODE;
// print_general_help();
// text_response(STAT_OK, cs.in_buf);
// break;
// }
case '$': case '?':{ // text-mode configs
cs.comm_mode = TEXT_MODE;
text_response(text_parser(cs.in_buf), cs.saved_buf);
break;
}
case '{': { // JSON input
cs.comm_mode = JSON_MODE;
json_parser(cs.in_buf);
break;
}
default: { // anything else must be Gcode
if (cs.comm_mode == JSON_MODE) {
strncpy(cs.out_buf, cs.in_buf, INPUT_BUFFER_LEN -8); // use out_buf as temp
sprintf((char *)cs.in_buf,"{\"gc\":\"%s\"}\n", (char *)cs.out_buf); // '-8' is used for JSON chars
json_parser(cs.in_buf);
} else {
// text_response(gc_gcode_parser(cs.in_buf), cs.saved_buf);
write (cs.in_buf, cs.linelen); //+++++ test statement
}
}
}
return (STAT_OK);
}
/*
* _normal_idler() - blink LED13 slowly to show everything is OK
*/
static status_t _normal_idler( )
{
if (GetTickCount() > cs.led_counter) {
cs.led_counter += LED_NORMAL_COUNTER;
IndicatorLed.toggle();
}
return (STAT_OK);
}
/*
* _alarm_idler() - revent any further activity form occurring if shut down
*
* This function returns EAGAIN causing the control loop to never advance beyond
* this point. It's important that the reset handler is still called so a SW reset
* (ctrl-x) can be processed.
*/
static uint8_t _alarm_idler( )
{
// if (cm_get_machine_state() != MACHINE_SHUTDOWN) { return (STAT_OK);}
if (GetTickCount() > cs.led_counter) {
cs.led_counter += LED_ALARM_COUNTER;
IndicatorLed.toggle();
}
return (STAT_EAGAIN); // EAGAIN prevents any lower-priority actions from running
}
/**** Utilities ****
* _sync_to_tx_buffer() - return eagain if TX queue is backed up
* _sync_to_planner() - return eagain if planner is not ready for a new command
* tg_reset_source() - reset source to default input device (see note)
* tg_set_active_source() - set current input source
*
* Note: Once multiple serial devices are supported reset_source() should
* be expanded to also set the stdout/stderr console device so the prompt
* and other messages are sent to the active device.
*/
/*
static uint8_t _sync_to_tx_buffer()
{
if ((xio_get_tx_bufcount_usart(ds[XIO_DEV_USB].x) >= XOFF_TX_LO_WATER_MARK)) {
return (STAT_EAGAIN);
}
return (STAT_OK);
}
static uint8_t _sync_to_planner()
{
// if (mp_get_planner_buffers_available() == 0) {
if (mp_get_planner_buffers_available() < 3) {
return (STAT_EAGAIN);
}
return (STAT_OK);
}
void tg_reset_source() { tg_set_primary_source(tg.default_src);}
void tg_set_primary_source(uint8_t dev) { tg.primary_src = dev;}
void tg_set_secondary_source(uint8_t dev) { tg.secondary_src = dev;}
*/
/*
* _system_assertions() - check memory integrity and other assertions
*/
/*
uint8_t _system_assertions()
{
uint8_t value = 0;
if (cs.magic_start != MAGICNUM) { value = 1; } // Note: reported VALue is offset by ALARM_MEMORY_OFFSET
if (cs.magic_end != MAGICNUM) { value = 2; }
if (cm.magic_start != MAGICNUM) { value = 3; }
if (cm.magic_end != MAGICNUM) { value = 4; }
if (gm.magic_start != MAGICNUM) { value = 5; }
if (gm.magic_end != MAGICNUM) { value = 6; }
if (cfg.magic_start != MAGICNUM) { value = 7; }
if (cfg.magic_end != MAGICNUM) { value = 8; }
if (cmdStr.magic_start != MAGICNUM) { value = 9; }
if (cmdStr.magic_end != MAGICNUM) { value = 10; }
if (mb.magic_start != MAGICNUM) { value = 11; }
if (mb.magic_end != MAGICNUM) { value = 12; }
if (mr.magic_start != MAGICNUM) { value = 13; }
if (mr.magic_end != MAGICNUM) { value = 14; }
if (ar.magic_start != MAGICNUM) { value = 15; }
if (ar.magic_end != MAGICNUM) { value = 16; }
if (st_get_st_magic() != MAGICNUM) { value = 17; }
if (st_get_sps_magic() != MAGICNUM) { value = 18; }
if (rtc.magic_end != MAGICNUM) { value = 19; }
xio_assertions(&value); // run xio assertions
if (value == 0) { return (STAT_OK);}
rpt_exception(STAT_MEMORY_CORRUPTION, value);
cm_shutdown(ALARM_MEMORY_OFFSET + value);
return (STAT_EAGAIN);
}
*/
#ifdef __cplusplus
}
#endif // __cplusplus