npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@smartdcc/duis-parser

v0.6.0

Published

Lightweight utility script wrapper around fast-xml-parser tuned for DUIS

Downloads

448

Readme

GitHub banner

DUIS Parser

License: GPL v3 Tests codecov GitHub version

Lightweight wrapper around fast-xml-parser that converts between JSON and DUIS formats. This tool has been developed to provide an easy to use client side parser for DCC Boxed. XML schema validation and digital signatures are out of scope of this tool, and it is intended to be used in conjunction with SmartDCCInnovation/dccboxed-signing-tool which provides these features.

Usage

Requirements

Developed and tested against node 16. Install from npm:

npm i @smartdcc/duis-parser

Parse DUIS

import { parseDuis } from '@smartdcc/duis-parser'
import { readFile } from 'node:fs/promises'

const duis = parseDuis('simplified', await readFile('/path/to/duis/file.xml'))

For example, a parsed Read Instantaneous Import Registers (SRV 4.1.1) could yield the following:

{
  header: {
    type: 'request',
    requestId: {
      originatorId: '90-b3-d5-1f-30-01-00-00',
      targetId: '00-db-12-34-56-78-90-a0',
      counter: 1000n
    },
    commandVariant: {
      number: 1,
      description: 'Non Critical Service Request for Command to be sent to a Device via the SM WAN',
      input: 'Service Request',
      output: 'Command',
      webService: 'Send Command Service',
      critical: 'No'
    },
    serviceReference: '4.1',
    serviceReferenceVariant: {
      'Service Request Name': 'Read Instantaneous Import Registers',
      'Service Reference': '4.1',
      'Service Reference Variant': '4.1.1',
      Critical: 'No',
      'On Demand': 'Yes',
      'Future Dated Response Pattern': 'DSP',
      'DCC Scheduled': 'No',
      'Non-Device Request': 'No',
      'Eligible User Roles': [ 'IS', 'GS', 'ED', 'GT' ]
    }
  },
  body: { ReadInstantaneousImportRegisters: '' }
}

As a convenience, the commandVariant and serviceReferenceVariant items are automatically converted from simple types (i.e. number and string, respectively) into meta-data objects with human readable meanings. These items are extracted from the DUIS Word document, the interested reader should view the DUIS Word document (available for download from the Smart Energy Code website) for their meanings.

To aid with interrogating a DUIS response to determine what type of response is provided a number of utility functions are provided:

  • isSimplifiedDuisInput
  • isSimplifiedDuisInputRequest
  • isSimplifiedDuisOutput
  • isSimplifiedDuisOutputRequest
  • isSimplifiedDuisOutputResponse
  • isSimplifiedDuisResponseBody_ResponseMessage_X
  • isSimplifiedDuisResponseBody_ResponseMessage
  • isSimplifiedDuisResponseBody_DeviceAlertMessage
  • isSimplifiedDuisResponseBody_DCCAlertMessage
  • isXMLData

These can be used from TypeScript as type predicates. E.g.

const duis = parseDuis('simplified', await readFile('/path/to/duis/file.xml'))
if (
  isSimplifiedDuisOutputResponse(duis) &&
  isSimplifiedDuisResponseBody_ResponseMessage_X('GBCSPayload', duis.body)
) {
  /* duis.body.ResponseMessage.GBCSPayload is valid */
}

Construct DUIS

Then to reconstruct the result back into a DUIS file:

import { constructDuis } from '@smartdcc/duis-parser'

const xml: string = constructDuis('simplified', duis)

When reconstructing, it is not required to give the full meta-data objects for commandVariant and serviceReferenceVariant. Instead, their simple types can be provided, thus the following will produce and equivalent XML as the example above (notice).

{
  header: {
    type: 'request',
    requestId: {
      originatorId: '90-b3-d5-1f-30-01-00-00',
      targetId: '00-db-12-34-56-78-90-a0',
      counter: 1000n
    },
    commandVariant: 1,
    serviceReference: '4.1',
    serviceReferenceVariant: '4.1.1'
  },
  body: { ReadInstantaneousImportRegisters: '' }
}

As another example. It is often not convenient (or necessary) to enter the counter as a BigInt as in the above example, the counter can be provided as a normal integer. This means the input can be JSON:

{
    "header": {
        "type": "request",
        "requestId": {
            "originatorId": "90-b3-d5-1f-30-01-00-00",
            "targetId": "00-db-12-34-56-78-90-a0",
            "counter": 0
        },
        "commandVariant": 1,
        "serviceReference": "6.24",
        "serviceReferenceVariant": "6.24.1"
    },
    "body": {
        "RetrieveDeviceSecurityCredentialsKRP": {
            "RemotePartyRole": [{"#text": "Root"}, {"#text": "Supplier"}, {"#text": "TransCoS"}]
        }
    }
}

In the above example, the usage of #text is required to build multiple RemotePartyRole entities. This is documented within the fast-xml-parser manual. But as an example, if only one RemotePartyRole is needed, then this can be simplified to:

{
    "header": {
        "type": "request",
        "requestId": {
            "originatorId": "90-b3-d5-1f-30-01-00-00",
            "targetId": "00-db-12-34-56-78-90-a0",
            "counter": 0
        },
        "commandVariant": 1,
        "serviceReference": "6.24",
        "serviceReferenceVariant": "6.24.1"
    },
    "body": {
        "RetrieveDeviceSecurityCredentialsKRP": {
            "RemotePartyRole": "Supplier"
        }
    }
}

Signing DUIS

To sign the resulting payload from constructDuis for use with DCC Boxed, please see the SmartDCCInnovation/dccboxed-signing-tool tool.

Advanced

In both examples above, simplified was used. This is recommended as it strips out the XML digital signature (if present), enriches the header with meta-data and removes the namespaces so it intuitive to use.

Alternatively, a normal mode is provided that is close to the unprocessed output from fast-xml-parser and has a direct correspondence with the original XML. For example:

console.log(parseDuis('normal', await readFile('/path/to/duis/file.xml')))

Could output the following. While initially it looks more complex, this is because the digital signature and XML namespaces are present.

{
  '?xml': { '@_version': '1.0', '@_encoding': 'UTF-8' },
  'sr:Request': {
    'sr:Header': {
      'sr:RequestID': '90-B3-D5-1F-30-01-00-00:00-DB-12-34-56-78-90-A0:1000',
      'sr:CommandVariant': '1',
      'sr:ServiceReference': '4.1',
      'sr:ServiceReferenceVariant': '4.1.1'
    },
    'sr:Body': { 'sr:ReadInstantaneousImportRegisters': '' },
    'ds:Signature': {
      'ds:SignedInfo': {
        'ds:CanonicalizationMethod': { '@_Algorithm': 'http://www.w3.org/2001/10/xml-exc-c14n#' },
        'ds:SignatureMethod': {
          '@_Algorithm': 'http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256'
        },
        'ds:Reference': {
          'ds:Transforms': {
            'ds:Transform': {
              '@_Algorithm': 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
            }
          },
          'ds:DigestMethod': { '@_Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256' },
          'ds:DigestValue': 'ZGVmYXVsdA==',
          '@_URI': ''
        }
      },
      'ds:SignatureValue': 'ZGVmYXVsdA==',
      'ds:KeyInfo': {
        'ds:X509Data': {
          'ds:X509IssuerSerial': {
            'ds:X509IssuerName': 'CN=U1, OU=07',
            'ds:X509SerialNumber': '1234567890'
          }
        }
      },
      '@_xmlns': 'http://www.w3.org/2000/09/xmldsig#'
    },
    '@_xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
    '@_xmlns:sr': 'http://www.dccinterface.co.uk/ServiceUserGateway',
    '@_xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
    '@_schemaVersion': '5.1'
  }
}

Contributing

Contributions are welcome!

When submitting a pull request, please ensure:

  1. Each PR is concise and provides only one feature/bug fix.
  2. Unit test are provided to cover feature. The project uses jest. To test, run npm run test:cov to view code coverage metrics.
  3. Bugfixes are reference the GitHub issue.
  4. If appropriate, update documentation.
  5. Before committing, run npm run lint and npm run prettier-check.

If you are planning a new non-trivial feature, please first raise a GitHub issue to discuss it to before investing your time to avoid disappointment.

Any contributions will be expected to be licensable under GPLv3.

Other Info

Copyright 2022, Smart DCC Limited, All rights reserved. Project is licensed under GPLv3.