diff --git a/src/jsd.c b/src/jsd.c index 3b0e9b4..fdc28f5 100644 --- a/src/jsd.c +++ b/src/jsd.c @@ -83,7 +83,7 @@ void jsd_set_slave_config(jsd_t* self, uint16_t slave_id, self->slave_configs[slave_id] = slave_config; } -bool jsd_init(jsd_t* self, const char* ifname, uint8_t enable_autorecovery) { +bool jsd_init(jsd_t* self, const char* ifname, uint8_t enable_autorecovery, int timeout_us) { assert(self); self->enable_autorecovery = enable_autorecovery; @@ -164,15 +164,33 @@ bool jsd_init(jsd_t* self, const char* ifname, uint8_t enable_autorecovery) { self->ecx_context.slavelist[0].state = EC_STATE_OPERATIONAL; + struct timespec start_processdata_time; + clock_gettime(CLOCK_REALTIME, &start_processdata_time); ecx_send_overlap_processdata(&self->ecx_context); - ecx_receive_processdata(&self->ecx_context, EC_TIMEOUTRET); + ecx_receive_processdata(&self->ecx_context, timeout_us); ecx_writestate(&self->ecx_context, 0); int attempt = 0; while (true) { + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + if ((start_processdata_time.tv_nsec - current_time.tv_nsec)/1e3 > timeout_us) { + MSG_DEBUG("Went over the loop period!"); + } + else { + struct timespec diff; + diff.tv_sec = current_time.tv_sec - start_processdata_time.tv_sec; + diff.tv_nsec = current_time.tv_nsec - start_processdata_time.tv_nsec; + if (nanosleep(&diff, NULL) < 0) { + perror("nanosleep failed"); + return 1; + } + } + + clock_gettime(CLOCK_REALTIME, &start_processdata_time); int sent = ecx_send_overlap_processdata(&self->ecx_context); - int wkc = ecx_receive_processdata(&self->ecx_context, EC_TIMEOUTRET); + int wkc = ecx_receive_processdata(&self->ecx_context, timeout_us); ec_state actual_state = ecx_statecheck( &self->ecx_context, 0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE); @@ -194,6 +212,8 @@ bool jsd_init(jsd_t* self, const char* ifname, uint8_t enable_autorecovery) { WARNING("Failed OP transition attempt %d of %d", attempt, JSD_PO2OP_MAX_ATTEMPTS); + jsd_inspect_context(self); + if (attempt >= JSD_PO2OP_MAX_ATTEMPTS) { ERROR("Max number of attempts to transition to OPERATIONAL exceeded."); return false; @@ -226,6 +246,70 @@ bool jsd_init(jsd_t* self, const char* ifname, uint8_t enable_autorecovery) { return true; } +bool jsd_all_slaves_operational(jsd_t* self) { + bool all_slaves_operational = true; + uint8_t currentgroup = 0; // only 1 rate group in JSD currently + /* one or more slaves may not be responding */ + for (int slave = 1; slave <= *self->ecx_context.slavecount; slave++) { + if (self->ecx_context.slavelist[slave].group != currentgroup) continue; + /* re-check bad slave individually */ + ecx_statecheck(&self->ecx_context, slave, EC_STATE_OPERATIONAL, EC_TIMEOUTRET); + if (self->ecx_context.slavelist[slave].state != EC_STATE_OPERATIONAL) { + all_slaves_operational = false; + if (self->ecx_context.slavelist[slave].state == + (EC_STATE_SAFE_OP + EC_STATE_ERROR)) { + ERROR("slave[%d] is in SAFE_OP + ERROR.", slave); + } else if (self->ecx_context.slavelist[slave].state == EC_STATE_SAFE_OP) { + ERROR("slave[%d] is in SAFE_OP.", slave); + } else if (self->ecx_context.slavelist[slave].state > EC_STATE_NONE) { + ERROR("slave[%d] is in state with hexadecimal: %x", slave, self->ecx_context.slavelist[slave].state); + } else { + ERROR("slave[%d] is lost", slave); + } + } + else { + MSG("slave[%d] is OPERATIONAL.", slave); + } + } + + return all_slaves_operational; +} + +void jsd_inspect_context(jsd_t* self) { + ec_state bus_state = jsd_get_device_state(self, 0); + + /* first check if the jsd bus is operational so we can get more info */ + if (bus_state != EC_STATE_OPERATIONAL) { + ERROR("JSD bus is not OPERATIONAL."); + } + + if (jsd_all_slaves_operational(self)) { + MSG("All slaves were operational at time of working counter fault."); + } + else { + MSG("Some slaves were not operational."); + if (self->ecx_context.ecaterror) { + MSG("We experienced an ECAT error. When this occurs, error information aught to be saved. " + "Errors in error list displayed below:\n"); + uint8_t i = 0; + while(self->ecx_context.ecaterror && i < JSD_ELIST_MAX_READS) { + MSG("%s\n", ecx_elist2string(&self->ecx_context)); + i++; + } + if (i == JSD_ELIST_MAX_READS) { + MSG("Maximum number of reads from error list experienced." + " This is likely a result of indefinite polling."); + } + else { + MSG("Finished reporting errors in error list."); + } + } + else { + MSG("Despite some slaves not being operational, an ECAT error was not experienced."); + } + } +} + void jsd_read(jsd_t* self, int timeout_us) { assert(self); @@ -237,7 +321,7 @@ void jsd_read(jsd_t* self, int timeout_us) { } if (self->last_wkc != self->expected_wkc && self->wkc == self->expected_wkc) { if (self->last_wkc != -1) { - MSG("ecx_receive_processdata is not longer reading bad wkc"); + MSG("ecx_receive_processdata is no longer reading bad wkc"); } } self->last_wkc = self->wkc; @@ -601,7 +685,7 @@ void jsd_ecatcheck(jsd_t* self) { if ((bus_state == EC_STATE_OPERATIONAL && self->wkc < self->expected_wkc) || self->ecx_context.grouplist[currentgroup].docheckstate) { - /* one ore more slaves are not responding */ + /* one or more slaves are not responding */ self->ecx_context.grouplist[currentgroup].docheckstate = FALSE; ecx_readstate(&self->ecx_context); for (slave = 1; slave <= *self->ecx_context.slavecount; slave++) { diff --git a/src/jsd.h b/src/jsd.h index 5d3f92c..33e2d6c 100644 --- a/src/jsd.h +++ b/src/jsd.h @@ -8,6 +8,7 @@ extern "C" { #include "jsd/jsd_pub.h" #define JSD_PO2OP_MAX_ATTEMPTS 3 +#define JSD_ELIST_MAX_READS 20 /** * @brief converts ec_state int to human-readable string diff --git a/src/jsd_pub.h b/src/jsd_pub.h index c69959d..ce09e84 100644 --- a/src/jsd_pub.h +++ b/src/jsd_pub.h @@ -51,7 +51,22 @@ void jsd_set_slave_config(jsd_t* self, uint16_t slave_id, * @param enable_autorecovery enables automatic recovery of lost devices * @return true on successful SOEM initialization */ -bool jsd_init(jsd_t* self, const char* ifname, uint8_t enable_autorecovery); +bool jsd_init(jsd_t* self, const char* ifname, uint8_t enable_autorecovery, int timeout_us); + +/** + * @brief Checks every state individually and reports its EC state. + * + * @param self pointer JSD context + * @return Whether all states are operational or not + */ +bool jsd_all_slaves_operational(jsd_t* self); + +/** + * @brief Determines if all slaves are operational. If not, reports errors from error list. + * + * @param self pointer JSD context + */ +void jsd_inspect_context(jsd_t* self); /** * @brief Receive data from slave devices and store on local IOmap. diff --git a/test/device/jsd_minimal_example_el3602.c b/test/device/jsd_minimal_example_el3602.c index f6a1d16..e57155a 100644 --- a/test/device/jsd_minimal_example_el3602.c +++ b/test/device/jsd_minimal_example_el3602.c @@ -23,7 +23,7 @@ int main() { jsd_set_slave_config(jsd, slave_id, my_config); // Slave configuration must come before initialization - if (!jsd_init(jsd, "eth9", 1)) { + if (!jsd_init(jsd, "eth9", 1, EC_TIMEOUTRET)) { ERROR("Could not init jsd"); return 0; } diff --git a/test/jsd_test_utils.c b/test/jsd_test_utils.c index ffd3f02..76dad08 100644 --- a/test/jsd_test_utils.c +++ b/test/jsd_test_utils.c @@ -103,7 +103,7 @@ void sds_run(single_device_server_t* self, char* device_name, char* filename) { uint32_t sds_iter = 0; - if (!jsd_init(self->jsd, device_name, 1)) { + if (!jsd_init(self->jsd, device_name, 1, EC_TIMEOUTRET)) { ERROR("Could not init jsd"); return; } diff --git a/test/unit/jsd_soem_init_close_test.c b/test/unit/jsd_soem_init_close_test.c index 55cfa00..38425d7 100644 --- a/test/unit/jsd_soem_init_close_test.c +++ b/test/unit/jsd_soem_init_close_test.c @@ -12,7 +12,7 @@ int main(int argc, char* argv[]) { jsd_t* jsd = jsd_alloc(); - if (!jsd_init(jsd, argv[1], 1)) { + if (!jsd_init(jsd, argv[1], 1, EC_TIMEOUTRET)) { ERROR("Could not init jsd"); return 0; } diff --git a/tools/jsd_egd_tlc_tty.c b/tools/jsd_egd_tlc_tty.c index f3e3be1..a6b6d3d 100644 --- a/tools/jsd_egd_tlc_tty.c +++ b/tools/jsd_egd_tlc_tty.c @@ -151,7 +151,7 @@ int main(int argc, char* argv[]) { jsd_set_slave_config(jsd, slave_id, my_config); // slave configuration must come before initialization - if (!jsd_init(jsd, ifname, 1)) { + if (!jsd_init(jsd, ifname, 1, EC_TIMEOUTRET)) { ERROR("Could not init jsd"); return 0; }