1 /**
2  * CANopen Heartbeat consumer protocol.
3  *
4  * @file        CO_HBconsumer.h
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 
27 #ifndef CO_HB_CONS_H
28 #define CO_HB_CONS_H
29 
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 /**
35  * @defgroup CO_HBconsumer Heartbeat consumer
36  * @ingroup CO_CANopen
37  * @{
38  *
39  * CANopen Heartbeat consumer protocol.
40  *
41  * Heartbeat consumer monitors Heartbeat messages from remote nodes. If any
42  * monitored node don't send his Heartbeat in specified time, Heartbeat consumer
43  * sends emergency message. If all monitored nodes are operational, then
44  * variable _allMonitoredOperational_ inside CO_HBconsumer_t is set to true.
45  * Monitoring starts after the reception of the first HeartBeat (not bootup).
46  *
47  * Heartbeat set up is done by writing to the OD registers 0x1016 or by using
48  * the function _CO_HBconsumer_initEntry()_
49  *
50  * @see  @ref CO_NMT_Heartbeat
51  */
52 
53 /**
54  * Heartbeat state of a node
55  */
56 typedef enum {
57   CO_HBconsumer_UNCONFIGURED = 0x00U,     /**< Consumer entry inactive */
58   CO_HBconsumer_UNKNOWN      = 0x01U,     /**< Consumer enabled, but no heartbeat received yet */
59   CO_HBconsumer_ACTIVE       = 0x02U,     /**< Heartbeat received within set time */
60   CO_HBconsumer_TIMEOUT      = 0x03U,     /**< No heatbeat received for set time */
61 } CO_HBconsumer_state_t;
62 
63 
64 /**
65  * One monitored node inside CO_HBconsumer_t.
66  */
67 typedef struct{
68     uint8_t                 nodeId;       /**< Node Id of the monitored node */
69     CO_NMT_internalState_t  NMTstate;     /**< Of the remote node (Heartbeat payload) */
70     CO_HBconsumer_state_t   HBstate;      /**< Current heartbeat state */
71     uint16_t                timeoutTimer; /**< Time since last heartbeat received */
72     uint16_t                time;         /**< Consumer heartbeat time from OD */
73     volatile void          *CANrxNew;     /**< Indication if new Heartbeat message received from the CAN bus */
74     /** Callback for heartbeat state change to active event */
75     void                  (*pFunctSignalHbStarted)(uint8_t nodeId, uint8_t idx, void *object); /**< From CO_HBconsumer_initTimeoutCallback() or NULL */
76     void                   *functSignalObjectHbStarted;/**< Pointer to object */
77     /** Callback for consumer timeout event */
78     void                  (*pFunctSignalTimeout)(uint8_t nodeId, uint8_t idx, void *object); /**< From CO_HBconsumer_initTimeoutCallback() or NULL */
79     void                   *functSignalObjectTimeout;/**< Pointer to object */
80     /** Callback for remote reset event */
81     void                  (*pFunctSignalRemoteReset)(uint8_t nodeId, uint8_t idx, void *object); /**< From CO_HBconsumer_initRemoteResetCallback() or NULL */
82     void                   *functSignalObjectRemoteReset;/**< Pointer to object */
83 }CO_HBconsNode_t;
84 
85 
86 /**
87  * Heartbeat consumer object.
88  *
89  * Object is initilaized by CO_HBconsumer_init(). It contains an array of
90  * CO_HBconsNode_t objects.
91  */
92 typedef struct{
93     CO_EM_t            *em;               /**< From CO_HBconsumer_init() */
94     const uint32_t     *HBconsTime;       /**< From CO_HBconsumer_init() */
95     CO_HBconsNode_t    *monitoredNodes;   /**< From CO_HBconsumer_init() */
96     uint8_t             numberOfMonitoredNodes; /**< From CO_HBconsumer_init() */
97     /** True, if all monitored nodes are NMT operational or no node is
98         monitored. Can be read by the application */
99     uint8_t             allMonitoredOperational;
100     CO_CANmodule_t     *CANdevRx;         /**< From CO_HBconsumer_init() */
101     uint16_t            CANdevRxIdxStart; /**< From CO_HBconsumer_init() */
102 }CO_HBconsumer_t;
103 
104 
105 /**
106  * Initialize Heartbeat consumer object.
107  *
108  * Function must be called in the communication reset section.
109  *
110  * @param HBcons This object will be initialized.
111  * @param em Emergency object.
112  * @param SDO SDO server object.
113  * @param HBconsTime Pointer to _Consumer Heartbeat Time_ array
114  * from Object Dictionary (index 0x1016). Size of array is equal to numberOfMonitoredNodes.
115  * @param monitoredNodes Pointer to the externaly defined array of the same size
116  * as numberOfMonitoredNodes.
117  * @param numberOfMonitoredNodes Total size of the above arrays.
118  * @param CANdevRx CAN device for Heartbeat reception.
119  * @param CANdevRxIdxStart Starting index of receive buffer in the above CAN device.
120  * Number of used indexes is equal to numberOfMonitoredNodes.
121  *
122  * @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
123  */
124 CO_ReturnError_t CO_HBconsumer_init(
125         CO_HBconsumer_t        *HBcons,
126         CO_EM_t                *em,
127         CO_SDO_t               *SDO,
128         const uint32_t          HBconsTime[],
129         CO_HBconsNode_t         monitoredNodes[],
130         uint8_t                 numberOfMonitoredNodes,
131         CO_CANmodule_t         *CANdevRx,
132         uint16_t                CANdevRxIdxStart);
133 
134 /**
135  * Initialize one Heartbeat consumer entry
136  *
137  * Calling this function has the same affect as writing to the corresponding
138  * entries in the Object Dictionary (index 0x1016)
139  * @remark The values in the Object Dictionary must be set manually by the
140  * calling function so that heartbeat consumer behaviour matches the OD value.
141  *
142  * @param HBcons This object.
143  * @param idx index of the node in HBcons object
144  * @param nodeId see OD 0x1016 description
145  * @param consumerTime see OD 0x1016 description
146  * @return
147  */
148 CO_ReturnError_t CO_HBconsumer_initEntry(
149         CO_HBconsumer_t        *HBcons,
150         uint8_t                 idx,
151         uint8_t                 nodeId,
152         uint16_t                consumerTime);
153 
154 /**
155  * Initialize Heartbeat consumer started callback function.
156  *
157  * Function initializes optional callback function, which is called for the
158  * first received heartbeat after activating hb consumer or timeout.
159  * Function may wake up external task, which handles this event.
160  *
161  * @param HBcons This object.
162  * @param idx index of the node in HBcons object
163  * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
164  * @param pFunctSignal Pointer to the callback function. Not called if NULL.
165  */
166 void CO_HBconsumer_initCallbackHeartbeatStarted(
167         CO_HBconsumer_t        *HBcons,
168         uint8_t                 idx,
169         void                   *object,
170         void                  (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object));
171 
172 
173 /**
174  * Initialize Heartbeat consumer timeout callback function.
175  *
176  * Function initializes optional callback function, which is called when the node
177  * state changes from active to timeout. Function may wake up external task,
178  * which handles this event.
179  *
180  * @param HBcons This object.
181  * @param idx index of the node in HBcons object
182  * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
183  * @param pFunctSignal Pointer to the callback function. Not called if NULL.
184  */
185 void CO_HBconsumer_initCallbackTimeout(
186         CO_HBconsumer_t        *HBcons,
187         uint8_t                 idx,
188         void                   *object,
189         void                  (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object));
190 
191 /**
192  * Initialize Heartbeat consumer remote reset detected callback function.
193  *
194  * Function initializes optional callback function, which is called when a bootup
195  * message is received from the remote node. Function may wake up external task,
196  * which handles this event.
197  *
198  * @param HBcons This object.
199  * @param idx index of the node in HBcons object
200  * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
201  * @param pFunctSignal Pointer to the callback function. Not called if NULL.
202  */
203 void CO_HBconsumer_initCallbackRemoteReset(
204         CO_HBconsumer_t        *HBcons,
205         uint8_t                 idx,
206         void                   *object,
207         void                  (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object));
208 
209 /**
210  * Process Heartbeat consumer object.
211  *
212  * Function must be called cyclically.
213  *
214  * @param HBcons This object.
215  * @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL.
216  * @param timeDifference_ms Time difference from previous function call in [milliseconds].
217  */
218 void CO_HBconsumer_process(
219         CO_HBconsumer_t        *HBcons,
220         bool_t                  NMTisPreOrOperational,
221         uint16_t                timeDifference_ms);
222 
223 /**
224  * Get the heartbeat producer object index by node ID
225  *
226  * @param HBcons This object.
227  * @param nodeId producer node ID
228  * @return index. -1 if not found
229  */
230 int8_t CO_HBconsumer_getIdxByNodeId(
231         CO_HBconsumer_t        *HBcons,
232         uint8_t                 nodeId);
233 
234 /**
235  * Get the current state of a heartbeat producer by the index in OD 0x1016
236  *
237  * @param HBcons This object.
238  * @param idx object sub index
239  * @return #CO_HBconsumer_state_t
240  */
241 CO_HBconsumer_state_t CO_HBconsumer_getState(
242         CO_HBconsumer_t        *HBcons,
243         uint8_t                 idx);
244 
245 /**
246  * Get the current NMT state of a heartbeat producer by the index in OD 0x1016
247  *
248  * NMT state is only available when heartbeat is enabled for this index!
249  *
250  * @param HBcons This object.
251  * @param idx object sub index
252  * @param [out] #CO_NMT_internalState_t of this index
253  * @retval 0 NMT state has been received and is valid
254  * @retval -1 not valid
255  */
256 int8_t CO_HBconsumer_getNmtState(
257         CO_HBconsumer_t        *HBcons,
258         uint8_t                 idx,
259         CO_NMT_internalState_t *nmtState);
260 
261 
262 #ifdef __cplusplus
263 }
264 #endif /*__cplusplus*/
265 
266 /** @} */
267 #endif
268