-
Notifications
You must be signed in to change notification settings - Fork 81
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 DiagLayer
into a class hierarchy
#326
Conversation
5e33bbb
to
545e7d6
Compare
return self._diag_variables | ||
|
||
@property | ||
def variable_groups(self) -> NamedItemList[VariableGroup]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
those are exclusive to ecu variants?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope, but the XSD defines them separately for ECU-SHARED-DATA, FUNCTIONAL-GROUP, BASE-VARIANT and ECU-VARIANT layers (note: not for PROTOCOL). To avoid confusion, I decided to simply match the spec even if I think that their approach is rather inelegant...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
python allows multi inheritance, so move them to shared class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this would be quite confusing for people who try to match the XSD with the python code. (if the specification did it this way, I would be all for it...)
|
||
|
||
@dataclass | ||
class EcuVariantRaw(HierarchyElementRaw): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the concept of raw diaglayer was introduced to reduce the complexity of the diag layer api
if diag layer was split, then that concept should have been no longer needed, instead it's getting duplicate, thus making the diaglayer api more complex, and negating its original purpose
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not quite: the reason why we have the split between "raw" and "full" layers is that the "full" layers provide more objects than they define directly because of the various inheritance mechanisms. IOW, it is impossible to reconstruct the contents of the original XML file using just full layers. I agree this makes it quite complex and error prone, but so far I could not come up with a better solution...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the "raw" part could be shared as far as I have seen the code, you don't need a new class for each "raw" type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to keep it simple and stupid even if this comes with a few copy-and-pasted lines. (who knows, maybe ODX 2.3 will modify one of the layers but not the others..)
# as self.diag_layer_raw. | ||
result.protocol_raw = deepcopy(self.protocol_raw, memo) | ||
|
||
return result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am seeing almost identical copy-pasted classes, and still don't see the benefit in term of making things simpler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the purpose of this PR is not to make this stuff simpler, it is to cling to the spec and allowing to eliminate a few systematic errors this way. E.g., if the spec says that a protocol ought to be referenced, we can now expect a Protocol
class instead of a DiagLayer
. Also, attributes which are not present in the specification do no longer exist in our classes (e.g., parent refs in ECU-SHARED-DATA)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ODX model is for serialization, for actual diagnostic encoding/decoding, you may want to take a look at the standard D-PDU API model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use this ODX data to talk to ECUs all the time. as far as I know, the PDX files are even the canonical source of diagnostics data upon which all tools except candela studio build in my company...
545e7d6
to
f315456
Compare
7a00f2b
to
0f9e950
Compare
num_dops.append(len(ddds.data_object_props)) | ||
else: | ||
num_dops.append(0) | ||
num_comparam_refs.append(len(getattr(variant, "comparams_refs", []))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's going on here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not all diag layers provide .comparam_refs
anymore. (Also, the compare
tool is in serious need of refactoring...)
for prot_snref in self.protocol_snrefs | ||
]) | ||
else: | ||
diag_layer = odxrequire(context.diag_layer) | ||
self._protocols = NamedItemList([ | ||
resolve_snref(prot_snref, diag_layer.protocols) | ||
resolve_snref(prot_snref, getattr(diag_layer, "protocols", [])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid getattr
, it allows for typing bugs that mypy can't catch, use isinstance instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tried this: This is pretty difficult to do as part of a list comprehension and it would not come with a major benefit because resolve_snref(..., Protocol)
returns a protocol object from the type checking perspective.
|
||
|
||
@dataclass | ||
class EcuVariant(HierarchyElement): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anything that BaseVariant have, should also be available in EcuVariant
class EcuVariant(HierarchyElement): | |
class EcuVariant(BaseVariant): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(note that base variants contain "BASE-VARIANT-PATTERNS" instead of "ECU-VARIANT-PATTERNS".)
class EcuVariant(HierarchyElement): | ||
|
||
@property | ||
def ecu_variant_raw(self) -> EcuVariantRaw: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why expose the raw data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because some low-level code might need to know what data was defined by the XML and in order to make typecheckers (mypy
) happy, we cannot use .diag_layer
to access EcuVariant
-specific attributes...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"might" or "do need" ? if "might" then delete, you are not gonna need it, see https://en.m.wikipedia.org/wiki/You_aren't_gonna_need_it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for code which is currently in the main branch odxtools
it is not needed, but I have some private scripts which fumble with the internals of ODX databases, and they would become even hackier than they already are...
return cast(EcuVariantRaw, self.diag_layer_raw) | ||
|
||
@property | ||
def diag_variables_raw(self) -> List[Union[DiagVariable, OdxLinkRef]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The raw part should be internal, why expose it as a property ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
basically just to make the API of EcuVariant
a superset of EcuVariantRaw
(i.e., substituting EcuVariantRaw
objects by EcuVariant
ones should just work without any changes.)
|
||
return self._compute_available_objects(get_local_objects_fn, not_inherited_fn) | ||
|
||
def _compute_available_variable_groups(self, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see copy-paste of this function and similar "compute" functions, move them to a helper class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I also don't like the copy and paste across diag layers, given that this is not too much code, the added complexity of the cure would IMO be worse than the disease...
@@ -17,6 +18,14 @@ | |||
from .variablegroup import VariableGroup | |||
|
|||
|
|||
@runtime_checkable | |||
class HasDiagVariables(typing.Protocol): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What layers don't have diag variables?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAICS just Protocol
.
d15213b
to
9da8569
Compare
Instead of a single class for all types of diagnostic layers, ODX specifies a hierarchy of types: ECU shared datas, protocols, functional groups, base variants and ECU variants whereas all but ECU shared data layers derive from a "hierarchy element" to indicate that these may be a member of a inheritance hierarchy defined using `PARENT-REF`s. This patch implements this approach. Signed-off-by: Andreas Lauser <andreas.lauser@mbition.io> Signed-off-by: Florian Jost <florian.jost@mbition.io>
instead, let's use type checking protocols. Note that this trick cannot be used for the `hasattr("protocol")` instances of `DiagComm` because this would lead to cyclic imports. thanks to [at]kayoub5 for suggesting this! Signed-off-by: Andreas Lauser <andreas.lauser@mbition.io> Signed-off-by: Florian Jost <florian.jost@mbition.io>
5038cf6
to
124e949
Compare
…g layers If the underlying raw layer does not provide a DDDS, and inheritance does not apply either, an empty object is created. This should help to avoid having to handle a few special cases in user code. Thanks to [at]kayoub for the catch! Signed-off-by: Andreas Lauser <andreas.lauser@mbition.io> Signed-off-by: Florian Jost <florian.jost@mbition.io>
this slightly increases the coverage of the tests. (With the exception of the variant pattern functionality, `BaseVariant`s are supposed to exhibit the same API ase `EcuVariant`s.) Signed-off-by: Andreas Lauser <andreas.lauser@mbition.io> Signed-off-by: Florian Jost <florian.jost@mbition.io>
124e949
to
bbd05fd
Compare
@kayoub5: do you consider this ready to be merged? (after merging this, I'll do an v8.0 release.) |
@kayoub5: if no new issues crop up until then, I'll merge this PR on Thursday or Friday... |
I am on the beach, with little access to the pc, I believe I already reviewed most of this PR. If I find issues later, I will create hotfix PRs. |
great, thanks. Let's get this merged. (have a nice vacation :)) |
Instead of a single class for all types of diagnostic layers, ODX specifies a hierarchy of types: ECU shared datas, protocols, functional groups, base variants and ECU variants where all but ECU shared data layers derive from a "hierarchy element" to indicate that these may be a member of a inheritance hierarchy defined using
PARENT-REF
s. This patch implements this approach.Andreas Lauser <andreas.lauser@mercedes-benz.com>, on behalf of MBition GmbH.
Provider Information