First pass at supporting devices that mute.

This commit is contained in:
Rob Giseburt
2016-12-13 12:49:27 -06:00
parent a70b3d89ec
commit 7a3899f141
10 changed files with 162 additions and 55 deletions
+1
View File
@@ -692,6 +692,7 @@ nvObj_t *nv_add_conditional_message(const char *string) // conditionally add
* Inputs:
* json_flags = JSON_OBJECT_FORMAT - print just the body w/o header or footer
* json_flags = JSON_RESPONSE_FORMAT - print a full "r" object with footer
* json_flags = JSON_RESPONSE_TO_MUTED_FORMAT - JSON_RESPONSE_FORMAT, but only to muted channels
*
* text_flags = TEXT_INLINE_PAIRS - print text as name/value pairs on a single line
* text_flags = TEXT_INLINE_VALUES - print text as comma separated values on a single line
+29 -5
View File
@@ -72,7 +72,7 @@ static stat_t _sync_to_planner(void);
static stat_t _sync_to_tx_buffer(void);
static stat_t _dispatch_command(void);
static stat_t _dispatch_control(void);
static void _dispatch_kernel(void);
static void _dispatch_kernel(const devflags_t flags);
static stat_t _controller_state(void); // manage controller state transitions
static Motate::OutputPin<Motate::kOutputSAFE_PinNumber> safe_pin;
@@ -191,7 +191,7 @@ static stat_t _dispatch_control()
if (cs.controller_state != CONTROLLER_PAUSED) {
devflags_t flags = DEV_IS_CTRL;
if ((cs.bufp = xio_readline(flags, cs.linelen)) != NULL) {
_dispatch_kernel();
_dispatch_kernel(flags);
}
}
return (STAT_OK);
@@ -202,15 +202,22 @@ static stat_t _dispatch_command()
if (cs.controller_state != CONTROLLER_PAUSED) {
devflags_t flags = DEV_IS_BOTH;
if ((!mp_planner_is_full()) && (cs.bufp = xio_readline(flags, cs.linelen)) != NULL) {
_dispatch_kernel();
_dispatch_kernel(flags);
}
}
return (STAT_OK);
}
static void _dispatch_kernel()
static void _dispatch_kernel(const devflags_t flags)
{
stat_t status;
if (flags & DEV_IS_MUTED) {
status = STAT_INPUT_FROM_MUTED_CHANNEL_ERROR;
nv_reset_nv_list(); // get a fresh nvObj list
nv_add_string((const char *)"msg", "lines from muted devices are ignored");
nv_print_list(status, TEXT_NO_PRINT, JSON_RESPONSE_TO_MUTED_FORMAT);
}
while ((*cs.bufp == SPC) || (*cs.bufp == TAB)) { // position past any leading whitespace
cs.bufp++;
@@ -299,6 +306,23 @@ void controller_set_connected(bool is_connected) {
}
}
/*
* controller_set_muted(bool) - hook for xio to tell the controller that we
* have/don't have one or more muted devices.
*/
void controller_set_muted(bool is_muted) {
// TODO: care about text mode
if (is_muted) {
// one channel just got muted.
const bool only_to_muted = true;
xio_writeline("{\"muted\":true}\n", only_to_muted);
} else {
// one channel just got unmuted, announce it (except to the muted)
xio_writeline("{\"muted\":false}\n");
}
}
/*
* controller_parse_control() - return true if command is a control (versus data)
* Note: parsing for control is somewhat naiive. This will need to get better
@@ -456,4 +480,4 @@ stat_t _test_system_assertions()
return (STAT_OK);
}
+1
View File
@@ -84,6 +84,7 @@ extern controller_t cs; // controller state structure
void controller_init(void);
void controller_run(void);
void controller_set_connected(bool is_connected);
void controller_set_muted(bool is_muted);
bool controller_parse_control(char *p);
#endif // End of include guard: CONTROLLER_H_ONCE
+1 -1
View File
@@ -204,7 +204,7 @@ char *get_status_message(stat_t status);
#define STAT_MAX_DEPTH_EXCEEDED 115 // JSON exceeded maximum nesting depth
#define STAT_VALUE_TYPE_ERROR 116 // JSON value does not agree with variable type
#define STAT_ERROR_117 117
#define STAT_INPUT_FROM_MUTED_CHANNEL_ERROR 117 // input from a muted channel was ignored
#define STAT_ERROR_118 118
#define STAT_ERROR_119 119
+5 -3
View File
@@ -499,7 +499,9 @@ void json_print_list(stat_t status, uint8_t flags)
switch (flags) {
case JSON_NO_PRINT: break;
case JSON_OBJECT_FORMAT: { json_print_object(nv_body); break; }
case JSON_RESPONSE_FORMAT: { json_print_response(status); break; }
case JSON_RESPONSE_FORMAT:
case JSON_RESPONSE_TO_MUTED_FORMAT:
{ json_print_response(status, flags == JSON_RESPONSE_TO_MUTED_FORMAT); break; }
}
}
@@ -522,7 +524,7 @@ void json_print_list(stat_t status, uint8_t flags)
* on all the (non-silent) responses.
*/
void json_print_response(uint8_t status)
void json_print_response(uint8_t status, const bool only_to_muted /*= false*/)
{
if (js.json_verbosity == JV_SILENT) { // silent means no responses
return;
@@ -599,7 +601,7 @@ void json_print_response(uint8_t status)
// serialize the JSON response and print it if there were no errors
if (json_serialize(nv_header, cs.out_buf, sizeof(cs.out_buf)) >= 0) {
xio_writeline(cs.out_buf);
xio_writeline(cs.out_buf, only_to_muted);
}
}
+3 -2
View File
@@ -57,7 +57,8 @@ typedef enum {
typedef enum { // json output print modes
JSON_NO_PRINT = 0, // don't print anything if you find yourself in JSON mode
JSON_OBJECT_FORMAT, // print just the body as a json object
JSON_RESPONSE_FORMAT // print the header/body/footer as a response object
JSON_RESPONSE_FORMAT, // print the header/body/footer as a response object
JSON_RESPONSE_TO_MUTED_FORMAT // print the header/body/footer as a response object, only to muted channels
} jsonFormats;
typedef struct jsSingleton {
@@ -85,7 +86,7 @@ void json_parser(char *str);
void json_parse_for_exec(char *str, bool execute);
uint16_t json_serialize(nvObj_t *nv, char *out_buf, uint16_t size);
void json_print_object(nvObj_t *nv);
void json_print_response(uint8_t status);
void json_print_response(uint8_t status, const bool only_to_muted = false);
void json_print_list(stat_t status, uint8_t flags);
stat_t json_set_jv(nvObj_t *nv);
@@ -59,6 +59,7 @@
#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE
#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS
#define XIO_UART_MUTES_WHEN_USB_CONNECTED 1 // Mute the UART when USB connects
#define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE
#define JSON_VERBOSITY JV_LINENUM // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE
+4
View File
@@ -141,6 +141,10 @@
#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_XON, FLOW_CONTROL_RTS
#endif
#ifndef XIO_UART_MUTES_WHEN_USB_CONNECTED
#define XIO_UART_MUTES_WHEN_USB_CONNECTED 0 // UART will be muted when USB connected (off by default)
#endif
#ifndef JSON_VERBOSITY
#define JSON_VERBOSITY JV_MESSAGES // {jv: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE
#endif
+108 -37
View File
@@ -116,14 +116,18 @@ struct xioDeviceWrapperBase { // C++ base class for device primit
// bool canWrite() { return caps & DEV_CAN_WRITE; }
// bool canBeCtrl() { return caps & DEV_CAN_BE_CTRL; }
// bool canBeData() { return caps & DEV_CAN_BE_DATA; }
bool isAlwaysDataAndCtrl() { return caps & DEV_IS_ALWAYS_BOTH; }
bool isCtrl() { return flags & DEV_IS_CTRL; } // called externally: DeviceWrappers[i]->isCtrl()
bool isData() { return flags & DEV_IS_DATA; } // subclasses can call directly (no pointer): isCtrl()
bool isPrimary() { return flags & DEV_IS_PRIMARY; }
bool isAlwaysDataAndCtrl() { return caps & DEV_IS_ALWAYS_BOTH; }
bool isMuteAsSecondary() { return caps & DEV_IS_MUTE_SECONDARY; }
bool isConnected() { return flags & DEV_IS_CONNECTED; }
bool isNotConnected() { return !(flags & DEV_IS_CONNECTED); }
bool isReady() { return flags & DEV_IS_READY; }
bool isActive() { return flags & DEV_IS_ACTIVE; }
bool isMuted() { return flags & DEV_IS_MUTED; }
// Combination checks
bool isCtrlAndActive() { return ((flags & (DEV_IS_CTRL|DEV_IS_ACTIVE)) == (DEV_IS_CTRL|DEV_IS_ACTIVE)); }
@@ -143,13 +147,16 @@ struct xioDeviceWrapperBase { // C++ base class for device primit
void setAsConnectedAndReady() { flags |= ( DEV_IS_CONNECTED | DEV_IS_READY); };
void setAsPrimaryActiveDualRole() {
if (isAlwaysDataAndCtrl()) {
flags |= (DEV_IS_CTRL | DEV_IS_DATA | DEV_IS_ACTIVE);
if (isAlwaysDataAndCtrl() || isMuteAsSecondary()) {
// In both cases, it cannot be a PRIMARY
// Also, we remove a MUTED flag
flags = (flags & ~DEV_IS_MUTED) | (DEV_IS_CTRL | DEV_IS_DATA | DEV_IS_ACTIVE);
} else {
flags |= (DEV_IS_CTRL | DEV_IS_DATA | DEV_IS_PRIMARY | DEV_IS_ACTIVE);
}
};
void setAsActiveData() { flags |= ( DEV_IS_DATA | DEV_IS_ACTIVE); };
void setAsMuted() { flags = (flags & ~(DEV_IS_PRIMARY | DEV_IS_DATA | DEV_IS_CTRL)) | DEV_IS_MUTED; };
void clearFlags() { flags = DEV_FLAGS_CLEAR; }
xioDeviceWrapperBase(uint8_t _caps) : caps(_caps),
@@ -201,6 +208,7 @@ struct xio_t {
};
void remove_data_from_primary() {
// Why is this first pass here? -RG
for (int8_t i = 0; i < _dev_count; ++i) {
if (DeviceWrappers[i]->isDataAndActive()) {
return;
@@ -214,10 +222,28 @@ struct xio_t {
}
};
void deactivate_all_channels() {
for(int8_t i = 0; i < _dev_count; ++i) {
DeviceWrappers[i]->clearActive();
bool check_muted_secondary_channels() {
bool muted_something = false;
for (int8_t i = 0; i < _dev_count; ++i) {
if (DeviceWrappers[i]->isMuteAsSecondary()) {
DeviceWrappers[i]->setAsMuted();
muted_something = true;
}
}
return muted_something;
}
bool deactivate_and_unmute_channels() {
bool unmuted_something = false;
for(int8_t i = 0; i < _dev_count; ++i) {
if (DeviceWrappers[i]->isMuted()) {
unmuted_something = true;
DeviceWrappers[i]->setAsPrimaryActiveDualRole(); // NOTE: muted secondary devices won't be set PRIMARY
} else {
DeviceWrappers[i]->clearActive();
}
}
return unmuted_something;
};
// ##### Cross-Device read/write/etc. functions
@@ -232,11 +258,17 @@ struct xio_t {
* In the current environment, these are not foreseen to cause trouble since these
* are blocking writes and we expect to only really be writing to one device.
*/
size_t write(const char *buffer, size_t size)
size_t write(const char *buffer, size_t size, bool only_to_muted)
{
size_t total_written = -1;
for (int8_t i = 0; i < _dev_count; ++i) {
if (DeviceWrappers[i]->isCtrlAndActive()) {
bool ok_channel = false;
if (!only_to_muted) {
ok_channel = DeviceWrappers[i]->isCtrlAndActive();
} else {
ok_channel = DeviceWrappers[i]->isMuted();
}
if (ok_channel) {
const char *buf = buffer;
int16_t to_write = size;
while (to_write > 0) {
@@ -256,10 +288,10 @@ struct xio_t {
* The input buffer must be NUL terminated
*/
int16_t writeline(const char *buffer)
int16_t writeline(const char *buffer, bool only_to_muted)
{
int16_t len = strlen(buffer);
return write(buffer, len);
return write(buffer, len, only_to_muted);
};
/*
@@ -331,8 +363,8 @@ struct xio_t {
// We only do this second pass if this is not a CTRL-only read
if (!checkForCtrlOnly(limit_flags)) {
for (uint8_t dev=0; dev < _dev_count; dev++) {
if (!DeviceWrappers[dev]->isActive())
continue;
// if (!DeviceWrappers[dev]->isActive())
// continue;
ret_buffer = DeviceWrappers[dev]->readline(limit_flags, size);
@@ -884,29 +916,60 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re
void connectedStateChanged(bool connected) {
if (connected) {
if (isNotConnected()) {
//USB0 or UART has just connected
//Case 1: This is the first channel to connect -
// set it as CTRL+DATA+PRIMARY channel
//Case 2: This is the second (or later) channel to connect -
// set it as DATA channel, remove DATA flag from PRIMARY channel
//... inactive channels are counted as closed
// USB0 or UART has just connected
// If one of the devices isAlwaysDataAndCtrl():
// We treat *it* as if it's the only device connected.
// We treat *the other devices* as if it's NOT connected.
// Case 1: This is the first channel to connect -
// set it as CTRL+DATA+PRIMARY channel
// mark all isMutedAsSecondary() as MUTED, and call controller_set_muted(true) if needed
// Case 2: This is the second (or later) channel to connect -
// Case 2a: This device is !isMuteAsSecondary()
// set it as DATA channel, remove DATA flag from PRIMARY channel
// mark all isMutedAsSecondary() as MUTED, and call controller_set_muted(true) if needed
// ... inactive channels are counted as closed
// Case 2b: This devices isMuteAsSecondary(), and needs to be "muted."
// set it as a MUTED channel, call controller_set_connected(true)
// then controller_set_muted(true)
setAsConnectedAndReady();
if (isAlwaysDataAndCtrl()) {
// Case 1 (ignoring others)
setActive();
controller_set_connected(true);
// Case 2b (not ignoring others)
if (isMuteAsSecondary() && xio.others_connected(this)) {
controller_set_muted(true); // something was muted
}
return;
}
if(!xio.others_connected(this)) {
// Case 1
setAsPrimaryActiveDualRole();
// report that we now have a connection (only for the first one)
// report that there is now have a connection (only for the first one)
controller_set_connected(true);
} else {
// Case 2
// make sure secondary channels (that don't show up in isConnected) are muted
if (xio.check_muted_secondary_channels()) {
controller_set_muted(true); // something was muted
}
}
else if (isMuteAsSecondary()) {
// Case 2b
setAsMuted();
controller_set_connected(true); // it DID just just get connected
controller_set_muted(true); // but it muted it too
}
else {
// Case 2a
xio.remove_data_from_primary();
if (xio.check_muted_secondary_channels()) {
controller_set_muted(true); // something was muted
}
setAsActiveData();
}
} // flags & DEV_IS_DISCONNECTED
@@ -916,9 +979,9 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re
//USB0 has just disconnected
//Case 1: This channel disconnected while it was a ctrl+data channel (and no other channels are open) -
// finalize this channel
// finalize this channel, unmute muted channels
//Case 2: This channel disconnected while it was a primary ctrl channel (and other channels are open) -
// finalize this channel, deactivate other channels
// finalize this channel, unmute muted channels, deactivate other channels
//Case 3: This channel disconnected while it was a non-primary ctrl channel (and other channels are open) -
// finalize this channel, leave other channels alone
//Case 4: This channel disconnected while it was a data channel (and other channels are open, including a primary)
@@ -933,20 +996,23 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re
flush();
flushRead();
if(checkForNotActive(oldflags) || isAlwaysDataAndCtrl()) {
if (checkForNotActive(oldflags) || isAlwaysDataAndCtrl()) {
// Case 5a, 5b
} else if(checkForCtrlAndData(oldflags) || !xio.others_connected(this)) {
} else if (checkForCtrlAndData(oldflags) || !xio.others_connected(this)) {
// Case 1
if(!checkForCtrlAndData(oldflags) || xio.others_connected(this)) {
rpt_exception(STAT_XIO_ASSERTION_FAILURE, "xio_dev() assertion error"); // where is this supposed to go!?
if (xio.deactivate_and_unmute_channels()) {
controller_set_muted(false); // something was unmuted
} else {
controller_set_connected(false);
}
controller_set_connected(false);
} else if(checkForCtrlAndPrimary(oldflags)) {
} else if (checkForCtrlAndPrimary(oldflags)) {
// Case 2
xio.deactivate_all_channels();
} else if(checkForCtrl(oldflags)) {
if (xio.deactivate_and_unmute_channels()) {
controller_set_muted(false); // something was unmuted
}
} else if (checkForCtrl(oldflags)) {
// Case 3
} else if(checkForData(oldflags)) {
} else if (checkForData(oldflags)) {
// Case 4
xio.remove_data_from_primary();
}
@@ -970,9 +1036,14 @@ xioDeviceWrapper<decltype(&SerialUSB1)> serialUSB1Wrapper {
#endif
#endif // XIO_HAS_USB
#if XIO_HAS_UART==1
#if defined(XIO_UART_MUTES_WHEN_USB_CONNECTED) && (XIO_UART_MUTES_WHEN_USB_CONNECTED==1)
constexpr devflags_t _serial0ExtraFlags = DEV_IS_ALWAYS_BOTH | DEV_IS_MUTE_SECONDARY;
#else
constexpr devflags_t _serial0ExtraFlags = DEV_IS_ALWAYS_BOTH;
#endif
xioDeviceWrapper<decltype(&Serial)> serial0Wrapper {
&Serial,
(DEV_CAN_READ | DEV_CAN_WRITE | DEV_IS_ALWAYS_BOTH)
(DEV_CAN_READ | DEV_CAN_WRITE | _serial0ExtraFlags)
};
#endif // XIO_HAS_UART
@@ -1032,9 +1103,9 @@ stat_t xio_test_assertions()
* write() - write a buffer to a device
*/
size_t xio_write(const char *buffer, size_t size)
size_t xio_write(const char *buffer, size_t size, bool only_to_muted /*= false*/)
{
return xio.write(buffer, size);
return xio.write(buffer, size, only_to_muted);
}
/*
@@ -1049,9 +1120,9 @@ char *xio_readline(devflags_t &flags, uint16_t &size)
return xio.readline(flags, size);
}
int16_t xio_writeline(const char *buffer)
int16_t xio_writeline(const char *buffer, bool only_to_muted /*= false*/)
{
return xio.writeline(buffer);
return xio.writeline(buffer, only_to_muted);
}
bool xio_connected()
+9 -7
View File
@@ -65,17 +65,19 @@
typedef uint16_t devflags_t; // might need to bump to 32 be 16 or 32
// device capabilities flags
#define DEV_CAN_BE_CTRL (0x0001) // device can be a control channel
#define DEV_CAN_BE_DATA (0x0002) // device can be a data channel
#define DEV_IS_ALWAYS_BOTH (0x0004) // device is always a control and a data channel
#define DEV_CAN_READ (0x0010)
#define DEV_CAN_WRITE (0x0020)
#define DEV_CAN_BE_CTRL (0x0001) // device can be a control channel
#define DEV_CAN_BE_DATA (0x0002) // device can be a data channel
#define DEV_IS_ALWAYS_BOTH (0x0004) // device is always a control and a data channel
#define DEV_IS_MUTE_SECONDARY (0x0008) // device is "muted" as a non-primary device
#define DEV_CAN_READ (0x0010)
#define DEV_CAN_WRITE (0x0020)
// Device state flags
// channel state
#define DEV_IS_CTRL (0x0001) // device is set as a control channel
#define DEV_IS_DATA (0x0002) // device is set as a data channel
#define DEV_IS_PRIMARY (0x0004) // device is the primary control channel
#define DEV_IS_MUTED (0x0008) // device is muted as it is currently the non-primary device
// device connection state
#define DEV_IS_CONNECTED (0x0020) // device is connected (e.g. USB)
@@ -113,9 +115,9 @@ enum xioSPIMode {
void xio_init(void);
stat_t xio_test_assertions(void);
size_t xio_write(const uint8_t *buffer, size_t size);
size_t xio_write(const char *buffer, size_t size, bool only_to_muted = false);
char *xio_readline(devflags_t &flags, uint16_t &size);
int16_t xio_writeline(const char *buffer);
int16_t xio_writeline(const char *buffer, bool only_to_muted = false);
bool xio_connected();
stat_t xio_set_spi(nvObj_t *nv);