updated CodeGeneration

This commit is contained in:
Marcel Peterkau 2024-01-09 20:47:28 +01:00
parent ac28ab21d4
commit 157d59963c
16 changed files with 357 additions and 171 deletions

1
Software/.gitignore vendored
View File

@ -5,3 +5,4 @@ data/
.vscode/launch.json
.vscode/ipch
wifi_credentials.ini
__pycache__

View File

@ -1,127 +0,0 @@
Import("env") # pylint: disable=undefined-variable
env.Execute("\"$PYTHONEXE\" -m pip install jinja2")
import os
import time
from jinja2 import Environment, FileSystemLoader
import json
# Pfad zur Eingabedatei und Ausgabedatei
input_file = "src/dtc_defs.txt"
output_file = "include/dtc_defs.h"
json_output_file = "data_src/static/dtc_table.json"
# Überprüfen, ob das Verzeichnis existiert, andernfalls erstellen
json_output_dir = os.path.dirname(json_output_file)
if not os.path.exists(json_output_dir):
os.makedirs(json_output_dir)
# Mehrdimensionales Array zum Speichern der Zeilen aus der Eingabedatei
dtc_lines = []
# Lesen und analysieren der Eingabedatei
with open(input_file, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split(";")
if len(parts) == 5:
num, dtc_name, dtc_severity, title, description = [part.strip() for part in parts]
dtc_lines.append([int(num), dtc_name, dtc_severity, title, description])
# Überprüfen auf Duplikate in den DTC-Nummern und DTC-Namen
num_set = set()
dtc_name_set = set()
duplicates = []
for line in dtc_lines:
num, dtc_name, _, _, _ = line
if num in num_set:
duplicates.append(f"DTC-Nummer {num} ist ein Duplikat.")
else:
num_set.add(num)
if dtc_name in dtc_name_set:
duplicates.append(f"DTC-Name '{dtc_name}' ist ein Duplikat.")
else:
dtc_name_set.add(dtc_name)
if duplicates:
for duplicate in duplicates:
print(f"Fehler: {duplicate}")
raise ValueError("Duplicate DTC Data detected")
# Suchen nach DTC_NO_DTC und DTC_LAST_DTC
dtc_no_dtc_added = False
dtc_last_dtc_line = None
for line in dtc_lines:
_, dtc_name, _, _, _ = line
if dtc_name == "DTC_NO_DTC":
dtc_no_dtc_added = True
elif dtc_name == "DTC_LAST_DTC":
dtc_last_dtc_line = line
# Einen DTC für DTC_NO_DTC hinzufügen (wenn nicht vorhanden)
if not dtc_no_dtc_added:
dtc_lines.insert(0, [0, "DTC_NO_DTC", "DTC_NONE", "No Error", "No Error"])
# Falls DTC_LAST_DTC existiert, lösche es
if dtc_last_dtc_line:
dtc_lines.remove(dtc_last_dtc_line)
# Einen DTC für DTC_LAST_DTC hinzufügen (mit der höchsten Nummer)
if dtc_lines:
highest_num = max([line[0] for line in dtc_lines])
else:
highest_num = 0
dtc_lines.append([highest_num + 1, "DTC_LAST_DTC", "DTC_NONE", "Last Error", "Last Error"])
# Sortieren der Zeilen nach der Nummer aufsteigend
dtc_lines.sort(key=lambda x: x[0])
# DTC_NAME_CONSTANT-Makros initialisieren
dtc_macros = []
dtc_structs = []
dtc_table_data = []
# Verarbeiten der sortierten Zeilen
for i, line in enumerate(dtc_lines):
num, dtc_name, dtc_severity, title, description = line
dtc_macros.append(f"#define {dtc_name:<30} {num}")
comma = "," if i < len(dtc_lines) - 1 else " "
dtc_structs.append(f" {{ {dtc_name:<30}, {dtc_severity:<12} }}{comma} // {description}")
dtc_table_data.append({"num": num, "title": title, "description": description})
# Unix-Zeitstempel hinzufügen
timestamp = int(time.time())
env = Environment(loader=FileSystemLoader('include', encoding='utf-8'))
# Lade das Jinja2-Template aus der Datei
template = env.get_template('dtc_defs.h.j2')
# Erstelle ein Context-Dictionary mit den erforderlichen Daten
context = {
'timestamp_unix': timestamp,
'timestamp' : time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp)),
'dtc_macros': dtc_macros, # Übergebe die dtc_macros-Liste direkt
'dtc_structs': dtc_structs, # Übergebe die dtc_structs-Liste direkt
}
# Rendere das Template mit den Werten und erhalte den Header-Text
header_text = template.render(context)
# Schreibe den generierten Header-Text in die Header-Datei
with open(output_file, "w", encoding='utf-8') as f:
f.write(header_text)
print(f"Header-Datei wurde erstellt: {output_file}")
# JSON-Datei mit UTF-8-Zeichencodierung erstellen
with open(json_output_file, 'w', encoding='utf-8') as json_f:
json.dump(dtc_table_data, json_f, ensure_ascii=False, indent=4, separators=(',', ': '))
print(f"JSON-Datei wurde erstellt: {json_output_file}")

127
Software/codegen/dtcs.py Normal file
View File

@ -0,0 +1,127 @@
import os
import time
from jinja2 import Environment, FileSystemLoader
import json
def build_dtcs():
# Pfad zur Eingabedatei und Ausgabedatei
input_file = "src/dtc_defs.txt"
output_file = "include/dtc_defs.h"
json_output_file = "data_src/static/dtc_table.json"
# Überprüfen, ob das Verzeichnis existiert, andernfalls erstellen
json_output_dir = os.path.dirname(json_output_file)
if not os.path.exists(json_output_dir):
os.makedirs(json_output_dir)
# Mehrdimensionales Array zum Speichern der Zeilen aus der Eingabedatei
dtc_lines = []
# Lesen und analysieren der Eingabedatei
with open(input_file, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split(";")
if len(parts) == 5:
num, dtc_name, dtc_severity, title, description = [part.strip() for part in parts]
dtc_lines.append([int(num), dtc_name, dtc_severity, title, description])
# Überprüfen auf Duplikate in den DTC-Nummern und DTC-Namen
num_set = set()
dtc_name_set = set()
duplicates = []
for line in dtc_lines:
num, dtc_name, _, _, _ = line
if num in num_set:
duplicates.append(f"DTC-Nummer {num} ist ein Duplikat.")
else:
num_set.add(num)
if dtc_name in dtc_name_set:
duplicates.append(f"DTC-Name '{dtc_name}' ist ein Duplikat.")
else:
dtc_name_set.add(dtc_name)
if duplicates:
for duplicate in duplicates:
print(f"Fehler: {duplicate}")
raise ValueError("Duplicate DTC Data detected")
# Suchen nach DTC_NO_DTC und DTC_LAST_DTC
dtc_no_dtc_added = False
dtc_last_dtc_line = None
for line in dtc_lines:
_, dtc_name, _, _, _ = line
if dtc_name == "DTC_NO_DTC":
dtc_no_dtc_added = True
elif dtc_name == "DTC_LAST_DTC":
dtc_last_dtc_line = line
# Einen DTC für DTC_NO_DTC hinzufügen (wenn nicht vorhanden)
if not dtc_no_dtc_added:
dtc_lines.insert(0, [0, "DTC_NO_DTC", "DTC_NONE", "No Error", "No Error"])
# Falls DTC_LAST_DTC existiert, lösche es
if dtc_last_dtc_line:
dtc_lines.remove(dtc_last_dtc_line)
# Einen DTC für DTC_LAST_DTC hinzufügen (mit der höchsten Nummer)
if dtc_lines:
highest_num = max([line[0] for line in dtc_lines])
else:
highest_num = 0
dtc_lines.append([highest_num + 1, "DTC_LAST_DTC", "DTC_NONE", "Last Error", "Last Error"])
# Sortieren der Zeilen nach der Nummer aufsteigend
dtc_lines.sort(key=lambda x: x[0])
# DTC_NAME_CONSTANT-Makros initialisieren
dtc_macros = []
dtc_structs = []
dtc_table_data = []
# Verarbeiten der sortierten Zeilen
for i, line in enumerate(dtc_lines):
num, dtc_name, dtc_severity, title, description = line
dtc_macros.append(f"#define {dtc_name:<30} {num}")
comma = "," if i < len(dtc_lines) - 1 else " "
dtc_structs.append(f" {{ {dtc_name:<30}, {dtc_severity:<12} }}{comma} // {description}")
dtc_table_data.append({"num": num, "title": title, "description": description})
# Unix-Zeitstempel hinzufügen
timestamp = int(time.time())
env = Environment(loader=FileSystemLoader('codegen/templates', encoding='utf-8'))
# Lade das Jinja2-Template aus der Datei
template = env.get_template('dtc_defs.h.j2')
# Erstelle ein Context-Dictionary mit den erforderlichen Daten
context = {
'timestamp_unix': timestamp,
'timestamp' : time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp)),
'date' : time.strftime('%d.%m.%Y', time.localtime(timestamp)),
'dtc_macros': dtc_macros, # Übergebe die dtc_macros-Liste direkt
'dtc_structs': dtc_structs, # Übergebe die dtc_structs-Liste direkt
}
# Rendere das Template mit den Werten und erhalte den Header-Text
header_text = template.render(context)
# Schreibe den generierten Header-Text in die Header-Datei
with open(output_file, "w", encoding='utf-8') as f:
f.write(header_text)
print(f"Header-Datei wurde erstellt: {output_file}")
# JSON-Datei mit UTF-8-Zeichencodierung erstellen
with open(json_output_file, 'w', encoding='utf-8') as json_f:
json.dump(dtc_table_data, json_f, ensure_ascii=False, indent=4, separators=(',', ': '))
print(f"JSON-Datei wurde erstellt: {json_output_file}")

View File

@ -0,0 +1,9 @@
Import("env") # pylint: disable=undefined-variable
env.Execute("\"$PYTHONEXE\" -m pip install jinja2")
env.Replace(PROGNAME="firmware_pcb_1.%s.fw" % env.GetProjectOption("custom_pcb_revision"))
import struct2json
import dtcs
struct2json.struct2json()
dtcs.build_dtcs()

View File

@ -0,0 +1,93 @@
import os
import time
from jinja2 import Environment, FileSystemLoader
import re
# Pfad zur Eingabedatei und Ausgabedatei
input_file = "include/config.h"
output_sourcefile = "src/struct2json.cpp"
output_headerfile = "include/struct2json.h"
# Liste der zu suchenden Variablen/Structs
variable_names = ['LubeConfig', 'PersistenceData']
def get_types(file_content, variable_names):
result = {}
# Entferne Kommentare, um unerwünschte Störungen zu vermeiden
file_content = re.sub(r'\/\*.*?\*\/', '', file_content, flags=re.DOTALL)
file_content = re.sub(r'\/\/.*', '', file_content)
for var_name in variable_names:
# Erstelle ein reguläres Ausdrucksmuster, um den Typ der Variable zu extrahieren
pattern = re.compile(r'\b(?:extern\s+)?(\w+)\s+' + re.escape(var_name) + r'\s*;')
match = pattern.search(file_content)
if match:
# Extrahiere den Typ aus dem Treffer
type_match = match.group(1)
result[var_name] = type_match
return result
def extract_struct_fields(file_content, variable_types):
result = {}
# Entferne Kommentare, um unerwünschte Störungen zu vermeiden
file_content = re.sub(r'\/\*.*?\*\/', '', file_content, flags=re.DOTALL)
file_content = re.sub(r'\/\/.*', '', file_content)
for var_name, var_type in variable_types.items():
# Erstelle ein reguläres Ausdrucksmuster, um das Strukturfeld zu extrahieren
pattern = re.compile(r'typedef\s+struct\s*{([^}]*)}\s*' + re.escape(var_type) + r'\s*;')
match = pattern.search(file_content)
if match:
# Extrahiere die Felder aus dem Treffer
fields_match = re.findall(r'\b(\w+)\s+(\w+)\s*;', match.group(1))
if fields_match:
result[var_name] = {'type': var_type, 'fields': {field_name: field_type for field_type, field_name in fields_match}}
return result
def struct2json():
# Überprüfen, ob die Verzeichnisse existieren, andernfalls erstellen
output_dir_source = os.path.dirname(output_sourcefile)
if not os.path.exists(output_dir_source):
os.makedirs(output_dir_source)
output_dir_header = os.path.dirname(output_headerfile)
if not os.path.exists(output_dir_header):
os.makedirs(output_dir_header)
# Unix-Zeitstempel hinzufügen
timestamp = int(time.time())
# Parse structs
with open(input_file, 'r') as file:
content = file.read()
variable_types = get_types(content, variable_names)
structs = extract_struct_fields(content, variable_types)
env = Environment(loader=FileSystemLoader('codegen/templates', encoding='utf-8'))
# Lade das Jinja2-Template aus der Datei
template_c = env.get_template('struct2json.cpp.j2')
template_h = env.get_template('struct2json.h.j2')
# Erstelle ein Context-Dictionary mit den erforderlichen Daten
context = {
'timestamp_unix': timestamp,
'timestamp' : time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp)),
'date' : time.strftime('%d.%m.%Y', time.localtime(timestamp)),
'structs': structs, # Übergebe die foo-Liste direkt
}
# Rendere das Template mit den Werten und erhalte den Source-Text
source_text = template_c.render(context)
header_text = template_h.render(context)
# Schreibe den generierten Source-Text in die Source-Dateien
with open(output_sourcefile, "w", encoding='utf-8') as f:
f.write(source_text)
with open(output_headerfile, "w", encoding='utf-8') as f:
f.write(header_text)
print(f"Source-Dateien wurde erstellt: {output_sourcefile}, {output_headerfile}")

View File

@ -10,7 +10,7 @@
* @note This file is auto-generated by a script on {{ timestamp }}.
*
* @author Marcel Peterkau
* @date 09.01.2024
* @date {{ date }}
*/
#ifndef DTC_DEFS_H

View File

@ -0,0 +1,23 @@
/**
* @file struct2json.cpp
*
* @brief Implementation file for converting structs to JSON objects.
*
* @note This file is auto-generated by a script on {{ timestamp }}.
*
* @author Marcel Peterkau
* @date {{ date }}
*/
#include "struct2json.h"
{% for var_name, var_info in structs.items() -%}
void generateJsonObject_{{ var_name }}(JsonObject& data)
{
{% for field_name, field_type in var_info['fields'].items() -%}
data["{{ field_name }}"] = {{ var_name }}.{{ field_name }};
{% endfor -%}
}
{% endfor %}

View File

@ -0,0 +1,24 @@
/**
* @file struct2json.h
*
* @brief Header file for converting structs to JSON objects.
*
* @note This file is auto-generated by a script on {{ timestamp }}.
*
* @author Marcel Peterkau
* @date {{ date }}
*/
#ifndef _STRUCT2JSON_H_
#define _STRUCT2JSON_H_
#include <Arduino.h>
#include <ArduinoJson.h>
#include "config.h"
{% for var_name, var_info in structs.items() -%}
void generateJsonObject_{{ var_name }}(JsonObject& data);
{% endfor %}
#endif /* _STRUCT2JSON_H_ */

View File

@ -0,0 +1,24 @@
/**
* @file struct2json.h
*
* @brief Header file for converting structs to JSON objects.
*
* @note This file is auto-generated by a script on 2024-01-09 20:41:57.
*
* @author Marcel Peterkau
* @date 09.01.2024
*/
#ifndef _STRUCT2JSON_H_
#define _STRUCT2JSON_H_
#include <Arduino.h>
#include <ArduinoJson.h>
#include "config.h"
void generateJsonObject_LubeConfig(JsonObject& data);
void generateJsonObject_PersistenceData(JsonObject& data);
#endif /* _STRUCT2JSON_H_ */

View File

@ -29,6 +29,7 @@
#include "dtc.h"
#include "common.h"
#include "debugger.h"
#include "struct2json.h"
void initWebUI();
void Webserver_Process();

View File

@ -28,7 +28,7 @@ upload_flags =
build_flags =
!python git_rev_macro.py
!python codegen/git_rev_macro.py
-DWIFI_SSID_CLIENT=${wifi_cred.wifi_ssid_client}
-DWIFI_PASSWORD_CLIENT=${wifi_cred.wifi_password_client}
-DADMIN_PASSWORD=${wifi_cred.admin_password}
@ -38,9 +38,8 @@ build_flags =
board_build.filesystem = littlefs
extra_scripts =
post:prepare_littlefs.py
pre:build_dtcs.py
pre:prepare_fwfiles.py
post:codegen/prepare_littlefs.py
pre:codegen/run_pre.py
monitor_filters = esp8266_exception_decoder
monitor_speed = 115200

View File

@ -1,3 +0,0 @@
Import("env")
env.Replace(PROGNAME="firmware_pcb_1.%s.fw" % env.GetProjectOption("custom_pcb_revision"))

View File

@ -0,0 +1,47 @@
/**
* @file struct2json.cpp
*
* @brief Implementation file for converting structs to JSON objects.
*
* @note This file is auto-generated by a script on 2024-01-09 20:41:57.
*
* @author Marcel Peterkau
* @date 09.01.2024
*/
#include "struct2json.h"
void generateJsonObject_LubeConfig(JsonObject& data)
{
data["EEPROM_Version"] = LubeConfig.EEPROM_Version;
data["DistancePerLube_Default"] = LubeConfig.DistancePerLube_Default;
data["DistancePerLube_Rain"] = LubeConfig.DistancePerLube_Rain;
data["tankCapacity_ml"] = LubeConfig.tankCapacity_ml;
data["amountPerDose_microL"] = LubeConfig.amountPerDose_microL;
data["TankRemindAtPercentage"] = LubeConfig.TankRemindAtPercentage;
data["PulsePerRevolution"] = LubeConfig.PulsePerRevolution;
data["TireWidth_mm"] = LubeConfig.TireWidth_mm;
data["TireWidthHeight_Ratio"] = LubeConfig.TireWidthHeight_Ratio;
data["RimDiameter_Inch"] = LubeConfig.RimDiameter_Inch;
data["DistancePerRevolution_mm"] = LubeConfig.DistancePerRevolution_mm;
data["BleedingPulses"] = LubeConfig.BleedingPulses;
data["SpeedSource"] = LubeConfig.SpeedSource;
data["GPSBaudRate"] = LubeConfig.GPSBaudRate;
data["CANSource"] = LubeConfig.CANSource;
data["LED_Mode_Flash"] = LubeConfig.LED_Mode_Flash;
data["LED_Max_Brightness"] = LubeConfig.LED_Max_Brightness;
data["LED_Min_Brightness"] = LubeConfig.LED_Min_Brightness;
data["checksum"] = LubeConfig.checksum;
}
void generateJsonObject_PersistenceData(JsonObject& data)
{
data["writeCycleCounter"] = PersistenceData.writeCycleCounter;
data["tankRemain_microL"] = PersistenceData.tankRemain_microL;
data["TravelDistance_highRes_mm"] = PersistenceData.TravelDistance_highRes_mm;
data["odometer_mm"] = PersistenceData.odometer_mm;
data["odometer"] = PersistenceData.odometer;
data["checksum"] = PersistenceData.checksum;
}

View File

@ -463,46 +463,14 @@ void WebServerEEJSON_Callback(AsyncWebServerRequest *request)
fwinfo["Git-Hash"] = buffer;
JsonObject config = json.createNestedObject("config");
config["EEPROM_Version"] = LubeConfig.EEPROM_Version;
config["DistancePerLube_Default"] = LubeConfig.DistancePerLube_Default;
config["DistancePerLube_Rain"] = LubeConfig.DistancePerLube_Rain;
config["tankCapacity_ml"] = LubeConfig.tankCapacity_ml;
config["amountPerDose_microL"] = LubeConfig.amountPerDose_microL;
config["TankRemindAtPercentage"] = LubeConfig.TankRemindAtPercentage;
config["PulsePerRevolution"] = LubeConfig.PulsePerRevolution;
config["TireWidth_mm"] = LubeConfig.TireWidth_mm;
config["TireWidthHeight_Ratio"] = LubeConfig.TireWidthHeight_Ratio;
config["RimDiameter_Inch"] = LubeConfig.RimDiameter_Inch;
config["DistancePerRevolution_mm"] = LubeConfig.DistancePerRevolution_mm;
config["BleedingPulses"] = LubeConfig.BleedingPulses;
config["SpeedSource"] = LubeConfig.SpeedSource;
config["SpeedSource_Str"] = SpeedSourceString[LubeConfig.SpeedSource];
config["GPSBaudRate"] = LubeConfig.GPSBaudRate;
config["GPSBaudRate_Str"] = GPSBaudRateString[LubeConfig.GPSBaudRate];
config["CANSource"] = LubeConfig.CANSource;
config["CANSource_Str"] = CANSourceString[LubeConfig.CANSource];
config["LED_Mode_Flash"] = LubeConfig.LED_Mode_Flash;
config["LED_Max_Brightness"] = LubeConfig.LED_Max_Brightness;
config["LED_Min_Brightness"] = LubeConfig.LED_Min_Brightness;
sprintf(buffer, "0x%08X", LubeConfig.checksum);
config["checksum"] = buffer;
generateJsonObject_LubeConfig(config);
JsonObject persis = json.createNestedObject("persis");
generateJsonObject_PersistenceData(persis);
JsonObject eepart = json.createNestedObject("eepart");
sprintf(buffer, "0x%04X", globals.eePersistanceAdress);
eepart["PersistanceAddress"] = buffer;
JsonObject persis = json.createNestedObject("persis");
persis["writeCycleCounter"] = PersistenceData.writeCycleCounter;
persis["tankRemain_microL"] = PersistenceData.tankRemain_microL;
persis["TravelDistance_highRes_mm"] = PersistenceData.TravelDistance_highRes_mm;
persis["odometer_mm"] = PersistenceData.odometer_mm;
persis["odometer"] = PersistenceData.odometer;
sprintf(buffer, "0x%08X", PersistenceData.checksum);
persis["checksum"] = buffer;
serializeJsonPretty(json, *response);
response->addHeader("Content-disposition", "attachment; filename=backup.ee.json");