foxBMS  1.4.0
The foxBMS Battery Management System API Documentation
diag.c
Go to the documentation of this file.
1 /**
2  *
3  * @copyright © 2010 - 2022, 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 2022-07-28 (date of last update)
47  * @version v1.4.0
48  * @ingroup ENGINE
49  * @prefix DIAG
50  *
51  * @brief Diagnosis driver implementation
52  *
53  * @details This diagnose module is responsible for error handling and
54  * reporting.
55  * Reported errors are logged into the global database and can be
56  * reviewed on user request.
57  */
58 
59 /*========== Includes =======================================================*/
60 #include "diag.h"
61 
62 #include "os.h"
63 
64 /*========== Macros and Definitions =========================================*/
65 
66 /*========== Static Constant and Variable Definitions =======================*/
67 /** state-variable of the diag module */
69 
70 /** pointer to the device configuration of the diag module */
72 
73 /** superb implementation of a mutex for the diag module */
74 static uint8_t diag_locked = 0;
75 
76 /*========== Extern Constant and Variable Definitions =======================*/
77 
78 /*========== Static Function Prototypes =====================================*/
79 static void DIAG_Reset(void);
80 
81 /**
82  * @brief DIAG_EntryWrite adds an error entry.
83  * @details This function adds an entry to the error buffer. It provides some
84  * functionality to prevent duplicates from being logged. Multiple
85  * occurring error doesn't get logged anymore after they reached a
86  * pre-defined error count.
87  * @param eventID ID of entry
88  * @param event OK, NOK or RESET
89  * @param data individual information for #DIAG_ID_e e.g. string number,..
90  * @return 0xFF if event is logged, otherwise 0
91  */
92 static uint8_t DIAG_EntryWrite(uint8_t eventID, DIAG_EVENT_e event, uint32_t data);
93 
94 /*========== Static Function Implementations ================================*/
95 /**
96  * @brief DIAG_Reset resetsall needed structures
97  * @details This function gets called during initialization of the diagnose
98  * module. It clears memory and counters used by diag later on.
99  */
100 
101 static void DIAG_Reset(void) {
102  diag_locked = 1;
103 
104  /* Reset counter */
105  for (uint32_t i = 0u; i < sizeof(diag.entry_cnt); i++) {
106  diag.entry_cnt[i] = 0;
107  }
108  diag.errcnttotal = 0;
109  diag_locked = 0;
110 }
111 
112 /*========== Extern Function Implementations ================================*/
114  FAS_ASSERT(diag_dev_pointer != NULL_PTR);
115 
116  STD_RETURN_TYPE_e retval = STD_OK;
117  uint8_t id_nr = (uint8_t)DIAG_ID_MAX;
118  /* take assumptions on the value of DIAG_ID_MAX */
119  FAS_ASSERT((uint16_t)DIAG_ID_MAX < UINT8_MAX);
120  uint32_t tmperr_Check[((uint16_t)DIAG_ID_MAX + 31u) / 32u] = {0};
121 
122  diag_devptr = diag_dev_pointer;
123 
125  uint16_t checkfail = 0u;
126 
127  /* TODO this will always evaluate to true?! */
128  if (checkfail > 0u) {
129  DIAG_Reset();
130  }
131 
132  /* Fill lookup table id2ch */
133  for (uint8_t c = 0; c < diag_dev_pointer->nrOfConfiguredDiagnosisEntries; c++) {
134  id_nr = diag_dev_pointer->pConfigurationOfDiagnosisEntries[c].id;
135  if (id_nr < (uint16_t)DIAG_ID_MAX) {
136  diag.id2ch[id_nr] = c; /* e.g. diag.id2ch[DIAG_ID_90] = configured channel index */
137  } else {
138  /* Configuration error -> set retval to #STD_NOT_OK */
139  checkfail |= 0x20u;
140  retval = STD_NOT_OK;
141  }
142  }
143 
144  for (uint8_t i = 0; i < (uint8_t)(((uint16_t)DIAG_ID_MAX + 31u) / 32u); i++) {
145  tmperr_Check[i] = 0u;
146  }
147 
148  /* Fill enable array err_enableflag */
149  for (uint8_t i = 0; i < diag_dev_pointer->nrOfConfiguredDiagnosisEntries; i++) {
151  /* Disable diagnosis entry */
152  tmperr_Check[diag_dev_pointer->pConfigurationOfDiagnosisEntries[i].id / 32] |=
153  (1 << (diag_dev_pointer->pConfigurationOfDiagnosisEntries[i].id % 32));
154  }
155  }
156 
157  /* take over configured error enable masks*/
158  for (uint8_t c = 0; c < (uint8_t)(((uint16_t)DIAG_ID_MAX + 31u) / 32u); c++) {
159  diag.err_enableflag[c] = ~tmperr_Check[c];
160  }
161 
162  /* Reset counter in case init function is called multiple times */
163  diag_dev_pointer->numberOfFatalErrors = 0u;
164  /* Fill pointer array with links to all diagnosis entries with a fatal error */
165  for (uint16_t entry = 0u; entry < (uint16_t)DIAG_ID_MAX; entry++) {
166  if (diag_diagnosisIdConfiguration[entry].severity == DIAG_FATAL_ERROR) {
167  diag_dev_pointer->pFatalErrorLinkTable[diag_dev_pointer->numberOfFatalErrors] =
169  diag_dev_pointer->numberOfFatalErrors++;
170  }
171  }
172 
174  return retval;
175 }
176 
178  STD_RETURN_TYPE_e retval = STD_OK;
179  const uint16_t errorThreshold =
180  diag_devptr->pConfigurationOfDiagnosisEntries[diag.id2ch[(uint16_t)diagnosisEntry]].threshold;
181 
182  /* Error if active if threshold counter is larger than configured error threshold */
183  for (uint8_t s = 0u; s < BS_NR_OF_STRINGS; s++) {
184  uint16_t thresholdCounter = diag.occurrenceCounter[s][(uint16_t)diagnosisEntry];
185  if (thresholdCounter > errorThreshold) {
186  /* error-threshold exceeded -> error is active */
187  retval = STD_NOT_OK;
188  }
189  }
190  return retval;
191 }
192 
193 void DIAG_PrintErrors(void) {
194 }
195 
196 static uint8_t DIAG_EntryWrite(uint8_t eventID, DIAG_EVENT_e event, uint32_t data) {
197  uint8_t ret_val = 0;
198  if (diag_locked > 0u) {
199  return ret_val; /* only locked when clearing the diagnosis memory */
200  }
201 
202  if (diag.entry_event[eventID] == event) {
203  /* same event of same error type already recorded before -> ignore until event toggles */
204  return ret_val;
205  }
206  if ((diag.entry_event[eventID] == DIAG_EVENT_OK) && (event == DIAG_EVENT_RESET)) {
207  /* do record DIAG_EVENT_RESET-event only if last event was an error (re-initialization) */
208  /* meaning: DIAG_EVENT_RESET-event at first time call or after DIAG_EVENT_OK-event will not be recorded */
209  return ret_val;
210  }
211 
212  if (++diag.entry_cnt[eventID] > DIAG_MAX_ENTRIES_OF_ERROR) {
213  /* this type of error has been recorded too many times -> ignore to avoid filling buffer with same failure codes */
215  return ret_val;
216  }
217 
218  /* now record failure code */
219  ret_val = 0xFF;
220 
221  /* counts of (new) diagnosis entry records which is still not been read by
222  * external Tool which will reset this value to 0 after having read all
223  * new entries which means <acknowledged by user> */
225  ++diag.errcnttotal; /* total counts of diagnosis entry records */
226  diag.entry_event[eventID] = event;
227 
228  return ret_val;
229 }
230 
233  uint32_t *u32ptr_errCodemsk = NULL_PTR;
234  uint32_t *u32ptr_warnCodemsk = NULL_PTR;
235  uint16_t *u16ptr_threshcounter = NULL_PTR;
236  uint16_t cfg_threshold = 0;
237  uint16_t err_enable_idx = 0;
238  uint32_t err_enable_bitmask = 0;
239 
240  DIAG_RECORDING_e recording_enabled;
241  DIAG_EVALUATE_e evaluate_enabled;
242 
245  }
246 
247  if (diagId >= DIAG_ID_MAX) {
249  }
250 
251  if (!((impact == DIAG_SYSTEM) || (DIAG_STRING))) {
253  }
254 
255  if ((impact == DIAG_STRING) && (data >= BS_NR_OF_STRINGS)) {
257  }
258 
259  /* Determine a stringID, for impact level #DIAG_SYSTEM this is
260  always 0. This stringID is used to access the #DIAG_DIAGNOSIS_STATE_s::occurrenceCounter
261  2D-array.
262  */
263  uint8_t stringID = 0u;
264  if (impact == DIAG_STRING) {
265  stringID = data;
266  }
267 
268  err_enable_idx = diagId / 32; /* array index of diag.err_enableflag[..] */
269  err_enable_bitmask = 1 << (diagId % 32); /* bit number (mask) of diag.err_enableflag[idx] */
270 
271  u32ptr_errCodemsk = &diag.errflag[err_enable_idx];
272  u32ptr_warnCodemsk = &diag.warnflag[err_enable_idx];
273  u16ptr_threshcounter = &diag.occurrenceCounter[stringID][diagId];
277 
278  if (event == DIAG_EVENT_OK) {
279  if ((diag.err_enableflag[err_enable_idx] & err_enable_bitmask) > 0u) {
280  /* if (((*u16ptr_threshcounter) == 0) && (*u32ptr_errCodemsk == 0)) */
281  if (((*u16ptr_threshcounter) == 0)) {
282  /* everything ok, nothing to be handled */
283  } else if ((*u16ptr_threshcounter) > 1) {
284  (*u16ptr_threshcounter)--; /* Error did not occur, decrement Error-Counter */
285  } else if ((*u16ptr_threshcounter) == 1) {
286  /* else if ((*u16ptr_threshcounter) <= 1) */
287  /* Error did not occur, now decrement to zero and clear Error- or Warning-Flag and make recording if enabled */
288  *u32ptr_errCodemsk &= ~err_enable_bitmask; /* ERROR: clear corresponding bit in errflag[idx] */
289  *u32ptr_warnCodemsk &= ~err_enable_bitmask; /* WARNING: clear corresponding bit in warnflag[idx] */
290  (*u16ptr_threshcounter) = 0;
291  /* Make entry in error-memory (error disappeared) */
292  if (recording_enabled == DIAG_RECORDING_ENABLED) {
293  DIAG_EntryWrite(diagId, event, data);
294  }
295 
296  if (evaluate_enabled == DIAG_EVALUATION_ENABLED) {
297  /* Call callback function and reset error */
299  diagId, DIAG_EVENT_RESET, &diag_kDatabaseShim, data);
300  }
301  }
302  }
303  ret_val = DIAG_HANDLER_RETURN_OK; /* Function does not return an error-message! */
304  } else if (event == DIAG_EVENT_NOT_OK) {
305  if ((diag.err_enableflag[err_enable_idx] & err_enable_bitmask) > 0u) {
306  if ((*u16ptr_threshcounter) < cfg_threshold) {
307  (*u16ptr_threshcounter)++; /* error-threshold not exceeded yet, increment Error-Counter */
308  ret_val = DIAG_HANDLER_RETURN_OK; /* Function does not return an error-message! */
309  } else if ((*u16ptr_threshcounter) == cfg_threshold) {
310  /* Error occured AND error-threshold exceeded */
311  (*u16ptr_threshcounter)++;
312  *u32ptr_errCodemsk |= err_enable_bitmask; /* ERROR: set corresponding bit in errflag[idx] */
313  *u32ptr_warnCodemsk &= ~err_enable_bitmask; /* WARNING: clear corresponding bit in warnflag[idx] */
314 
315  /* Make entry in error-memory (error occurred) */
316  if (recording_enabled == DIAG_RECORDING_ENABLED) {
317  DIAG_EntryWrite(diagId, event, data);
318  }
319 
320  if (evaluate_enabled == DIAG_EVALUATION_ENABLED) {
321  /* Call callback function and set error */
323  diagId, DIAG_EVENT_NOT_OK, &diag_kDatabaseShim, data);
324  }
325  /* Function returns an error-message! */
327  } else if (((*u16ptr_threshcounter) > cfg_threshold)) {
328  /* error-threshold already exceeded, nothing to be handled */
330  }
331  } else {
332  /* Error occurred BUT NOT enabled by mask */
333  *u32ptr_errCodemsk &= ~err_enable_bitmask; /* ERROR: clear corresponding bit in errflag[idx] */
334  *u32ptr_warnCodemsk |= err_enable_bitmask; /* WARNING: set corresponding bit in warnflag[idx] */
335  ret_val = DIAG_HANDLER_RETURN_WARNING_OCCURRED; /* Function returns an error-message! */
336  }
337  } else if (event == DIAG_EVENT_RESET) {
338  if ((diag.err_enableflag[err_enable_idx] & err_enable_bitmask) > 0u) {
339  /* clear counter, Error-, Warning-Flag and make recording if enabled */
340  *u32ptr_errCodemsk &= ~err_enable_bitmask; /* ERROR: clear corresponding bit in errflag[idx] */
341  *u32ptr_warnCodemsk &= ~err_enable_bitmask; /* WARNING: clear corresponding bit in warnflag[idx] */
342  (*u16ptr_threshcounter) = 0;
343  if (recording_enabled == DIAG_RECORDING_ENABLED) {
344  /* Make entry in error-memory (error disappeared) if error was recorded before */
345  DIAG_EntryWrite(diagId, event, data);
346  }
347  if (evaluate_enabled == DIAG_EVALUATION_ENABLED) {
348  /* Call callback function and reset error */
350  diagId, DIAG_EVENT_RESET, &diag_kDatabaseShim, data);
351  }
352  }
353  ret_val = DIAG_HANDLER_RETURN_OK; /* Function does not return an error-message! */
354  }
355 
356  return ret_val;
357 }
358 
360  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
361 
362  if (cond == STD_OK) {
363  DIAG_Handler(diagId, DIAG_EVENT_OK, impact, data);
364  } else {
365  DIAG_Handler(diagId, DIAG_EVENT_NOT_OK, impact, data);
366  }
367 
368  return retVal;
369 }
370 
371 uint32_t DIAG_GetDelay(DIAG_ID_e diagnosisEntry) {
372  FAS_ASSERT(diagnosisEntry < DIAG_ID_MAX);
373  return diag_diagnosisIdConfiguration[diag.id2ch[(uint16_t)diagnosisEntry]].delay_ms;
374 }
375 
377  bool fatalErrorActive = false;
378  for (uint16_t entry = 0u; entry < diag_device.numberOfFatalErrors; entry++) {
379  const STD_RETURN_TYPE_e diagnosisState =
381  if (STD_NOT_OK == diagnosisState) {
382  fatalErrorActive = true;
383  }
384  }
385  return fatalErrorActive;
386 }
387 
388 /*========== Externalized Static Function Implementations (Unit Test) =======*/
#define BS_NR_OF_STRINGS
Number of parallel strings in the battery pack.
STD_RETURN_TYPE_e DIAG_CheckEvent(STD_RETURN_TYPE_e cond, DIAG_ID_e diagId, DIAG_IMPACT_LEVEL_e impact, uint32_t data)
DIAG_CheckEvent provides a simple interface to check an event for STD_OK.
Definition: diag.c:359
static DIAG_DEV_s * diag_devptr
Definition: diag.c:71
static uint8_t DIAG_EntryWrite(uint8_t eventID, DIAG_EVENT_e event, uint32_t data)
DIAG_EntryWrite adds an error entry.
Definition: diag.c:196
uint32_t DIAG_GetDelay(DIAG_ID_e diagnosisEntry)
Get configured delay of passed diagnosis entry.
Definition: diag.c:371
void DIAG_PrintErrors(void)
Prints contents of the error buffer on user request.
Definition: diag.c:193
STD_RETURN_TYPE_e DIAG_GetDiagnosisEntryState(DIAG_ID_e diagnosisEntry)
Checks if passed diagnosis entry has been triggered or not.
Definition: diag.c:177
bool DIAG_IsAnyFatalErrorSet(void)
Check if any fatal error is set.
Definition: diag.c:376
static DIAG_DIAGNOSIS_STATE_s diag
Definition: diag.c:68
DIAG_RETURNTYPE_e DIAG_Handler(DIAG_ID_e diagId, 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:231
static uint8_t diag_locked
Definition: diag.c:74
STD_RETURN_TYPE_e DIAG_Initialize(DIAG_DEV_s *diag_dev_pointer)
DIAG_Init initializes all needed structures/buffers.
Definition: diag.c:113
static void DIAG_Reset(void)
DIAG_Reset resetsall needed structures.
Definition: diag.c:101
Diagnosis driver header.
@ DIAG_STATE_UNINITIALIZED
Definition: diag.h:78
@ DIAG_STATE_INITIALIZED
Definition: diag.h:79
DIAG_RETURNTYPE_e
Definition: diag.h:64
@ DIAG_HANDLER_RETURN_NOT_READY
Definition: diag.h:73
@ DIAG_HANDLER_INVALID_DATA
Definition: diag.h:71
@ DIAG_HANDLER_RETURN_WRONG_ID
Definition: diag.h:68
@ DIAG_HANDLER_RETURN_WARNING_OCCURRED
Definition: diag.h:67
@ DIAG_HANDLER_RETURN_ERR_OCCURRED
Definition: diag.h:66
@ DIAG_HANDLER_INVALID_ERR_IMPACT
Definition: diag.h:72
@ DIAG_HANDLER_RETURN_OK
Definition: diag.h:65
@ DIAG_HANDLER_RETURN_UNKNOWN
Definition: diag.h:69
DIAG_ID_CFG_s diag_diagnosisIdConfiguration[]
Definition: diag_cfg.c:122
const DIAG_DATABASE_SHIM_s diag_kDatabaseShim
Definition: diag_cfg.c:111
DIAG_DEV_s diag_device
Definition: diag_cfg.c:224
DIAG_EVENT_e
Definition: diag_cfg.h:237
@ DIAG_EVENT_RESET
Definition: diag_cfg.h:240
@ DIAG_EVENT_NOT_OK
Definition: diag_cfg.h:239
@ DIAG_EVENT_OK
Definition: diag_cfg.h:238
DIAG_IMPACT_LEVEL_e
Definition: diag_cfg.h:250
@ DIAG_SYSTEM
Definition: diag_cfg.h:251
@ DIAG_STRING
Definition: diag_cfg.h:252
DIAG_EVALUATE_e
Definition: diag_cfg.h:244
@ DIAG_EVALUATION_ENABLED
Definition: diag_cfg.h:245
@ DIAG_EVALUATION_DISABLED
Definition: diag_cfg.h:246
@ DIAG_FATAL_ERROR
Definition: diag_cfg.h:279
DIAG_ID_e
Definition: diag_cfg.h:157
@ DIAG_ID_MAX
Definition: diag_cfg.h:233
#define DIAG_MAX_ENTRIES_OF_ERROR
Definition: diag_cfg.h:143
DIAG_RECORDING_e
Definition: diag_cfg.h:285
@ DIAG_RECORDING_ENABLED
Definition: diag_cfg.h:286
#define FAS_ASSERT(x)
Assertion macro that asserts that x is true.
Definition: fassert.h:252
STD_RETURN_TYPE_e
Definition: fstd_types.h:81
@ STD_NOT_OK
Definition: fstd_types.h:83
@ STD_OK
Definition: fstd_types.h:82
#define NULL_PTR
Null pointer.
Definition: fstd_types.h:76
Declaration of the OS wrapper interface.
uint8_t nrOfConfiguredDiagnosisEntries
Definition: diag_cfg.h:324
DIAG_ID_CFG_s * pConfigurationOfDiagnosisEntries
Definition: diag_cfg.h:325
DIAG_ID_CFG_s * pFatalErrorLinkTable[DIAG_ID_MAX]
Definition: diag_cfg.h:328
uint16_t numberOfFatalErrors
Definition: diag_cfg.h:326
uint16_t errcnttotal
Definition: diag.h:85
uint16_t errcntreported
Definition: diag.h:86
uint32_t entry_event[DIAG_ID_MAX]
Definition: diag.h:87
uint32_t errflag[(DIAG_ID_MAX+31)/32]
Definition: diag.h:92
uint8_t entry_cnt[DIAG_ID_MAX]
Definition: diag.h:88
uint16_t occurrenceCounter[BS_NR_OF_STRINGS][DIAG_ID_MAX]
Definition: diag.h:89
DIAG_MODULE_STATE_e state
Definition: diag.h:84
uint32_t warnflag[(DIAG_ID_MAX+31)/32]
Definition: diag.h:93
uint8_t id2ch[DIAG_ID_MAX]
Definition: diag.h:90
uint32_t err_enableflag[(DIAG_ID_MAX+31)/32]
Definition: diag.h:94
DIAG_RECORDING_e enable_recording
Definition: diag_cfg.h:314
DIAG_CALLBACK_FUNCTION_f * fpCallback
Definition: diag_cfg.h:316
uint16_t threshold
Definition: diag_cfg.h:306
DIAG_EVALUATE_e enable_evaluate
Definition: diag_cfg.h:315
DIAG_ID_e id
Definition: diag_cfg.h:305