inserita nuova logica per errori B8, modificata documentazione, prearazione readme per presentare modifiche.
This commit is contained in:
parent
0b0f7b9f10
commit
50a5a97820
87
GRIFO_M_PBIT_README.md
Normal file
87
GRIFO_M_PBIT_README.md
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# GRIFO_M_PBIT — Release Notes & Feature Summary
|
||||||
|
|
||||||
|
Questo documento sintetizza le funzionalità implementate nella versione corrente dello
|
||||||
|
script `GRIFO_M_PBIT.py` rispetto a una versione iniziale minimale (senza GUI, senza
|
||||||
|
handling avanzato di "known failures", senza propagation della versione dello script, senza aggregazione dei risultati dei test, senza gestione del test del target).
|
||||||
|
Serve per informare altri sviluppatori sulle modifiche principali e sulle modalità d'uso.
|
||||||
|
|
||||||
|
## Scopo
|
||||||
|
Fornire un elenco chiaro e conciso delle funzionalità aggiunte, dei comportamenti modificati
|
||||||
|
(e dove cercare il codice/documentazione per approfondire).
|
||||||
|
|
||||||
|
## Principali funzionalità implementate
|
||||||
|
- `TEST_VERSION`:
|
||||||
|
- Aggiunta di una costante `TEST_VERSION` nello script principale.
|
||||||
|
- La stringa di versione viene inserita in tutti gli output generati: log, CSV, JSON e PDF.
|
||||||
|
|
||||||
|
- Gestione estesa di `KNOWN_FAILURES` (ora applicata anche a B8):
|
||||||
|
- `KNOWN_FAILURES` può ora essere usata per classificare come "Known" anche le failure
|
||||||
|
individuate nel drill-down B8.
|
||||||
|
- Aggiunto helper `is_known_failure(field_name)` (matching case-insensitive, substring).
|
||||||
|
- Comportamento: le failure classificate come "known" sono registrate ma NON rendono
|
||||||
|
la run fallita; le failure non note su B8 causano la FAIL della run.
|
||||||
|
|
||||||
|
- B8 drill-down migliorato:
|
||||||
|
- Il drill-down B8 classifica ogni field in PASS / REAL FAIL / KNOWN FAIL.
|
||||||
|
- Conta `b8_pass`, `b8_fail`, `b8_known_fail` e mantiene dettagli separati per real e known.
|
||||||
|
- Drill-down eseguito solo se necessario (presenza di real failures) oppure se forzato
|
||||||
|
tramite `FORCE_B8_DRILL_DOWN`.
|
||||||
|
|
||||||
|
- GUI real-time (Tkinter) — `GRIFO_M_PBIT_gui.py`:
|
||||||
|
- Aggiunta di un monitor in tempo reale opzionale (avviabile di default, disabilitabile con `--no-gui`).
|
||||||
|
- Tabella `Runs Overview` aggiornata per mostrare la colonna B8 nel formato `Pass/Fail/Known`.
|
||||||
|
- Comunicazione thread-safe tra test thread e GUI tramite `queue.Queue()`.
|
||||||
|
|
||||||
|
- Reportistica aggiornata (CSV / JSON / PDF):
|
||||||
|
- I file CSV/JSON includono ora `script_version` e i conteggi `b8_known_fail` per run.
|
||||||
|
- Il PDF include la `Script Version` nella sezione metadati e mostra, nella tabella Run Details,
|
||||||
|
la colonna B8 come `Pass/Fail/Known`.
|
||||||
|
|
||||||
|
- Simulation & Mocks migliorati:
|
||||||
|
- `GRIFO_M_PBIT_mock.py` include scenari aggiuntivi (`pedestal_fail`, `mixed_b8_failures`, `agc_tests_fail`, ecc.)
|
||||||
|
- Launcher `run_simulate.bat` e `run_simulate_simple.bat` sono mantenuti per eseguire rapidamente
|
||||||
|
test in modalità simulata.
|
||||||
|
|
||||||
|
## Differenze rispetto alla versione iniziale (baseline)
|
||||||
|
Baseline: script senza GUI, senza gestione avanzata di known-failures, senza propagation della versione, ecc.
|
||||||
|
|
||||||
|
Cosa è stato aggiunto/alterato:
|
||||||
|
- Interfaccia grafica opzionale per monitoraggio realtime.
|
||||||
|
- Meccanismo `KNOWN_FAILURES` esteso a B8 con helper dedicato.
|
||||||
|
- Maggiori dettagli nei report (terzo campo B8: known count; script version metadata).
|
||||||
|
- Drill-down B8 condizionato e classificazione Known vs Real.
|
||||||
|
- Mocks e scenari di simulazione arricchiti per testare casi complessi.
|
||||||
|
- Test automatici per prevenire regressioni nelle regole di notazione delle failure.
|
||||||
|
- Correzioni logiche che garantiscono che real B8 failures provochino correttamente la FAIL della run.
|
||||||
|
- Gestione dei test con target generator
|
||||||
|
|
||||||
|
## Dove trovare il codice e la documentazione
|
||||||
|
- Script principale: `TestEnvironment/scripts/GRIFO_M_PBIT.py`
|
||||||
|
- GUI: `TestEnvironment/scripts/GRIFO_M_PBIT_gui.py`
|
||||||
|
- Mock / simulazione: `TestEnvironment/scripts/GRIFO_M_PBIT_mock.py`
|
||||||
|
- Report / PDF generator: `TestEnvironment/env/leo_grifo_pdf2report.py`
|
||||||
|
|
||||||
|
|
||||||
|
## Esempi di esecuzione
|
||||||
|
- Modalità simulazione (rapida):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\run_simulate.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
- Modalità hardware:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\run_target.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
Output generati in (cartelle):
|
||||||
|
- Log: `TestEnvironment/LOG/GRIFO_M_PBIT.log`
|
||||||
|
- PDF/CSV/JSON: `TestEnvironment/pdf_reports/` (file con prefisso `GRIFO_M_PBIT_YYYYMMDD_HHMMSS`)
|
||||||
|
|
||||||
|
## Note per sviluppatori
|
||||||
|
- Matching `KNOWN_FAILURES`: la logica attuale usa substring case-insensitive. Per renderla più restrittiva
|
||||||
|
usare regex o nomi completi: modificare `is_known_failure()` in `GRIFO_M_PBIT.py`.
|
||||||
|
- Per aggiungere un known failure permanente, aggiornare la lista `KNOWN_FAILURES` in `GRIFO_M_PBIT.py`.
|
||||||
|
- Se si modifica la struttura dei report JSON, aggiornare anche `TestEnvironment/env/leo_grifo_test_report.py`.
|
||||||
|
|
||||||
14
TestEnvironment/env/leo_grifo_pdf2report.py
vendored
14
TestEnvironment/env/leo_grifo_pdf2report.py
vendored
@ -373,7 +373,7 @@ class PDFClass:
|
|||||||
|
|
||||||
# Table header row (added Target column)
|
# Table header row (added Target column)
|
||||||
headers = ('Run', 'Status', 'PBIT\nTime', 'B6 Status\n(P/F/K)',
|
headers = ('Run', 'Status', 'PBIT\nTime', 'B6 Status\n(P/F/K)',
|
||||||
'B8 Checks\n(Pass/Fail)', 'Serial Messages\n(E/F/R)', 'Target', 'Target Test')
|
'B8 Checks\n(P/F/K)', 'Serial Messages\n(E/F/R)', 'Target', 'Target Test')
|
||||||
|
|
||||||
with doc.table(
|
with doc.table(
|
||||||
borders_layout="ALL",
|
borders_layout="ALL",
|
||||||
@ -401,7 +401,7 @@ class PDFClass:
|
|||||||
# Format data compactly
|
# Format data compactly
|
||||||
pbit_time = f"{run['pbit_time']:.1f}s" if run.get('bit_available', True) else "N/A"
|
pbit_time = f"{run['pbit_time']:.1f}s" if run.get('bit_available', True) else "N/A"
|
||||||
b6_status = f"{run['b6_pass']}/{run['b6_fail']}/{run['b6_known_fail']}"
|
b6_status = f"{run['b6_pass']}/{run['b6_fail']}/{run['b6_known_fail']}"
|
||||||
b8_checks = f"{run['b8_pass']}/{run['b8_fail']}" if run['b8_checked'] > 0 else "-"
|
b8_checks = f"{run['b8_pass']}/{run['b8_fail']}/{run.get('b8_known_fail',0)}" if run['b8_checked'] > 0 else "-"
|
||||||
serial_stats = f"{run.get('serial_errors', 0)}/{run.get('serial_fatal', 0)}/{run.get('serial_recycles', 0)}"
|
serial_stats = f"{run.get('serial_errors', 0)}/{run.get('serial_fatal', 0)}/{run.get('serial_recycles', 0)}"
|
||||||
result_text = f"{result_symbol} {status_text}"
|
result_text = f"{result_symbol} {status_text}"
|
||||||
|
|
||||||
@ -438,8 +438,8 @@ class PDFClass:
|
|||||||
doc.set_font(size=8, style='I')
|
doc.set_font(size=8, style='I')
|
||||||
doc.multi_cell(0, 4,
|
doc.multi_cell(0, 4,
|
||||||
"Legend: B6 Status = Pass/Fail/Known (known failures ignored) | "
|
"Legend: B6 Status = Pass/Fail/Known (known failures ignored) | "
|
||||||
"B8 Checks = Pass/Fail (diagnostic drill-down) | "
|
"B8 Checks = Pass/Fail/Known (known failures ignored) | "
|
||||||
"Serial = Errors(%%E)/Fatal(%%F)/Recycles | "
|
"Serial = Errors(%E)/Fatal(%F)/Recycles | "
|
||||||
"Target Test = Target detection result (PASS/FAIL)",
|
"Target Test = Target detection result (PASS/FAIL)",
|
||||||
align='L')
|
align='L')
|
||||||
doc.reset_font()
|
doc.reset_font()
|
||||||
@ -531,11 +531,15 @@ class PDFClass:
|
|||||||
b8_pass_rate = (aggregate['total_b8_pass'] / total_b8 * 100)
|
b8_pass_rate = (aggregate['total_b8_pass'] / total_b8 * 100)
|
||||||
b8_fail_rate = (aggregate['total_b8_fail'] / total_b8 * 100)
|
b8_fail_rate = (aggregate['total_b8_fail'] / total_b8 * 100)
|
||||||
|
|
||||||
|
b8_known = aggregate.get('total_b8_known', 0)
|
||||||
|
b8_known_rate = (b8_known / total_b8 * 100) if total_b8 > 0 else 0
|
||||||
|
|
||||||
rows_b8 = (
|
rows_b8 = (
|
||||||
('Metric', 'Count', 'Percentage'),
|
('Metric', 'Count', 'Percentage'),
|
||||||
('B8 Diagnostic - Total Checks', str(total_b8), '100%'),
|
('B8 Diagnostic - Total Checks', str(total_b8), '100%'),
|
||||||
('B8 - Pass', str(aggregate['total_b8_pass']), f"{b8_pass_rate:.1f}%"),
|
('B8 - Pass', str(aggregate['total_b8_pass']), f"{b8_pass_rate:.1f}%"),
|
||||||
('B8 - Fail', str(aggregate['total_b8_fail']), f"{b8_fail_rate:.1f}%")
|
('B8 - Fail', str(aggregate['total_b8_fail']), f"{b8_fail_rate:.1f}%"),
|
||||||
|
('B8 - Known Failures (Ignored)', str(b8_known), f"{b8_known_rate:.1f}%")
|
||||||
)
|
)
|
||||||
|
|
||||||
with doc.table(
|
with doc.table(
|
||||||
|
|||||||
@ -138,8 +138,40 @@ FORCE_B8_DRILL_DOWN = True
|
|||||||
KNOWN_FAILURES = [
|
KNOWN_FAILURES = [
|
||||||
"radar_health_status_and_bit_report_valid_RdrHealthStatusAndBitReport_pedestal_status",
|
"radar_health_status_and_bit_report_valid_RdrHealthStatusAndBitReport_pedestal_status",
|
||||||
# Add more known HW setup limitations here as needed
|
# Add more known HW setup limitations here as needed
|
||||||
|
# Treat any pedestal-related B8 fields as known failures by default
|
||||||
|
"pedestal",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def is_known_failure(field_name: str) -> bool:
|
||||||
|
"""
|
||||||
|
Decide whether a given diagnostic `field_name` should be considered a known
|
||||||
|
(ignored) failure based on the `KNOWN_FAILURES` list.
|
||||||
|
|
||||||
|
Matching rules:
|
||||||
|
- Substring match (case-insensitive): if any entry in `KNOWN_FAILURES`
|
||||||
|
is found inside `field_name`, it's considered known.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
field_name: Full diagnostic field identifier (e.g. '..._pedestal_status')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the field is configured as a known failure, False otherwise.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not field_name:
|
||||||
|
return False
|
||||||
|
fname = field_name.lower()
|
||||||
|
for k in KNOWN_FAILURES:
|
||||||
|
try:
|
||||||
|
if k.lower() in fname:
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
interruptRequest = False # Global flag for graceful Ctrl-C handling
|
interruptRequest = False # Global flag for graceful Ctrl-C handling
|
||||||
|
|
||||||
# ====================
|
# ====================
|
||||||
@ -292,6 +324,7 @@ def generate_final_statistics_report(report, stats):
|
|||||||
total_b8_checks = sum(r['b8_checked'] for r in stats['repetitions'])
|
total_b8_checks = sum(r['b8_checked'] for r in stats['repetitions'])
|
||||||
total_b8_pass = sum(r['b8_pass'] for r in stats['repetitions'])
|
total_b8_pass = sum(r['b8_pass'] for r in stats['repetitions'])
|
||||||
total_b8_fail = sum(r['b8_fail'] for r in stats['repetitions'])
|
total_b8_fail = sum(r['b8_fail'] for r in stats['repetitions'])
|
||||||
|
total_b8_known = sum(r.get('b8_known_fail', 0) for r in stats['repetitions'])
|
||||||
|
|
||||||
total_serial_msgs = sum(r.get('serial_total', 0) for r in stats['repetitions'])
|
total_serial_msgs = sum(r.get('serial_total', 0) for r in stats['repetitions'])
|
||||||
total_serial_errors = sum(r.get('serial_errors', 0) for r in stats['repetitions'])
|
total_serial_errors = sum(r.get('serial_errors', 0) for r in stats['repetitions'])
|
||||||
@ -343,6 +376,7 @@ def generate_final_statistics_report(report, stats):
|
|||||||
'total_b8_checks': total_b8_checks,
|
'total_b8_checks': total_b8_checks,
|
||||||
'total_b8_pass': total_b8_pass,
|
'total_b8_pass': total_b8_pass,
|
||||||
'total_b8_fail': total_b8_fail,
|
'total_b8_fail': total_b8_fail,
|
||||||
|
'total_b8_known': total_b8_known,
|
||||||
|
|
||||||
# Serial communication statistics
|
# Serial communication statistics
|
||||||
'total_serial_msgs': total_serial_msgs,
|
'total_serial_msgs': total_serial_msgs,
|
||||||
@ -380,7 +414,7 @@ def generate_final_statistics_report(report, stats):
|
|||||||
logging.info(f"Successful: {stats['successful_runs']} ({stats['successful_runs']/stats['total_runs']*100:.1f}%)")
|
logging.info(f"Successful: {stats['successful_runs']} ({stats['successful_runs']/stats['total_runs']*100:.1f}%)")
|
||||||
logging.info(f"Failed: {stats['failed_runs']} ({stats['failed_runs']/stats['total_runs']*100:.1f}%)")
|
logging.info(f"Failed: {stats['failed_runs']} ({stats['failed_runs']/stats['total_runs']*100:.1f}%)")
|
||||||
logging.info(f"B6 Checks: {total_b6_checks} (Pass: {total_b6_pass}, Fail: {total_b6_fail}, Known: {total_b6_known})")
|
logging.info(f"B6 Checks: {total_b6_checks} (Pass: {total_b6_pass}, Fail: {total_b6_fail}, Known: {total_b6_known})")
|
||||||
logging.info(f"B8 Checks: {total_b8_checks} (Pass: {total_b8_pass}, Fail: {total_b8_fail})")
|
logging.info(f"B8 Checks: {total_b8_checks} (Pass: {total_b8_pass}, Fail: {total_b8_fail}, Known: {total_b8_known})")
|
||||||
logging.info(f"Serial: {total_serial_msgs} messages ({total_serial_errors} errors, {total_serial_fatal} fatal, {total_serial_recycles} recycles)")
|
logging.info(f"Serial: {total_serial_msgs} messages ({total_serial_errors} errors, {total_serial_fatal} fatal, {total_serial_recycles} recycles)")
|
||||||
logging.info(f"Target: {target_detected_count} detected, {target_not_detected_count} not detected ({target_pass_rate:.1f}% pass rate)")
|
logging.info(f"Target: {target_detected_count} detected, {target_not_detected_count} not detected ({target_pass_rate:.1f}% pass rate)")
|
||||||
if avg_pbit is not None:
|
if avg_pbit is not None:
|
||||||
@ -441,6 +475,7 @@ def export_statistics_to_csv(custom_statistics, test_name, output_folder):
|
|||||||
'Scenario',
|
'Scenario',
|
||||||
'B6 Total', 'B6 Pass', 'B6 Fail', 'B6 Known',
|
'B6 Total', 'B6 Pass', 'B6 Fail', 'B6 Known',
|
||||||
'B8 Checked', 'B8 Pass', 'B8 Fail',
|
'B8 Checked', 'B8 Pass', 'B8 Fail',
|
||||||
|
'B8 Known',
|
||||||
'Serial Msgs', 'Serial Errors', 'Serial Fatal', 'Serial Recycles',
|
'Serial Msgs', 'Serial Errors', 'Serial Fatal', 'Serial Recycles',
|
||||||
'Real Failures', 'Known Failures', 'Target Simulated',
|
'Real Failures', 'Known Failures', 'Target Simulated',
|
||||||
'Failures Detail (JSON)', 'Known Failures Detail (JSON)', 'Serial Details (JSON)'
|
'Failures Detail (JSON)', 'Known Failures Detail (JSON)', 'Serial Details (JSON)'
|
||||||
@ -489,6 +524,7 @@ def export_statistics_to_csv(custom_statistics, test_name, output_folder):
|
|||||||
run.get('b8_checked', 0),
|
run.get('b8_checked', 0),
|
||||||
run.get('b8_pass', 0),
|
run.get('b8_pass', 0),
|
||||||
run.get('b8_fail', 0),
|
run.get('b8_fail', 0),
|
||||||
|
run.get('b8_known_fail', 0),
|
||||||
run.get('serial_total', 0),
|
run.get('serial_total', 0),
|
||||||
run.get('serial_errors', 0),
|
run.get('serial_errors', 0),
|
||||||
run.get('serial_fatal', 0),
|
run.get('serial_fatal', 0),
|
||||||
@ -530,6 +566,7 @@ def export_statistics_to_csv(custom_statistics, test_name, output_folder):
|
|||||||
writer.writerow(['B8 Total Checks', agg['total_b8_checks']])
|
writer.writerow(['B8 Total Checks', agg['total_b8_checks']])
|
||||||
writer.writerow(['B8 Pass', agg['total_b8_pass']])
|
writer.writerow(['B8 Pass', agg['total_b8_pass']])
|
||||||
writer.writerow(['B8 Fail', agg['total_b8_fail']])
|
writer.writerow(['B8 Fail', agg['total_b8_fail']])
|
||||||
|
writer.writerow(['B8 Known Fail', agg.get('total_b8_known', 0)])
|
||||||
writer.writerow([]) # Blank line
|
writer.writerow([]) # Blank line
|
||||||
|
|
||||||
# Serial Communication
|
# Serial Communication
|
||||||
@ -1364,6 +1401,7 @@ def execute_pbit_test(interface, report, run_stats, bit_fields, bit_fields_categ
|
|||||||
|
|
||||||
b8_fields = bit_fields[12:]
|
b8_fields = bit_fields[12:]
|
||||||
b8_failures = []
|
b8_failures = []
|
||||||
|
b8_known_failures = []
|
||||||
|
|
||||||
for category, fields in list(bit_fields_categories.items())[1:]:
|
for category, fields in list(bit_fields_categories.items())[1:]:
|
||||||
category_fail = 0
|
category_fail = 0
|
||||||
@ -1384,29 +1422,46 @@ def execute_pbit_test(interface, report, run_stats, bit_fields, bit_fields_categ
|
|||||||
category_pass += 1
|
category_pass += 1
|
||||||
run_stats['b8_pass'] += 1
|
run_stats['b8_pass'] += 1
|
||||||
else:
|
else:
|
||||||
category_fail += 1
|
# Classify known vs real B8 failures
|
||||||
run_stats['b8_fail'] += 1
|
if is_known_failure(f):
|
||||||
b8_failures.append((category, f, err))
|
run_stats['b8_known_fail'] = run_stats.get('b8_known_fail', 0) + 1
|
||||||
test_passed = False
|
b8_known_failures.append((category, f, err))
|
||||||
|
run_stats['known_failures'].append((f, err))
|
||||||
|
else:
|
||||||
|
category_fail += 1
|
||||||
|
run_stats['b8_fail'] += 1
|
||||||
|
b8_failures.append((category, f, err))
|
||||||
|
run_stats['failures'].append((f, err))
|
||||||
|
test_passed = False
|
||||||
|
|
||||||
if category_fail > 0:
|
if category_fail > 0:
|
||||||
logging.warning(f"{category}: {category_fail}/{len(fields)} failures")
|
logging.warning(f"{category}: {category_fail}/{len(fields)} failures")
|
||||||
|
|
||||||
logging.info(f"B8 Diagnostics: {run_stats['b8_checked']} checked, "
|
logging.info(f"B8 Diagnostics: {run_stats['b8_checked']} checked, "
|
||||||
f"{run_stats['b8_pass']} pass, {run_stats['b8_fail']} fail")
|
f"{run_stats['b8_pass']} pass, {run_stats['b8_fail']} fail, {run_stats.get('b8_known_fail',0)} known")
|
||||||
|
|
||||||
if b8_failures:
|
if b8_failures or b8_known_failures:
|
||||||
for cat, field, err in b8_failures:
|
# Log real failures by category
|
||||||
run_stats['failures'].append((field, err))
|
if b8_failures:
|
||||||
|
fail_by_cat = {}
|
||||||
fail_by_cat = {}
|
for cat, field, err in b8_failures:
|
||||||
for cat, field, err in b8_failures:
|
if cat not in fail_by_cat:
|
||||||
if cat not in fail_by_cat:
|
fail_by_cat[cat] = []
|
||||||
fail_by_cat[cat] = []
|
fail_by_cat[cat].append(field.split('_')[-1])
|
||||||
fail_by_cat[cat].append(field.split('_')[-1])
|
|
||||||
|
for cat, fails in fail_by_cat.items():
|
||||||
for cat, fails in fail_by_cat.items():
|
logging.warning(f" {cat}: {len(fails)} failures - {', '.join(fails[:3])}{'...' if len(fails) > 3 else ''}")
|
||||||
logging.warning(f" {cat}: {len(fails)} failures - {', '.join(fails[:3])}{'...' if len(fails) > 3 else ''}")
|
|
||||||
|
# Log known (ignored) failures summary
|
||||||
|
if b8_known_failures:
|
||||||
|
known_by_cat = {}
|
||||||
|
for cat, field, err in b8_known_failures:
|
||||||
|
if cat not in known_by_cat:
|
||||||
|
known_by_cat[cat] = []
|
||||||
|
known_by_cat[cat].append(field.split('_')[-1])
|
||||||
|
|
||||||
|
for cat, fails in known_by_cat.items():
|
||||||
|
logging.warning(f" {cat}: {len(fails)} KNOWN failures (ignored) - {', '.join(fails[:3])}{'...' if len(fails) > 3 else ''}")
|
||||||
else:
|
else:
|
||||||
logging.info(f"All B6 LRU Status PASS (no B8 drill-down needed)")
|
logging.info(f"All B6 LRU Status PASS (no B8 drill-down needed)")
|
||||||
|
|
||||||
@ -1418,6 +1473,12 @@ def execute_pbit_test(interface, report, run_stats, bit_fields, bit_fields_categ
|
|||||||
remaining_time = 0
|
remaining_time = 0
|
||||||
logging.info(f'{remaining_time:.1f}s remaining ...')
|
logging.info(f'{remaining_time:.1f}s remaining ...')
|
||||||
|
|
||||||
|
# Ensure run_stats records final success flag (reflect B6 and B8 real failures)
|
||||||
|
try:
|
||||||
|
run_stats['success'] = bool(test_passed)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
return test_passed
|
return test_passed
|
||||||
|
|
||||||
|
|
||||||
@ -1929,6 +1990,7 @@ def test_proc():
|
|||||||
'b8_checked': 0,
|
'b8_checked': 0,
|
||||||
'b8_pass': 0,
|
'b8_pass': 0,
|
||||||
'b8_fail': 0,
|
'b8_fail': 0,
|
||||||
|
'b8_known_fail': 0,
|
||||||
'failures': [],
|
'failures': [],
|
||||||
'known_failures': [],
|
'known_failures': [],
|
||||||
'success': True,
|
'success': True,
|
||||||
|
|||||||
@ -285,7 +285,7 @@ class TestMonitorGUI:
|
|||||||
('Status', None, 80),
|
('Status', None, 80),
|
||||||
('PBIT Time', None, 80),
|
('PBIT Time', None, 80),
|
||||||
('B6 Status (P/F/K)', 'P = Pass\nF = Fail\nK = Known Failures', 120),
|
('B6 Status (P/F/K)', 'P = Pass\nF = Fail\nK = Known Failures', 120),
|
||||||
('B8 Checks (Pass/Fail)', 'Number of B8 checks\nPass/Fail format', 120),
|
('B8 Checks (P/F/K)', 'P = Pass\nF = Fail\nK = Known Failures', 120),
|
||||||
('Serial Messages (E/F/R)', 'E = Errors\nF = Fatal\nR = Recycles', 120),
|
('Serial Messages (E/F/R)', 'E = Errors\nF = Fatal\nR = Recycles', 120),
|
||||||
('Target', 'Target distance and cycle\nFormat: XXXXm @ cycle Y', 140),
|
('Target', 'Target distance and cycle\nFormat: XXXXm @ cycle Y', 140),
|
||||||
('Target Test', None, 80)
|
('Target Test', None, 80)
|
||||||
@ -707,13 +707,14 @@ class TestMonitorGUI:
|
|||||||
b6_fail = data.get('b6_fail', 0)
|
b6_fail = data.get('b6_fail', 0)
|
||||||
b6_known = data.get('b6_known_fail', 0)
|
b6_known = data.get('b6_known_fail', 0)
|
||||||
new[3] = f"{b6_pass}/{b6_fail}/{b6_known}"
|
new[3] = f"{b6_pass}/{b6_fail}/{b6_known}"
|
||||||
elif k in ('b8_pass', 'b8_fail', 'b8_checked'):
|
elif k in ('b8_pass', 'b8_fail', 'b8_checked', 'b8_known_fail'):
|
||||||
# Column 4: B8 Checks (Pass/Fail) - format like PDF
|
# Column 4: B8 Checks (Pass/Fail/Known) - format like PDF
|
||||||
b8_pass = data.get('b8_pass', 0)
|
b8_pass = data.get('b8_pass', 0)
|
||||||
b8_fail = data.get('b8_fail', 0)
|
b8_fail = data.get('b8_fail', 0)
|
||||||
|
b8_known = data.get('b8_known_fail', 0)
|
||||||
b8_checked = data.get('b8_checked', 0)
|
b8_checked = data.get('b8_checked', 0)
|
||||||
if b8_checked > 0:
|
if b8_checked > 0:
|
||||||
new[4] = f"{b8_pass}/{b8_fail}"
|
new[4] = f"{b8_pass}/{b8_fail}/{b8_known}"
|
||||||
else:
|
else:
|
||||||
new[4] = "-"
|
new[4] = "-"
|
||||||
elif k in ('serial_errors', 'serial_fatal', 'serial_recycles'):
|
elif k in ('serial_errors', 'serial_fatal', 'serial_recycles'):
|
||||||
|
|||||||
@ -12,6 +12,48 @@
|
|||||||
2. **Orchestrazione scenari**: Configurazione failure simulati
|
2. **Orchestrazione scenari**: Configurazione failure simulati
|
||||||
3. **Monitoring multi-canale**: 1553, seriale, power control
|
3. **Monitoring multi-canale**: 1553, seriale, power control
|
||||||
4. **Aggregazione statistiche**: B6/B8/Serial/Target metrics
|
4. **Aggregazione statistiche**: B6/B8/Serial/Target metrics
|
||||||
|
|
||||||
|
## 3.2.2 Versione Script e gestione KNOWN_FAILURES
|
||||||
|
|
||||||
|
Lo script espone ora una variabile di versione `TEST_VERSION` che viene
|
||||||
|
inserita in tutti gli output (log, CSV, JSON e PDF). Questa stringa è utile
|
||||||
|
per tracciare quale versione del codice ha generato un report.
|
||||||
|
|
||||||
|
Esempio in codice:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Script version propagated to outputs
|
||||||
|
TEST_VERSION = "0.0.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
KNOWN_FAILURES: il comportamento della lista `KNOWN_FAILURES` è stato
|
||||||
|
esteso rispetto alle versioni precedenti. Ora gli elementi configurati in
|
||||||
|
questa lista sono usati per classificare come "Known" anche le failure
|
||||||
|
emerse durante il drill-down di B8. In pratica:
|
||||||
|
|
||||||
|
- Se un campo B6 è in fallimento ma il relativo test B8 corrispondente è
|
||||||
|
segnato come "known" nella configurazione, quello specifico fallimento
|
||||||
|
NON rende la run fallita (viene registrato come `known_fail`).
|
||||||
|
- Se un campo B8 fallisce e non è classificato come known, viene marcato
|
||||||
|
come real failure e la run viene considerata FAILED.
|
||||||
|
|
||||||
|
Implementazione helper (semplificata):
|
||||||
|
|
||||||
|
```python
|
||||||
|
def is_known_failure(field_name: str) -> bool:
|
||||||
|
"""Restituisce True se `field_name` corrisponde a una voce di KNOWN_FAILURES.
|
||||||
|
|
||||||
|
Matching: case-insensitive substring match (config semplice, ma efficace).
|
||||||
|
"""
|
||||||
|
for k in KNOWN_FAILURES:
|
||||||
|
if k.lower() in field_name.lower():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
```
|
||||||
|
|
||||||
|
Questa estensione permette di mantenere la stessa logica operativa (known
|
||||||
|
failures ignorate per il giudizio di PASS/FAIL) anche quando il dettaglio
|
||||||
|
diagnostico è ottenuto con il drill-down B8.
|
||||||
5. **Generazione report**: PDF, CSV, JSON output
|
5. **Generazione report**: PDF, CSV, JSON output
|
||||||
|
|
||||||
### 3.1.1 Caratteristiche Chiave
|
### 3.1.1 Caratteristiche Chiave
|
||||||
|
|||||||
@ -392,16 +392,27 @@ Run Result: FAIL (due to 2 real failures)
|
|||||||
|
|
||||||
### 4.4.1 Quando Eseguire B8 Drill-Down
|
### 4.4.1 Quando Eseguire B8 Drill-Down
|
||||||
|
|
||||||
**Trigger Condition**:
|
Il comportamento è stato aggiornato per tenere conto delle `KNOWN_FAILURES`.
|
||||||
|
Il drill-down B8 viene eseguito quando sono presenti real failures (non-known)
|
||||||
|
o quando è forzato (opzione `FORCE_B8_DRILL_DOWN`). Se tutti i fallimenti
|
||||||
|
osservati sono conosciuti, il drill-down può essere saltato per risparmiare tempo.
|
||||||
|
|
||||||
|
**Trigger Condition (semplificata)**:
|
||||||
```python
|
```python
|
||||||
if b6_fail > 0: # Almeno un real failure in B6
|
# Esegui drill-down se ci sono real failures oppure se forzato
|
||||||
|
if b6_fail > 0 and b6_real_fail > 0:
|
||||||
|
drill_down_b8()
|
||||||
|
elif FORCE_B8_DRILL_DOWN:
|
||||||
drill_down_b8()
|
drill_down_b8()
|
||||||
else:
|
else:
|
||||||
# Skip B8 (solo known failures o tutto OK)
|
# Tutti i fallimenti (se presenti) sono classificati come KNOWN -> skip
|
||||||
logging.info("B8 drill-down skipped (no real failures)")
|
logging.info("B8 drill-down skipped (no real failures)")
|
||||||
```
|
```
|
||||||
|
|
||||||
**Rationale**: B8 contiene 185 campi diagnostici dettagliati. Eseguire sempre sarebbe dispendioso (5-10s). Si esegue solo quando necessario per analisi failure.
|
Rationale: B8 contiene 185 campi diagnostici. Eseguire sempre sarebbe dispendioso
|
||||||
|
(5-10s). Il criterio adottato mantiene il drill-down per le situazioni che
|
||||||
|
richiedono indagine (real failures) e lo salta quando si tratta solo di
|
||||||
|
known failures attese.
|
||||||
|
|
||||||
### 4.4.2 Struttura Messaggio B8
|
### 4.4.2 Struttura Messaggio B8
|
||||||
|
|
||||||
@ -446,8 +457,10 @@ def drill_down_b8() -> Dict:
|
|||||||
|
|
||||||
b8_pass = 0
|
b8_pass = 0
|
||||||
b8_fail = 0
|
b8_fail = 0
|
||||||
|
b8_known_fail = 0
|
||||||
b8_checked = len(B8_FIELDS)
|
b8_checked = len(B8_FIELDS)
|
||||||
b8_failures = []
|
b8_failures = []
|
||||||
|
b8_known_failures = []
|
||||||
|
|
||||||
logging.info(f"Starting B8 drill-down: {b8_checked} fields to check")
|
logging.info(f"Starting B8 drill-down: {b8_checked} fields to check")
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
@ -460,10 +473,17 @@ def drill_down_b8() -> Dict:
|
|||||||
if ret:
|
if ret:
|
||||||
b8_pass += 1
|
b8_pass += 1
|
||||||
else:
|
else:
|
||||||
b8_fail += 1
|
# Leggi valore per logging
|
||||||
value = getValue(theGrifo1553, "B8_MsgBitReport", field)
|
value = getValue(theGrifo1553, "B8_MsgBitReport", field)
|
||||||
b8_failures.append((field, value))
|
# Classifica known vs real failure usando is_known_failure()
|
||||||
logging.warning(f"B8 FAIL: {field} = {value}")
|
if is_known_failure(field):
|
||||||
|
b8_known_fail += 1
|
||||||
|
b8_known_failures.append((field, value))
|
||||||
|
logging.info(f"Known failure (B8 ignored): {field} = {value}")
|
||||||
|
else:
|
||||||
|
b8_fail += 1
|
||||||
|
b8_failures.append((field, value))
|
||||||
|
logging.warning(f"B8 REAL FAIL: {field} = {value}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"B8 check failed for {field}: {e}")
|
logging.error(f"B8 check failed for {field}: {e}")
|
||||||
@ -471,13 +491,15 @@ def drill_down_b8() -> Dict:
|
|||||||
|
|
||||||
elapsed = time.time() - start_time
|
elapsed = time.time() - start_time
|
||||||
logging.info(f"B8 drill-down completed in {elapsed:.2f}s")
|
logging.info(f"B8 drill-down completed in {elapsed:.2f}s")
|
||||||
logging.info(f"B8 Results: {b8_pass} PASS, {b8_fail} FAIL")
|
logging.info(f"B8 Results: {b8_pass} PASS, {b8_fail} FAIL, {b8_known_fail} KNOWN")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'b8_pass': b8_pass,
|
'b8_pass': b8_pass,
|
||||||
'b8_fail': b8_fail,
|
'b8_fail': b8_fail,
|
||||||
|
'b8_known_fail': b8_known_fail,
|
||||||
'b8_checked': b8_checked,
|
'b8_checked': b8_checked,
|
||||||
'b8_failures': b8_failures,
|
'b8_failures': b8_failures,
|
||||||
|
'b8_known_failures': b8_known_failures,
|
||||||
'b8_time': elapsed
|
'b8_time': elapsed
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@ -47,16 +47,16 @@ L'interfaccia grafica fornisce **monitoraggio real-time** dell'esecuzione test,
|
|||||||
│ Runs Overview │
|
│ Runs Overview │
|
||||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||||
│ │ Run Status PBIT B6 Status B8 Checks Serial Targ..│ │
|
│ │ Run Status PBIT B6 Status B8 Checks Serial Targ..│ │
|
||||||
│ │ Time (P/F/K) (Pass/Fail) Msgs et │ │
|
│ │ Time (P/F/K) (Pass/Fail/Known) Msgs et │ │
|
||||||
│ │ (E/F/R) │ │
|
│ │ (E/F/R) │ │
|
||||||
│ ├──────────────────────────────────────────────────────────┤ │
|
│ ├──────────────────────────────────────────────────────────┤ │
|
||||||
│ │ 1/10 PASS 2.2s 11/0/1 173/0 0/0/0 2000m │ │
|
│ │ 1/10 PASS 2.2s 11/0/1 173/0/0 0/0/0 2000m │ │
|
||||||
│ │ 2/10 PASS 0.7s 11/0/1 173/0 0/0/0 - │ │
|
│ │ 2/10 PASS 0.7s 11/0/1 173/0/0 0/0/0 - │ │
|
||||||
│ │ 3/10 FAIL 0.1s 9/2/1 - 0/3/3 - │ │
|
│ │ 3/10 FAIL 0.1s 9/2/1 - 0/3/3 - │ │
|
||||||
│ │ 4/10 FAIL 0.1s 9/2/1 173/0 0/3/0 - │ │
|
│ │ 4/10 FAIL 0.1s 9/2/1 173/0/0 0/3/0 - │ │
|
||||||
│ │ 5/10 PASS 2.0s 9/2/1 173/0 0/0/0 - │ │
|
│ │ 5/10 PASS 2.0s 9/2/1 173/0/0 0/0/0 - │ │
|
||||||
│ │ 6/10 PASS 0.3s 11/0/1 173/0 0/2/2 - │ │
|
│ │ 6/10 PASS 0.3s 11/0/1 173/0/0 0/2/2 - │ │
|
||||||
│ │ 7/10 PASS 0.0s 11/0/1 173/0 0/2/2 2000m │ │
|
│ │ 7/10 PASS 0.0s 11/0/1 173/0/0 0/2/2 2000m │ │
|
||||||
│ │ 8/10 PASS .... ...... ...... ...... .... │ │◄ Running
|
│ │ 8/10 PASS .... ...... ...... ...... .... │ │◄ Running
|
||||||
│ │ 9/10 │ │
|
│ │ 9/10 │ │
|
||||||
│ │10/10 │ │
|
│ │10/10 │ │
|
||||||
@ -392,7 +392,7 @@ def _create_runs_table(self, parent, row):
|
|||||||
('Status', None, 80),
|
('Status', None, 80),
|
||||||
('PBIT Time', None, 80),
|
('PBIT Time', None, 80),
|
||||||
('B6 Status (P/F/K)', 'P = Pass\nF = Fail\nK = Known Failures', 120),
|
('B6 Status (P/F/K)', 'P = Pass\nF = Fail\nK = Known Failures', 120),
|
||||||
('B8 Checks (Pass/Fail)', 'Number of B8 checks\nPass/Fail format', 120),
|
('B8 Checks (Pass/Fail/Known)', 'Number of B8 checks\nFormat: Pass/Fail/Known', 140),
|
||||||
('Serial Messages (E/F/R)', 'E = Errors\nF = Fatal\nR = Recycles', 120),
|
('Serial Messages (E/F/R)', 'E = Errors\nF = Fatal\nR = Recycles', 120),
|
||||||
('Target', 'Target distance and cycle\nFormat: XXXXm @ cycle Y', 140),
|
('Target', 'Target distance and cycle\nFormat: XXXXm @ cycle Y', 140),
|
||||||
('Target Test', None, 80)
|
('Target Test', None, 80)
|
||||||
|
|||||||
@ -217,11 +217,13 @@ def generate_pdf_report(output_folder: str, json_data: Dict) -> str:
|
|||||||
story.append(Spacer(1, 0.2*inch))
|
story.append(Spacer(1, 0.2*inch))
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
|
script_version = json_data.get('test_metadata', {}).get('script_version', 'unknown')
|
||||||
meta_data = [
|
meta_data = [
|
||||||
['Date:', timestamp.split('_')[0]],
|
['Date:', timestamp.split('_')[0]],
|
||||||
['Time:', timestamp.split('_')[1].replace('-', ':') if '_' in timestamp else ''],
|
['Time:', timestamp.split('_')[1].replace('-', ':') if '_' in timestamp else ''],
|
||||||
['Total Runs:', str(total_runs)],
|
['Script Version:', script_version],
|
||||||
['Duration:', _format_duration(duration)]
|
['Total Runs:', str(total_runs)],
|
||||||
|
['Duration:', _format_duration(duration)]
|
||||||
]
|
]
|
||||||
meta_table = Table(meta_data, colWidths=[1.5*inch, 3*inch])
|
meta_table = Table(meta_data, colWidths=[1.5*inch, 3*inch])
|
||||||
meta_table.setStyle(TableStyle([
|
meta_table.setStyle(TableStyle([
|
||||||
@ -272,8 +274,8 @@ def generate_pdf_report(output_folder: str, json_data: Dict) -> str:
|
|||||||
|
|
||||||
# Build table data
|
# Build table data
|
||||||
table_data = [
|
table_data = [
|
||||||
['Run', 'Status', 'PBIT\nTime', 'B6 Status\n(P/F/K)',
|
['Run', 'Status', 'PBIT\nTime', 'B6 Status\n(P/F/K)',
|
||||||
'B8 Checks\n(Pass/Fail)', 'Serial\n(E/F/R)', 'Target']
|
'B8 Checks\n(Pass/Fail/Known)', 'Serial\n(E/F/R)', 'Target']
|
||||||
]
|
]
|
||||||
|
|
||||||
runs = json_data.get('runs', [])
|
runs = json_data.get('runs', [])
|
||||||
@ -291,15 +293,16 @@ def generate_pdf_report(output_folder: str, json_data: Dict) -> str:
|
|||||||
serial_r = r.get('serial_recycles', 0)
|
serial_r = r.get('serial_recycles', 0)
|
||||||
target = r.get('target_info', '-')
|
target = r.get('target_info', '-')
|
||||||
|
|
||||||
table_data.append([
|
# Per-run B8 now includes known failures count
|
||||||
f"{run_id}/{total_runs}",
|
table_data.append([
|
||||||
status,
|
f"{run_id}/{total_runs}",
|
||||||
f"{pbit_time:.1f}s" if pbit_time else '-',
|
status,
|
||||||
f"{b6_pass}/{b6_fail}/{b6_known}",
|
f"{pbit_time:.1f}s" if pbit_time else '-',
|
||||||
f"{b8_pass}/{b8_fail}" if b8_pass or b8_fail else '-',
|
f"{b6_pass}/{b6_fail}/{b6_known}",
|
||||||
f"{serial_e}/{serial_f}/{serial_r}",
|
f"{b8_pass}/{b8_fail}/{r.get('b8_known_fail', 0)}" if (b8_pass or b8_fail or r.get('b8_known_fail', 0)) else '-',
|
||||||
target
|
f"{serial_e}/{serial_f}/{serial_r}",
|
||||||
])
|
target
|
||||||
|
])
|
||||||
|
|
||||||
runs_table = Table(table_data, colWidths=[
|
runs_table = Table(table_data, colWidths=[
|
||||||
0.6*inch, 0.7*inch, 0.7*inch, 0.9*inch,
|
0.6*inch, 0.7*inch, 0.7*inch, 0.9*inch,
|
||||||
|
|||||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user