foxBMS  1.1.2
The foxBMS Battery Management System API Documentation
diag.c
Go to the documentation of this file.
1 /**
2  *
3  * @copyright © 2010 - 2021, Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V.
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution.
17  *
18  * 3. Neither the name of the copyright holder nor the names of its
19  * contributors may be used to endorse or promote products derived from
20  * this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * We kindly request you to use one or more of the following phrases to refer to
34  * foxBMS in your hardware, software, documentation or advertising materials:
35  *
36  * - ″This product uses parts of foxBMS®″
37  * - ″This product includes parts of foxBMS®″
38  * - ″This product is derived from foxBMS®″
39  *
40  */
41 
42 /**
43  * @file diag.c
44  * @author foxBMS Team
45  * @date 2019-11-28 (date of creation)
46  * @updated 2021-07-29 (date of last update)
47  * @ingroup ENGINE
48  * @prefix DIAG
49  *
50  * @brief Diagnosis driver implementation
51  *
52  * This diagnose module is responsible for error handling and reporting.
53  * Reported errors are logged into the global database and can be reviewed
54  * on user request.
55  */
56 
57 /*========== Includes =======================================================*/
58 #include "diag.h"
59 
60 #include "os.h"
61 
62 /*========== Macros and Definitions =========================================*/
63 
64 /*========== Static Constant and Variable Definitions =======================*/
65 /** state-variable of the diag module */
66 static DIAG_s diag;
67 
68 /** pointer to the device configuration of the diag module */
70 
71 /** superb implementation of a mutex for the diag module */
72 static uint8_t diag_locked = 0;
73 
74 /*========== Extern Constant and Variable Definitions =======================*/
75 
76 /*========== Static Function Prototypes =====================================*/
77 static void DIAG_Reset(void);
78 
79 /**
80  * @brief DIAG_EntryWrite adds an error entry.
81  * @details This function adds an entry to the error buffer. It provides some
82  * functionality to prevent duplicates from being logged. Multiple
83  * occurring error doesn't get logged anymore after they reached a
84  * pre-defined error count.
85  * @param eventID ID of entry
86  * @param event OK, NOK or RESET
87  * @param data individual information for #DIAG_ID_e e.g. string number,..
88  * @return 0xFF if event is logged, otherwise 0
89  */
90 static uint8_t DIAG_EntryWrite(uint8_t eventID, DIAG_EVENT_e event, uint32_t data);
91 
92 /*========== Static Function Implementations ================================*/
93 /**
94  * @brief DIAG_Reset resetsall needed structures
95  * @details This function gets called during initialization of the diagnose
96  * module. It clears memory and counters used by diag later on.
97  */
98 
99 static void DIAG_Reset(void) {
100  diag_locked = 1;
101 
102  /* Reset counter */
103  for (uint32_t i = 0u; i < sizeof(diag.entry_cnt); i++) {
104  diag.entry_cnt[i] = 0;
105  }
106  diag.errcnttotal = 0;
107  diag_locked = 0;
108 }
109 
110 /*========== Extern Function Implementations ================================*/
112  FAS_ASSERT(diag_dev_pointer != NULL_PTR);
113 
114  STD_RETURN_TYPE_e retval = STD_OK;
115  uint8_t id_nr = (uint8_t)DIAG_ID_MAX;
116  /* take assumptions on the value of DIAG_ID_MAX */
117  FAS_ASSERT((uint16_t)DIAG_ID_MAX < UINT8_MAX);
118  uint32_t tmperr_Check[((uint16_t)DIAG_ID_MAX + 31u) / 32u] = {0};
119 
120  diag_devptr = diag_dev_pointer;
121 
123  uint16_t checkfail = 0u;
124 
125  /* TODO this will always evaluate to true?! */
126  if (checkfail > 0u) {
127  DIAG_Reset();
128  }
129 
130  /* Fill lookup table id2ch */
131  for (uint8_t c = 0; c < diag_dev_pointer->nr_of_ch; c++) {
132  id_nr = diag_dev_pointer->ch_cfg[c].id;
133  if (id_nr < (uint16_t)DIAG_ID_MAX) {
134  diag.id2ch[id_nr] = c; /* e.g. diag.id2ch[DIAG_ID_90] = configured channel index */
135  } else {
136  /* Configuration error -> set retval to #STD_NOT_OK */
137  checkfail |= 0x20u;
138  retval = STD_NOT_OK;
139  }
140  }
141 
142  for (uint8_t i = 0; i < (uint8_t)(((uint16_t)DIAG_ID_MAX + 31u) / 32u); i++) {
143  tmperr_Check[i] = 0u;
144  }
145 
146  /* Fill enable array err_enableflag */
147  for (uint8_t i = 0; i < diag_dev_pointer->nr_of_ch; i++) {
148  if (diag_dev_pointer->ch_cfg[i].enable_evaluate == DIAG_EVALUATION_DISABLED) {
149  /* Disable diagnosis entry */
150  tmperr_Check[diag_dev_pointer->ch_cfg[i].id / 32] |= (1 << (diag_dev_pointer->ch_cfg[i].id % 32));
151  }
152  }
153 
154  /* take over configured error enable masks*/
155  for (uint8_t c = 0; c < (uint8_t)(((uint16_t)DIAG_ID_MAX + 31u) / 32u); c++) {
156  diag.err_enableflag[c] = ~tmperr_Check[c];
157  }
158 
159  /* Reset counter in case init function is called multiple times */
160  diag_dev_pointer->numberOfFatalErrors = 0u;
161  /* Fill pointer array with links to all diagnosis entries with a fatal error */
162  for (uint16_t entry = 0u; entry < (uint16_t)DIAG_ID_MAX; entry++) {
163  if (DIAG_ID_cfg[entry].severity == DIAG_FATAL_ERROR) {
164  diag_dev_pointer->pFatalErrorLinkTable[diag_dev_pointer->numberOfFatalErrors] = &DIAG_ID_cfg[entry];
165  diag_dev_pointer->numberOfFatalErrors++;
166  }
167  }
168 
170  return retval;
171 }
172 
174  STD_RETURN_TYPE_e retval = STD_OK;
175  const uint16_t errorThreshold = diag_devptr->ch_cfg[diag.id2ch[(uint16_t)diagnosisEntry]].threshold;
176 
177  /* Error if active if threshold counter is larger than configured error threshold */
178  for (uint8_t s = 0u; s < BS_NR_OF_STRINGS; s++) {
179  uint16_t thresholdCounter = diag.occurrenceCounter[s][(uint16_t)diagnosisEntry];
180  if (thresholdCounter > errorThreshold) {
181  /* error-threshold exceeded -> error is active */
182  retval = STD_NOT_OK;
183  }
184  }
185  return retval;
186 }
187 
188 void DIAG_PrintErrors(void) {
189 }
190 
191 static uint8_t DIAG_EntryWrite(uint8_t eventID, DIAG_EVENT_e event, uint32_t data) {
192  uint8_t ret_val = 0;
193  if (diag_locked > 0u) {
194  return ret_val; /* only locked when clearing the diagnosis memory */
195  }
196 
197  if (diag.entry_event[eventID] == event) {
198  /* same event of same error type already recorded before -> ignore until event toggles */
199  return ret_val;
200  }
201  if ((diag.entry_event[eventID] == DIAG_EVENT_OK) && (event == DIAG_EVENT_RESET)) {
202  /* do record DIAG_EVENT_RESET-event only if last event was an error (re-initialization) */
203  /* meaning: DIAG_EVENT_RESET-event at first time call or after DIAG_EVENT_OK-event will not be recorded */
204  return ret_val;
205  }
206 
207  if (++diag.entry_cnt[eventID] > DIAG_MAX_ENTRIES_OF_ERROR) {
208  /* this type of error has been recorded too many times -> ignore to avoid filling buffer with same failurecodes */
210  return ret_val;
211  }
212 
213  /* now record failurecode */
214  ret_val = 0xFF;
215 
216  /* counts of (new) diagnosis entry records which is still not been read by
217  * external Tool which will reset this value to 0 after having read all
218  * new entries which means <acknowledged by user> */
220  ++diag.errcnttotal; /* total counts of diagnosis entry records */
221  diag.entry_event[eventID] = event;
222 
223  return ret_val;
224 }
225 
228  uint32_t *u32ptr_errCodemsk = NULL;
229  uint32_t *u32ptr_warnCodemsk = NULL;
230  uint16_t *u16ptr_threshcounter = NULL;
231  uint16_t cfg_threshold = 0;
232  uint16_t err_enable_idx = 0;
233  uint32_t err_enable_bitmask = 0;
234 
235  DIAG_RECORDING_e recording_enabled;
236  DIAG_EVALUATE_e evaluate_enabled;
237 
240  }
241 
242  if (diag_id >= DIAG_ID_MAX) {
244  }
245 
246  if (!((impact == DIAG_SYSTEM) || (DIAG_STRING))) {
248  }
249 
250  if ((impact == DIAG_STRING) && (data >= BS_NR_OF_STRINGS)) {
252  }
253 
254  /* Determine a stringID, for impact level #DIAG_SYSTEM this is
255  always 0. This stringID is used to access the #DIAG_s::occurrenceCounter
256  2D-array.
257  */
258  uint8_t stringID = 0u;
259  if (impact == DIAG_STRING) {
260  stringID = data;
261  }
262 
263  err_enable_idx = diag_id / 32; /* array index of diag.err_enableflag[..] */
264  err_enable_bitmask = 1 << (diag_id % 32); /* bit number (mask) of diag.err_enableflag[idx] */
265 
266  u32ptr_errCodemsk = &diag.errflag[err_enable_idx];
267  u32ptr_warnCodemsk = &diag.warnflag[err_enable_idx];
268  u16ptr_threshcounter = &diag.occurrenceCounter[stringID][diag_id];
269  cfg_threshold = diag_devptr->ch_cfg[diag.id2ch[diag_id]].threshold;
270  recording_enabled = diag_devptr->ch_cfg[diag.id2ch[diag_id]].enable_recording;
271  evaluate_enabled = diag_devptr->ch_cfg[diag.id2ch[diag_id]].enable_evaluate;
272 
273  if (event == DIAG_EVENT_OK) {
274  if ((diag.err_enableflag[err_enable_idx] & err_enable_bitmask) > 0u) {
275  /* if (((*u16ptr_threshcounter) == 0) && (*u32ptr_errCodemsk == 0)) */
276  if (((*u16ptr_threshcounter) == 0)) {
277  /* everything ok, nothing to be handled */
278  } else if ((*u16ptr_threshcounter) > 1) {
279  (*u16ptr_threshcounter)--; /* Error did not occur, decrement Error-Counter */
280  } else if ((*u16ptr_threshcounter) == 1) {
281  /* else if ((*u16ptr_threshcounter) <= 1) */
282  /* Error did not occur, now decrement to zero and clear Error- or Warning-Flag and make recording if enabled */
283  *u32ptr_errCodemsk &= ~err_enable_bitmask; /* ERROR: clear corresponding bit in errflag[idx] */
284  *u32ptr_warnCodemsk &= ~err_enable_bitmask; /* WARNING: clear corresponding bit in warnflag[idx] */
285  (*u16ptr_threshcounter) = 0;
286  /* Make entry in error-memory (error disappeared) */
287  if (recording_enabled == DIAG_RECORDING_ENABLED) {
288  DIAG_EntryWrite(diag_id, event, data);
289  }
290 
291  if (evaluate_enabled == DIAG_EVALUATION_ENABLED) {
292  /* Call callback function and reset error */
294  }
295  }
296  }
297  ret_val = DIAG_HANDLER_RETURN_OK; /* Function does not return an error-message! */
298  } else if (event == DIAG_EVENT_NOT_OK) {
299  if ((diag.err_enableflag[err_enable_idx] & err_enable_bitmask) > 0u) {
300  if ((*u16ptr_threshcounter) < cfg_threshold) {
301  (*u16ptr_threshcounter)++; /* error-threshold not exceeded yet, increment Error-Counter */
302  ret_val = DIAG_HANDLER_RETURN_OK; /* Function does not return an error-message! */
303  } else if ((*u16ptr_threshcounter) == cfg_threshold) {
304  /* Error occured AND error-threshold exceeded */
305  (*u16ptr_threshcounter)++;
306  *u32ptr_errCodemsk |= err_enable_bitmask; /* ERROR: set corresponding bit in errflag[idx] */
307  *u32ptr_warnCodemsk &= ~err_enable_bitmask; /* WARNING: clear corresponding bit in warnflag[idx] */
308 
309  /* Make entry in error-memory (error occurred) */
310  if (recording_enabled == DIAG_RECORDING_ENABLED) {
311  DIAG_EntryWrite(diag_id, event, data);
312  }
313 
314  if (evaluate_enabled == DIAG_EVALUATION_ENABLED) {
315  /* Call callback function and set error */
317  }
318  /* Function returns an error-message! */
320  } else if (((*u16ptr_threshcounter) > cfg_threshold)) {
321  /* error-threshold already exceeded, nothing to be handled */
323  }
324  } else {
325  /* Error occured BUT NOT enabled by mask */
326  *u32ptr_errCodemsk &= ~err_enable_bitmask; /* ERROR: clear corresponding bit in errflag[idx] */
327  *u32ptr_warnCodemsk |= err_enable_bitmask; /* WARNING: set corresponding bit in warnflag[idx] */
328  ret_val = DIAG_HANDLER_RETURN_WARNING_OCCURRED; /* Function returns an error-message! */
329  }
330  } else if (event == DIAG_EVENT_RESET) {
331  if ((diag.err_enableflag[err_enable_idx] & err_enable_bitmask) > 0u) {
332  /* clear counter, Error-, Warning-Flag and make recording if enabled */
333  *u32ptr_errCodemsk &= ~err_enable_bitmask; /* ERROR: clear corresponding bit in errflag[idx] */
334  *u32ptr_warnCodemsk &= ~err_enable_bitmask; /* WARNING: clear corresponding bit in warnflag[idx] */
335  (*u16ptr_threshcounter) = 0;
336  if (recording_enabled == DIAG_RECORDING_ENABLED) {
338  diag_id,
339  event,
340  data); /* Make entry in error-memory (error disappeared) if error was recorded before */
341  }
342  if (evaluate_enabled == DIAG_EVALUATION_ENABLED) {
343  /* Call callback function and reset error */
345  }
346  }
347  ret_val = DIAG_HANDLER_RETURN_OK; /* Function does not return an error-message! */
348  }
349 
350  return ret_val;
351 }
352 
354  STD_RETURN_TYPE_e cond,
355  DIAG_ID_e diag_id,
356  DIAG_IMPACT_LEVEL_e impact,
357  uint32_t data) {
358  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
359 
360  if (cond == STD_OK) {
361  DIAG_Handler(diag_id, DIAG_EVENT_OK, impact, data);
362  } else {
363  DIAG_Handler(diag_id, DIAG_EVENT_NOT_OK, impact, data);
364  }
365 
366  return retVal;
367 }
368 
369 uint32_t DIAG_GetDelay(DIAG_ID_e diagnosisEntry) {
370  FAS_ASSERT(diagnosisEntry < DIAG_ID_MAX);
371  return DIAG_ID_cfg[diag.id2ch[(uint16_t)diagnosisEntry]].delay_ms;
372 }
373 
375  bool fatalErrorActive = false;
376  for (uint16_t entry = 0u; entry < diag_device.numberOfFatalErrors; entry++) {
377  const STD_RETURN_TYPE_e diagnosisState =
379  if (STD_NOT_OK == diagnosisState) {
380  fatalErrorActive = true;
381  }
382  }
383  return fatalErrorActive;
384 }
385 
386 /*========== Externalized Static Function Implementations (Unit Test) =======*/
#define BS_NR_OF_STRINGS
static DIAG_DEV_s * diag_devptr
Definition: diag.c:69
static uint8_t DIAG_EntryWrite(uint8_t eventID, DIAG_EVENT_e event, uint32_t data)
DIAG_EntryWrite adds an error entry.
Definition: diag.c:191
uint32_t DIAG_GetDelay(DIAG_ID_e diagnosisEntry)
Get configured delay of passed diagnosis entry.
Definition: diag.c:369
void DIAG_PrintErrors(void)
Prints contents of the error buffer on user request.
Definition: diag.c:188
STD_RETURN_TYPE_e DIAG_GetDiagnosisEntryState(DIAG_ID_e diagnosisEntry)
Checks if passed diagnosis entry has been triggered or not.
Definition: diag.c:173
bool DIAG_IsAnyFatalErrorSet(void)
Check if any fatal error is set.
Definition: diag.c:374
static uint8_t diag_locked
Definition: diag.c:72
DIAG_RETURNTYPE_e DIAG_Handler(DIAG_ID_e diag_id, DIAG_EVENT_e event, DIAG_IMPACT_LEVEL_e impact, uint32_t data)
DIAG_Handler provides generic error handling, based on diagnosis group.
Definition: diag.c:226
STD_RETURN_TYPE_e DIAG_Initialize(DIAG_DEV_s *diag_dev_pointer)
DIAG_Init initializes all needed structures/buffers.
Definition: diag.c:111
STD_RETURN_TYPE_e DIAG_CheckEvent(STD_RETURN_TYPE_e cond, DIAG_ID_e diag_id, DIAG_IMPACT_LEVEL_e impact, uint32_t data)
DIAG_CheckEvent provides a simple interface to check an event for STD_OK.
Definition: diag.c:353
static DIAG_s diag
Definition: diag.c:66
static void DIAG_Reset(void)
DIAG_Reset resetsall needed structures.
Definition: diag.c:99
Diagnosis driver header.
@ DIAG_HANDLER_RETURN_NOT_READY
Definition: diag.h:72
@ DIAG_HANDLER_INVALID_DATA
Definition: diag.h:70
@ DIAG_HANDLER_RETURN_WRONG_ID
Definition: diag.h:67
@ DIAG_HANDLER_RETURN_WARNING_OCCURRED
Definition: diag.h:66
@ DIAG_HANDLER_RETURN_ERR_OCCURRED
Definition: diag.h:65
@ DIAG_HANDLER_INVALID_ERR_IMPACT
Definition: diag.h:71
@ DIAG_HANDLER_RETURN_OK
Definition: diag.h:64
@ DIAG_HANDLER_RETURN_UNKNOWN
Definition: diag.h:68
enum DIAG_RETURNTYPE DIAG_RETURNTYPE_e
@ DIAG_STATE_UNINITIALIZED
Definition: diag.h:79
@ DIAG_STATE_INITIALIZED
Definition: diag.h:80
const DIAG_DATABASE_SHIM_s diag_kDatabaseShim
Definition: diag_cfg.c:103
DIAG_ID_CFG_s DIAG_ID_cfg[]
Definition: diag_cfg.c:114
DIAG_DEV_s diag_device
Definition: diag_cfg.c:212
@ DIAG_SYSTEM
Definition: diag_cfg.h:247
@ DIAG_STRING
Definition: diag_cfg.h:248
@ DIAG_FATAL_ERROR
Definition: diag_cfg.h:275
enum DIAG_EVENT DIAG_EVENT_e
@ DIAG_RECORDING_ENABLED
Definition: diag_cfg.h:282
@ DIAG_EVENT_RESET
Definition: diag_cfg.h:236
@ DIAG_EVENT_NOT_OK
Definition: diag_cfg.h:235
@ DIAG_EVENT_OK
Definition: diag_cfg.h:234
enum DIAG_IMPACT_LEVEL DIAG_IMPACT_LEVEL_e
#define DIAG_MAX_ENTRIES_OF_ERROR
Definition: diag_cfg.h:142
enum DIAG_ID DIAG_ID_e
@ DIAG_ID_MAX
Definition: diag_cfg.h:229
@ DIAG_EVALUATION_ENABLED
Definition: diag_cfg.h:241
@ DIAG_EVALUATION_DISABLED
Definition: diag_cfg.h:242
enum DIAG_RECORDING DIAG_RECORDING_e
enum DIAG_EVALUATE DIAG_EVALUATE_e
#define FAS_ASSERT(x)
Assertion macro that asserts that x is true.
Definition: fassert.h:237
@ STD_NOT_OK
Definition: fstd_types.h:73
@ STD_OK
Definition: fstd_types.h:72
#define NULL_PTR
Null pointer.
Definition: fstd_types.h:66
enum STD_RETURN_TYPE STD_RETURN_TYPE_e
Implementation of the tasks used by the system, headers.
DIAG_RECORDING_e enable_recording
Definition: diag_cfg.h:310
DIAG_CALLBACK_FUNCTION_f * fpCallback
Definition: diag_cfg.h:312
DIAG_ID_e id
Definition: diag_cfg.h:301
DIAG_EVALUATE_e enable_evaluate
Definition: diag_cfg.h:311
uint16_t threshold
Definition: diag_cfg.h:302
DIAG_ID_CFG_s * pFatalErrorLinkTable[DIAG_ID_MAX]
Definition: diag_cfg.h:324
uint8_t nr_of_ch
Definition: diag_cfg.h:320
DIAG_ID_CFG_s * ch_cfg
Definition: diag_cfg.h:321
uint16_t numberOfFatalErrors
Definition: diag_cfg.h:322
Definition: diag.h:84
uint8_t id2ch[DIAG_ID_MAX]
Definition: diag.h:91
uint32_t err_enableflag[(DIAG_ID_MAX+31)/32]
Definition: diag.h:95
DIAG_STATE_e state
Definition: diag.h:85
uint16_t errcntreported
Definition: diag.h:87
uint8_t entry_cnt[DIAG_ID_MAX]
Definition: diag.h:89
uint16_t errcnttotal
Definition: diag.h:86
uint16_t occurrenceCounter[BS_NR_OF_STRINGS][DIAG_ID_MAX]
Definition: diag.h:90
uint32_t errflag[(DIAG_ID_MAX+31)/32]
Definition: diag.h:93
uint32_t warnflag[(DIAG_ID_MAX+31)/32]
Definition: diag.h:94
uint32_t entry_event[DIAG_ID_MAX]
Definition: diag.h:88