SystemK v1.00 (#9)

This change cleans up the final bits before the release of SystemK version 1.00.

It implements the [KTag Beacon Specification v0.12](https://ktag.clubk.club/Technology/BLE/KTag%20Beacon%20Specification%20v0.12.pdf).

Co-authored-by: Joe Kearney <joe@clubk.club>
Reviewed-on: #9
This commit is contained in:
Joe Kearney 2025-11-30 21:20:05 +00:00
parent 6d8dab53e0
commit f80cb59828
82 changed files with 300 additions and 132 deletions

View file

@ -0,0 +1,220 @@
#!/usr/bin/env python3
"""
SystemK settings code generator.
This program source code file is part of SystemK, a library in the KTag project.
🛡 https://ktag.clubk.club
Copyright © 2025 Joseph P. Kearney and the KTag developers.
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
A copy of the GNU Affero General Public License should be available in the
LICENSE file in the root of this repository. If not, see http://www.gnu.org/licenses/.
"""
import csv
from typing import List, Dict
def parse_csv(filename: str) -> List[Dict[str, str]]:
"""Parse the settings CSV file."""
settings = []
with open(filename, "r") as f:
reader = csv.DictReader(f)
for row in reader:
settings.append(row)
return settings
def generate_get_function(settings: List[Dict[str, str]], datatype: str) -> str:
"""Generate the SETTINGS_get function for a specific datatype."""
func_name = f"SETTINGS_get_{datatype}"
code = f"""SystemKResult_T {func_name}(SystemKSettingID_T id, {datatype} *value)
{{
SystemKResult_T result = SYSTEMK_RESULT_UNSPECIFIED_FAILURE;
switch (id)
{{
"""
for setting in settings:
if setting["Type"] != datatype:
continue
setting_id = setting["Setting ID"]
key = setting["Key"]
cache_var = f"Cached_{key}"
code += f""" case {setting_id}:
*value = {cache_var};
result = SYSTEMK_RESULT_SUCCESS;
break;
"""
code += """ default:
result = SYSTEMK_RESULT_WRONG_DATATYPE;
break;
}
return result;
}
"""
return code
def generate_set_function(settings: List[Dict[str, str]], datatype: str) -> str:
"""Generate the SETTINGS_set function for a specific datatype."""
func_name = f"SETTINGS_set_{datatype}"
kv_func = f"KV_Set_Value_{datatype.replace('_t', '')}"
code = f"""SystemKResult_T {func_name}(SystemKSettingID_T id, {datatype} value)
{{
SystemKResult_T result = SYSTEMK_RESULT_SUCCESS;
switch (id)
{{
"""
for setting in settings:
if setting["Type"] != datatype:
continue
setting_id = setting["Setting ID"]
key = setting["Key"]
cache_var = f"Cached_{key}"
code += f""" case {setting_id}:
{cache_var} = value;
result = {kv_func}(CONFIG_FILE, "{key}", &value);
break;
"""
code += """ default:
result = SYSTEMK_RESULT_WRONG_DATATYPE;
break;
}
return result;
}
"""
return code
def generate_initialize_function(settings: List[Dict[str, str]]) -> str:
"""Generate the Initialize_Settings function."""
code = """SystemKResult_T Initialize_Settings(void)
{
SystemKResult_T result = SYSTEMK_RESULT_SUCCESS;
SystemKResult_T temp_result;
"""
# Group settings by datatype.
datatypes = set(s["Type"] for s in settings)
for datatype in sorted(datatypes):
code += f" {datatype} value_{datatype};\n"
code += """
KLOG_INFO(TAG, "Initializing settings...");
"""
# Generate initialization code for each setting.
for setting in settings:
setting_id = setting["Setting ID"]
datatype = setting["Type"]
key = setting["Key"]
default_value = setting["Default Value"]
code += f""" // Initialize {key}
temp_result = KV_Get_Value_{datatype.replace('_t', '')}(CONFIG_FILE, "{key}", &value_{datatype});
if (temp_result != SYSTEMK_RESULT_SUCCESS)
{{
// Try to get from default config
temp_result = KV_Get_Value_{datatype.replace('_t', '')}(DEFAULT_CONFIG_FILE, "{key}", &value_{datatype});
if (temp_result != SYSTEMK_RESULT_SUCCESS)
{{
// Use hardcoded default
value_{datatype} = {default_value};
KLOG_WARN(TAG, "{key} not found in config files; using default value of {default_value}.");
}}
// Save to config file
temp_result = KV_Set_Value_{datatype.replace('_t', '')}(CONFIG_FILE, "{key}", &value_{datatype});
if (temp_result != SYSTEMK_RESULT_SUCCESS)
{{
result = temp_result;
KLOG_ERROR(TAG, "Failed to set {key} in config file.");
}}
}}
"""
# Initialize the cache variable
cache_var = f"Cached_{key}"
code += f" {cache_var} = value_{datatype};\n"
code += "\n"
code += """ xEventGroupSetBits(Get_System_Events(), SYS_SETTINGS_READY);
KLOG_INFO(TAG, "Settings initialized.");
return result;
}
"""
return code
def generate_cache_declarations(settings: List[Dict[str, str]]) -> str:
"""Generate static cache variable declarations initialized to defaults."""
code = ""
# Generate cache variable declarations for all settings
for setting in settings:
datatype = setting["Type"]
key = setting["Key"]
description = setting["Description"]
default_value = setting["Default Value"]
cache_var = f"Cached_{key}"
code += f"// {description}\n"
code += f"static {datatype} {cache_var} = {default_value};\n"
return code
def main():
"""Main function to generate all settings code."""
settings = parse_csv("Settings.csv")
# Get unique datatypes
datatypes = sorted(set(s["Type"] for s in settings))
print("// Cache variable declarations")
print(generate_cache_declarations(settings))
print()
# Generate get and set functions for each datatype
for datatype in datatypes:
print(generate_get_function(settings, datatype))
print()
print(generate_set_function(settings, datatype))
print()
# Generate initialize function
print(generate_initialize_function(settings))
if __name__ == "__main__":
main()