CORC Project
CANOpen Robot Controller Software Documentation
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 #include "application.h"
33 /* Threads and thread safety variables***********************************************************/
38 pthread_mutex_t CO_CAN_VALID_mtx = PTHREAD_MUTEX_INITIALIZER;
39 
40 static int mainline_epoll_fd;
41 static CO_time_t CO_time;
42 bool readyToStart = false;
44 
45 /*CAN msg processing thread variables*/
46 static int rtPriority = 2;
47 static void *rt_thread(void *arg);
48 static pthread_t rt_thread_id;
49 static int rt_thread_epoll_fd;
50 /* Application Control loop thread */
51 static int rtControlPriority = 80;
52 static void *rt_control_thread(void *arg);
53 static pthread_t rt_control_thread_id;
54 static int rt_control_thread_epoll_fd;
57 struct period_info {
58  struct timespec next_period;
59  long period_ns;
60 };
61 /* Forward declartion of control loop thread timer functions*/
62 static void inc_period(struct period_info *pinfo);
63 static void periodic_task_init(struct period_info *pinfo);
64 static void wait_rest_of_period(struct period_info *pinfo);
65 /* Forward declartion of CAN helper functions*/
66 void configureCANopen(int nodeId, int rtPriority, int CANdevice0Index, char *CANdevice);
67 void CO_errExit(char *msg);
68 void CO_error(const uint32_t info);
69 volatile uint32_t CO_timer1ms = 0U;
70 volatile sig_atomic_t CO_endProgram = 0;
71 static void sigHandler(int sig) {
72  CO_endProgram = 1;
73 }
74 
75 /******************************************************************************/
77 /******************************************************************************/
78 int main(int argc, char *argv[]) {
79  /* TODO : MOVE bellow definitionsTO SOME KIND OF CANobject, struct or the like*/
80  CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
81  bool_t firstRun = true;
82  bool_t rebootEnable = false; // TODO: DO WE EVER RESET? OR NEED TO?
83  char CANdevice[10] = "vcan0";
84  int nodeId = NODEID;
85  /*map linux CAN interface to corresponding int index return zero if no interface exists.*/
86  int CANdevice0Index = if_nametoindex(CANdevice);
87  configureCANopen(nodeId, rtPriority, CANdevice0Index, CANdevice);
88 
89  /* Set up catch of linux signals SIGINT(ctrl+c) and SIGTERM (terminate program - shell kill command)
90  bind to sigHandler -> raise CO_endProgram flag and safely close application threads*/
91  if (signal(SIGINT, sigHandler) == SIG_ERR)
92  CO_errExit("Program init - SIGINIT handler creation failed");
93  if (signal(SIGTERM, sigHandler) == SIG_ERR)
94  CO_errExit("Program init - SIGTERM handler creation failed");
95  printf("starting CANopen device with Node ID %d(0x%02X)", nodeId, nodeId);
96 
97  while (reset != CO_RESET_APP && reset != CO_RESET_QUIT && CO_endProgram == 0) {
98  /* CANopen communication reset || first run of app- initialize CANopen objects *******************/
99  CO_ReturnError_t err;
100  /*mutex locking for thread safe OD access*/
101  pthread_mutex_lock(&CO_CAN_VALID_mtx);
102  /* Wait rt_thread. */
103  if (!firstRun) {
104  CO_LOCK_OD();
105  CO->CANmodule[0]->CANnormal = false;
106  CO_UNLOCK_OD();
107  }
108  /* initialize CANopen with CAN interface and nodeID */
109  if (CO_init(CANdevice0Index, nodeId, 0) != CO_ERROR_NO) {
110  char s[120];
111  snprintf(s, 120, "Communication reset - CANopen initialization failed, err=%d", err);
112  CO_errExit(s);
113  }
114  /* Configure callback functions for task control */
115  CO_EM_initCallback(CO->em, taskMain_cbSignal);
116  CO_SDO_initCallback(CO->SDO[0], taskMain_cbSignal);
117  CO_SDOclient_initCallback(CO->SDOclient, taskMain_cbSignal);
118 
119  /* Initialize time */
120  CO_time_init(&CO_time, CO->SDO[0], &OD_time.epochTimeBaseMs, &OD_time.epochTimeOffsetMs, 0x2130);
121 
122  /* First time only initialization */
123  if (firstRun) {
124  firstRun = false;
125  /* Configure epoll for mainline */
126  mainline_epoll_fd = epoll_create(4);
127  if (mainline_epoll_fd == -1)
128  CO_errExit("Program init - epoll_create mainline failed");
129 
130  /* Init mainline */
131  taskMain_init(mainline_epoll_fd, &OD_performance[ODA_performance_mainCycleMaxTime]);
132  /* Configure epoll for rt_thread */
133  rt_thread_epoll_fd = epoll_create(2);
134  if (rt_thread_epoll_fd == -1)
135  CO_errExit("Program init - epoll_create rt_thread failed");
136  /* Init taskRT */
137  CANrx_taskTmr_init(rt_thread_epoll_fd, TMR_TASK_INTERVAL_NS, &OD_performance[ODA_performance_timerCycleMaxTime]);
138  OD_performance[ODA_performance_timerCycleTime] = TMR_TASK_INTERVAL_NS / 1000; /* informative */
139 
140  /* Create rt_thread */
141  if (pthread_create(&rt_thread_id, NULL, rt_thread, NULL) != 0)
142  CO_errExit("Program init - rt_thread creation failed");
143  /* Set priority for rt_thread */
144  if (rtPriority > 0) {
145  struct sched_param param;
146  param.sched_priority = rtPriority;
147  if (pthread_setschedparam(rt_thread_id, SCHED_FIFO, &param) != 0)
148  CO_errExit("Program init - rt_thread set scheduler failed");
149  }
150  /* Create control_thread */
151  if (pthread_create(&rt_control_thread_id, NULL, rt_control_thread, NULL) != 0)
152  CO_errExit("Program init - rt_thread_control creation failed");
153  /* Set priority for control thread */
154  if (rtPriority > 0) {
155  struct sched_param paramc;
156  paramc.sched_priority = rtControlPriority;
157  if (pthread_setschedparam(rt_control_thread_id, SCHED_FIFO, &paramc) != 0)
158  CO_errExit("Program init - rt_thread set scheduler failed");
159  }
160  /* start CAN */
161  CO_CANsetNormalMode(CO->CANmodule[0]);
162  pthread_mutex_unlock(&CO_CAN_VALID_mtx);
163  reset = CO_RESET_NOT;
164  /* Execute optional additional application code */
166  readyToStart = true;
167  while (reset == CO_RESET_NOT && CO_endProgram == 0) {
168  /* loop for normal program execution main epoll reading ******************************************/
169  int ready;
170  int first = 0;
171  struct epoll_event ev;
172  ready = epoll_wait(mainline_epoll_fd, &ev, 1, -1);
173  if (ready != 1) {
174  if (errno != EINTR) {
175  CO_error(0x11100000L + errno);
176  }
177  } else if (taskMain_process(ev.data.fd, &reset, CO_timer1ms)) {
178  uint32_t timer1msDiff;
179  timer1msDiff = CO_timer1ms - tmr1msPrev;
180  tmr1msPrev = CO_timer1ms;
181  /* Execute optional additional alication code */
182  app_programAsync(timer1msDiff);
183  }
184 
185  else {
186  /* No file descriptor was processed. */
187  CO_error(0x11200000L);
188  }
189  }
190  }
191  /* program exit ***************************************************************/
192  CO_endProgram = 1;
193  if (pthread_join(rt_thread_id, NULL) != 0) {
194  CO_errExit("Program end - pthread_join failed");
195  }
196  if (pthread_join(rt_control_thread_id, NULL) != 0) {
197  CO_errExit("Program end - pthread_join failed");
198  }
199  app_programEnd();
200  /* delete objects from memory */
201  CANrx_taskTmr_close();
202  taskMain_close();
203  CO_delete(CANdevice0Index);
204  printf("Canopend on %s (nodeId=0x%02X) - finished.\n\n", CANdevice, nodeId);
205  /* Flush all buffers (and reboot) */
206  if (rebootEnable && reset == CO_RESET_APP) {
207  sync();
208  if (reboot(LINUX_REBOOT_CMD_RESTART) != 0) {
209  CO_errExit("Program end - reboot failed");
210  }
211  }
212 
213  exit(EXIT_SUCCESS);
214  }
215 }
216 
217 /* Function for CAN send, receive and taskTmr ********************************/
218 static void *rt_thread(void *arg) {
219  while (CO_endProgram == 0) {
220  struct epoll_event ev;
221  int ready = epoll_wait(rt_thread_epoll_fd, &ev, 1, -1);
222  if (ready != 1) {
223  if (errno != EINTR) {
224  CO_error(0x12100000L + errno);
225  }
226  } else if (CANrx_taskTmr_process(ev.data.fd)) {
227  /* code was processed in the above function. Additional code process below */
229  /* Monitor variables with trace objects */
230  CO_time_process(&CO_time);
231 #if CO_NO_TRACE > 0
232  for (i = 0; i < OD_traceEnable && i < CO_NO_TRACE; i++) {
233  CO_trace_process(CO->trace[i], *CO_time.epochTimeOffsetMs);
234  }
235 #endif
236  /* Detect timer large overflow */
237  if (OD_performance[ODA_performance_timerCycleMaxTime] > TMR_TASK_OVERFLOW_US && rtPriority > 0 && CO->CANmodule[0]->CANnormal) {
238  CO_errorReport(CO->em, CO_EM_ISR_TIMER_OVERFLOW, CO_EMC_SOFTWARE_INTERNAL, 0x22400000L | OD_performance[ODA_performance_timerCycleMaxTime]);
239  }
240  }
241 
242  else {
243  /* No file descriptor was processed. */
244  CO_error(0x12200000L);
245  }
246  }
247 
248  return NULL;
249 }
250 /* Control thread function ********************************/
251 static void *rt_control_thread(void *arg) {
252  struct period_info pinfo;
253  periodic_task_init(&pinfo);
255  while (!readyToStart) {
256  wait_rest_of_period(&pinfo);
257  }
258  while (CO_endProgram == 0) {
260  wait_rest_of_period(&pinfo);
261  }
262  return NULL;
263 }
264 /* Control thread time functions ********************************/
265 static void inc_period(struct period_info *pinfo) {
266  pinfo->next_period.tv_nsec += pinfo->period_ns;
267 
268  while (pinfo->next_period.tv_nsec >= 1000000000) {
269  /* timespec nsec overflow */
270  pinfo->next_period.tv_sec++;
271  pinfo->next_period.tv_nsec -= 1000000000;
272  }
273 }
274 static void periodic_task_init(struct period_info *pinfo) {
275  /* for simplicity, hardcoding a 1ms period */
276  pinfo->period_ns = 5000000;
277 
278  clock_gettime(CLOCK_MONOTONIC, &(pinfo->next_period));
279 }
280 static void wait_rest_of_period(struct period_info *pinfo) {
281  inc_period(pinfo);
282 
283  /* for simplicity, ignoring possibilities of signal wakes */
284  clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &pinfo->next_period, NULL);
285 }
286 /* CAN messaging helper functions ********************************/
287 
288 void configureCANopen(int nodeId, int rtPriority, int CANdevice0Index, char *CANdevice) {
289  if (nodeId < 1 || nodeId > 127) {
290  fprintf(stderr, "NODE ID outside range (%d)\n", nodeId);
291  exit(EXIT_FAILURE);
292  }
293  // rt Thread priority sanity check
294  if (rtPriority != -1 && (rtPriority < sched_get_priority_min(SCHED_FIFO) || rtPriority > sched_get_priority_max(SCHED_FIFO))) {
295  fprintf(stderr, "Wrong RT priority (%d)\n", rtPriority);
296  exit(EXIT_FAILURE);
297  }
298 
299  if (CANdevice0Index == 0) {
300  char s[120];
301  snprintf(s, 120, "Can't find CAN device \"%s\"", CANdevice);
302  CO_errExit(s);
303  }
304 
305  /* Verify, if OD structures have proper alignment of initial values */
306  if (CO_OD_RAM.FirstWord != CO_OD_RAM.LastWord) {
307  fprintf(stderr, "Program init - Canopend- Error in CO_OD_RAM.\n");
308  exit(EXIT_FAILURE);
309  }
310 };
311 void CO_errExit(char *msg) {
312  perror(msg);
313  exit(EXIT_FAILURE);
314 }
315 void CO_error(const uint32_t info) {
316  CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, info);
317  fprintf(stderr, "canopend generic error: 0x%X\n", info);
318 }
void app_programEnd(void)
Function is called just before program ends.
Definition: application.cpp:28
void CO_errExit(char *msg)
Definition: main.cpp:311
void CO_error(const uint32_t info)
Definition: main.cpp:315
struct timespec next_period
Definition: main.cpp:58
volatile uint32_t CO_timer1ms
Definition: main.cpp:69
bool readyToStart
Definition: main.cpp:42
uint32_t tmr1msPrev
Definition: main.cpp:43
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)
Function is called cyclically from Control loop thread at constant intervals.
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:57
#define INCREMENT_1MS(var)
Definition: application.h:64
long period_ns
Definition: main.cpp:59
void app_programAsync(uint16_t timer1msDiffy)
Definition: application.cpp:32
pthread_mutex_t CO_CAN_VALID_mtx
Definition: main.cpp:38
volatile sig_atomic_t CO_endProgram
Definition: main.cpp:70
int main(int argc, char *argv[])
Definition: main.cpp:78
void configureCANopen(int nodeId, int rtPriority, int CANdevice0Index, char *CANdevice)
Definition: main.cpp:288
uint32_t * epochTimeOffsetMs
Definition: CO_time.h:39