From 849cbc1ddfadb21310f249f6ed4c1c55738ef299 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Tue, 14 Apr 2026 14:55:31 +1000 Subject: [PATCH 1/4] scripts: tdf_decoder_build: format Automatically format the script according to `ruff`. Signed-off-by: Jordan Yates --- scripts/tdf_decoder_build.py | 112 +++++++++++++++++------------------ 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/scripts/tdf_decoder_build.py b/scripts/tdf_decoder_build.py index 45b1fd1..9f1d4cb 100644 --- a/scripts/tdf_decoder_build.py +++ b/scripts/tdf_decoder_build.py @@ -11,20 +11,21 @@ # C type: (rust_type, ::?) rust_type = { - 'char': ('u8', False), - 'int8_t': ('i8', False), - 'uint8_t': ('u8', False), - 'int16_t': ('i16', True), - 'uint16_t': ('u16', True), - 'int32_t': ('i32', True), - 'uint32_t': ('u32', True), - 'int64_t': ('i64', True), - 'uint64_t': ('u64', True), - 'float': ('f32', True), - 'float32_t': ('f32', True), - 'float64_t': ('f64', True), + "char": ("u8", False), + "int8_t": ("i8", False), + "uint8_t": ("u8", False), + "int16_t": ("i16", True), + "uint16_t": ("u16", True), + "int32_t": ("i32", True), + "uint32_t": ("u32", True), + "int64_t": ("i64", True), + "uint64_t": ("u64", True), + "float": ("f32", True), + "float32_t": ("f32", True), + "float64_t": ("f64", True), } + def decoders_gen(tdf_defs, output): env = Environment( loader=FileSystemLoader(pathlib.Path(__file__).parent), @@ -35,30 +36,30 @@ def decoders_gen(tdf_defs, output): tdf_template = env.get_template("tdf_decoder.rs.jinja") def field_conv_func(field, name_prefix=None): - t = rust_type[field['type']] + t = rust_type[field["type"]] func = f"cursor.read_{t[0]}" if t[1]: func += "::" func += "()?" - if c := field.get('conversion'): - if endian := c.get('int', None): - assert 'num' in field - assert t[0] == 'u8' - e = 'LittleEndian' if endian == 'little' else 'BigEndian' - if field['num'] == 3: - t = 'u24' - elif field['num'] == 6: - t = 'u48' + if c := field.get("conversion"): + if endian := c.get("int", None): + assert "num" in field + assert t[0] == "u8" + e = "LittleEndian" if endian == "little" else "BigEndian" + if field["num"] == 3: + t = "u24" + elif field["num"] == 6: + t = "u48" else: raise RuntimeError("Unknown integer length") func = f"cursor.read_{t}::<{e}>()?" - del field['num'] + del field["num"] - if 'm' in c or 'c' in c: - func += ' as f64' - if 'm' in c and c['m'] != 0: - val = c['m'] + if "m" in c or "c" in c: + func += " as f64" + if "m" in c and c["m"] != 0: + val = c["m"] inverse_ratio = (1 / val).as_integer_ratio() # If number can be represented as a whole number division, use that # instead for numerical stability (/ 10) is better than (* 0.1) as @@ -67,81 +68,78 @@ def field_conv_func(field, name_prefix=None): func += f" / {inverse_ratio[0]}.0" else: func += f" * {float_format(c['m'])}" - if 'c' in c and c['c'] != 0: + if "c" in c and c["c"] != 0: func += f" + {float_format(c['c'])}" - n = field['name'] + n = field["name"] if name_prefix is not None: n = f"{name_prefix}." + n - if 'num' in field: - if field['type'] == 'char': + if "num" in field: + if field["type"] == "char": return [(n, f"tdf_field_read_string(cursor, {field['num']})?")] else: - if field['num'] == 0: + if field["num"] == 0: return [(n, f"tdf_field_read_vla(cursor, cursor_start, size)?")] else: - return [(n + f'[{idx}]', func) for idx in range(field['num'])] + return [(n + f"[{idx}]", func) for idx in range(field["num"])] else: return [(n, func)] def field_fmt(field): - if field['type'] == 'char': + if field["type"] == "char": return ["{}"] - if 'display' in field and field['display'].get('fmt', '') == "hex": - if digits := field['display'].get('digits', None): + if "display" in field and field["display"].get("fmt", "") == "hex": + if digits := field["display"].get("digits", None): single = [f"0x{{:0{digits}x}}"] else: single = ["0x{:x}"] else: single = ["{}"] - if field.get('num', None) == 0: + if field.get("num", None) == 0: return ["{}"] - return single * field.get('num', 1) - + return single * field.get("num", 1) structs = {} struct_fmts = {} - for name, struct in tdf_defs['structs'].items(): + for name, struct in tdf_defs["structs"].items(): funcs = [] fmts = [] - for f in struct['fields']: + for f in struct["fields"]: funcs += field_conv_func(f) fmts += field_fmt(f) structs[f"struct {name}"] = funcs struct_fmts[f"struct {name}"] = fmts # Generate rust conversion functions - for tdf_id, info in tdf_defs['definitions'].items(): - info['rust_convs'] = [] + for tdf_id, info in tdf_defs["definitions"].items(): + info["rust_convs"] = [] fmt = [] - for f in info['fields']: - if f['type'] in structs: - info['rust_convs'] += structs[f['type']] - fmt += struct_fmts[f['type']] - elif f['type'] in rust_type: - info['rust_convs'] += field_conv_func(f) + for f in info["fields"]: + if f["type"] in structs: + info["rust_convs"] += structs[f["type"]] + fmt += struct_fmts[f["type"]] + elif f["type"] in rust_type: + info["rust_convs"] += field_conv_func(f) fmt += field_fmt(f) else: raise RuntimeError(f"Bad type '{f['type']}'") - info['rust_head'] = ",".join([f"\"{c[0]}\"" for c in info['rust_convs']]) - info['rust_fmt'] = ",".join(fmt) - + info["rust_head"] = ",".join([f'"{c[0]}"' for c in info["rust_convs"]]) + info["rust_fmt"] = ",".join(fmt) - tdf_output = pathlib.Path(output) / 'decoders.rs' + tdf_output = pathlib.Path(output) / "decoders.rs" with tdf_output.open("w") as f: f.write( tdf_template.render( - structs=tdf_defs["structs"], definitions=tdf_defs["definitions"] + structs=tdf_defs["structs"], definitions=tdf_defs["definitions"] ) ) f.write(os.linesep) + if __name__ == "__main__": - parser = argparse.ArgumentParser( - "Generate rust TDF decoders", allow_abbrev=False - ) + parser = argparse.ArgumentParser("Generate rust TDF decoders", allow_abbrev=False) parser.add_argument("--json", required=True, type=str, help="TDF json description") parser.add_argument("--out", required=True, type=str, help="Output folder") args = parser.parse_args() From dda092c5e9e1b31be254adfc16583b1cb610c158 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Tue, 14 Apr 2026 14:37:04 +1000 Subject: [PATCH 2/4] tdf: decoders: fix VLA string decoding Fix decoding VLA string outputs, instead of reading 0 bytes. Signed-off-by: Jordan Yates --- scripts/tdf_decoder.rs.jinja | 38 +++++++++++++++++++++----------- scripts/tdf_decoder_build.py | 9 ++++++-- tdf/src/decoders.rs | 42 +++++++++++++++++++++++------------- 3 files changed, 59 insertions(+), 30 deletions(-) diff --git a/scripts/tdf_decoder.rs.jinja b/scripts/tdf_decoder.rs.jinja index 2c0130f..719c53a 100644 --- a/scripts/tdf_decoder.rs.jinja +++ b/scripts/tdf_decoder.rs.jinja @@ -22,9 +22,29 @@ pub fn tdf_fields(tdf_id: &u16) -> Vec<&'static str> } } -fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, size: u8) -> Result +fn vla_bytes_remaining(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) -> Result { - let mut buf = vec![0u8; size as usize]; + let cursor_current = cursor.position(); + let cursor_read = cursor_current - cursor_start; + if cursor_read >= size as u64 { + return Result::Err(Error::new( + ErrorKind::InvalidData, + "Insufficient data remaining", + )); + } + let bytes_remaining = size as u64 - cursor_read; + + Ok(bytes_remaining as usize) +} + +fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, cursor_start: u64, num: u8, size: u8) -> Result +{ + let string_length = match num { + 0 => vla_bytes_remaining(cursor, cursor_start, size)?, + _ => size as usize, + }; + + let mut buf = vec![0u8; string_length]; cursor.read_exact(&mut buf)?; match String::from_utf8(buf) { @@ -35,16 +55,8 @@ fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, size: u8) -> Result, cursor_start: u64, size: u8) -> Result { - let cursor_current = cursor.position(); - let cursor_read = cursor_current - cursor_start; - if cursor_read >= size as u64 { - return Result::Err(Error::new( - ErrorKind::InvalidData, - "Insufficient data remaining", - )); - } - let bytes_remaining = size as u64 - cursor_read; - let mut buf = vec![0u8; bytes_remaining as usize]; + let bytes_remaining = vla_bytes_remaining(cursor, cursor_start, size)?; + let mut buf = vec![0u8; bytes_remaining]; cursor.read_exact(&mut buf)?; Ok(format!("{}", hex::encode(buf))) @@ -85,7 +97,7 @@ pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> // Handle read underflow (more data specified than expected) if underflow > 0 { - tdf_field_read_string(cursor, underflow as u8)?; + tdf_field_read_string(cursor, cursor_start, 0, underflow as u8)?; } res } diff --git a/scripts/tdf_decoder_build.py b/scripts/tdf_decoder_build.py index 9f1d4cb..99714ba 100644 --- a/scripts/tdf_decoder_build.py +++ b/scripts/tdf_decoder_build.py @@ -77,10 +77,15 @@ def field_conv_func(field, name_prefix=None): if "num" in field: if field["type"] == "char": - return [(n, f"tdf_field_read_string(cursor, {field['num']})?")] + return [ + ( + n, + f"tdf_field_read_string(cursor, cursor_start, {field['num']}, size)?", + ) + ] else: if field["num"] == 0: - return [(n, f"tdf_field_read_vla(cursor, cursor_start, size)?")] + return [(n, "tdf_field_read_vla(cursor, cursor_start, size)?")] else: return [(n + f"[{idx}]", func) for idx in range(field["num"])] else: diff --git a/tdf/src/decoders.rs b/tdf/src/decoders.rs index d91de03..c2d100e 100644 --- a/tdf/src/decoders.rs +++ b/tdf/src/decoders.rs @@ -134,9 +134,29 @@ pub fn tdf_fields(tdf_id: &u16) -> Vec<&'static str> } } -fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, size: u8) -> Result +fn vla_bytes_remaining(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) -> Result { - let mut buf = vec![0u8; size as usize]; + let cursor_current = cursor.position(); + let cursor_read = cursor_current - cursor_start; + if cursor_read >= size as u64 { + return Result::Err(Error::new( + ErrorKind::InvalidData, + "Insufficient data remaining", + )); + } + let bytes_remaining = size as u64 - cursor_read; + + Ok(bytes_remaining as usize) +} + +fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, cursor_start: u64, num: u8, size: u8) -> Result +{ + let string_length = match num { + 0 => vla_bytes_remaining(cursor, cursor_start, size)?, + _ => size as usize, + }; + + let mut buf = vec![0u8; string_length]; cursor.read_exact(&mut buf)?; match String::from_utf8(buf) { @@ -147,16 +167,8 @@ fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, size: u8) -> Result, cursor_start: u64, size: u8) -> Result { - let cursor_current = cursor.position(); - let cursor_read = cursor_current - cursor_start; - if cursor_read >= size as u64 { - return Result::Err(Error::new( - ErrorKind::InvalidData, - "Insufficient data remaining", - )); - } - let bytes_remaining = size as u64 - cursor_read; - let mut buf = vec![0u8; bytes_remaining as usize]; + let bytes_remaining = vla_bytes_remaining(cursor, cursor_start, size)?; + let mut buf = vec![0u8; bytes_remaining]; cursor.read_exact(&mut buf)?; Ok(format!("{}", hex::encode(buf))) @@ -215,7 +227,7 @@ pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> cursor.read_u32::()?, cursor.read_u32::()?, cursor.read_u32::()?, - tdf_field_read_string(cursor, 8)?, + tdf_field_read_string(cursor, cursor_start, 8, size)?, )), 7 => Ok(format!( @@ -532,7 +544,7 @@ pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> Ok(format!( "{},{}", cursor.read_u32::()?, - tdf_field_read_string(cursor, 0)?, + tdf_field_read_string(cursor, cursor_start, 0, size)?, )), 44 => Ok(format!( @@ -655,7 +667,7 @@ pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> // Handle read underflow (more data specified than expected) if underflow > 0 { - tdf_field_read_string(cursor, underflow as u8)?; + tdf_field_read_string(cursor, cursor_start, 0, underflow as u8)?; } res } From 42e7b438de085f726db9d3fab16cde7d350670a9 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Tue, 14 Apr 2026 14:51:50 +1000 Subject: [PATCH 3/4] tdf: decoders: update to latest Pull in latest `tdf.json`. Signed-off-by: Jordan Yates --- scripts/tdf.json | 31 ++++++++++++++++++++++++++----- tdf/src/decoders.rs | 12 ++++++++++-- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/scripts/tdf.json b/scripts/tdf.json index c6ca8d0..d01316b 100644 --- a/scripts/tdf.json +++ b/scripts/tdf.json @@ -182,7 +182,7 @@ { "name": "type", "type": "uint8_t", - "description": "Address type (0 == Public, 1 == Random)" + "description": "Address type (0 = Public, 1 = Random)" }, { "name": "val", @@ -211,7 +211,7 @@ "digits": 12 }, "conversion": { - "int": "little" + "int": "big" }, "description": "Address bytes" } @@ -322,7 +322,7 @@ { "name": "flags", "type": "uint8_t", - "description": "Flags (BIT(0) == SD blocks)", + "description": "Flags (BIT(0) = SD blocks)", "display": { "fmt": "hex", "digits": 2 @@ -559,7 +559,7 @@ { "name": "flags", "type": "uint8_t", - "description": "Flags (BIT(0) == SD blocks)", + "description": "Flags (BIT(0) = SD blocks, BIT(7) = Shipping)", "display": { "fmt": "hex", "digits": 2 @@ -1244,7 +1244,7 @@ "display": { "postfix": "B/sec" }, - "description": "Data throughput (-1 == disconnected)" + "description": "Data throughput (-1 = disconnected)" } ] }, @@ -1622,6 +1622,10 @@ { "name": "infuse_id", "type": "uint64_t", + "display": { + "fmt": "hex", + "digits": 16 + }, "description": "Infuse-IoT ID of remote device" }, { @@ -1938,6 +1942,23 @@ "description": "Right channel sample" } ] + }, + "61": { + "name": "KVS_VALUE_CHANGED", + "description": "Record of key value store data updates", + "fields": [ + { + "name": "key", + "type": "uint16_t", + "description": "KV Store key identifier" + }, + { + "name": "value", + "type": "uint8_t", + "num": 0, + "description": "New data value, empty for delete, '*' for write-only" + } + ] } } } \ No newline at end of file diff --git a/tdf/src/decoders.rs b/tdf/src/decoders.rs index c2d100e..1694adf 100644 --- a/tdf/src/decoders.rs +++ b/tdf/src/decoders.rs @@ -64,6 +64,7 @@ pub fn tdf_name(tdf_id: &u16) -> String 58 => String::from("PCM_16BIT_CHAN_LEFT"), 59 => String::from("PCM_16BIT_CHAN_RIGHT"), 60 => String::from("PCM_16BIT_CHAN_DUAL"), + 61 => String::from("KVS_VALUE_CHANGED"), _ => format!("{}", tdf_id), } } @@ -130,6 +131,7 @@ pub fn tdf_fields(tdf_id: &u16) -> Vec<&'static str> 58 => vec!["val"], 59 => vec!["val"], 60 => vec!["left","right"], + 61 => vec!["key","value"], _ => vec!["unknown"], } } @@ -477,7 +479,7 @@ pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> 35 => Ok(format!( "0x{:012x},{},{}", - cursor.read_u48::()?, + cursor.read_u48::()?, cursor.read_u8()?, cursor.read_i8()?, )), @@ -521,7 +523,7 @@ pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> )), 39 => Ok(format!( - "{},{}", + "0x{:016x},{}", cursor.read_u64::()?, cursor.read_i8()?, )), @@ -646,6 +648,12 @@ pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> cursor.read_i16::()?, cursor.read_i16::()?, )), + 61 => + Ok(format!( + "{},{}", + cursor.read_u16::()?, + tdf_field_read_vla(cursor, cursor_start, size)?, + )), _ => { let mut buf = vec![0; size as usize]; cursor.read_exact(&mut buf)?; From b3b21cb7c0c024f6e0dcb4d0f54015647b131828 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Tue, 14 Apr 2026 14:52:43 +1000 Subject: [PATCH 4/4] Cargo.toml: `v1.7.0` - Add button to open output folder in system viewer - Fix decoding of ANNOTATION TDF - Update TDF definitions Signed-off-by: Jordan Yates --- CHANGELOG.md | 4 +++- Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fe9b6f..3ba9cd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org). -## [1.7.0] - 2026-xx-xx +## [1.7.0] - 2026-04-14 - Add button to open output folder in system viewer + - Fix decoding of ANNOTATION TDF + - Update TDF definitions ## [1.6.0] - 2026-01-16 diff --git a/Cargo.lock b/Cargo.lock index 8c1780e..88285dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2082,7 +2082,7 @@ dependencies = [ [[package]] name = "infuse_decoder" -version = "1.6.0" +version = "1.7.0" dependencies = [ "blocks", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index 5186b68..3c8dcf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "infuse_decoder" -version = "1.6.0" +version = "1.7.0" edition = "2024" [[bin]]