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 if(monitoredNode->time > 0){/* is node monitored */
279 /* Verify if received message is heartbeat or bootup */
280 if(IS_CANrxNew(monitoredNode->CANrxNew)){
281 if(monitoredNode->NMTstate == CO_NMT_INITIALIZING){
282 /* bootup message, call callback */
283 if (monitoredNode->pFunctSignalRemoteReset != NULL) {
284 monitoredNode->pFunctSignalRemoteReset(monitoredNode->nodeId, i,
285 monitoredNode->functSignalObjectRemoteReset);
286 }
287 }
288 else {
289 /* heartbeat message */
290 if (monitoredNode->HBstate!=CO_HBconsumer_ACTIVE &&
291 monitoredNode->pFunctSignalHbStarted!=NULL) {
292 monitoredNode->pFunctSignalHbStarted(monitoredNode->nodeId, i,
293 monitoredNode->functSignalObjectHbStarted);
294 }
295 monitoredNode->HBstate = CO_HBconsumer_ACTIVE;
296 monitoredNode->timeoutTimer = 0; /* reset timer */
297 timeDifference_ms = 0;
298 }
299 CLEAR_CANrxNew(monitoredNode->CANrxNew);
300 }
301
302 /* Verify timeout */
303 if(monitoredNode->timeoutTimer < monitoredNode->time) {
304 monitoredNode->timeoutTimer += timeDifference_ms;
305 }
306 if(monitoredNode->HBstate!=CO_HBconsumer_UNCONFIGURED &&
307 monitoredNode->HBstate!=CO_HBconsumer_UNKNOWN) {
308 if(monitoredNode->timeoutTimer >= monitoredNode->time){
309 /* timeout expired */
310 CO_errorReport(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, CO_EMC_HEARTBEAT, i);
311 emcyHeartbeatTimeoutActive = 1;
312
313 monitoredNode->NMTstate = CO_NMT_INITIALIZING;
314 if (monitoredNode->HBstate!=CO_HBconsumer_TIMEOUT &&
315 monitoredNode->pFunctSignalTimeout!=NULL) {
316 monitoredNode->pFunctSignalTimeout(monitoredNode->nodeId, i,
317 monitoredNode->functSignalObjectTimeout);
318 }
319 monitoredNode->HBstate = CO_HBconsumer_TIMEOUT;
320 }
321 else if(monitoredNode->NMTstate == CO_NMT_INITIALIZING){
322 /* there was a bootup message */
323 CO_errorReport(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, CO_EMC_HEARTBEAT, i);
324 emcyRemoteResetActive = 1;
325
326 monitoredNode->HBstate = CO_HBconsumer_UNKNOWN;
327 }
328 }
329 if(monitoredNode->NMTstate != CO_NMT_OPERATIONAL) {
330 AllMonitoredOperationalCopy = 0;
331 }
332 }
333 monitoredNode++;
334 }
335 }
336 else{ /* not in (pre)operational state */
337 for(i=0; i<HBcons->numberOfMonitoredNodes; i++){
338 monitoredNode->NMTstate = CO_NMT_INITIALIZING;
339 CLEAR_CANrxNew(monitoredNode->CANrxNew);
340 if(monitoredNode->HBstate != CO_HBconsumer_UNCONFIGURED){
341 monitoredNode->HBstate = CO_HBconsumer_UNKNOWN;
342 }
343 monitoredNode++;
344 }
345 AllMonitoredOperationalCopy = 0;
346 }
347 /* clear emergencies. We only have one emergency index for all
348 * monitored nodes! */
349 if ( ! emcyHeartbeatTimeoutActive) {
350 CO_errorReset(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, 0);
351 }
352 if ( ! emcyRemoteResetActive) {
353 CO_errorReset(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, 0);
354 }
355
356 HBcons->allMonitoredOperational = AllMonitoredOperationalCopy;
357 }
358
359
360 /******************************************************************************/
CO_HBconsumer_getIdxByNodeId(CO_HBconsumer_t * HBcons,uint8_t nodeId)361 int8_t CO_HBconsumer_getIdxByNodeId(
362 CO_HBconsumer_t *HBcons,
363 uint8_t nodeId)
364 {
365 uint8_t i;
366 CO_HBconsNode_t *monitoredNode;
367
368 if (HBcons == NULL) {
369 return -1;
370 }
371
372 /* linear search for the node */
373 monitoredNode = &HBcons->monitoredNodes[0];
374 for(i=0; i<HBcons->numberOfMonitoredNodes; i++){
375 if (monitoredNode->nodeId == nodeId) {
376 return i;
377 }
378 monitoredNode ++;
379 }
380 /* not found */
381 return -1;
382 }
383
384
385 /******************************************************************************/
CO_HBconsumer_getState(CO_HBconsumer_t * HBcons,uint8_t idx)386 CO_HBconsumer_state_t CO_HBconsumer_getState(
387 CO_HBconsumer_t *HBcons,
388 uint8_t idx)
389 {
390 CO_HBconsNode_t *monitoredNode;
391
392 if (HBcons==NULL || idx>HBcons->numberOfMonitoredNodes) {
393 return CO_HBconsumer_UNCONFIGURED;
394 }
395
396 monitoredNode = &HBcons->monitoredNodes[idx];
397 return monitoredNode->HBstate;
398 }
399
400 /******************************************************************************/
CO_HBconsumer_getNmtState(CO_HBconsumer_t * HBcons,uint8_t idx,CO_NMT_internalState_t * nmtState)401 int8_t CO_HBconsumer_getNmtState(
402 CO_HBconsumer_t *HBcons,
403 uint8_t idx,
404 CO_NMT_internalState_t *nmtState)
405 {
406 CO_HBconsNode_t *monitoredNode;
407
408 if (HBcons==NULL || nmtState==NULL || idx>HBcons->numberOfMonitoredNodes) {
409 return -1;
410 }
411 *nmtState = CO_NMT_INITIALIZING;
412
413 monitoredNode = &HBcons->monitoredNodes[idx];
414
415 if (monitoredNode->HBstate == CO_HBconsumer_ACTIVE) {
416 *nmtState = monitoredNode->NMTstate;
417 return 0;
418 }
419 return -1;
420 }
421