EVerest / cbexigen

cbExiGen - The V2GTP EXI codec generator for cbV2G
Apache License 2.0
30 stars 16 forks source link

Question: How to code <ANY> element in the xsd #50

Closed gaohtao closed 10 months ago

gaohtao commented 11 months ago

I am looking at the generation of iso15118-2 schemas using cbexigen, and in particular how the Header sections with signatures are encoded. I've noticed Element in xmldsig-core-schema.xsd, as shown in the following example:

<element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/> 
  <complexType name="CanonicalizationMethodType" mixed="true">
    <sequence>
      <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
      <!-- (0,unbounded) elements from (1,1) namespace -->
    </sequence>
    <attribute name="Algorithm" type="anyURI" use="required"/> 
  </complexType>

Question 1: should be interpreted as an struct array, but it is only one struct in the generated code:

iso2_msgDefDatatypes.h
struct iso2_CanonicalizationMethodType {
    // Attribute: Algorithm, anyURI
    struct {
        char characters[iso2_Algorithm_CHARACTER_SIZE];
        uint16_t charactersLen;
    } Algorithm;
    // ANY, anyType (base: base64Binary)
    struct {
        uint8_t bytes[iso2_anyType_BYTES_SIZE];
        uint16_t bytesLen;
    } ANY;
    unsigned int ANY_isUsed:1;

};

Question 2: When encoding, why is the handling method of this element special? What are the coding rules of the event code? Why is the grammar written START (ANY), END Element, START (ANY)? The event encode should be START (ANY)-00, END Element-01, why is it actually written START (ANY)-02?

iso2_msgDefEncoder.c
int encode_iso2_CanonicalizationMethodType(exi_bitstream_t* stream, struct iso2_CanonicalizationMethodType* CanonicalizationMethodType) {
case 24:
            // Grammar: ID=24; read/write bits=2; START (ANY), END Element, START (ANY)
            // ***** //
            //{
                // No code for unsupported generic event: ANY (index=0)
            //{
            // ***** //
            if (CanonicalizationMethodType->ANY_isUsed == 1u)
            {
                error = exi_basetypes_encoder_nbit_uint(stream, 2, 2);
                if (error == EXI_ERROR__NO_ERROR)
                {
                    // Event: START (ANY, base64Binary); next=3
                    error = exi_basetypes_encoder_nbit_uint(stream, 1, 0);
                    if (error == EXI_ERROR__NO_ERROR)
                    {
                        error = exi_basetypes_encoder_uint_16(stream, (uint16_t)CanonicalizationMethodType->ANY.bytesLen);
                        if (error == EXI_ERROR__NO_ERROR)
                        {
                            error = exi_basetypes_encoder_bytes(stream, CanonicalizationMethodType->ANY.bytesLen, CanonicalizationMethodType->ANY.bytes, iso2_anyType_BYTES_SIZE);
                            if (error == EXI_ERROR__NO_ERROR)
                            {
                                // encode END Element
                                error = exi_basetypes_encoder_nbit_uint(stream, 1, 0);
                                if (error == EXI_ERROR__NO_ERROR)
                                {
                                    grammar_id = 3;
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                error = exi_basetypes_encoder_nbit_uint(stream, 2, 1);
                if (error == EXI_ERROR__NO_ERROR)
                {
                    // Event: END Element; next=4
                    done = 1;
                    grammar_id = 4;
                }
            }
            break;

}

Does this encoding of in the specification cause exi data errors that cannot be recognized by other exi decoders? I tried such exi data can not be decoded by OpenExiGUI.jar, it will cause an exception.

barsnick commented 11 months ago

[Updated your comment to fix formatting of code for readability.]

Regarding question 1: ISO 15118-2 demands, in requirement [V2G2-771], that the ##any element in CanonicalizationMethod not be used. Therefore, in terms of API, it does not matter whether we present it as array or single element. All that matters is that the grammar logic surrounding this element is correct.

Regarding question 2: The handling of wildcard elements such as ##any is covered by this section of the EXI standard: https://www.w3.org/TR/2014/REC-exi-20140211/#wildcardTerms

We confirmed that our coding works with other implementations which we had available, that being: OpenV2G, Josev/iso15118 (EXIficient-based), and V2Gdecoder (RISE-V2G-based).

Where did you get OpenExiGUI.jar from, and how did you use it? Are you sure it works correctly for V2GTP EXI streams? Which stream did you attempt to decode?

gaohtao commented 11 months ago

I have been confused about the use of this ANY, your answer is particularly helpful, I will take a closer look at [V2G2-771] and #wildcardTerms. Thanks very much! I also cross-tested with ExiCodec.jar and OpenExiGUI.jar, and the results were terrible, the exi data of the three could not be decoded to each other. I will submit more details tomorrow.

gaohtao commented 11 months ago

I used these two decoding libraries for interoperability tests: 1 EXICodec.jar https://github.com/SwitchEV/iso15118/iso15118/shared 15118-2:ChargeParameterDiscoveryReq Successful cross-decoding I am testing more commands which has SignedInfo in Header, such as CertificateInstallationReq, ChargeParameterDiscoveryRes, MeteringReceiptReq. 2 OpenExiGUI.jar https://exip.sourceforge.net, exip-0.5.4\externalTools failed cross-decoding with these commands which has SignedInfo in Header, such as CertificateInstallationReq, ChargeParameterDiscoveryRes, MeteringReceiptReq.

barsnick commented 11 months ago

We should be fully compatible with the former, do tell us if you find any discrepancies.

I'm not sure exip ever got full support for the features required by V2GTP, or is bug-free using those.

gaohtao commented 11 months ago

@barsnick This is the test result of failure。 In view of the command ChargeParameterDiscoveryRes ISO15118-2, I use EXICodec jar and cbexigen to cross test, cbexigen decoding failure, the results are as follows: ChargeParameterDiscoveryRes.json: {"V2G_Message":{"Header":{"Signature":{"SignatureValue":{"value":"MEUCICSta1OLLMIo3/H59qg4Dareuy/+UDr6p6fb/9TRPlFRAiEAx09IkrD7wL1f220dNKwH83WxRY6Yngx1BjAj3vMTDHw="},"SignedInfo":{"Reference":[{"Transforms":{"Transform":[{"Algorithm":"http://www.w3.org/TR/canonical-exi/"}]},"DigestMethod":{"Algorithm":"http://www.w3.org/2001/04/xmlenc#sha256"},"DigestValue":"FOPYeuJCS2wwX9XxiiT+Ly9ofNZY1Zu+UafOP1zwhvA=","URI":"#id1"}],"CanonicalizationMethod":{"Algorithm":"http://www.w3.org/TR/canonical-exi/"},"SignatureMethod":{"Algorithm":"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"}}},"SessionID":"225FFA52C1BDAE2F"},"Body":{"ChargeParameterDiscoveryRes":{"ResponseCode":"OK","EVSEProcessing":"Finished","AC_EVSEChargeParameter":{"EVSEMaxCurrent":{"Multiplier":0,"Value":32,"Unit":"A"},"AC_EVSEStatus":{"RCD":false,"NotificationMaxDelay":0,"EVSENotification":"None"},"EVSENominalVoltage":{"Multiplier":0,"Value":400,"Unit":"V"}},"SAScheduleList":{"SAScheduleTuple":[{"PMaxSchedule":{"PMaxScheduleEntry":[{"PMax":{"Multiplier":0,"Value":11000,"Unit":"W"},"RelativeTimeInterval":{"duration":3600,"start":0}}]},"SAScheduleTupleID":1,"SalesTariff":{"SalesTariffID":10,"NumEPriceLevels":2,"SalesTariffEntry":[{"RelativeTimeInterval":{"start":0},"EPriceLevel":1},{"RelativeTimeInterval":{"duration":1799,"start":1801},"EPriceLevel":2}],"Id":"id1"}}]}}}}}

cbexigen: Coding results: ChargeParameterDiscoveryRes_out. Exi, 335 bytes Decoding ChargeParameterDiscoveryRes.Exi fails, the error = EXI_ERROR__UNKNOWN_EVENT_FOR_DECODING. EXICodec.jar Coding results: ChargeParameterDiscoveryRes. Exi, 527 bytes Decoding ChargeParameterDiscoveryRes_out.Exi successful and perfect in content.

I tracked the code that failed to decode, specifically because: In the encoded data of the EXICodec.jar package, ANY in the CanonicalizationMethod field is encoded as the event code 00, but the 00 event code is not supported in the cbexigen code,then return EXI_ERROR__UNKNOWN_EVENT_FOR_DECODING

So that's what I'm asking how to deal with , I hope you can verify this decoding failure in time.

gaohtao commented 11 months ago

Could you leave me your Email? I can send you these exi files.

barsnick commented 11 months ago

Could you leave me your Email? I can send you these exi files.

You can attach files here as well. Or you can process the binary file through a hexdumper (e.g. on Linux through xxd -ps) and quote the resulting hexadecimal representation here.

cbexigen: Coding results: ChargeParameterDiscoveryRes_out. Exi, 335 bytes Decoding ChargeParameterDiscoveryRes.Exi fails, the error = EXI_ERROR__UNKNOWN_EVENT_FOR_DECODING. EXICodec.jar Coding results: ChargeParameterDiscoveryRes. Exi, 527 bytes Decoding ChargeParameterDiscoveryRes_out.Exi successful and perfect in content.

The steps are not quite clear. Who encodes what, and which step fails?

If we encode your JSON to EXI using Josev (which uses your EXICodec.jar), I can decode that successfully using cbV2G (from cbExiGen).

JSON -> Josev -> EXI (335 bytes) -> cbV2g -> C structs :white_check_mark:

We haven't gotten around to encoding your JSON using cbV2G yet, as we need to create some code for that. ;-)

gaohtao commented 11 months ago

I followed the steps below to test: JSON -> Josev -> EXI (527 bytes)->cbExiGen failed! JSON -> cbExiGen -> EXI (335 bytes)->Josev successful! exi.zip

Why is the result of EXICodec.jar 527 bytes? Is there any difference between the jar package which I use and yours?

gaohtao commented 11 months ago

Good news, I use EXICodec.jar to recreate the Java project, and then use the ChargeParameterDiscoveryRes.Json encoding, this time I got the exi result is 335 bytes. cbexigen decode successfully and get the correct content.

Thanks for your support. Through these tests, I like cbexigen more and more. It is really a great project. Now I write the I/O layer code on top of cbexigen, which is a very tedious job. The architecture I designed is: json--cJSON--Input/Output--cbexigen--PLC I'm looking forward to your cbV2G open source soon, so I can save these work.

barsnick commented 11 months ago

@gaohtao, great news, that saves us from doing more experiments.

We use the EXICodec.jar "binary" which is included in the Josev iso15118 repository, and that has always worked fine. (We put some Python wrapper code, to convert arbitrary JSON messages to EXI binary or hexadecimal streams.)

cbV2G is what we call the result of the cbExiGen generator, the actual C code. We have just not released it in a separate repository yet, so you still need to go through the steps of code generation.

Creating all the calls to cbV2G is indeed tedious, but currently left to the user, who has control over the actual data (i.e. C structs). If you can provide a complete JSON -> cbV2G -> EXI wrapping (and/or vice versa), please feel free to publish or contribute it.

barsnick commented 11 months ago

By the way:

json--cJSON--Input/Output--cbexigen--PLC

Please note that cJSON does not (yet) support 64-bit integers. In ISO 15118-2, this affects MeterReading, TMeter and EVSETimeStamp; in ISO 15118-20, this affects many related values (timestamps and meter readings).

You may need to use cJSON's valuedouble and some funky typecasting.

Reference: https://github.com/DaveGamble/cJSON/issues/162

gaohtao commented 11 months ago

Thanks for pointing out the flaws in cJSON, I've added code to make cJSON support 64-bit integers.

#ifdef CONFIG_CJSON_SUPPORT_64BIT 
extern int cJSON_Get_LongLong(cJSON * const object, const char * key, long long* out);
extern cJSON * cJSON_CreateLongLong(long long num);
extern void cJSON_AddLongLongToObject(cJSON * const object, const char * const name, const long long valuell);
#endif