mirror of https://github.com/openXC7/prjxray.git
232 lines
6.0 KiB
Python
Executable File
232 lines
6.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2017-2020 The Project X-Ray Authors.
|
|
#
|
|
# Use of this source code is governed by a ISC-style
|
|
# license that can be found in the LICENSE file or at
|
|
# https://opensource.org/licenses/ISC
|
|
#
|
|
# SPDX-License-Identifier: ISC
|
|
"""
|
|
This is a db fixup script that does two things:
|
|
|
|
1. Clears (removes) all bits found in "IN_USE" tag(s) and removes the IN_USE
|
|
tag itself
|
|
|
|
2. Reads tag group definition from a file and applies the tag grouping. First
|
|
a set of common bits for each group is found (logical OR among all tags
|
|
belonging to the group). Then in each tag belonging to the group those
|
|
bits are set to 0 but only if they are not already present there.
|
|
|
|
The resulting data is written into a segbits file.
|
|
"""
|
|
import argparse
|
|
import re
|
|
import itertools
|
|
|
|
# =============================================================================
|
|
|
|
|
|
def load_tag_groups(file_name):
|
|
"""
|
|
Loads tag groups from a text file.
|
|
|
|
A tag group is defined by specifying a space separated list of tags within
|
|
a single line. Lines that are empty or start with '#' are ignored.
|
|
"""
|
|
tag_groups = []
|
|
|
|
# Load tag group specifications
|
|
with open(file_name, "r") as fp:
|
|
for line in fp:
|
|
line = line.strip()
|
|
|
|
if len(line) == 0 or line.startswith("#"):
|
|
continue
|
|
|
|
group = set(line.split())
|
|
if len(group):
|
|
tag_groups.append(group)
|
|
|
|
# Check if all tag groups are exclusive
|
|
for tag_group_a, tag_group_b in itertools.combinations(tag_groups, 2):
|
|
|
|
tags = tag_group_a & tag_group_b
|
|
if len(tags):
|
|
raise RuntimeError(
|
|
"Tag(s) {} are present in multiple groups".format(
|
|
" ".join(tags)))
|
|
|
|
return tag_groups
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
def parse_bit(bit):
|
|
"""
|
|
Decodes string describing a bit. Returns a tuple (frame, bit, value)
|
|
"""
|
|
match = re.match("^(!?)([0-9]+)_([0-9]+)$", bit)
|
|
assert match != None, bit
|
|
|
|
val = int(match.group(1) != "!")
|
|
frm = int(match.group(2))
|
|
bit = int(match.group(3))
|
|
|
|
return frm, bit, val
|
|
|
|
|
|
def bit_to_str(bit):
|
|
"""
|
|
Converts a tuple (frame, bit, value) to its string representation.
|
|
"""
|
|
s = "!" if not bit[2] else ""
|
|
return "{}{}_{:02d}".format(s, bit[0], bit[1])
|
|
|
|
|
|
def load_segbits(file_name):
|
|
"""
|
|
Loads a segbits file.
|
|
"""
|
|
|
|
segbits = {}
|
|
|
|
with open(file_name, "r") as fp:
|
|
for line in fp:
|
|
line = line.strip()
|
|
fields = line.split()
|
|
|
|
if len(fields) < 2:
|
|
raise RuntimeError("Malformed line: '%s'" % line)
|
|
|
|
tag = fields[0]
|
|
|
|
if "<" in line or ">" in line:
|
|
segbits[tag] = set()
|
|
|
|
else:
|
|
bits = set([parse_bit(bit) for bit in fields[1:]])
|
|
segbits[tag] = bits
|
|
|
|
return segbits
|
|
|
|
|
|
def save_segbits(file_name, segbits):
|
|
"""
|
|
Save segbits to a .db or .rdb file
|
|
"""
|
|
|
|
with open(file_name, "w") as fp:
|
|
for tag, bits in segbits.items():
|
|
if not len(bits):
|
|
continue
|
|
|
|
line = tag + " "
|
|
line += " ".join([bit_to_str(bit) for bit in sorted(list(bits))])
|
|
fp.write(line + "\n")
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
def mask_out_bits(segbits, mask, tags_to_mask=None):
|
|
"""
|
|
Given a set of bits and a list of tags to affect (optional) removes all
|
|
the bits from each tag that are present (and equal) in the masking set.
|
|
"""
|
|
|
|
if tags_to_mask is None:
|
|
tags_to_mask = segbits.keys()
|
|
|
|
# Mask out matching bits
|
|
for tag in tags_to_mask:
|
|
bits = segbits[tag]
|
|
|
|
bits = set(bits) - set(mask)
|
|
segbits[tag] = bits
|
|
|
|
return segbits
|
|
|
|
|
|
def find_common_bits_for_tag_groups(segbits, tag_groups):
|
|
"""
|
|
For each tag group finds a common set of bits that have value of one.
|
|
"""
|
|
|
|
bit_groups = []
|
|
|
|
for tag_group in tag_groups:
|
|
bit_group = set()
|
|
|
|
for tag, bits in segbits.items():
|
|
if tag in tag_group:
|
|
ones = set([b for b in bits if b[2]])
|
|
bit_group |= ones
|
|
|
|
bit_groups.append(bit_group)
|
|
|
|
return bit_groups
|
|
|
|
|
|
def group_tags(segbits, tag_groups, bit_groups):
|
|
"""
|
|
Implements tag grouping. If a tag belongs to a group then the common bits
|
|
of that group are added to is as zeros.
|
|
"""
|
|
|
|
for tag_group, bit_group in zip(tag_groups, bit_groups):
|
|
zeros = set([(b[0], b[1], 0) for b in bit_group])
|
|
for tag in tag_group:
|
|
|
|
# Insert zero bits
|
|
if tag in segbits.keys():
|
|
bits = segbits[tag]
|
|
for z in zeros:
|
|
if not (z[0], z[1], 1) in bits:
|
|
bits.add(z)
|
|
|
|
return segbits
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
def main():
|
|
|
|
# Parse arguments
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("-i", required=True, type=str, help="Input .rdb file")
|
|
parser.add_argument(
|
|
"-g", required=True, type=str, help="Input tag group definition file")
|
|
parser.add_argument("-o", required=True, type=str, help="Output .rdb file")
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Load tag groups
|
|
tag_groups = load_tag_groups(args.g)
|
|
|
|
# Load raw database file
|
|
segbits = load_segbits(args.i)
|
|
|
|
# Mask out bits present in "IN_USE" tag(s) as they are common to other tags
|
|
for tag in segbits.keys():
|
|
if tag.endswith("IN_USE"):
|
|
prefix = tag[:tag.index("IN_USE")]
|
|
tags_to_mask = [t for t in segbits.keys() if t.startswith(prefix)]
|
|
mask_out_bits(segbits, segbits[tag], tags_to_mask)
|
|
|
|
# Find common bits
|
|
bit_groups = find_common_bits_for_tag_groups(segbits, tag_groups)
|
|
# Apply tag grouping
|
|
segbits = group_tags(segbits, tag_groups, bit_groups)
|
|
|
|
# Save fixed database file
|
|
save_segbits(args.o, segbits)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|