Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor decoding, part 2 #265

Merged
merged 7 commits into from
Feb 13, 2024
Merged

Conversation

andlaus
Copy link
Collaborator

@andlaus andlaus commented Feb 9, 2024

The approach taken in this PR will serve as the template for all other objects which can be decoded (i.e., DOPs and parameters): decoding is done by a decode_from_pdu() method which takes a DecodeState object as their lone argument and returns the decoded physical value and the passed DecodeState is supposed to be changed so that it is set up to decode the next object after calling decode_from_pdu().

IMO, this substantially reduces the mental load required to understand the decoding logic, because before this, parameters, diag coded types, and DOPs all used identical names for the decoding functions but they behaved slightly different in terms of the parameters which they took and the types they returned...

Andreas Lauser <[email protected]>, on behalf of MBition GmbH.
Provider Information

these methods are now called `decode_from_pdu()` and are the template
for all other objects which can be decoded (i.e., DOPs and
parameters): they take a `DecodeState` object as their lone argument
and return the decoded physical value. Note that these methods are
supposed to change the passed `DecodeState` object so that it is set
up to decode the next object after calling `decode_from_pdu()`.

This reduces the mental load required because before this, parameters,
diag coded types, and DOPs all used the same name for functions which
do similar but slightly different things...

Signed-off-by: Andreas Lauser <[email protected]>
Signed-off-by: Alexander Walz <[email protected]>
…eState`

if no bit position is specified/applicable, decoding continues at
first bit of the byte pointed to by `cursor_position`

Signed-off-by: Andreas Lauser <[email protected]>
Signed-off-by: Alexander Walz <[email protected]>
@andlaus andlaus requested a review from kayoub5 February 9, 2024 12:44
odxtools/decodestate.py Outdated Show resolved Hide resolved
#:
#: If bit position is undefined (`None`), the object to be extracted
#: starts at bit 0.
cursor_bit_position: Optional[int] = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this can't be 0 by default

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the idea is that None means "not specified" while 0 indicates that it was explicitly set to 0 and thus code where specifying a bit position is not allowed can error out even if 0 was specified. (with this approach, code where bit positions are allowed needs to use decode_state.cursor_bit_position or 0, which is not too bad IMO...)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any case where None and 0 would have different logic?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all parameters which do not accept bit positions, e.g. structures, muxes, value parameters with non-integer values, etc...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of those either require bit position to be non specified or 0

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

point taken & code changed. I wanted to be able to check if a bit position is set to 0 where it is not allowed to be defined in the decoding logic, but I finally saw the light and concluded that this is the wrong abstraction level to check this...

return value, byte_pos

decode_state.cursor_position = byte_pos
decode_state.cursor_bit_position = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a goto(byte_pos, bit_pos = 0) function to decode_state ?

Copy link
Collaborator Author

@andlaus andlaus Feb 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this as a prototype, but I was not really happy with it because it obfuscated what was happening too much. That said, how do you interpret the following sentence from section 7.3.5.2 of the ASAM version of the spec:

The parameter starts then at the byte edge next to the end of the last parameter.

does "last parameter" mean
a) the currently decoded parameter in the list of parameters
b) the parameter which encompasses the byte of the PDU furthest to the back which has been seen so far
c) the parameter specified last in the list

IMO, (c) does not make sense, we currently use interpretation (b), but the more I think about it, (a) makes more sense (and, as a bonus, is easier to implement)...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both A and B are valid interpretations given how vague the spec is, and would give identical results given sane enough PDX file that does order the parameters by desired position.

That being said, the specs didn't use the term "previous parameter" which is more common, making option B more likely.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay. would you support switching the interpretation to option (a) in the future anyway? (it simplifies things considerably...)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not seen a PDX so far, where choosing A or B matters, if I ran into issues I will let you know.

browse.run(browse_args)
with self.assertRaises(SystemError):
# browse can only be run interactively
browse.run(browse_args)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe mock the interactive part for testing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea. I tested to implement this, but the assumption that sys.stdout is a tty is burried too deeply within PyInquirer. (there are plenty of asserts and probably quite a few implicit assumptions...)

That said, I've noticed that this test stopped to ask for user input when PyInquirer is available and it is run in "bare metal mode" (without using pytest), so I mocked sys.stdout.isatty() to make it reliably bail out...

this should make it clear what kind of position is meant.

thanks to [at]kayoub5 for the suggestion.

Signed-off-by: Andreas Lauser <[email protected]>
Signed-off-by: Alexander Walz <[email protected]>
@andlaus andlaus force-pushed the refactor_decoding2 branch 2 times, most recently from c93ae9e to 88bee93 Compare February 12, 2024 18:16
if something went wrong while loading a CLI tool, the backtrace can be
accessed via `odxtools $TOOL --help`.

Signed-off-by: Andreas Lauser <[email protected]>
Signed-off-by: Alexander Walz <[email protected]>
rather "work" (mind the quotes): If pyinquirer is working and this was
called "bare metal" (i.e., not via `pytest`), the `browse` tool
stopped to ask the user for input. Since this is undesireable for a
unit test, we now mock `sys.stdout.isatty()` to always return `False`.

Signed-off-by: Andreas Lauser <[email protected]>
Signed-off-by: Alexander Walz <[email protected]>
i.e., it is now always an integer, never `None`. This looses us the
ability of distinguishing between "no bit position defined" and "bit
position set to 0" in the decoding machinery, but this is not too
relevant as the ODX spec says that, the bit position ought to be
assumed 0 if it is not defined and policing if it is allowed to be
defined or not ought not should not be part of the decoding logic.

thanks to [at]kayoub5 for his persistence!

Signed-off-by: Andreas Lauser <[email protected]>
Signed-off-by: Alexander Walz <[email protected]>
It seems like PyInquirer does not work more often than not (e.g.,
importing the module fails). Since InquirerPy describes itself as `a
re-implementation of the PyInquirer project, with bug fixes of known
issues, new prompts, backward compatible APIs as well as more
customisation options.`, let's use it instead.

Signed-off-by: Andreas Lauser <[email protected]>
Signed-off-by: Alexander Walz <[email protected]>
@andlaus andlaus merged commit 911df59 into mercedes-benz:main Feb 13, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants