IgH EtherCAT Master  1.6.1
fsm_change.c
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH
4  *
5  * This file is part of the IgH EtherCAT Master.
6  *
7  * The IgH EtherCAT Master is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License version 2, as
9  * published by the Free Software Foundation.
10  *
11  * The IgH EtherCAT Master is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14  * Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with the IgH EtherCAT Master; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  *
20  ****************************************************************************/
21 
27 /****************************************************************************/
28 
29 #include "globals.h"
30 #include "master.h"
31 #include "fsm_change.h"
32 #include "slave_config.h"
33 
34 /****************************************************************************/
35 
36 unsigned int ec_fsm_change_timeout_ms(const ec_fsm_change_t *);
37 
47 
48 /****************************************************************************/
49 
55  ec_datagram_t *datagram
56  )
57 {
58  fsm->state = NULL;
59  fsm->datagram = datagram;
60  fsm->spontaneous_change = 0;
61 }
62 
63 /****************************************************************************/
64 
70 {
71 }
72 
73 /****************************************************************************/
74 
80  const ec_fsm_change_t *fsm
81  )
82 {
83  ec_slave_state_t from = fsm->old_state;
85 
86  /* Search for specified timeout in slave configuration */
87  if (fsm->slave->config) {
88  unsigned int timeout_ms =
89  ec_slave_config_al_timeout(fsm->slave->config, from, to);
90  if (timeout_ms) {
91  return timeout_ms;
92  }
93  }
94 
95  /* No specific timeout found. Use defaults from spec. */
96 
97  if (from == EC_SLAVE_STATE_INIT &&
98  (to == EC_SLAVE_STATE_PREOP || to == EC_SLAVE_STATE_BOOT)) {
99  return 3000; // PreopTimeout
100  }
101  if ((from == EC_SLAVE_STATE_PREOP && to == EC_SLAVE_STATE_SAFEOP) ||
102  (from == EC_SLAVE_STATE_SAFEOP && to == EC_SLAVE_STATE_OP)) {
103  return 10000; // SafeopOpTimeout
104  }
105  if (to == EC_SLAVE_STATE_INIT ||
106  ((from == EC_SLAVE_STATE_OP || from == EC_SLAVE_STATE_SAFEOP)
107  && to == EC_SLAVE_STATE_PREOP)) {
108  return 5000; // BackToInitTimeout
109  }
110  if (from == EC_SLAVE_STATE_OP && to == EC_SLAVE_STATE_SAFEOP) {
111  return 200; // BackToSafeopTimeout
112  }
113 
114  return 10000; // default [ms]
115 }
116 
117 /****************************************************************************/
118 
124  ec_slave_t *slave,
125  ec_slave_state_t state
126  )
127 {
129  fsm->slave = slave;
130  fsm->requested_state = state;
132 }
133 
134 /****************************************************************************/
135 
141  ec_slave_t *slave
142  )
143 {
145  fsm->slave = slave;
148 }
149 
150 /****************************************************************************/
151 
158 {
159  fsm->state(fsm);
160 
161  return fsm->state != ec_fsm_change_state_end
162  && fsm->state != ec_fsm_change_state_error;
163 }
164 
165 /****************************************************************************/
166 
173 {
174  return fsm->state == ec_fsm_change_state_end;
175 }
176 
177 /*****************************************************************************
178  * state change state machine
179  ****************************************************************************/
180 
187 {
188  ec_datagram_t *datagram = fsm->datagram;
189  ec_slave_t *slave = fsm->slave;
190 
191  fsm->take_time = 1;
192  fsm->old_state = fsm->slave->current_state;
193 
194  // write new state to slave
195  ec_datagram_fpwr(datagram, slave->station_address, 0x0120, 2);
196  EC_WRITE_U16(datagram->data, fsm->requested_state);
197  fsm->retries = EC_FSM_RETRIES;
199 }
200 
201 /****************************************************************************/
202 
209 {
210  ec_datagram_t *datagram = fsm->datagram;
211  ec_slave_t *slave = fsm->slave;
212 
213  if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
214  return;
215 
216  if (datagram->state != EC_DATAGRAM_RECEIVED) {
218  EC_SLAVE_ERR(slave, "Failed to receive state datagram: ");
219  ec_datagram_print_state(datagram);
220  return;
221  }
222 
223  if (fsm->take_time) {
224  fsm->take_time = 0;
225  fsm->jiffies_start = datagram->jiffies_sent;
226  }
227 
228  if (datagram->working_counter == 0) {
229  if (datagram->jiffies_received - fsm->jiffies_start >= 3 * HZ) {
230  char state_str[EC_STATE_STRING_SIZE];
231  ec_state_string(fsm->requested_state, state_str, 0);
233  EC_SLAVE_ERR(slave, "Failed to set state %s: ", state_str);
234  ec_datagram_print_wc_error(datagram);
235  return;
236  }
237 
238  // repeat writing new state to slave
239  ec_datagram_fpwr(datagram, slave->station_address, 0x0120, 2);
240  EC_WRITE_U16(datagram->data, fsm->requested_state);
241  fsm->retries = EC_FSM_RETRIES;
242  return;
243  }
244 
245  if (unlikely(datagram->working_counter > 1)) {
246  char state_str[EC_STATE_STRING_SIZE];
247  ec_state_string(fsm->requested_state, state_str, 0);
249  EC_SLAVE_ERR(slave, "Failed to set state %s: ", state_str);
250  ec_datagram_print_wc_error(datagram);
251  return;
252  }
253 
254  fsm->take_time = 1;
255 
256  // read AL status from slave
257  ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2);
258  ec_datagram_zero(datagram);
259  fsm->retries = EC_FSM_RETRIES;
260  fsm->spontaneous_change = 0;
262 }
263 
264 /****************************************************************************/
265 
272 {
273  ec_datagram_t *datagram = fsm->datagram;
274  ec_slave_t *slave = fsm->slave;
275  unsigned int timeout_ms;
276 
277  if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
278  return;
279 
280  if (datagram->state != EC_DATAGRAM_RECEIVED) {
282  EC_SLAVE_ERR(slave, "Failed to receive state checking datagram: ");
283  ec_datagram_print_state(datagram);
284  return;
285  }
286 
287  if (datagram->working_counter != 1) {
288  char req_state[EC_STATE_STRING_SIZE];
289  ec_state_string(fsm->requested_state, req_state, 0);
291  EC_SLAVE_ERR(slave, "Failed to check state %s: ", req_state);
292  ec_datagram_print_wc_error(datagram);
293  return;
294  }
295 
296  if (fsm->take_time) {
297  fsm->take_time = 0;
298  fsm->jiffies_start = datagram->jiffies_sent;
299  }
300 
301  slave->current_state = EC_READ_U8(datagram->data);
302 
303  if (slave->current_state == fsm->requested_state) {
304  // state has been set successfully
306  return;
307  }
308 
309  if (slave->current_state != fsm->old_state) { // state changed
310  char req_state[EC_STATE_STRING_SIZE], cur_state[EC_STATE_STRING_SIZE];
311 
312  ec_state_string(slave->current_state, cur_state, 0);
313 
314  if ((slave->current_state & 0x0F) != (fsm->old_state & 0x0F)) {
315  // Slave spontaneously changed its state just before the new state
316  // was written. Accept current state as old state and wait for
317  // state change
318  fsm->spontaneous_change = 1;
319  fsm->old_state = slave->current_state;
320  EC_SLAVE_WARN(slave, "Changed to %s in the meantime.\n",
321  cur_state);
322  goto check_again;
323  }
324 
325  // state change error
326 
327  slave->error_flag = 1;
328  ec_state_string(fsm->requested_state, req_state, 0);
329 
330  EC_SLAVE_ERR(slave, "Failed to set %s state, slave refused state"
331  " change (%s).\n", req_state, cur_state);
332 
334  return;
335  }
336 
337  // still old state
338 
339  timeout_ms = ec_fsm_change_timeout_ms(fsm);
340  if (datagram->jiffies_received - fsm->jiffies_start >=
341  timeout_ms * HZ / 1000) {
342  // timeout while checking
343  char state_str[EC_STATE_STRING_SIZE];
344  ec_state_string(fsm->requested_state, state_str, 0);
346  EC_SLAVE_ERR(slave, "Timeout after %u ms while setting state %s.\n",
347  timeout_ms, state_str);
348  return;
349  }
350 
351  check_again:
352  // no timeout yet. check again
353  ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2);
354  ec_datagram_zero(datagram);
355  fsm->retries = EC_FSM_RETRIES;
356 }
357 
358 /****************************************************************************/
359 
363  ec_fsm_change_t *fsm
364  )
365 {
366  ec_slave_t *slave = fsm->slave;
367  ec_datagram_t *datagram = fsm->datagram;
368 
369  // fetch AL status error code
370  ec_datagram_fprd(datagram, slave->station_address, 0x0134, 2);
371  ec_datagram_zero(datagram);
372  fsm->retries = EC_FSM_RETRIES;
374 }
375 
376 /****************************************************************************/
377 
383  {0x0000, "No error"},
384  {0x0001, "Unspecified error"},
385  {0x0002, "No Memory"},
386  {0x0011, "Invalid requested state change"},
387  {0x0012, "Unknown requested state"},
388  {0x0013, "Bootstrap not supported"},
389  {0x0014, "No valid firmware"},
390  {0x0015, "Invalid mailbox configuration"},
391  {0x0016, "Invalid mailbox configuration"},
392  {0x0017, "Invalid sync manager configuration"},
393  {0x0018, "No valid inputs available"},
394  {0x0019, "No valid outputs"},
395  {0x001A, "Synchronization error"},
396  {0x001B, "Sync manager watchdog"},
397  {0x001C, "Invalid sync manager types"},
398  {0x001D, "Invalid output configuration"},
399  {0x001E, "Invalid input configuration"},
400  {0x001F, "Invalid watchdog configuration"},
401  {0x0020, "Slave needs cold start"},
402  {0x0021, "Slave needs INIT"},
403  {0x0022, "Slave needs PREOP"},
404  {0x0023, "Slave needs SAFEOP"},
405  {0x0024, "Invalid Input Mapping"},
406  {0x0025, "Invalid Output Mapping"},
407  {0x0026, "Inconsistent Settings"},
408  {0x0027, "Freerun not supported"},
409  {0x0028, "Synchronization not supported"},
410  {0x0029, "Freerun needs 3 Buffer Mode"},
411  {0x002A, "Background Watchdog"},
412  {0x002B, "No Valid Inputs and Outputs"},
413  {0x002C, "Fatal Sync Error"},
414  {0x002D, "No Sync Error"},
415  {0x0030, "Invalid DC SYNCH configuration"},
416  {0x0031, "Invalid DC latch configuration"},
417  {0x0032, "PLL error"},
418  {0x0033, "DC Sync IO Error"},
419  {0x0034, "DC Sync Timeout Error"},
420  {0x0035, "DC Invalid Sync Cycle Time"},
421  {0x0036, "DC Sync0 Cycle Time"},
422  {0x0037, "DC Sync1 Cycle Time"},
423  {0x0041, "MBX_AOE"},
424  {0x0042, "MBX_EOE"},
425  {0x0043, "MBX_COE"},
426  {0x0044, "MBX_FOE"},
427  {0x0045, "MBX_SOE"},
428  {0x004F, "MBX_VOE"},
429  {0x0050, "EEPROM No Access"},
430  {0x0051, "EEPROM Error"},
431  {0x0060, "Slave Restarted Locally"},
432  {0xffff}
433 };
434 
435 
436 /****************************************************************************/
437 
444 {
445  ec_datagram_t *datagram = fsm->datagram;
446  uint32_t code;
447  const ec_code_msg_t *al_msg;
448 
449  if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
450  return;
451 
452  if (datagram->state != EC_DATAGRAM_RECEIVED) {
454  EC_SLAVE_ERR(fsm->slave, "Failed to receive"
455  " AL status code datagram: ");
456  ec_datagram_print_state(datagram);
457  return;
458  }
459 
460  if (datagram->working_counter != 1) {
461  EC_SLAVE_WARN(fsm->slave, "Reception of AL status code"
462  " datagram failed: ");
463  ec_datagram_print_wc_error(datagram);
464  } else {
465  code = EC_READ_U16(datagram->data);
466  for (al_msg = al_status_messages; al_msg->code != 0xffff; al_msg++) {
467  if (al_msg->code != code) {
468  continue;
469  }
470 
471  EC_SLAVE_ERR(fsm->slave, "AL status message 0x%04X: \"%s\".\n",
472  al_msg->code, al_msg->message);
473  break;
474  }
475  if (al_msg->code == 0xffff) { /* not found in our list. */
476  EC_SLAVE_ERR(fsm->slave, "Unknown AL status code 0x%04X.\n",
477  code);
478  }
479  }
480 
481  // acknowledge "old" slave state
482  ec_datagram_fpwr(datagram, fsm->slave->station_address, 0x0120, 2);
483  EC_WRITE_U16(datagram->data, fsm->slave->current_state);
484  fsm->retries = EC_FSM_RETRIES;
486 }
487 
488 /****************************************************************************/
489 
495 {
496  ec_datagram_t *datagram = fsm->datagram;
497  ec_slave_t *slave = fsm->slave;
498 
499  if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
500  return;
501 
502  if (datagram->state != EC_DATAGRAM_RECEIVED) {
504  EC_SLAVE_ERR(slave, "Failed to receive state ack datagram: ");
505  ec_datagram_print_state(datagram);
506  return;
507  }
508 
509  if (datagram->working_counter != 1) {
511  EC_SLAVE_ERR(slave, "Reception of state ack datagram failed: ");
512  ec_datagram_print_wc_error(datagram);
513  return;
514  }
515 
516  fsm->take_time = 1;
517 
518  // read new AL status
519  ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2);
520  ec_datagram_zero(datagram);
521  fsm->retries = EC_FSM_RETRIES;
523 }
524 
525 /****************************************************************************/
526 
533 {
534  ec_datagram_t *datagram = fsm->datagram;
535  ec_slave_t *slave = fsm->slave;
536  unsigned int timeout_ms;
537 
538  if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
539  return;
540 
541  if (datagram->state != EC_DATAGRAM_RECEIVED) {
543  EC_SLAVE_ERR(slave, "Failed to receive state ack check datagram: ");
544  ec_datagram_print_state(datagram);
545  return;
546  }
547 
548  if (datagram->working_counter != 1) {
550  EC_SLAVE_ERR(slave, "Reception of state ack check datagram failed: ");
551  ec_datagram_print_wc_error(datagram);
552  return;
553  }
554 
555  if (fsm->take_time) {
556  fsm->take_time = 0;
557  fsm->jiffies_start = datagram->jiffies_sent;
558  }
559 
560  slave->current_state = EC_READ_U8(datagram->data);
561 
562  if (!(slave->current_state & EC_SLAVE_STATE_ACK_ERR)) {
563  char state_str[EC_STATE_STRING_SIZE];
564  ec_state_string(slave->current_state, state_str, 0);
565  if (fsm->mode == EC_FSM_CHANGE_MODE_FULL) {
567  }
568  else { // EC_FSM_CHANGE_MODE_ACK_ONLY
570  }
571  EC_SLAVE_INFO(slave, "Acknowledged state %s.\n", state_str);
572  return;
573  }
574 
575  timeout_ms = ec_fsm_change_timeout_ms(fsm);
576  if (datagram->jiffies_received - fsm->jiffies_start >=
577  timeout_ms * HZ / 1000) {
578  // timeout while checking
579  char state_str[EC_STATE_STRING_SIZE];
580  ec_state_string(slave->current_state, state_str, 0);
582  EC_SLAVE_ERR(slave, "Timeout after %u ms while acknowledging"
583  " state %s.\n", timeout_ms, state_str);
584  return;
585  }
586 
587  // reread new AL status
588  ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2);
589  ec_datagram_zero(datagram);
590  fsm->retries = EC_FSM_RETRIES;
591 }
592 
593 /****************************************************************************/
594 
601 {
602 }
603 
604 /****************************************************************************/
605 
612 {
613 }
614 
615 /****************************************************************************/
#define EC_FSM_RETRIES
Number of state machine retries on datagram timeout.
Definition: globals.h:47
unsigned long jiffies_sent
Jiffies, when the datagram was sent.
Definition: datagram.h:98
uint8_t spontaneous_change
spontaneous state change detected
Definition: fsm_change.h:68
unsigned int ec_slave_config_al_timeout(const ec_slave_config_t *sc, ec_slave_state_t from, ec_slave_state_t to)
Return an AL state timeout.
Definition: slave_config.c:661
OP (mailbox communication and input/output update)
Definition: globals.h:132
EtherCAT state change FSM.
size_t ec_state_string(uint8_t, char *, uint8_t)
Prints slave states in clear text.
Definition: module.c:401
uint8_t take_time
take sending timestamp
Definition: fsm_change.h:67
ec_slave_state_t current_state
Current application state.
Definition: slave.h:184
uint32_t code
Code.
Definition: globals.h:276
#define EC_SLAVE_WARN(slave, fmt, args...)
Convenience macro for printing slave-specific warnings to syslog.
Definition: slave.h:82
EtherCAT datagram.
Definition: datagram.h:79
Bootstrap state (mailbox communication, firmware update)
Definition: globals.h:128
void ec_fsm_change_start(ec_fsm_change_t *fsm, ec_slave_t *slave, ec_slave_state_t state)
Starts the change state machine.
Definition: fsm_change.c:123
uint16_t working_counter
Working counter.
Definition: datagram.h:93
unsigned int ec_fsm_change_timeout_ms(const ec_fsm_change_t *)
Get timeout in ms.
Definition: fsm_change.c:79
void ec_fsm_change_state_end(ec_fsm_change_t *)
State: END.
Definition: fsm_change.c:610
Acknowledge/Error bit (no actual state)
Definition: globals.h:134
uint16_t station_address
Configured station address.
Definition: slave.h:176
const char * message
Message belonging to code.
Definition: globals.h:277
Global definitions and macros.
ec_datagram_t * datagram
datagram used in the state machine
Definition: fsm_change.h:59
EtherCAT master structure.
SAFEOP (mailbox communication and input update)
Definition: globals.h:130
void ec_fsm_change_state_error(ec_fsm_change_t *)
State: ERROR.
Definition: fsm_change.c:599
EtherCAT slave.
Definition: slave.h:168
only state acknowledgement
Definition: fsm_change.h:44
void ec_datagram_zero(ec_datagram_t *datagram)
Fills the datagram payload memory with zeros.
Definition: datagram.c:170
Code/Message pair.
Definition: globals.h:275
ec_datagram_state_t state
State.
Definition: datagram.h:94
ec_slave_config_t * config
Current configuration.
Definition: slave.h:182
unsigned long jiffies_start
change timer
Definition: fsm_change.h:66
#define EC_SLAVE_ERR(slave, fmt, args...)
Convenience macro for printing slave-specific errors to syslog.
Definition: slave.h:68
void(* state)(ec_fsm_change_t *)
slave state change state function
Definition: fsm_change.h:62
void ec_datagram_print_wc_error(const ec_datagram_t *datagram)
Evaluates the working counter of a single-cast datagram.
Definition: datagram.c:594
int ec_fsm_change_exec(ec_fsm_change_t *fsm)
Executes the current state of the state machine.
Definition: fsm_change.c:157
#define EC_WRITE_U16(DATA, VAL)
Write a 16-bit unsigned value to EtherCAT data.
Definition: ecrt.h:3154
void ec_fsm_change_state_start(ec_fsm_change_t *)
Change state: START.
Definition: fsm_change.c:185
void ec_fsm_change_clear(ec_fsm_change_t *fsm)
Destructor.
Definition: fsm_change.c:69
int ec_datagram_fpwr(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size)
Initializes an EtherCAT FPWR datagram.
Definition: datagram.c:290
ec_fsm_change_mode_t mode
full state change, or ack only.
Definition: fsm_change.h:63
ec_slave_state_t
State of an EtherCAT slave.
Definition: globals.h:121
void ec_fsm_change_state_check_ack(ec_fsm_change_t *)
Change state: CHECK ACK.
Definition: fsm_change.c:531
int ec_datagram_fprd(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size)
Initializes an EtherCAT FPRD datagram.
Definition: datagram.c:265
full state change
Definition: fsm_change.h:43
void ec_fsm_change_state_status(ec_fsm_change_t *)
Change state: STATUS.
Definition: fsm_change.c:270
#define EC_SLAVE_INFO(slave, fmt, args...)
Convenience macro for printing slave-specific information to syslog.
Definition: slave.h:54
void ec_fsm_change_init(ec_fsm_change_t *fsm, ec_datagram_t *datagram)
Constructor.
Definition: fsm_change.c:54
void ec_fsm_change_state_ack(ec_fsm_change_t *)
Change state: ACK.
Definition: fsm_change.c:494
const ec_code_msg_t al_status_messages[]
Application layer status messages.
Definition: fsm_change.c:382
INIT state (no mailbox communication, no IO)
Definition: globals.h:124
#define EC_READ_U16(DATA)
Read a 16-bit unsigned value from EtherCAT data.
Definition: ecrt.h:3045
ec_slave_state_t requested_state
input: state
Definition: fsm_change.h:64
void ec_datagram_print_state(const ec_datagram_t *datagram)
Prints the state of a datagram.
Definition: datagram.c:557
ec_slave_t * slave
slave the FSM runs on
Definition: fsm_change.h:58
#define EC_STATE_STRING_SIZE
Minimum size of a buffer used with ec_state_string().
Definition: globals.h:54
void ec_fsm_change_ack(ec_fsm_change_t *fsm, ec_slave_t *slave)
Starts the change state machine to only acknowlegde a slave's state.
Definition: fsm_change.c:140
Timed out (dequeued).
Definition: datagram.h:71
uint8_t * data
Datagram payload.
Definition: datagram.h:88
#define EC_READ_U8(DATA)
Read an 8-bit unsigned value from EtherCAT data.
Definition: ecrt.h:3029
void ec_fsm_change_state_code(ec_fsm_change_t *)
Change state: CODE.
Definition: fsm_change.c:442
EtherCAT slave configuration structure.
void ec_fsm_change_state_check(ec_fsm_change_t *)
Change state: CHECK.
Definition: fsm_change.c:207
PREOP state (mailbox communication, no IO)
Definition: globals.h:126
void ec_fsm_change_state_start_code(ec_fsm_change_t *)
Enter reading AL status code.
Definition: fsm_change.c:362
Received (dequeued).
Definition: datagram.h:70
ec_slave_state_t old_state
prior slave state
Definition: fsm_change.h:65
unsigned int retries
retries upon datagram timeout
Definition: fsm_change.h:60
unsigned int error_flag
Stop processing after an error.
Definition: slave.h:185
unsigned long jiffies_received
Jiffies, when the datagram was received.
Definition: datagram.h:102
unknown state
Definition: globals.h:122
EtherCAT state change FSM.
Definition: fsm_change.h:56
int ec_fsm_change_success(ec_fsm_change_t *fsm)
Returns, if the state machine terminated with success.
Definition: fsm_change.c:172