1 /*
2 * CANopen Heartbeat consumer object.
3 *
4 * @file CO_HBconsumer.c
5 * @ingroup CO_HBconsumer
6 * @author Janez Paternoster
7 * @copyright 2004 - 2020 Janez Paternoster
8 *
9 * This file is part of CANopenNode, an opensource CANopen Stack.
10 * Project home page is <https://github.com/CANopenNode/CANopenNode>.
11 * For more information on CANopen see <http://www.can-cia.org/>.
12 *
13 * Licensed under the Apache License, Version 2.0 (the "License");
14 * you may not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
16 *
17 * http://www.apache.org/licenses/LICENSE-2.0
18 *
19 * Unless required by applicable law or agreed to in writing, software
20 * distributed under the License is distributed on an "AS IS" BASIS,
21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 * See the License for the specific language governing permissions and
23 * limitations under the License.
24 */
25
26 #include "CANopen.h"
27 #include "CO_HBconsumer.h"
28
29 /*
30 * Read received message from CAN module.
31 *
32 * Function will be called (by CAN receive interrupt) every time, when CAN
33 * message with correct identifier will be received. For more information and
34 * description of parameters see file CO_driver.h.
35 */
CO_HBcons_receive(void * object,const CO_CANrxMsg_t * msg)36 static void CO_HBcons_receive(void *object, const CO_CANrxMsg_t *msg){
37 CO_HBconsNode_t *HBconsNode;
38
39 HBconsNode = (CO_HBconsNode_t*) object; /* this is the correct pointer type of the first argument */
40
41 /* verify message length */
42 if(msg->DLC == 1){
43 /* copy data and set 'new message' flag. */
44 HBconsNode->NMTstate = (CO_NMT_internalState_t)msg->data[0];
45 SET_CANrxNew(HBconsNode->CANrxNew);
46 }
47 }
48
49
50 /*
51 * Configure one monitored node.
52 */
CO_HBcons_monitoredNodeConfig(CO_HBconsumer_t * HBcons,uint8_t idx,uint8_t nodeId,uint16_t time)53 static void CO_HBcons_monitoredNodeConfig(
54 CO_HBconsumer_t *HBcons,
55 uint8_t idx,
56 uint8_t nodeId,
57 uint16_t time)
58 {
59 uint16_t COB_ID;
60 CO_HBconsNode_t *monitoredNode;
61
62 if(idx >= HBcons->numberOfMonitoredNodes) return;
63
64 monitoredNode = &HBcons->monitoredNodes[idx];
65 monitoredNode->nodeId = nodeId;
66 monitoredNode->time = time;
67 monitoredNode->NMTstate = CO_NMT_INITIALIZING;
68 monitoredNode->HBstate = CO_HBconsumer_UNCONFIGURED;
69
70 /* is channel used */
71 if(monitoredNode->nodeId && monitoredNode->time){
72 COB_ID = monitoredNode->nodeId + CO_CAN_ID_HEARTBEAT;
73 monitoredNode->HBstate = CO_HBconsumer_UNKNOWN;
74
75 }
76 else{
77 COB_ID = 0;
78 monitoredNode->time = 0;
79 }
80
81 /* configure Heartbeat consumer CAN reception */
82 if (monitoredNode->HBstate != CO_HBconsumer_UNCONFIGURED) {
83 CO_CANrxBufferInit(
84 HBcons->CANdevRx,
85 HBcons->CANdevRxIdxStart + idx,
86 COB_ID,
87 0x7FF,
88 0,
89 (void*)&HBcons->monitoredNodes[idx],
90 CO_HBcons_receive);
91 }
92 }
93
94
95 /*
96 * OD function for accessing _Consumer Heartbeat Time_ (index 0x1016) from SDO server.
97 *
98 * For more information see file CO_SDO.h.
99 */
CO_ODF_1016(CO_ODF_arg_t * ODF_arg)100 static CO_SDO_abortCode_t CO_ODF_1016(CO_ODF_arg_t *ODF_arg)
101 {
102 CO_HBconsumer_t *HBcons;
103 uint8_t NodeID;
104 uint16_t HBconsTime;
105 uint32_t value;
106 CO_ReturnError_t ret;
107
108 if(ODF_arg->reading){
109 return CO_SDO_AB_NONE;
110 }
111
112 HBcons = (CO_HBconsumer_t*) ODF_arg->object;
113 value = CO_getUint32(ODF_arg->data);
114 NodeID = (value >> 16U) & 0xFFU;
115 HBconsTime = value & 0xFFFFU;
116
117 if((value & 0xFF800000U) != 0){
118 return CO_SDO_AB_PRAM_INCOMPAT;
119 }
120
121 ret = CO_HBconsumer_initEntry(HBcons, ODF_arg->subIndex-1U, NodeID, HBconsTime);
122 if (ret != CO_ERROR_NO) {
123 return CO_SDO_AB_PRAM_INCOMPAT;
124 }
125 return CO_SDO_AB_NONE;
126 }
127
128
129 /******************************************************************************/
CO_HBconsumer_init(CO_HBconsumer_t * HBcons,CO_EM_t * em,CO_SDO_t * SDO,const uint32_t HBconsTime[],CO_HBconsNode_t monitoredNodes[],uint8_t numberOfMonitoredNodes,CO_CANmodule_t * CANdevRx,uint16_t CANdevRxIdxStart)130 CO_ReturnError_t CO_HBconsumer_init(
131 CO_HBconsumer_t *HBcons,
132 CO_EM_t *em,
133 CO_SDO_t *SDO,
134 const uint32_t HBconsTime[],
135 CO_HBconsNode_t monitoredNodes[],
136 uint8_t numberOfMonitoredNodes,
137 CO_CANmodule_t *CANdevRx,
138 uint16_t CANdevRxIdxStart)
139 {
140 uint8_t i;
141
142 /* verify arguments */
143 if(HBcons==NULL || em==NULL || SDO==NULL || HBconsTime==NULL ||
144 monitoredNodes==NULL || CANdevRx==NULL){
145 return CO_ERROR_ILLEGAL_ARGUMENT;
146 }
147
148 /* Configure object variables */
149 HBcons->em = em;
150 HBcons->HBconsTime = HBconsTime;
151 HBcons->monitoredNodes = monitoredNodes;
152 HBcons->numberOfMonitoredNodes = numberOfMonitoredNodes;
153 HBcons->allMonitoredOperational = 0;
154 HBcons->CANdevRx = CANdevRx;
155 HBcons->CANdevRxIdxStart = CANdevRxIdxStart;
156
157 for(i=0; i<HBcons->numberOfMonitoredNodes; i++) {
158 uint8_t nodeId = (HBcons->HBconsTime[i] >> 16U) & 0xFFU;
159 uint16_t time = HBcons->HBconsTime[i] & 0xFFFFU;
160 CO_HBconsumer_initEntry(HBcons, i, nodeId, time);
161 }
162
163 /* Configure Object dictionary entry at index 0x1016 */
164 CO_OD_configure(SDO, OD_H1016_CONSUMER_HB_TIME, CO_ODF_1016, (void*)HBcons, 0, 0);
165
166 return CO_ERROR_NO;
167 }
168
169
170 /******************************************************************************/
CO_HBconsumer_initEntry(CO_HBconsumer_t * HBcons,uint8_t idx,uint8_t nodeId,uint16_t consumerTime)171 CO_ReturnError_t CO_HBconsumer_initEntry(
172 CO_HBconsumer_t *HBcons,
173 uint8_t idx,
174 uint8_t nodeId,
175 uint16_t consumerTime)
176 {
177 CO_ReturnError_t ret = CO_ERROR_NO;
178
179 /* verify arguments */
180 if(HBcons==NULL){
181 return CO_ERROR_ILLEGAL_ARGUMENT;
182 }
183
184 if((consumerTime != 0) && (nodeId != 0)){
185 uint8_t i;
186 /* there must not be more entries with same index and time different than zero */
187 for(i = 0U; i<HBcons->numberOfMonitoredNodes; i++){
188 uint32_t objectCopy = HBcons->HBconsTime[i];
189 uint8_t NodeIDObj = (objectCopy >> 16U) & 0xFFU;
190 uint16_t HBconsTimeObj = objectCopy & 0xFFFFU;
191 if((idx != i) && (HBconsTimeObj != 0) && (nodeId == NodeIDObj)){
192 ret = CO_ERROR_ILLEGAL_ARGUMENT;
193 }
194 }
195 }
196
197 /* Configure */
198 if(ret == CO_ERROR_NO){
199 CO_HBcons_monitoredNodeConfig(HBcons, idx, nodeId, consumerTime);
200 }
201 return ret;
202 }
203
204
205 /******************************************************************************/
CO_HBconsumer_initCallbackHeartbeatStarted(CO_HBconsumer_t * HBcons,uint8_t idx,void * object,void (* pFunctSignal)(uint8_t nodeId,uint8_t idx,void * object))206 void CO_HBconsumer_initCallbackHeartbeatStarted(
207 CO_HBconsumer_t *HBcons,
208 uint8_t idx,
209 void *object,
210 void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object))
211 {
212 CO_HBconsNode_t *monitoredNode;
213
214 if (HBcons==NULL || idx>=HBcons->numberOfMonitoredNodes) {
215 return;
216 }
217
218 monitoredNode = &HBcons->monitoredNodes[idx];
219 monitoredNode->pFunctSignalHbStarted = pFunctSignal;
220 monitoredNode->functSignalObjectHbStarted = object;
221 }
222
223
224 /******************************************************************************/
CO_HBconsumer_initCallbackTimeout(CO_HBconsumer_t * HBcons,uint8_t idx,void * object,void (* pFunctSignal)(uint8_t nodeId,uint8_t idx,void * object))225 void CO_HBconsumer_initCallbackTimeout(
226 CO_HBconsumer_t *HBcons,
227 uint8_t idx,
228 void *object,
229 void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object))
230 {
231 CO_HBconsNode_t *monitoredNode;
232
233 if (HBcons==NULL || idx>=HBcons->numberOfMonitoredNodes) {
234 return;
235 }
236
237 monitoredNode = &HBcons->monitoredNodes[idx];
238 monitoredNode->pFunctSignalTimeout = pFunctSignal;
239 monitoredNode->functSignalObjectTimeout = object;
240 }
241
242
243 /******************************************************************************/
CO_HBconsumer_initCallbackRemoteReset(CO_HBconsumer_t * HBcons,uint8_t idx,void * object,void (* pFunctSignal)(uint8_t nodeId,uint8_t idx,void * object))244 void CO_HBconsumer_initCallbackRemoteReset(
245 CO_HBconsumer_t *HBcons,
246 uint8_t idx,
247 void *object,
248 void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object))
249 {
250 CO_HBconsNode_t *monitoredNode;
251
252 if (HBcons==NULL || idx>=HBcons->numberOfMonitoredNodes) {
253 return;
254 }
255
256 monitoredNode = &HBcons->monitoredNodes[idx];
257 monitoredNode->pFunctSignalRemoteReset = pFunctSignal;
258 monitoredNode->functSignalObjectRemoteReset = object;
259 }
260
261 /******************************************************************************/
CO_HBconsumer_process(CO_HBconsumer_t * HBcons,bool_t NMTisPreOrOperational,uint16_t timeDifference_ms)262 void CO_HBconsumer_process(
263 CO_HBconsumer_t *HBcons,
264 bool_t NMTisPreOrOperational,
265 uint16_t timeDifference_ms)
266 {
267 uint8_t i;
268 uint8_t emcyHeartbeatTimeoutActive = 0;
269 uint8_t emcyRemoteResetActive = 0;
270 uint8_t AllMonitoredOperationalCopy;
271 CO_HBconsNode_t *monitoredNode;
272
273 AllMonitoredOperationalCopy = 5;
274 monitoredNode = &HBcons->monitoredNodes[0];
275
276 if(NMTisPreOrOperational){
277 for(i=0; i<HBcons->numberOfMonitoredNodes; i++){
278 uint16_t timeDifference_ms_copy = timeDifference_ms;
279 if(monitoredNode->time > 0){/* is node monitored */
280 /* Verify if received message is heartbeat or bootup */
281 if(IS_CANrxNew(monitoredNode->CANrxNew)){
282 if(monitoredNode->NMTstate == CO_NMT_INITIALIZING){
283 /* bootup message, call callback */
284 if (monitoredNode->pFunctSignalRemoteReset != NULL) {
285 monitoredNode->pFunctSignalRemoteReset(monitoredNode->nodeId, i,
286 monitoredNode->functSignalObjectRemoteReset);
287 }
288 }
289 else {
290 /* heartbeat message */
291 if (monitoredNode->HBstate!=CO_HBconsumer_ACTIVE &&
292 monitoredNode->pFunctSignalHbStarted!=NULL) {
293 monitoredNode->pFunctSignalHbStarted(monitoredNode->nodeId, i,
294 monitoredNode->functSignalObjectHbStarted);
295 }
296 monitoredNode->HBstate = CO_HBconsumer_ACTIVE;
297 monitoredNode->timeoutTimer = 0; /* reset timer */
298 timeDifference_ms_copy = 0;
299 }
300 CLEAR_CANrxNew(monitoredNode->CANrxNew);
301 }
302
303 /* Verify timeout */
304 if(monitoredNode->timeoutTimer < monitoredNode->time) {
305 monitoredNode->timeoutTimer += timeDifference_ms_copy;
306 }
307 if(monitoredNode->HBstate!=CO_HBconsumer_UNCONFIGURED &&
308 monitoredNode->HBstate!=CO_HBconsumer_UNKNOWN) {
309 if(monitoredNode->timeoutTimer >= monitoredNode->time){
310 /* timeout expired */
311 CO_errorReport(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, CO_EMC_HEARTBEAT, i);
312 emcyHeartbeatTimeoutActive = 1;
313
314 monitoredNode->NMTstate = CO_NMT_INITIALIZING;
315 if (monitoredNode->HBstate!=CO_HBconsumer_TIMEOUT &&
316 monitoredNode->pFunctSignalTimeout!=NULL) {
317 monitoredNode->pFunctSignalTimeout(monitoredNode->nodeId, i,
318 monitoredNode->functSignalObjectTimeout);
319 }
320 monitoredNode->HBstate = CO_HBconsumer_TIMEOUT;
321 }
322 else if(monitoredNode->NMTstate == CO_NMT_INITIALIZING){
323 /* there was a bootup message */
324 CO_errorReport(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, CO_EMC_HEARTBEAT, i);
325 emcyRemoteResetActive = 1;
326
327 monitoredNode->HBstate = CO_HBconsumer_UNKNOWN;
328 }
329 }
330 if(monitoredNode->NMTstate != CO_NMT_OPERATIONAL) {
331 AllMonitoredOperationalCopy = 0;
332 }
333 }
334 monitoredNode++;
335 }
336 }
337 else{ /* not in (pre)operational state */
338 for(i=0; i<HBcons->numberOfMonitoredNodes; i++){
339 monitoredNode->NMTstate = CO_NMT_INITIALIZING;
340 CLEAR_CANrxNew(monitoredNode->CANrxNew);
341 if(monitoredNode->HBstate != CO_HBconsumer_UNCONFIGURED){
342 monitoredNode->HBstate = CO_HBconsumer_UNKNOWN;
343 }
344 monitoredNode++;
345 }
346 AllMonitoredOperationalCopy = 0;
347 }
348 /* clear emergencies. We only have one emergency index for all
349 * monitored nodes! */
350 if ( ! emcyHeartbeatTimeoutActive) {
351 CO_errorReset(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, 0);
352 }
353 if ( ! emcyRemoteResetActive) {
354 CO_errorReset(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, 0);
355 }
356
357 HBcons->allMonitoredOperational = AllMonitoredOperationalCopy;
358 }
359
360
361 /******************************************************************************/
CO_HBconsumer_getIdxByNodeId(CO_HBconsumer_t * HBcons,uint8_t nodeId)362 int8_t CO_HBconsumer_getIdxByNodeId(
363 CO_HBconsumer_t *HBcons,
364 uint8_t nodeId)
365 {
366 uint8_t i;
367 CO_HBconsNode_t *monitoredNode;
368
369 if (HBcons == NULL) {
370 return -1;
371 }
372
373 /* linear search for the node */
374 monitoredNode = &HBcons->monitoredNodes[0];
375 for(i=0; i<HBcons->numberOfMonitoredNodes; i++){
376 if (monitoredNode->nodeId == nodeId) {
377 return i;
378 }
379 monitoredNode ++;
380 }
381 /* not found */
382 return -1;
383 }
384
385
386 /******************************************************************************/
CO_HBconsumer_getState(CO_HBconsumer_t * HBcons,uint8_t idx)387 CO_HBconsumer_state_t CO_HBconsumer_getState(
388 CO_HBconsumer_t *HBcons,
389 uint8_t idx)
390 {
391 CO_HBconsNode_t *monitoredNode;
392
393 if (HBcons==NULL || idx>=HBcons->numberOfMonitoredNodes) {
394 return CO_HBconsumer_UNCONFIGURED;
395 }
396
397 monitoredNode = &HBcons->monitoredNodes[idx];
398 return monitoredNode->HBstate;
399 }
400
401 /******************************************************************************/
CO_HBconsumer_getNmtState(CO_HBconsumer_t * HBcons,uint8_t idx,CO_NMT_internalState_t * nmtState)402 int8_t CO_HBconsumer_getNmtState(
403 CO_HBconsumer_t *HBcons,
404 uint8_t idx,
405 CO_NMT_internalState_t *nmtState)
406 {
407 CO_HBconsNode_t *monitoredNode;
408
409 if (HBcons==NULL || nmtState==NULL || idx>=HBcons->numberOfMonitoredNodes) {
410 return -1;
411 }
412 *nmtState = CO_NMT_INITIALIZING;
413
414 monitoredNode = &HBcons->monitoredNodes[idx];
415
416 if (monitoredNode->HBstate == CO_HBconsumer_ACTIVE) {
417 *nmtState = monitoredNode->NMTstate;
418 return 0;
419 }
420 return -1;
421 }
422