This looks really good to me and should have pretty good performance.
I would make one minor suggestion, which is to change your payload
choiceDispatchKey expression from:
{../../parameters[1]/parameter[fn:count(../payload)]/type}
to
{../../parameters[1]/parameter[dfdl:occursIndex()]/type}
So use the dfdl:occursIndex() function to find the associated parameter
instead of the fn:count() function. The dfdl:occursIndex() function
returns the index of the current array, which in this context is the
payload array. This is functionally the same as fn:count(../payload),
but dfdl:occursIndex() should be a bit more efficient. This is because
fn:count(../payload) needs to evaluate the ../payload path to find the
payload array and then count the number of elements, whereas
dfdl:occursIndex() can access internal state to know the exact current
index in constant time.
- Steve
On 1/16/19 7:28 AM, Christofer Dutz wrote:
> Hi all,
>
> I think I found a solution on my own ... the key is that I now output an
> payload element for every parameter, even if that is empty in some cases,
> with this, I was able to do the following:
>
> <xs:element name="S7ResponseMessage">
> <xs:complexType>
> <xs:sequence>
> <!-- Reserved value always 0x0000 -->
> <xs:element name="reserved" type="s7:short" fixed="0"/>
> <xs:element name="tpduReference" type="s7:short"/>
> <xs:element name="parametersLength" type="s7:short"/>
> <xs:element name="payloadsLength" type="s7:short"/>
> <!-- UserData (type 7) responses don't have the error class
> and code -->
> <xs:element name="errorClass" type="s7:byte" minOccurs="0"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{if(../../type eq 3) then 1 else 0}"/>
> <xs:element name="errorCode" type="s7:byte" minOccurs="0"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{if(../../type eq 3) then 1 else 0}"/>
> <xs:element name="parameters" minOccurs="0"
> dfdl:lengthKind="explicit"
> dfdl:lengthUnits="bytes" dfdl:length="{../parametersLength}"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{if(../parametersLength gt 0) then 1 else 0}">
> <xs:complexType>
> <xs:sequence>
> <xs:element name="parameter"
> maxOccurs="unbounded">
> <xs:complexType>
> <xs:sequence>
> <xs:element name="type"
> type="s7:byte"/>
> <xs:choice
> dfdl:choiceDispatchKey="{type}">
> <xs:element
> dfdl:choiceBranchKey="240" ref="s7:S7GeneralParameterSetupCommunication"/>
> <xs:element
> dfdl:choiceBranchKey="0" ref="s7:S7ResponseParameterCPUService"/>
> <xs:element
> dfdl:choiceBranchKey="4" ref="s7:S7ResponseParameterReadVar"/>
> <xs:element
> dfdl:choiceBranchKey="5" ref="s7:S7ResponseParameterWriteVar"/>
> </xs:choice>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
> <xs:element name="payloads" minOccurs="0"
> dfdl:lengthKind="explicit"
> dfdl:lengthUnits="bytes" dfdl:length="{../payloadsLength}"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{if((../payloadsLength gt 0) or (../parametersLength gt 0))
> then 1 else 0}">
> <xs:complexType>
> <xs:sequence>
> <xs:element name="payload" maxOccurs="unbounded"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{fn:count(../../parameters[1]/parameter)}">
> <xs:complexType>
> <xs:sequence>
> <xs:choice
> dfdl:choiceDispatchKey="{../../parameters[1]/parameter[fn:count(../payload)]/type}">
> <xs:element
> dfdl:choiceBranchKey="240" ref="s7:S7GeneralPayloadSetupCommunication"/>
> <xs:element
> dfdl:choiceBranchKey="0" ref="s7:S7ResponsePayloadCpuServices"/>
> <xs:element
> dfdl:choiceBranchKey="4" ref="s7:S7ResponsePayloadReadVar"/>
> <xs:element
> dfdl:choiceBranchKey="5" ref="s7:S7ResponsePayloadWriteVar"/>
> </xs:choice>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
>
> So what I do now is that I use the type-element of the matching parameter for
> the second decision.
>
> However I would like to report, das not even by debugging the code was I able
> to understand the error message about not supporting arrays.
> "Query-style paths not supported. Must have '[...]' after array-element's
> name."
> Cause the real reason is that the problem was that on the up-path only the
> last part can exist without the array notation and in this case the
> intermediate "parameters" fragment had to be added an array index.
>
> How is the above solution performance-wise?
>
> Chris
>
>
>
> Am 16.01.19, 11:54 schrieb "Christofer Dutz" <[email protected]>:
>
> Hi all,
>
> thanks for all of your help ... I am coming closer and closer to a first
> fully operational DFDL schema for S7 communication ... really looking forward
> to experimenting with that.
>
> Now I ran into something probably quite special:
>
> In my messages I have a header, parameters and payloads
> While each payload has a "type" indicator, the payloads don't. The
> payloads are somewhat parsed by iterating over the previously parsed
> parameters and for each parameter (in exactly the same order) create a
> payload.
> However not all parameters have payloads ...
>
> So here's what I've come up so far:
>
> <xs:element name="S7ResponseMessage">
> <xs:complexType>
> <xs:sequence>
> <!-- Reserved value always 0x0000 -->
> <xs:element name="reserved" type="s7:short" fixed="0"/>
> <xs:element name="tpduReference" type="s7:short"/>
> <xs:element name="parametersLength" type="s7:short"/>
> <xs:element name="payloadsLength" type="s7:short"/>
> <!-- UserData (type 7) responses don't have the error
> class and code -->
> <xs:element name="errorClass" type="s7:byte" minOccurs="0"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{if(../../type eq 3) then 1 else 0}"/>
> <xs:element name="errorCode" type="s7:byte" minOccurs="0"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{if(../../type eq 3) then 1 else 0}"/>
> <xs:element name="parameters" minOccurs="0"
> dfdl:lengthKind="explicit"
> dfdl:lengthUnits="bytes" dfdl:length="{../parametersLength}"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{if(../parametersLength gt 0) then 1 else 0}">
> <xs:complexType>
> <xs:sequence>
> <xs:element name="parameter"
> maxOccurs="unbounded">
> <xs:complexType>
> <xs:sequence>
> <xs:element name="type"
> type="s7:byte"/>
> <xs:choice
> dfdl:choiceDispatchKey="{type}">
> <xs:element
> dfdl:choiceBranchKey="240" ref="s7:S7GeneralParameterSetupCommunication"/>
> <xs:element
> dfdl:choiceBranchKey="0" ref="s7:S7ResponseParameterCPUService"/>
> <xs:element
> dfdl:choiceBranchKey="4" ref="s7:S7ResponseParameterReadVar"/>
> <xs:element
> dfdl:choiceBranchKey="5" ref="s7:S7ResponseParameterWriteVar"/>
> </xs:choice>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
> <xs:element name="payloads" minOccurs="0"
> dfdl:lengthKind="explicit"
> dfdl:lengthUnits="bytes" dfdl:length="{../payloadsLength}"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{if(../payloadsLength gt 0) then 1 else 0}">
> <xs:complexType>
> <xs:sequence>
> <xs:element name="payload"
> maxOccurs="unbounded"
> dfdl:occursCountKind="expression"
> dfdl:occursCount="{count(../parameters/parameter)}">
> <xs:complexType>
> <!-- TODO: Somehow loop over the
> parameters and use those elements as type keys -->
> <xs:sequence>
> <xs:element name="type"
> type="s7:byte"/>
> <xs:choice
> dfdl:choiceDispatchKey="{type}">
> <xs:element
> dfdl:choiceBranchKey="0" ref="s7:S7ResponsePayloadCpuServices"/>
> <xs:element
> dfdl:choiceBranchKey="4" ref="s7:S7ResponsePayloadReadVar"/>
> <xs:element
> dfdl:choiceBranchKey="5" ref="s7:S7ResponsePayloadWriteVar"/>
> </xs:choice>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
>
> So the "payload" element should occur exactly as often as there are
> parameters. Now I was thinking that if for a parameter there is no matching
> payload, I'll just not output any or just some dummy output.
> The problem however is, how can I iterate over the parameters and for
> each get the "type" and depending on that select the right choice branch?
>
> Chris
>
>
> Am 16.01.19, 02:08 schrieb "Beckerle, Mike" <[email protected]>:
>
> Christofer,
>
>
> So in DFDL, unlike regular XSD, only elements can be
> repeating/optional.
>
>
> You can't have max/minOccurs on a choice or sequence, only on an
> element.
>
>
> DFDL isn't only for XML. Many data models that the DFDL infoset could
> be projected into, those data models don't allow repeating or optional
> entities that aren't named and unitary.
>
>
> If we had allowed repeating anonymous groups, then if you interfaced
> DFDL directly to a language with tthis, we'd be having to generate names for
> these anonymous groups. We decided instead to keep the data model for DFDL
> simpler. If it repeats or is optional it has to be an element.
>
>
> The rationale here is the same reason we left out attributes of XSD.
> DFDL has only elements. This is because this dual-child tree where a node can
> have element children AND attribute children that have the same names that do
> not get mixed up... that's unique to XML, and DFDL is trying to be not so
> tied to XML/XSD, but able to describe data and project that data into the
> native data structures of many data models.
>
>
> ...mike beckerle
>
> Tresys Technology
>
>
> ________________________________
> From: Christofer Dutz <[email protected]>
> Sent: Tuesday, January 15, 2019 4:15:26 PM
> To: [email protected]
> Subject: Re: Daffodil ignoring dfdl:length=0 when
> dfdl:lengthKind=explicit
>
> (Wonder why every response turns out to be a private off-list
> response ... gotta remember to hit "reply to all")
>
> Thanks guys ... that worked like a charm ;-)
>
> Now I stumbled into the next little problem I couldn't find any
> documentation on.
>
> Now I have two situations:
> 1) The "parameterLength" provides the number of bytes all parameters
> consume, so the parser should continue parsing parameters as long as there
> are bytes left to read.
> How can I tell the sequence or the choice allow multiple instances?
>
> 2) I have "numItems" specifying the number of item-elements (also
> different types) so here not the number of bytes controls how many items are
> parsed, but the plain number of elements.
> Here too I cant find a way to specify a xs:minOccurs, xs:maxOccurs or
> dfdl:occursCount ... all seem to be invalid for both sequence and choice.
>
> Chris
>
> Am 15.01.19, 18:52 schrieb "Steve Lawrence" <[email protected]>:
>
> It's not that Daffodil is ignoring dfdl:lengthKind="0", it's just
> that
> is allows length of zero to be valid. There are actually some use
> cases
> where zero length is valid, and you would want this to cause a
> backtrack
> if the children required more than zero bytes.
>
> In this case, you just need to make it so that if the length is
> zero
> then it does not attempt to parse any child elements. One way to
> accomplish this is via dfdl:occursCountKind="expression" and
> dfdl:occursCount, something like so:
>
> <xs:element name="payloads" minOccurs="0" maxOccurs="1
> dfdl:lengthKind="explicit" dfdl:lengthUnits="bytes"
> dfdl:length="{../payloadsLength }"
> dfdl:occursCountKind="expression" dfdl:occursCount="{
> if (../payloadsLength eq 0) then 0 else 1
> }"
> <xs:complexType>
> <xs:sequence>
> <xs:choice>
> <xs:element ref="s7:S7RequestPayloadCpuServices"/>
> <xs:element ref="s7:S7RequestPayloadWriteVar"/>
> </xs:choice>
> </xs:sequence>
> </xs:complexType>
> </xs:element>
>
> This changes the payloads element to be optional (minOccurs=0) and
> defines the occurrences as either 0 or 1 based on the value of the
> payloadsLength. If the length is 1, the payloads element will not
> exist
> in the infoset and it will not attempt to parse the child data.
>
> - Steve
>
>
>
> On 1/15/19 10:39 AM, Christofer Dutz wrote:
> > Hi all,
> >
> > after working though the 6 tutorials on DFDL in general I think
> I have a much greater understanding on how I have to do things. I even
> managed to get my S7 protocol messages schema in a somewhat working condition.
> > Right now I’m having one problem:
> > A S7 Messages consists of a header, a number of variable length
> parameters and a number of variable length payloads.
> > As parameters and payloads are of variable length, the header
> contains a “parametersLength” and “payloadsLength” field which contains the
> number of bytes the parameters and payloads require in total.
> > So I defined something like this:
> >
> >
> > <xs:element name="parametersLength" type="s7:short"/>
> > <xs:element name="payloadsLength" type="s7:short"/>
> > <xs:element name="parameters" dfdl:lengthKind="explicit"
> dfdl:lengthUnits="bytes" dfdl:length="{../parametersLength}">
> > <xs:complexType>
> > <xs:sequence>
> > <xs:choice>
> > <xs:element
> ref="s7:S7GeneralParameterSetupCommunication"/>
> > <xs:element
> ref="s7:SS7RequestParameterCPUService"/>
> > <xs:element ref="s7:S7RequestParameterReadVar"/>
> > <xs:element
> ref="s7:S7RequestParameterWriteVar"/>
> > </xs:choice>
> > </xs:sequence>
> > </xs:complexType>
> > </xs:element>
> > <xs:element name="payloads" dfdl:lengthKind="explicit"
> dfdl:lengthUnits="bytes" dfdl:length="{../payloadsLength}">
> > <xs:complexType>
> > <xs:sequence>
> > <xs:choice>
> > <xs:element
> ref="s7:S7RequestPayloadCpuServices"/>
> > <xs:element ref="s7:S7RequestPayloadWriteVar"/>
> > </xs:choice>
> > </xs:sequence>
> > </xs:complexType>
> > </xs:element>
> >
> > However in a request, that doesn’t contain any payloads,
> daffodil still tries to parse the payloads, even if the “length” of the
> sequence is set to explicit and to a length of 0 … why is it doing that?
> > Each parameter and payloads first byte contains the code that
> tells the parser what type it is and each of the elements in my schema use a
> discriminator to tell the parser which input it is requiring.
> >
> >
> > <xs:element name="S7RequestPayloadCpuServices">
> > <xs:annotation>
> > <xs:appinfo source="http://www.ogf.org/dfdl/">
> > <dfdl:discriminator test="{./type eq 0}"/>
> > </xs:appinfo>
> > </xs:annotation>
> > <xs:complexType>
> > <xs:sequence>
> > <xs:element name="type" type="s7:byte"/>
> > <xs:element name="transportSize"
> type="s7:byte"/><!-- fixed="9"-->
> > <xs:element name="length" type="s7:byte"/>
> > <xs:element name="sslId" type="s7:short"/>
> > <xs:element name="sslIndex" type="s7:short"/>
> > </xs:sequence>
> > </xs:complexType>
> > </xs:element>
> >
> > Hope it’s correct to do things like that …
> >
> > The effect is that Daffodil has correctly parsed all the
> parameters and as there is no payload (payloadLength = 0) it shouldn’t try to
> parse a payload, but it does and when reading the first byte it instantly
> fails as there is no data to parse anymore.
> >
> >
> > Chris
> >
>
>
>
>
>
>
>