-
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
incorrect handling of ref with target in multiple odx files #200
Comments
I'm not sure whether I understand your issue correctly: for ECUs of ODX2 which specify a DOP with the same name, overriding the one from ODX1 it is correct as far as I know. The original DOP still ought to be exposed in ECUs which don't specify a new one and the DOP from ODX1 can still be referenced via ODXLINK by any ECU (i.e., even the ECUs which override it), but if the ECU does not reside in ODX1, the But maybe I misunderstood and you mean to "extend" the data of the DOP from ODX1 with the data specified by ODX2? As far as I know, the ODX standard does not allow this, as this would easily lead to breaking the data model as some tags are mutually exclusive and there is no way of "deleting" an arbitrary inherited one. |
I'll try another approach to explain my problem. So for me what makes sense is that the refernce in the parameter of the request should point to the DOP Object in the ECU's data_dictionary_spec, and not to the DOP of the parent Ref, whereever that is stored. I hope that makes my problem any clearer |
so, I guess you have something like this
If this is the case, it is the correct behavior as far as I can see: if DERIVED defines objects with the same ID and short name as BASE, the references in BASE are not magically replaced by the ones from DERIVED. This is because the ODXLINK mechanism also considers the document fragment of the referenced objects, i.e., if no DOCREF attribute is specified in the respective -REF tag, it is implicitly assumed that the referenced objects resides in the same document fragment as the referencing one. (this allows to specify objects with identical short names and IDs in different document fragments, thus simplifying copy-and-paste jobs.) To my knowledge, the only mechanism which -- in principle -- works like you seem expect are short name references: to resolve these, the full diagnostic layer needs to be used, so if DERIVED overrides an object from BASE with the same short name, the objects in BASE should -- in principle -- use the object from DERIVED. Note though, that odxtools does not support this because it would require full copies of all objects from the base layers to any derived layers and would this increase the memory consumption by a significant factor (and I regularly have to deal with files that gobble up several Gigabytes of RAM with the current version of odxtools.) That said, do any of the commercial tools behave as you describe? |
Indeed i have to exact scenario you described where the Objects in BASE reference the DOP through a short name reference. Other commercial tools, as well as the "odx commander" VS Code extension behave the way expected. |
the problem with this is that one and the same object can behave completely differently depending on which diagnostic layer it is part of. you can work around this like this, though: import odxtools
from copy import deepcopy
db = odxtools.load_file("my_pdx.pdx")
ecu = deepcopy(db.ecus.MyECU)
ecu.diag_layer_raw._resolve_snrefs(ecu) (if you do this for each and every diagnostic layer of very large files, you will quickly find out that 256 GB of RAM is not that much...) |
Those lines of code just like that had no effect. (Requests of DERIVED still have DOP of BASE). if self.request:
self.request._resolve_snrefs(diag_layer) to the _resolve_snrefs() function in diagservice.py, all snrefs seem to be correctly resolved. That beeing said, the code from your comment is not needed if,
for ecu in container.ecus:
ecu.diag_layer_raw._resolve_snrefs(ecu) again, for pdxs with a single ecu this should do the trick, for pdxs with multiple ecus, i don't think that'll help. For that case 1. might be a better approach. |
hm, weird. maybe I misunderstood how
even if this would solve the problem in your specific case, the short-name references for ECUs accessed via BASE would be wrong because they would point to the ones defined in DERIVED. (also, if there are multiple derived ECUs, the SNREFs would be incorrect for all but one of them...)
Considering the fact that such short-name references must be resolved differently depending on the ECU via they are accessed, I think the only somewhat practical "comprehensive" solution I can come up with is to copy the "full" ECU before resolving the references. That's because if this is not done, resolving a SNREF in a different context will override the resolution stemming from the previous context. The only "correct but impractical" alternative which I can see, is passing the ECU for every property that can be referenced via SNREFs, i.e., dop = param.dop would become something like dop = param.get_dop(ecu) IMO this is too clunky, and also pretty slow because the short-name resolution would need to be done every single time the property is accessed... |
I came up why it does not work: the problem is that import odxtools
from copy import deepcopy
db = odxtools.load_file("my_pdx.pdx")
base_ecu = db.ecus.MyBaseECU
base_ecu.diag_layer_raw._resolve_snrefs(base_ecu)
base_ecu = deepcopy(base_ecu)
derived_ecu = db.ecus.MyDerivedECU
derived_ecu.diag_layer_raw._resolve_snrefs(derived_ecu)
derived_ecu = deepcopy(derived_ecu) |
Im not sure if we understood each other correctly, so just to be clear. import odxtools
from copy import deepcopy
db = odxtools.load_file("my_pdx.pdx")
ecu = deepcopy(db.ecus.MyECU)
ecu.diag_layer_raw._resolve_snrefs(ecu) and 2. import odxtools
from copy import deepcopy
db = odxtools.load_file("my_pdx.pdx")
base_ecu = db.ecus.MyBaseECU
base_ecu.diag_layer_raw._resolve_snrefs(base_ecu)
base_ecu = deepcopy(base_ecu)
derived_ecu = db.ecus.MyDerivedECU
derived_ecu.diag_layer_raw._resolve_snrefs(derived_ecu)
derived_ecu = deepcopy(derived_ecu) DON'T work themselves. Just resolving the snref like that, either before of after copying has NO effect. At least not until adding if self.request:
self.request._resolve_snrefs(diag_layer) to the _resolve_snrefs() function in your Diagservice.py Again, i can only talk about the case of a single ECU in a pdx file. Still ,even with a single ECU the additional lines of code in the Diagservice.py are NECCESARY to see any changes to the resolving of snrefs. (In my case now seemingly correctly resolving them) |
okay, probably the SNREFs in the base variant need to be determined using the ECU. try this: import odxtools
from copy import deepcopy
db = odxtools.load_file("my_pdx.pdx")
base_variant = db.diag_layers.MyBaseVariant
ecu = db.ecus.MyECU
base_variant.diag_layer_raw._resolve_snrefs(ecu)
ecu.diag_layer_raw._resolve_snrefs(ecu)
ecu = deepcopy(ecu) Alternatively the issue could be caused if there are at least two services which use the same request object and resolving the SNREFs is dependent on the service object and the service which you are interested in requires to use the result of the first service, and finally there might be an obscure bug in the reference resolution code, but I can't think of a way of finding the exact reason without having access to some file where this happens...
the problem is that your solution likely is not "more correct" than the current state; it is just different, but the different version is what fits you needs. (for others this might be the other way round...) |
Again the code you posted has no effect. (Parameter of Request of DERIVED still have DOP of BASE)
I'm not 100% sure, but i think this is not the cause in my pdx. Im going to check it again just to be sure.
I'm not able to provide you the pdx i have. I can only offer to schedule a Teams meeting, to show you the pdx, maybe you can build a dummy one with the same structure to reproduce it then. |
can you verify the following:
yes, but that is conceptually incorrect: the "owner" of all requests is the diagnostic layer (i.e., protocol, Base variant, ECU, ...), not the service objects. For this reason reference resolution must be triggered from the diagnostic layer or fun things like infinite recursions may happen quite quickly. |
The Request-Ref existing in DERIVED points to the Request in: DIAG_LAYER_CONTAINER -> PROTOCOL->REQUESTS . so im guessing NO
YES
YES
NO |
okay, that explains it. the code above does not work because the request is defined by the protocol instead of the base variant. (IMO it is a bit questionable to do this from the logical point of view, BTW.) Anyway, if the request is defined in the protocol, this should work: import odxtools
from copy import deepcopy
db = odxtools.load_file("my_pdx.pdx")
protocol = db.diag_layers.MyProtocol
ecu = db.ecus.MyECU
protocol.diag_layer_raw._resolve_snrefs(ecu)
ecu.diag_layer_raw._resolve_snrefs(ecu)
ecu = deepcopy(ecu) If this still does not work, try calling |
Now this code itself works. Edit: Edit2: and loading in a different pdx (that has been working before) throws an exception Traceback (most recent call last): Edit3: With the pdx that atleast is parseable with odxtools, the problem now is that a parameter of a request in the PROTOCOL has a dop_snref. But now since im resolving the Protocols snrefs with the ECU, that as it seems did not inherit any structures from the Protocol, the parameter is not able to resolve its snref, because its only checking for that snref in the DiagLayer. In that Case if the DOP beeing referenced does not exist in the DiagLayer im resolving with, what DOP should it take. Then just take the DOP from the source DiagLayer? My Temporary fix, so that im able to atlease parse all ODXs, and that resolves some Issues with the Problem Mentioned in Edit3 is, replacing following code in parameterwithdop.py _resolve_snrefs() function self._dop = (
spec.data_object_props.get(self.dop_snref) or spec.structures.get(self.dop_snref)) with if spec.data_object_props.get(self.dop_snref) or spec.structures.get(self.dop_snref):
self._dop = (
spec.data_object_props.get(self.dop_snref) or spec.structures.get(self.dop_snref)) Since the Problem was that the correct dop that has been resolved by the protocol, with itself as diaglayer, was overwritten with None by resolving snrefs of protocol with the ECU diag layer (cause the dop only exists in the protocol and is not inherited by the ECU), i now only update the DOP via resolve_snrefs if the function can successfully resolve a Dop. |
I think that's correct: As far as I can see you "only" need to call
this is probably a known incompleteness of the current implementation: the DOP is almost certainly not yet implemented, for example STATIC-FIELD. Patches welcome ;)
this might be a bug in odxtools, structures should be inherited
looks like you're not using the latest git version of odxtools: @kayoub5 recently extended this short name lookup considerably. I'll make a release, so this fix is available via pypi
I wonder why they are not inherited in your case. the code seems to suggest that they are. Would be nice if you had a look... |
how comes? is it explicitly excluded by some NOT-INHERITED-$FOO tag in a PARENT-REF? |
Sounds good to me
Yes and no, i have some parameters where that is the case, and some parameters where the DOP is a Structure, where the Problem might be:
Going through the Codebase yesterday i noticed, thanks for the new Tag :)
I will be checking to see if i find any reason for the structures not beeing inherited and give out an update asap EDIT 1: Im not entirely sure why, but now with the 5.3.1 instead of the 5.3.0 my Ecu object correctly inherited all structrure Objects from BASE |
So, with Odxtools version 5.3.1 the Structure objects are correctly and completly inherited now. for diaglayer in db.diag_layers:
diaglayer.diag_layer_raw._resolve_snrefs(ecu) For 2 of 3 pdx files this works, and as far as i can see snrefs are correctly resolved. Traceback (most recent call last): Which again can be fixed by adding the if statement to resolve_snrefs in parameterwithdop, to only update the DOP object if it is able to resolve a DOP. (Correct behaviour?) |
To me, this smells a bit like an incorrectness in that PDX file, namely trying to reference an object via SNREF which does not exist in a given context. if you do not really need the service which uses that DOP, you can also try to use odxtools' non-strict mode.
I believe that both, the current behaviour of odxtools and the behaviour discussed above can be seen as conforming to the words of the ODX specification. (Whether these are "correct" is a different discussion IMO, in particular in the light all the semantic weaknesses of short-name references which lead to the discussion here...) |
Since i need that service, non-strict mode won't be an option. About the incorecctness of the pxd file i myself don't know pdxs well enough to judge whether that is the case or not. I can only reference to the VS Code extension: ODX Commander, and Vectors ODX Studio, which both are able to resolve all references.
What a great specification... Thanks for all the help and explainations you provided. :) |
Hi andlaus this is not the correct behaviour. So to recap:
The correct behaviour is that if I “look” from the LAYER "ODX1" I see the inherited service Foo but this one must resolve the DOP-SNREF with the DOP bar from ODX1! |
that's possible. that said, I consider this to be pretty much an edge case and it is next to impossible to implement that behavior without falling back to either extensive copying or using "lazy" SHORT-NAME lookups. For now, if you have a case like this, you can get around this using the snippet above: ecu = db.diag_layers.MyNiceEcu
for diaglayer in db.diag_layers:
diaglayer.diag_layer_raw._resolve_snrefs(ecu) (if you need more than one ECU object, you need to load a fresh database for each of them, though.) On the other hand, maybe there is a simple solution for this issue which I haven't seen yet. patches welcome. |
I came to the conclusion that the only way to implement calling context dependent SNREF object resolution properly ahead of time (i.e., not twisting the whole database towards a single diaglayer) is to implement a custom version of |
The inheritance of the request in ODX2 works fine. But the DOP beeing refered to in the request in ODX2 exists in both ODX files in different versions. From my understanding, and the way other odx-interpreting tools handle this situation is, that the DOP of ODX1 is used, and the DOP of ODX2 is ignored/overwritten.
Odxtools though takes the DOP from ODX2 and ignores/overwrites the DOP from ODX1, which leads to the data of the DOP in ODX1 beeing lost.
The text was updated successfully, but these errors were encountered: