optimized scripts

This commit is contained in:
proddy
2025-10-25 09:53:25 +02:00
parent a2823563bf
commit 0e4f6f4209
2 changed files with 146 additions and 123 deletions

View File

@@ -1,98 +1,111 @@
import fileinput import fileinput
import csv import csv
import sys
from itertools import groupby from itertools import groupby
from collections import defaultdict
# #
# This is used to build the contents of the `Modbus-Entity-Registers.md` file used in the emsesp.org documentation. # This is used to build the contents of the `Modbus-Entity-Registers.md` file used in the emsesp.org documentation.
# #
# static data
tag_to_tagtype = { def get_tag_type(modbus_block):
-1: "TAG_TYPE_NONE", """Convert modbus block number to tag type using lookup."""
0: "DEVICE_DATA", block = int(modbus_block)
1: "HC",
2: "HC",
3: "HC",
4: "HC",
5: "HC",
6: "HC",
7: "HC",
8: "HC",
9: "DHW",
10: "DHW",
11: "DHW",
12: "DHW",
13: "DHW",
14: "DHW",
15: "DHW",
16: "DHW",
17: "DHW",
18: "DHW",
19: "AHS",
20: "HS",
21: "HS",
22: "HS",
23: "HS",
24: "HS",
25: "HS",
26: "HS",
27: "HS",
28: "HS",
29: "HS",
30: "HS",
31: "HS",
32: "HS",
33: "HS",
34: "HS",
35: "HS"
}
# read entities csv from stdin # Handle special cases first
if block == -1:
return "TAG_TYPE_NONE"
if block == 0:
return "DEVICE_DATA"
if block == 19:
return "AHS"
# Use ranges for efficiency
if 1 <= block <= 8:
return "HC"
if 9 <= block <= 18:
return "DHW"
if 20 <= block <= 35:
return "HS"
if 36 <= block <= 50 or block == 52:
return "SRC"
# Default fallback
return "UNKNOWN"
def read_entities():
"""Read and parse CSV entities from stdin with error handling."""
entities = [] entities = []
try:
with fileinput.input() as f_input: with fileinput.input() as f_input:
entities_reader = csv.reader(f_input, delimiter=',', quotechar='"') entities_reader = csv.reader(f_input, delimiter=',', quotechar='"')
headers = next(entities_reader) headers = next(entities_reader)
for row in entities_reader: # Validate required headers
entity = {} required_headers = {'device name', 'device type', 'shortname', 'fullname',
for i, val in enumerate(row): 'type [options...] \\| (min/max)', 'uom', 'writeable',
entity[headers[i]] = val 'modbus block', 'modbus offset', 'modbus count', 'modbus scale factor'}
missing_headers = required_headers - set(headers)
if missing_headers:
raise ValueError(
f"Missing required headers: {missing_headers}")
for row_num, row in enumerate(entities_reader, start=2):
if len(row) != len(headers):
print(
f"Warning: Row {row_num} has {len(row)} columns, expected {len(headers)}", file=sys.stderr)
continue
entity = dict(zip(headers, row))
entities.append(entity) entities.append(entity)
except Exception as e:
print(f"Error reading CSV data: {e}", file=sys.stderr)
sys.exit(1)
def device_name_key(e): return e["device name"] return entities
def device_type_key(e): return e["device type"] def group_entities_by_device_type(entities):
"""Group entities by device type efficiently using defaultdict."""
grouped = defaultdict(list)
for entity in entities:
grouped[entity["device type"]].append(entity)
return grouped
def grouped_by(list, key): return groupby(sorted(list, key=key), key) def print_device_entities(device_name, device_entities):
"""Print device entities table using f-strings for better performance."""
print(f"### {device_name}")
# entities_by_device_type = grouped_by(entities, device_type_key)
def printDeviceEntities(device_name, device_entities):
print("### " + device_name)
print("| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |") print("| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |")
print("|-|-|-|-|-|-|-|-|-|") print("|-|-|-|-|-|-|-|-|-|")
for de in device_entities:
print("| " + de["shortname"] + " | " + de["fullname"] + " | " + de["type [options...] \\| (min/max)"] + " | " + de["uom"] + " | " + de["writeable"] + for entity in device_entities:
" | " + tag_to_tagtype[int(de["modbus block"])] + " | " + de["modbus offset"] + " | " + de["modbus count"] + " | " + de["modbus scale factor"] + " | ") tag_type = get_tag_type(entity["modbus block"])
print(f"| {entity['shortname']} | {entity['fullname']} | {entity['type [options...] \\| (min/max)']} | "
f"{entity['uom']} | {entity['writeable']} | {tag_type} | {entity['modbus offset']} | "
f"{entity['modbus count']} | {entity['modbus scale factor']} |")
print() print()
def printDeviceTypeDevices(device_type, devices): def print_device_type_devices(device_type, devices):
print("## Devices of type *" + device_type + "*") """Print all devices of a specific type."""
for device_name, device_entities in grouped_by(devices, device_name_key): print(f"## Devices of type *{device_type}*")
printDeviceEntities(device_name, device_entities)
# Group devices by name
device_groups = defaultdict(list)
for device in devices:
device_groups[device["device name"]].append(device)
for device_name, device_entities in device_groups.items():
print_device_entities(device_name, device_entities)
# write header def print_header():
"""Print the markdown document header."""
print("<!-- Use full browser width for this page, the tables are wide -->") print("<!-- Use full browser width for this page, the tables are wide -->")
print("<style>") print("<style>")
print(".md-grid {") print(".md-grid {")
@@ -107,16 +120,26 @@ print()
print(" This file has been auto-generated. Do not modify.") print(" This file has been auto-generated. Do not modify.")
print() print()
for device_type, devices in grouped_by(entities, device_type_key):
printDeviceTypeDevices(device_type, devices)
# def printGroupedData(groupedData): def main():
# for k, v in groupedData: """Main function to process entities and generate documentation."""
# # print("Group {} {}".format(k, list(v))) # Read entities from stdin
# print(k) entities = read_entities()
if not entities:
print("No entities found in input data.", file=sys.stderr)
sys.exit(1)
# Print header
print_header()
# Group entities by device type and process
grouped_entities = group_entities_by_device_type(entities)
# Print documentation for each device type
for device_type, devices in grouped_entities.items():
print_device_type_devices(device_type, devices)
# printGroupedData(grouped_entities) if __name__ == "__main__":
main()
# for e in entities:
# print(e)

View File

@@ -158,15 +158,15 @@ cpp_entry_template = Template(
# read translations # read translations
listNames = {} listNames = {}
transre = re.compile(r'^MAKE_TRANSLATION\(([^,\s]+)\s*,\s*\"([^\"]+)\"') transre = re.compile(r'^MAKE_TRANSLATION\(([^,\s]+)\s*,\s*\"([^\"]+)\"')
transf = open('./src/core/locale_translations.h', 'r') try:
while True: with open('./src/core/locale_translations.h', 'r') as transf:
line = transf.readline() for line in transf:
if not line:
break
m = transre.match(line) m = transre.match(line)
if m is not None: if m is not None:
listNames[m.group(2)] = m.group(1) listNames[m.group(2)] = m.group(1)
transf.close() except FileNotFoundError:
# Handle case where file doesn't exist
pass
entities = [] entities = []
@@ -175,10 +175,8 @@ with fileinput.input() as f_input:
headers = next(entities_reader) headers = next(entities_reader)
for row in entities_reader: for row in entities_reader:
entity = {} # Use dict comprehension for better performance
for i, val in enumerate(row): entity = {headers[i]: val for i, val in enumerate(row)}
# print(headers[i] + ": " + val)
entity[headers[i]] = val
entities.append(entity) entities.append(entity)
# print(json.dumps(entities, indent=" ")) # print(json.dumps(entities, indent=" "))
@@ -234,30 +232,28 @@ for entity in entities:
for device_type_name, device_type in device_types.items(): for device_type_name, device_type in device_types.items():
for tag, entities in device_type.items(): for tag, entities in device_type.items():
total_registers = 0 # Pre-calculate all register info to avoid repeated int() conversions
register_info = []
next_free_offset = 0 next_free_offset = 0
for entity_name, modbus_info in entities.items(): for entity_name, modbus_info in entities.items():
register_offset = int(modbus_info['modbus offset']) register_offset = int(modbus_info['modbus offset'])
register_count = int(modbus_info['modbus count']) register_count = int(modbus_info['modbus count'])
total_registers += register_count register_info.append(
(entity_name, modbus_info, register_offset, register_count))
if register_offset >= 0 and register_offset + register_count > next_free_offset: if register_offset >= 0 and register_offset + register_count > next_free_offset:
next_free_offset = register_offset + register_count next_free_offset = register_offset + register_count
# print(device_type_name + "/" + tag + ": total_registers=" + str(total_registers) + "; next_free_offset=" + str( # Assign registers for unassigned entities
# next_free_offset)) for entity_name, modbus_info, register_offset, register_count in register_info:
for entity_name, modbus_info in entities.items():
register_offset = int(modbus_info['modbus offset'])
register_count = int(modbus_info['modbus count'])
if register_offset < 0 and register_count > 0: if register_offset < 0 and register_count > 0:
# assign register
# print("assign " + entity_name + " -> " + str(next_free_offset))
modbus_info['modbus offset'] = str(next_free_offset) modbus_info['modbus offset'] = str(next_free_offset)
next_free_offset += register_count next_free_offset += register_count
# OUTPUT # OUTPUT
cpp_entries = "" cpp_entries = []
# traverse all elements in correct order so they are correctly sorted # traverse all elements in correct order so they are correctly sorted
for device_type_name in device_type_names: for device_type_name in device_type_names:
@@ -267,18 +263,22 @@ for device_type_name in device_type_names:
tag = str(ntag) tag = str(ntag)
if tag in device_type: if tag in device_type:
entities = device_type[tag] entities = device_type[tag]
for entity_name, modbus_info in sorted(entities.items(), key=lambda x: int(x[1]["modbus offset"])): # Sort once and reuse
sorted_entities = sorted(
entities.items(), key=lambda x: int(x[1]["modbus offset"]))
for entity_name, modbus_info in sorted_entities:
params = { params = {
'devtype': "dt::" + device_type_name, 'devtype': "dt::" + device_type_name,
# re.sub(r"[0-9]+", "*", tag),
"tagtype": tag_to_tagtype[int(tag)], "tagtype": tag_to_tagtype[int(tag)],
"shortname": 'FL_(' + listNames[entity_name] + ")", "shortname": 'FL_(' + listNames[entity_name] + ")",
"entity_name": entity_name, "entity_name": entity_name,
'registeroffset': modbus_info["modbus offset"], 'registeroffset': modbus_info["modbus offset"],
'registercount': modbus_info["modbus count"] 'registercount': modbus_info["modbus count"]
} }
# print(entitypath + ": " + str(modbus_info)) cpp_entries.append(cpp_entry_template.substitute(params))
cpp_entries += cpp_entry_template.substitute(params)
cpp_src = cpp_file_template.substitute({'entries': cpp_entries}) # Join all entries at once
cpp_entries_str = "".join(cpp_entries)
cpp_src = cpp_file_template.substitute({'entries': cpp_entries_str})
print(cpp_src) print(cpp_src)