Alex exoskeleton
ALEX SoftwareDocumentation
main.cpp
Go to the documentation of this file.
1 /*
2  *
3  * @file main.cpp
4  * @author William Campbell
5  * @version 0.1
6  * @date 2020-04-09
7  *
8  * @copyright Copyright (c) 2020
9  *
10  *
11  * This file is an adaptation of CANopenSocket, a Linux implementation of CANopen
12  * stack with master functionality. Project home page is
13  * <https://github.com/CANopenNode/CANopenSocket>. CANopenSocket is based
14  * on CANopenNode: <https://github.com/CANopenNode/CANopenNode>.
15  *
16  * The adaptation is specifically designed for use with the RobotCANControl design stack and
17  * a multi limbed robot. It has been tested using a Beagle Bone black and the Fourier Intelligence X2
18  * exoskelton in a lab testing setting.It can be addapted for use with other CANopen enabled linux based robotic projects.
19  *
20  * Licensed under the Apache License, Version 2.0 (the "License");
21  * you may not use this file except in compliance with the License.
22  * You may obtain a copy of the License at
23  *
24  * http://www.apache.org/licenses/LICENSE-2.0
25  *
26  * Unless required by applicable law or agreed to in writing, software
27  * distributed under the License is distributed on an "AS IS" BASIS,
28  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29  * See the License for the specific language governing permissions and
30  * limitations under the License.
31  */
32 
33 #include "application.h"
34 /* Threads and thread safety variables***********************************************************/
39 pthread_mutex_t CO_CAN_VALID_mtx = PTHREAD_MUTEX_INITIALIZER;
40 
41 static int mainline_epoll_fd;
42 static CO_time_t CO_time;
43 bool readyToStart = false;
45 
46 /*CAN msg processing thread variables*/
47 static int rtPriority = 2;
48 static void *rt_thread(void *arg);
49 static pthread_t rt_thread_id;
50 static int rt_thread_epoll_fd;
51 /* Application Control loop thread */
52 static int rtControlPriority = 80;
53 static void *rt_control_thread(void *arg);
54 static pthread_t rt_control_thread_id;
55 static int rt_control_thread_epoll_fd;
58 struct period_info {
59  struct timespec next_period;
60  long period_ns;
61 };
62 /* Forward declartion of control loop thread timer functions*/
63 static void inc_period(struct period_info *pinfo);
64 static void periodic_task_init(struct period_info *pinfo);
65 static void wait_rest_of_period(struct period_info *pinfo);
66 /* Forward declartion of CAN helper functions*/
67 void configureCANopen(int nodeId, int rtPriority, int CANdevice0Index, char *CANdevice);
68 void CO_errExit(char *msg);
69 void CO_error(const uint32_t info);
70 volatile uint32_t CO_timer1ms = 0U;
71 volatile sig_atomic_t CO_endProgram = 0;
72 static void sigHandler(int sig) {
73  CO_endProgram = 1;
74 }
75 
76 /******************************************************************************/
78 /******************************************************************************/
79 int main(int argc, char *argv[]) {
80  /* TODO : MOVE bellow definitionsTO SOME KIND OF CANobject, struct or the like*/
81  CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
82  bool_t firstRun = true;
83  bool_t rebootEnable = false; // TODO: DO WE EVER RESET? OR NEED TO?
84  char CANdevice[10] = "vcan0";
85  int nodeId = NODEID;
86  /*map linux CAN interface to corresponding int index return zero if no interface exists.*/
87  int CANdevice0Index = if_nametoindex(CANdevice);
88  configureCANopen(nodeId, rtPriority, CANdevice0Index, CANdevice);
89 
90  /* Set up catch of linux signals SIGINT(ctrl+c) and SIGTERM (terminate program - shell kill command)
91  bind to sigHandler -> raise CO_endProgram flag and safely close application threads*/
92  if (signal(SIGINT, sigHandler) == SIG_ERR)
93  CO_errExit("Program init - SIGINIT handler creation failed");
94  if (signal(SIGTERM, sigHandler) == SIG_ERR)
95  CO_errExit("Program init - SIGTERM handler creation failed");
96  printf("starting CANopen device with Node ID %d(0x%02X)", nodeId, nodeId);
97 
98  while (reset != CO_RESET_APP && reset != CO_RESET_QUIT && CO_endProgram == 0) {
99  /* CANopen communication reset || first run of app- initialize CANopen objects *******************/
100  CO_ReturnError_t err;
101  /*mutex locking for thread safe OD access*/
102  pthread_mutex_lock(&CO_CAN_VALID_mtx);
103  /* Wait rt_thread. */
104  if (!firstRun) {
105  CO_LOCK_OD();
106  CO->CANmodule[0]->CANnormal = false;
107  CO_UNLOCK_OD();
108  }
109  /* initialize CANopen with CAN interface and nodeID */
110  if (CO_init(CANdevice0Index, nodeId, 0) != CO_ERROR_NO) {
111  char s[120];
112  snprintf(s, 120, "Communication reset - CANopen initialization failed, err=%d", err);
113  CO_errExit(s);
114  }
115  /* Configure callback functions for task control */
116  CO_EM_initCallback(CO->em, taskMain_cbSignal);
117  CO_SDO_initCallback(CO->SDO[0], taskMain_cbSignal);
118  CO_SDOclient_initCallback(CO->SDOclient, taskMain_cbSignal);
119 
120  /* Initialize time */
121  CO_time_init(&CO_time, CO->SDO[0], &OD_time.epochTimeBaseMs, &OD_time.epochTimeOffsetMs, 0x2130);
122 
123  /* First time only initialization */
124  if (firstRun) {
125  firstRun = false;
126  /* Configure epoll for mainline */
127  mainline_epoll_fd = epoll_create(4);
128  if (mainline_epoll_fd == -1)
129  CO_errExit("Program init - epoll_create mainline failed");
130 
131  /* Init mainline */
132  taskMain_init(mainline_epoll_fd, &OD_performance[ODA_performance_mainCycleMaxTime]);
133  /* Configure epoll for rt_thread */
134  rt_thread_epoll_fd = epoll_create(2);
135  if (rt_thread_epoll_fd == -1)
136  CO_errExit("Program init - epoll_create rt_thread failed");
137  /* Init taskRT */
138  CANrx_taskTmr_init(rt_thread_epoll_fd, TMR_TASK_INTERVAL_NS, &OD_performance[ODA_performance_timerCycleMaxTime]);
139  OD_performance[ODA_performance_timerCycleTime] = TMR_TASK_INTERVAL_NS / 1000; /* informative */
140 
141  /* Create rt_thread */
142  if (pthread_create(&rt_thread_id, NULL, rt_thread, NULL) != 0)
143  CO_errExit("Program init - rt_thread creation failed");
144  /* Set priority for rt_thread */
145  if (rtPriority > 0) {
146  struct sched_param param;
147  param.sched_priority = rtPriority;
148  if (pthread_setschedparam(rt_thread_id, SCHED_FIFO, &param) != 0)
149  CO_errExit("Program init - rt_thread set scheduler failed");
150  }
151  /* Create control_thread */
152  if (pthread_create(&rt_control_thread_id, NULL, rt_control_thread, NULL) != 0)
153  CO_errExit("Program init - rt_thread_control creation failed");
154  /* Set priority for control thread */
155  if (rtPriority > 0) {
156  struct sched_param paramc;
157  paramc.sched_priority = rtControlPriority;
158  if (pthread_setschedparam(rt_control_thread_id, SCHED_FIFO, &paramc) != 0)
159  CO_errExit("Program init - rt_thread set scheduler failed");
160  }
161  /* start CAN */
162  CO_CANsetNormalMode(CO->CANmodule[0]);
163  pthread_mutex_unlock(&CO_CAN_VALID_mtx);
164  reset = CO_RESET_NOT;
165  /* Execute optional additional application code */
167  readyToStart = true;
168  while (reset == CO_RESET_NOT && CO_endProgram == 0) {
169  /* loop for normal program execution main epoll reading ******************************************/
170  int ready;
171  int first = 0;
172  struct epoll_event ev;
173  ready = epoll_wait(mainline_epoll_fd, &ev, 1, -1);
174  if (ready != 1) {
175  if (errno != EINTR) {
176  CO_error(0x11100000L + errno);
177  }
178  } else if (taskMain_process(ev.data.fd, &reset, CO_timer1ms)) {
179  uint32_t timer1msDiff;
180  timer1msDiff = CO_timer1ms - tmr1msPrev;
181  tmr1msPrev = CO_timer1ms;
182  /* Execute optional additional alication code */
183  // Update loop counter -> Can run in Async or RT thread for faster execution.
184  app_programAsync(timer1msDiff);
185  }
186 
187  else {
188  /* No file descriptor was processed. */
189  CO_error(0x11200000L);
190  }
191  }
192  }
193  /* program exit ***************************************************************/
194  CO_endProgram = 1;
195  if (pthread_join(rt_thread_id, NULL) != 0) {
196  CO_errExit("Program end - pthread_join failed");
197  }
198  if (pthread_join(rt_control_thread_id, NULL) != 0) {
199  CO_errExit("Program end - pthread_join failed");
200  }
201  app_programEnd();
202  /* delete objects from memory */
203  CANrx_taskTmr_close();
204  taskMain_close();
205  CO_delete(CANdevice0Index);
206  printf("Canopend on %s (nodeId=0x%02X) - finished.\n\n", CANdevice, nodeId);
207  /* Flush all buffers (and reboot) */
208  if (rebootEnable && reset == CO_RESET_APP) {
209  sync();
210  if (reboot(LINUX_REBOOT_CMD_RESTART) != 0) {
211  CO_errExit("Program end - reboot failed");
212  }
213  }
214 
215  exit(EXIT_SUCCESS);
216  }
217 }
218 
219 /* Function for CAN send, receive and taskTmr ********************************/
220 static void *rt_thread(void *arg) {
221  while (CO_endProgram == 0) {
222  struct epoll_event ev;
223  int ready = epoll_wait(rt_thread_epoll_fd, &ev, 1, -1);
224  if (ready != 1) {
225  if (errno != EINTR) {
226  CO_error(0x12100000L + errno);
227  }
228  } else if (CANrx_taskTmr_process(ev.data.fd)) {
229  /* code was processed in the above function. Additional code process below */
231  /* Monitor variables with trace objects */
232  CO_time_process(&CO_time);
233 #if CO_NO_TRACE > 0
234  for (i = 0; i < OD_traceEnable && i < CO_NO_TRACE; i++) {
235  CO_trace_process(CO->trace[i], *CO_time.epochTimeOffsetMs);
236  }
237 #endif
238  /* Detect timer large overflow */
239  if (OD_performance[ODA_performance_timerCycleMaxTime] > TMR_TASK_OVERFLOW_US && rtPriority > 0 && CO->CANmodule[0]->CANnormal) {
240  CO_errorReport(CO->em, CO_EM_ISR_TIMER_OVERFLOW, CO_EMC_SOFTWARE_INTERNAL, 0x22400000L | OD_performance[ODA_performance_timerCycleMaxTime]);
241  }
242  }
243 
244  else {
245  /* No file descriptor was processed. */
246  CO_error(0x12200000L);
247  }
248  }
249 
250  return NULL;
251 }
252 /* Control thread function ********************************/
253 static void *rt_control_thread(void *arg) {
254  struct period_info pinfo;
255  periodic_task_init(&pinfo);
257  while (!readyToStart) {
258  wait_rest_of_period(&pinfo);
259  }
260  while (CO_endProgram == 0) {
262  wait_rest_of_period(&pinfo);
263  }
264  return NULL;
265 }
266 /* Control thread time functions ********************************/
267 static void inc_period(struct period_info *pinfo) {
268  pinfo->next_period.tv_nsec += pinfo->period_ns;
269 
270  while (pinfo->next_period.tv_nsec >= 1000000000) {
271  /* timespec nsec overflow */
272  pinfo->next_period.tv_sec++;
273  pinfo->next_period.tv_nsec -= 1000000000;
274  }
275 }
276 static void periodic_task_init(struct period_info *pinfo) {
277  /* for simplicity, hardcoding a 1ms period */
278  pinfo->period_ns = 5000000;
279 
280  clock_gettime(CLOCK_MONOTONIC, &(pinfo->next_period));
281 }
282 static void wait_rest_of_period(struct period_info *pinfo) {
283  inc_period(pinfo);
284 
285  /* for simplicity, ignoring possibilities of signal wakes */
286  clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &pinfo->next_period, NULL);
287 }
288 /* CAN messaging helper functions ********************************/
289 
290 void configureCANopen(int nodeId, int rtPriority, int CANdevice0Index, char *CANdevice) {
291  if (nodeId < 1 || nodeId > 127) {
292  fprintf(stderr, "NODE ID outside range (%d)\n", nodeId);
293  exit(EXIT_FAILURE);
294  }
295  // rt Thread priority sanity check
296  if (rtPriority != -1 && (rtPriority < sched_get_priority_min(SCHED_FIFO) || rtPriority > sched_get_priority_max(SCHED_FIFO))) {
297  fprintf(stderr, "Wrong RT priority (%d)\n", rtPriority);
298  exit(EXIT_FAILURE);
299  }
300 
301  if (CANdevice0Index == 0) {
302  char s[120];
303  snprintf(s, 120, "Can't find CAN device \"%s\"", CANdevice);
304  CO_errExit(s);
305  }
306 
307  /* Verify, if OD structures have proper alignment of initial values */
308  if (CO_OD_RAM.FirstWord != CO_OD_RAM.LastWord) {
309  fprintf(stderr, "Program init - Canopend- Error in CO_OD_RAM.\n");
310  exit(EXIT_FAILURE);
311  }
312 };
313 void CO_errExit(char *msg) {
314  perror(msg);
315  exit(EXIT_FAILURE);
316 }
317 void CO_error(const uint32_t info) {
318  CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, info);
319  fprintf(stderr, "canopend generic error: 0x%X\n", info);
320 }
void app_programEnd(void)
Definition: application.cpp:28
void CO_errExit(char *msg)
Definition: main.cpp:313
void CO_error(const uint32_t info)
Definition: main.cpp:317
struct timespec next_period
Definition: main.cpp:59
volatile uint32_t CO_timer1ms
Definition: main.cpp:70
bool readyToStart
Definition: main.cpp:43
uint32_t tmr1msPrev
Definition: main.cpp:44
void app_communicationReset(void)
Definition: application.cpp:25
void CO_time_init(CO_time_t *tm, CO_SDO_t *SDO, uint64_t *epochTimeBaseMs, uint32_t *epochTimeOffsetMs, uint16_t idx_OD_time)
Definition: CO_time.c:89
void CO_time_process(CO_time_t *tm)
Definition: CO_time.c:109
uint64_t * epochTimeBaseMs
Definition: CO_time.h:38
void app_programControlLoop(void)
Definition: application.cpp:35
Time object, usable for timestamping - Defined in CANOpen code.
Definition: CO_time.h:37
void app_programStart(void)
Definition: application.cpp:19
#define NODEID
Definition: application.h:65
unsigned int uint32_t
Definition: CO_command.h:31
#define TMR_TASK_OVERFLOW_US
Definition: application.h:63
#define TMR_TASK_INTERVAL_NS
Definition: application.h:62
Task Timer used for the Control Loop.
Definition: main.cpp:58
#define INCREMENT_1MS(var)
Definition: application.h:64
long period_ns
Definition: main.cpp:60
void app_programAsync(uint16_t timer1msDiffy)
Definition: application.cpp:32
pthread_mutex_t CO_CAN_VALID_mtx
Definition: main.cpp:39
volatile sig_atomic_t CO_endProgram
Definition: main.cpp:71
int main(int argc, char *argv[])
Definition: main.cpp:79
void configureCANopen(int nodeId, int rtPriority, int CANdevice0Index, char *CANdevice)
Definition: main.cpp:290
uint32_t * epochTimeOffsetMs
Definition: CO_time.h:39