|
@@ -0,0 +1,989 @@
|
|
|
|
|
+'use strict';
|
|
|
|
|
+
|
|
|
|
|
+const {
|
|
|
|
|
+ parseXML,
|
|
|
|
|
+ childAttr,
|
|
|
|
|
+ required,
|
|
|
|
|
+ boolean,
|
|
|
|
|
+ dateTime,
|
|
|
|
|
+ duration,
|
|
|
|
|
+ number,
|
|
|
|
|
+} = require('../parser');
|
|
|
|
|
+const { create, fragment } = require('xmlbuilder2');
|
|
|
|
|
+
|
|
|
|
|
+const xsiNs = 'http://www.w3.org/2001/XMLSchema-instance';
|
|
|
|
|
+const oadrPayloadNs = 'http://www.w3.org/2000/09/xmldsig#';
|
|
|
|
|
+const oadrNs = 'http://openadr.org/oadr-2.0b/2012/07';
|
|
|
|
|
+const emixNs = 'http://docs.oasis-open.org/ns/emix/2011/06';
|
|
|
|
|
+const powerNs = 'http://docs.oasis-open.org/ns/emix/2011/06/power';
|
|
|
|
|
+const energyInteropNs = 'http://docs.oasis-open.org/ns/energyinterop/201110';
|
|
|
|
|
+const energyInteropPayloadsNs =
|
|
|
|
|
+ 'http://docs.oasis-open.org/ns/energyinterop/201110/payloads';
|
|
|
|
|
+const calendarNs = 'urn:ietf:params:xml:ns:icalendar-2.0';
|
|
|
|
|
+const calendarStreamNs = 'urn:ietf:params:xml:ns:icalendar-2.0:stream';
|
|
|
|
|
+const siScaleNs = 'http://docs.oasis-open.org/ns/emix/2011/06/siscale';
|
|
|
|
|
+
|
|
|
|
|
+const eiTargetMappings = [
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'endDeviceAsset',
|
|
|
|
|
+ xmlChildElement: 'mrid',
|
|
|
|
|
+ json: 'endDeviceAsset',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'aggregatedPnode',
|
|
|
|
|
+ xmlChildElement: 'node',
|
|
|
|
|
+ json: 'aggregatedPnode',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'meterAsset',
|
|
|
|
|
+ xmlChildElement: 'mrid',
|
|
|
|
|
+ json: 'meterAsset',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'pnode',
|
|
|
|
|
+ xmlChildElement: 'node',
|
|
|
|
|
+ json: 'pNode',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'serviceDeliveryPoint',
|
|
|
|
|
+ xmlChildElement: 'node',
|
|
|
|
|
+ json: 'serviceDeliveryPoint',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'ei',
|
|
|
|
|
+ xmlNsUri: energyInteropNs,
|
|
|
|
|
+ xmlElement: 'groupID',
|
|
|
|
|
+ json: 'groupId',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'ei',
|
|
|
|
|
+ xmlNsUri: energyInteropNs,
|
|
|
|
|
+ xmlElement: 'groupName',
|
|
|
|
|
+ json: 'groupName',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'ei',
|
|
|
|
|
+ xmlNsUri: energyInteropNs,
|
|
|
|
|
+ xmlElement: 'resourceID',
|
|
|
|
|
+ json: 'resourceId',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'ei',
|
|
|
|
|
+ xmlNsUri: energyInteropNs,
|
|
|
|
|
+ xmlElement: 'venID',
|
|
|
|
|
+ json: 'venId',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'ei',
|
|
|
|
|
+ xmlNsUri: energyInteropNs,
|
|
|
|
|
+ xmlElement: 'partyID',
|
|
|
|
|
+ json: 'partyId',
|
|
|
|
|
+ },
|
|
|
|
|
+];
|
|
|
|
|
+
|
|
|
|
|
+const itemBaseMappings = [
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'powerReal',
|
|
|
|
|
+ additionalAttributes: [
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlElement: 'powerAttributes',
|
|
|
|
|
+ json: 'powerAttributes',
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ json: 'powerReal',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'powerReactive',
|
|
|
|
|
+ additionalAttributes: [
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlElement: 'powerAttributes',
|
|
|
|
|
+ json: 'powerAttributes',
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ json: 'powerReactive',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'powerApparent',
|
|
|
|
|
+ additionalAttributes: [
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlElement: 'powerAttributes',
|
|
|
|
|
+ json: 'powerAttributes',
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ json: 'powerApparent',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'voltage',
|
|
|
|
|
+ json: 'voltage',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'energyApparent',
|
|
|
|
|
+ json: 'energyApparent',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'energyReactive',
|
|
|
|
|
+ json: 'energyReactive',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'power',
|
|
|
|
|
+ xmlNsUri: powerNs,
|
|
|
|
|
+ xmlElement: 'energyReal',
|
|
|
|
|
+ json: 'energyReal',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'currencyPerKWh',
|
|
|
|
|
+ json: 'currencyPerKWh',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'currencyPerKW',
|
|
|
|
|
+ json: 'currencyPerKW',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'currencyPerThm',
|
|
|
|
|
+ json: 'currencyPerThm',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'currency',
|
|
|
|
|
+ json: 'currency',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'current',
|
|
|
|
|
+ json: 'current',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'frequency',
|
|
|
|
|
+ json: 'frequency',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'Therm',
|
|
|
|
|
+ json: 'therm',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'temperature',
|
|
|
|
|
+ json: 'temperature',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'pulseCount',
|
|
|
|
|
+ json: 'pulseCount',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ xmlNs: 'oadr',
|
|
|
|
|
+ xmlNsUri: oadrNs,
|
|
|
|
|
+ xmlElement: 'customUnit',
|
|
|
|
|
+ json: 'customUnit',
|
|
|
|
|
+ },
|
|
|
|
|
+];
|
|
|
|
|
+
|
|
|
|
|
+function parseItemBase(x) {
|
|
|
|
|
+ for (const itemBaseMapping of itemBaseMappings) {
|
|
|
|
|
+ const { json, xmlElement, additionalAttributes } = itemBaseMapping;
|
|
|
|
|
+ const itemBaseList = x[xmlElement];
|
|
|
|
|
+ if (itemBaseList) {
|
|
|
|
|
+ const itemBase = itemBaseList[0]['$$'];
|
|
|
|
|
+ const result = {
|
|
|
|
|
+ type: json,
|
|
|
|
|
+ description: required(
|
|
|
|
|
+ childAttr(itemBase, 'itemDescription'),
|
|
|
|
|
+ 'itemDescription',
|
|
|
|
|
+ ),
|
|
|
|
|
+ units: required(childAttr(itemBase, 'itemUnits'), 'itemUnits'),
|
|
|
|
|
+ siScaleCode: required(
|
|
|
|
|
+ childAttr(itemBase, 'siScaleCode'),
|
|
|
|
|
+ 'siScaleCode',
|
|
|
|
|
+ ),
|
|
|
|
|
+ };
|
|
|
|
|
+ for (const additionalMapping of additionalAttributes || []) {
|
|
|
|
|
+ const unNamespacedAdditionalAttribute = additionalMapping.xmlElement;
|
|
|
|
|
+ const attributesList = itemBase[unNamespacedAdditionalAttribute];
|
|
|
|
|
+ if (attributesList) {
|
|
|
|
|
+ const xmlAttributes = attributesList[0]['$$'];
|
|
|
|
|
+ const attributes = {};
|
|
|
|
|
+ Object.keys(xmlAttributes).forEach(key => {
|
|
|
|
|
+ attributes[key] = required(childAttr(xmlAttributes, key), key);
|
|
|
|
|
+ });
|
|
|
|
|
+ result[additionalMapping.json] = attributes;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeItemBase(x) {
|
|
|
|
|
+ for (const itemBaseMapping of itemBaseMappings) {
|
|
|
|
|
+ const {
|
|
|
|
|
+ xmlNs,
|
|
|
|
|
+ xmlNsUri,
|
|
|
|
|
+ json,
|
|
|
|
|
+ xmlElement,
|
|
|
|
|
+ additionalAttributes,
|
|
|
|
|
+ } = itemBaseMapping;
|
|
|
|
|
+ if (x.type === json) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ const innerResult = result.ele(xmlNsUri, `${xmlNs}:${xmlElement}`);
|
|
|
|
|
+ innerResult.ele(xmlNsUri, xmlNs + ':itemDescription').txt(x.description);
|
|
|
|
|
+ innerResult.ele(xmlNsUri, xmlNs + ':itemUnits').txt(x.units);
|
|
|
|
|
+ innerResult.ele(siScaleNs, 'scale:siScaleCode').txt(x.siScaleCode);
|
|
|
|
|
+ if (additionalAttributes) {
|
|
|
|
|
+ additionalAttributes.forEach(additionalAttribute => {
|
|
|
|
|
+ const additionalResult = innerResult.ele(
|
|
|
|
|
+ xmlNsUri,
|
|
|
|
|
+ `${xmlNs}:${additionalAttribute.xmlElement}`,
|
|
|
|
|
+ );
|
|
|
|
|
+ const additionalJson = x[additionalAttribute.json];
|
|
|
|
|
+ Object.keys(additionalJson).forEach(key => {
|
|
|
|
|
+ additionalResult
|
|
|
|
|
+ .ele(xmlNsUri, `${xmlNs}:${key}`)
|
|
|
|
|
+ .txt(additionalJson[key]);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEiResponse(response) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ code: required(childAttr(response, 'responseCode'), 'responseCode'),
|
|
|
|
|
+ description: childAttr(response, 'responseDescription'),
|
|
|
|
|
+ requestId: required(childAttr(response, 'requestID'), 'requestID'),
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEventDescriptor(eventDescriptor) {
|
|
|
|
|
+ const result = {
|
|
|
|
|
+ eventId: required(childAttr(eventDescriptor, 'eventID'), 'eventID'),
|
|
|
|
|
+ modificationNumber: required(
|
|
|
|
|
+ number(childAttr(eventDescriptor, 'modificationNumber')),
|
|
|
|
|
+ 'modificationNumber',
|
|
|
|
|
+ ),
|
|
|
|
|
+ marketContext: childAttr(
|
|
|
|
|
+ eventDescriptor.eiMarketContext[0]['$$'],
|
|
|
|
|
+ 'marketContext',
|
|
|
|
|
+ ),
|
|
|
|
|
+ createdDateTime: required(
|
|
|
|
|
+ childAttr(eventDescriptor, 'createdDateTime'),
|
|
|
|
|
+ 'createdDateTime',
|
|
|
|
|
+ ),
|
|
|
|
|
+ eventStatus: required(
|
|
|
|
|
+ childAttr(eventDescriptor, 'eventStatus'),
|
|
|
|
|
+ 'eventStatus',
|
|
|
|
|
+ ),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const testEvent = boolean(childAttr(eventDescriptor, 'testEvent'));
|
|
|
|
|
+ if (testEvent != null) {
|
|
|
|
|
+ result.testEvent = testEvent;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const modificationDateTime = childAttr(
|
|
|
|
|
+ eventDescriptor,
|
|
|
|
|
+ 'modificationDateTime',
|
|
|
|
|
+ );
|
|
|
|
|
+ if (modificationDateTime != null)
|
|
|
|
|
+ result.modificationDateTime = modificationDateTime;
|
|
|
|
|
+
|
|
|
|
|
+ const modificationReason = childAttr(eventDescriptor, 'modificationReason');
|
|
|
|
|
+ if (modificationReason != null)
|
|
|
|
|
+ result.modificationReason = modificationReason;
|
|
|
|
|
+
|
|
|
|
|
+ const priority = number(childAttr(eventDescriptor, 'priority'));
|
|
|
|
|
+ if (priority != null) result.priority = priority;
|
|
|
|
|
+
|
|
|
|
|
+ const vtnComment = childAttr(eventDescriptor, 'vtnComment');
|
|
|
|
|
+ if (vtnComment != null) result.vtnComment = vtnComment;
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEventDescriptor(eventDescriptor) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+
|
|
|
|
|
+ const eventDescriptorResult = result.ele(
|
|
|
|
|
+ energyInteropNs,
|
|
|
|
|
+ 'ei:eventDescriptor',
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ eventDescriptorResult
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:eventID')
|
|
|
|
|
+ .txt(eventDescriptor.eventId)
|
|
|
|
|
+ .up()
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:modificationNumber')
|
|
|
|
|
+ .txt(eventDescriptor.modificationNumber)
|
|
|
|
|
+ .up()
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:eiMarketContext')
|
|
|
|
|
+ .ele(emixNs, 'emix:marketContext')
|
|
|
|
|
+ .txt(eventDescriptor.marketContext)
|
|
|
|
|
+ .up()
|
|
|
|
|
+ .up()
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:createdDateTime')
|
|
|
|
|
+ .txt(eventDescriptor.createdDateTime)
|
|
|
|
|
+ .up()
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:eventStatus')
|
|
|
|
|
+ .txt(eventDescriptor.eventStatus);
|
|
|
|
|
+
|
|
|
|
|
+ if (eventDescriptor.testEvent != null) {
|
|
|
|
|
+ eventDescriptorResult
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:testEvent')
|
|
|
|
|
+ .txt(eventDescriptor.testEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventDescriptor.modificationDateTime != null) {
|
|
|
|
|
+ eventDescriptorResult
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:modificationDateTime')
|
|
|
|
|
+ .txt(eventDescriptor.modificationDateTime);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventDescriptor.modificationReason != null) {
|
|
|
|
|
+ eventDescriptorResult
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:modificationReason')
|
|
|
|
|
+ .txt(eventDescriptor.modificationReason);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventDescriptor.priority != null) {
|
|
|
|
|
+ eventDescriptorResult
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:priority')
|
|
|
|
|
+ .txt(eventDescriptor.priority);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventDescriptor.vtnComment != null) {
|
|
|
|
|
+ eventDescriptorResult
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:vtnComment')
|
|
|
|
|
+ .txt(eventDescriptor.vtnComment);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseToleranceTolerateStartAfter(tolerance) {
|
|
|
|
|
+ if (tolerance) {
|
|
|
|
|
+ const tolerate = childAttr(tolerance['$$'], 'tolerate');
|
|
|
|
|
+ if (tolerate) {
|
|
|
|
|
+ return duration(tolerate, 'startafter');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parsePayloadFloat(payloadFloatInput) {
|
|
|
|
|
+ const payloadFloat = required(
|
|
|
|
|
+ childAttr(payloadFloatInput, 'payloadFloat'),
|
|
|
|
|
+ 'payloadFloat',
|
|
|
|
|
+ )['$$'];
|
|
|
|
|
+ return required(number(childAttr(payloadFloat, 'value')), 'value');
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializePayloadFloat(payloadFloat) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ result
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:payloadFloat')
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:value')
|
|
|
|
|
+ .txt(payloadFloat);
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeSignalPayload(signalPayload) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ result
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:signalPayload')
|
|
|
|
|
+ .import(serializePayloadFloat(signalPayload));
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseSignalPayloads(signalPayloads) {
|
|
|
|
|
+ return signalPayloads.map(x => parsePayloadFloat(x['$$']));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeSignalPayloads(signalPayloads) {
|
|
|
|
|
+ return signalPayloads.map(x => serializeSignalPayload(x));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEventSignalInterval(eventSignalInterval) {
|
|
|
|
|
+ const result = {
|
|
|
|
|
+ signalPayloads: parseSignalPayloads(eventSignalInterval.signalPayload),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const durationValue = duration(
|
|
|
|
|
+ childAttr(eventSignalInterval, 'duration'),
|
|
|
|
|
+ 'duration',
|
|
|
|
|
+ );
|
|
|
|
|
+ if (durationValue != null) result.duration = durationValue;
|
|
|
|
|
+
|
|
|
|
|
+ const dtStartValue = dateTime(
|
|
|
|
|
+ childAttr(eventSignalInterval, 'dtstart'),
|
|
|
|
|
+ 'date-time',
|
|
|
|
|
+ );
|
|
|
|
|
+ if (dtStartValue != null) {
|
|
|
|
|
+ result.startDate = dtStartValue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const uidHolder = childAttr(eventSignalInterval, 'uid');
|
|
|
|
|
+ if (uidHolder != null) {
|
|
|
|
|
+ result.uid = required(childAttr(uidHolder['$$'], 'text'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEventSignalInterval(eventSignalInterval) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ const interval = result.ele(energyInteropNs, 'ei:interval');
|
|
|
|
|
+
|
|
|
|
|
+ if (eventSignalInterval.duration) {
|
|
|
|
|
+ interval
|
|
|
|
|
+ .ele(calendarNs, 'cal:duration')
|
|
|
|
|
+ .import(serializeDuration(eventSignalInterval.duration));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventSignalInterval.startDate) {
|
|
|
|
|
+ interval
|
|
|
|
|
+ .ele(calendarNs, 'cal:dtstart')
|
|
|
|
|
+ .import(serializeDateTime(eventSignalInterval.startDate));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventSignalInterval.uid) {
|
|
|
|
|
+ interval
|
|
|
|
|
+ .ele(calendarNs, 'cal:uid')
|
|
|
|
|
+ .ele(calendarNs, 'cal:text')
|
|
|
|
|
+ .txt(eventSignalInterval.uid);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ serializeSignalPayloads(eventSignalInterval.signalPayloads).forEach(payload =>
|
|
|
|
|
+ interval.import(payload),
|
|
|
|
|
+ );
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEventSignalIntervals(eventSignalIntervals) {
|
|
|
|
|
+ if (!eventSignalIntervals) {
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+ return eventSignalIntervals['interval'].map(x =>
|
|
|
|
|
+ parseEventSignalInterval(x['$$']),
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEventSignalIntervals(eventSignalIntervals) {
|
|
|
|
|
+ return eventSignalIntervals.map(x => serializeEventSignalInterval(x));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEiTarget(eiTarget) {
|
|
|
|
|
+ const result = {};
|
|
|
|
|
+ for (const eiTargetMapping of eiTargetMappings) {
|
|
|
|
|
+ const unNamespacedAttribute = eiTargetMapping.xmlElement;
|
|
|
|
|
+ if (eiTarget[unNamespacedAttribute]) {
|
|
|
|
|
+ const eiTargetValue = eiTarget[unNamespacedAttribute];
|
|
|
|
|
+ let newValues;
|
|
|
|
|
+ if (eiTargetMapping.xmlChildElement) {
|
|
|
|
|
+ const unNamespacedChildAttribute = eiTargetMapping.xmlChildElement;
|
|
|
|
|
+ newValues = eiTargetValue[0]['$$'][unNamespacedChildAttribute];
|
|
|
|
|
+ } else {
|
|
|
|
|
+ newValues = eiTargetValue;
|
|
|
|
|
+ }
|
|
|
|
|
+ const existing = result[eiTargetMapping.json] || [];
|
|
|
|
|
+ result[eiTargetMapping.json] = [...existing, ...newValues];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEiTarget(eiTarget) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ const targetElement = result.ele(energyInteropNs, 'ei:eiTarget');
|
|
|
|
|
+
|
|
|
|
|
+ for (const eiTargetMapping of eiTargetMappings) {
|
|
|
|
|
+ if (eiTarget[eiTargetMapping.json]) {
|
|
|
|
|
+ eiTarget[eiTargetMapping.json].forEach(target => {
|
|
|
|
|
+ const {
|
|
|
|
|
+ xmlNs,
|
|
|
|
|
+ xmlNsUri,
|
|
|
|
|
+ xmlElement,
|
|
|
|
|
+ xmlChildElement,
|
|
|
|
|
+ } = eiTargetMapping;
|
|
|
|
|
+ if (xmlChildElement) {
|
|
|
|
|
+ targetElement
|
|
|
|
|
+ .ele(xmlNsUri, `${xmlNs}:${xmlElement}`)
|
|
|
|
|
+ .ele(xmlNsUri, `${xmlNs}:${xmlChildElement}`)
|
|
|
|
|
+ .txt(target);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ targetElement.ele(xmlNsUri, `${xmlNs}:${xmlElement}`).txt(target);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEventSignal(eventSignal) {
|
|
|
|
|
+ const result = {
|
|
|
|
|
+ intervals: parseEventSignalIntervals(eventSignal['intervals'][0]['$$']),
|
|
|
|
|
+ signalName: required(childAttr(eventSignal, 'signalName'), 'signalName'),
|
|
|
|
|
+ signalType: required(childAttr(eventSignal, 'signalType'), 'signalType'),
|
|
|
|
|
+ signalId: required(childAttr(eventSignal, 'signalID'), 'signalID'),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const eiTarget = childAttr(eventSignal, 'eiTarget');
|
|
|
|
|
+ if (eiTarget != null) {
|
|
|
|
|
+ result.target = parseEiTarget(eiTarget['$$']);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const currentValue = childAttr(eventSignal, 'currentValue');
|
|
|
|
|
+ if (currentValue != null)
|
|
|
|
|
+ result.currentValue = parsePayloadFloat(currentValue['$$']);
|
|
|
|
|
+
|
|
|
|
|
+ const itemBase = parseItemBase(eventSignal);
|
|
|
|
|
+ if (itemBase != null) result.itemBase = itemBase;
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEventSignal(eventSignal) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ const eiEventSignal = result.ele(energyInteropNs, 'ei:eiEventSignal');
|
|
|
|
|
+
|
|
|
|
|
+ const intervals = eiEventSignal.ele(calendarStreamNs, 'strm:intervals');
|
|
|
|
|
+
|
|
|
|
|
+ serializeEventSignalIntervals(eventSignal.intervals).forEach(interval =>
|
|
|
|
|
+ intervals.import(interval),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ eiEventSignal.ele(energyInteropNs, 'ei:signalID').txt(eventSignal.signalId);
|
|
|
|
|
+ eiEventSignal
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:signalName')
|
|
|
|
|
+ .txt(eventSignal.signalName);
|
|
|
|
|
+ eiEventSignal
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:signalType')
|
|
|
|
|
+ .txt(eventSignal.signalType);
|
|
|
|
|
+
|
|
|
|
|
+ if (eventSignal.target) {
|
|
|
|
|
+ eiEventSignal.import(serializeEiTarget(eventSignal.target));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventSignal.currentValue != null) {
|
|
|
|
|
+ eiEventSignal
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:currentValue')
|
|
|
|
|
+ .import(serializePayloadFloat(eventSignal.currentValue));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventSignal.itemBase != null) {
|
|
|
|
|
+ eiEventSignal.import(serializeItemBase(eventSignal.itemBase));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEventBaseline(eventSignal) {
|
|
|
|
|
+ const result = {
|
|
|
|
|
+ startDate: required(
|
|
|
|
|
+ dateTime(childAttr(eventSignal, 'dtstart'), 'date-time'),
|
|
|
|
|
+ 'dtstart',
|
|
|
|
|
+ ),
|
|
|
|
|
+ duration: required(
|
|
|
|
|
+ duration(childAttr(eventSignal, 'duration'), 'duration'),
|
|
|
|
|
+ 'duration',
|
|
|
|
|
+ ),
|
|
|
|
|
+ intervals: parseEventSignalIntervals(eventSignal['intervals'][0]['$$']),
|
|
|
|
|
+ baselineId: required(childAttr(eventSignal, 'baselineID'), 'baselineID'),
|
|
|
|
|
+ baselineName: required(
|
|
|
|
|
+ childAttr(eventSignal, 'baselineName'),
|
|
|
|
|
+ 'baselineName',
|
|
|
|
|
+ ),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const itemBase = parseItemBase(eventSignal);
|
|
|
|
|
+ if (itemBase != null) result.itemBase = itemBase;
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEventBaseline(eventBaseline) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ const eiEventBaseline = result.ele(energyInteropNs, 'ei:eiEventBaseline');
|
|
|
|
|
+ eiEventBaseline
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:baselineID')
|
|
|
|
|
+ .txt(eventBaseline.baselineId);
|
|
|
|
|
+ eiEventBaseline
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:baselineName')
|
|
|
|
|
+ .txt(eventBaseline.baselineName);
|
|
|
|
|
+
|
|
|
|
|
+ if (eventBaseline.duration) {
|
|
|
|
|
+ eiEventBaseline
|
|
|
|
|
+ .ele(calendarNs, 'cal:duration')
|
|
|
|
|
+ .import(serializeDuration(eventBaseline.duration));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventBaseline.startDate) {
|
|
|
|
|
+ eiEventBaseline
|
|
|
|
|
+ .ele(calendarNs, 'cal:dtstart')
|
|
|
|
|
+ .import(serializeDateTime(eventBaseline.startDate));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const intervals = eiEventBaseline.ele(calendarStreamNs, 'strm:intervals');
|
|
|
|
|
+ serializeEventSignalIntervals(eventBaseline.intervals).forEach(interval =>
|
|
|
|
|
+ intervals.import(interval),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (eventBaseline.itemBase != null) {
|
|
|
|
|
+ eiEventBaseline.import(serializeItemBase(eventBaseline.itemBase));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEiEventSignals(eiEventSignals) {
|
|
|
|
|
+ const wrappedEventSignals = eiEventSignals.eiEventSignal;
|
|
|
|
|
+ const wrappedBaselines = eiEventSignals.eiEventBaseline;
|
|
|
|
|
+
|
|
|
|
|
+ const result = {};
|
|
|
|
|
+
|
|
|
|
|
+ if (wrappedEventSignals) {
|
|
|
|
|
+ result.event = wrappedEventSignals.map(x => parseEventSignal(x['$$']));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (wrappedBaselines) {
|
|
|
|
|
+ result.baseline = wrappedBaselines.map(x => parseEventBaseline(x['$$']));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEiEventSignals(eiEventSignals) {
|
|
|
|
|
+ const eventSignals = eiEventSignals.event.map(x => serializeEventSignal(x));
|
|
|
|
|
+ const eventBaselines = eiEventSignals.baseline
|
|
|
|
|
+ ? eiEventSignals.baseline.map(x => serializeEventBaseline(x))
|
|
|
|
|
+ : [];
|
|
|
|
|
+
|
|
|
|
|
+ return [...eventSignals, ...eventBaselines];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEiActivePeriod(activePeriod) {
|
|
|
|
|
+ const properties = activePeriod['properties'][0]['$$'];
|
|
|
|
|
+ const result = {
|
|
|
|
|
+ startDate: required(
|
|
|
|
|
+ dateTime(childAttr(properties, 'dtstart'), 'date-time'),
|
|
|
|
|
+ 'dtstart',
|
|
|
|
|
+ ),
|
|
|
|
|
+ duration: required(
|
|
|
|
|
+ duration(childAttr(properties, 'duration'), 'duration'),
|
|
|
|
|
+ 'duration',
|
|
|
|
|
+ ),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const toleranceTolerateStartAfter = parseToleranceTolerateStartAfter(
|
|
|
|
|
+ childAttr(properties, 'tolerance'),
|
|
|
|
|
+ );
|
|
|
|
|
+ if (toleranceTolerateStartAfter != null) {
|
|
|
|
|
+ result.toleranceTolerateStartAfter = toleranceTolerateStartAfter;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const notificationDuration = duration(
|
|
|
|
|
+ childAttr(properties, 'x-eiNotification'),
|
|
|
|
|
+ 'duration',
|
|
|
|
|
+ );
|
|
|
|
|
+ if (notificationDuration != null) {
|
|
|
|
|
+ result.notificationDuration = notificationDuration;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const rampUpDuration = duration(
|
|
|
|
|
+ childAttr(properties, 'x-eiRampUp'),
|
|
|
|
|
+ 'duration',
|
|
|
|
|
+ );
|
|
|
|
|
+ if (rampUpDuration != null) {
|
|
|
|
|
+ result.rampUpDuration = rampUpDuration;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const recoveryDuration = duration(
|
|
|
|
|
+ childAttr(properties, 'x-eiRecovery'),
|
|
|
|
|
+ 'duration',
|
|
|
|
|
+ );
|
|
|
|
|
+ if (recoveryDuration != null) {
|
|
|
|
|
+ result.recoveryDuration = recoveryDuration;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeDateTime(dateTime) {
|
|
|
|
|
+ return fragment()
|
|
|
|
|
+ .ele(calendarNs, 'cal:date-time')
|
|
|
|
|
+ .txt(dateTime)
|
|
|
|
|
+ .up();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeToleranceTolerateStartAfter(duration) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ result
|
|
|
|
|
+ .ele(calendarNs, 'cal:tolerance')
|
|
|
|
|
+ .ele(calendarNs, 'cal:tolerate')
|
|
|
|
|
+ .ele(calendarNs, 'cal:startafter')
|
|
|
|
|
+ .txt(duration);
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeActivePeriod(activePeriod) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+
|
|
|
|
|
+ const activePeriodResult = result.ele(energyInteropNs, 'ei:eiActivePeriod');
|
|
|
|
|
+ const properties = activePeriodResult.ele(calendarNs, 'cal:properties');
|
|
|
|
|
+ const components = activePeriodResult.ele(calendarNs, 'cal:components');
|
|
|
|
|
+ components.att(xsiNs, 'xsi:nil', 'true');
|
|
|
|
|
+
|
|
|
|
|
+ properties
|
|
|
|
|
+ .ele(calendarNs, 'cal:dtstart')
|
|
|
|
|
+ .import(serializeDateTime(activePeriod.startDate))
|
|
|
|
|
+ .up()
|
|
|
|
|
+ .ele(calendarNs, 'cal:duration')
|
|
|
|
|
+ .import(serializeDuration(activePeriod.duration))
|
|
|
|
|
+ .up();
|
|
|
|
|
+
|
|
|
|
|
+ if (activePeriod.toleranceTolerateStartAfter) {
|
|
|
|
|
+ properties.import(
|
|
|
|
|
+ serializeToleranceTolerateStartAfter(
|
|
|
|
|
+ activePeriod.toleranceTolerateStartAfter,
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (activePeriod.notificationDuration) {
|
|
|
|
|
+ properties
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:x-eiNotification')
|
|
|
|
|
+ .import(serializeDuration(activePeriod.notificationDuration));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (activePeriod.rampUpDuration) {
|
|
|
|
|
+ properties
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:x-eiRampUp')
|
|
|
|
|
+ .import(serializeDuration(activePeriod.rampUpDuration));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (activePeriod.recoveryDuration) {
|
|
|
|
|
+ properties
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:x-eiRecovery')
|
|
|
|
|
+ .import(serializeDuration(activePeriod.recoveryDuration));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEiEvent(eiEvent) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ eventDescriptor: parseEventDescriptor(eiEvent.eventDescriptor[0]['$$']),
|
|
|
|
|
+ activePeriod: parseEiActivePeriod(eiEvent.eiActivePeriod[0]['$$']),
|
|
|
|
|
+ signals: parseEiEventSignals(eiEvent.eiEventSignals[0]['$$']),
|
|
|
|
|
+ target: parseEiTarget(eiEvent.eiTarget[0]['$$']),
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEiEvent(eiEvent) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+
|
|
|
|
|
+ const eiEventResult = result.ele(energyInteropNs, 'ei:eiEvent');
|
|
|
|
|
+
|
|
|
|
|
+ eiEventResult.import(serializeEventDescriptor(eiEvent.eventDescriptor));
|
|
|
|
|
+ eiEventResult.import(serializeActivePeriod(eiEvent.activePeriod));
|
|
|
|
|
+
|
|
|
|
|
+ const eiEventSignals = eiEventResult.ele(
|
|
|
|
|
+ energyInteropNs,
|
|
|
|
|
+ 'ei:eiEventSignals',
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ serializeEiEventSignals(eiEvent.signals).forEach(signal =>
|
|
|
|
|
+ eiEventSignals.import(signal),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (eiEvent.target) {
|
|
|
|
|
+ eiEventResult.import(serializeEiTarget(eiEvent.target));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function parseEvents(eventList) {
|
|
|
|
|
+ const events = [];
|
|
|
|
|
+ for (const wrappedEvent of eventList) {
|
|
|
|
|
+ const event = wrappedEvent['$$'];
|
|
|
|
|
+ const parsedEiEvent = parseEiEvent(event.eiEvent[0]['$$']);
|
|
|
|
|
+ parsedEiEvent.responseRequired = childAttr(event, 'oadrResponseRequired');
|
|
|
|
|
+ events.push(parsedEiEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+ return events;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function parse(input) {
|
|
|
|
|
+ const json = await parseXML(input);
|
|
|
|
|
+ const o =
|
|
|
|
|
+ json['oadrPayload']['$$']['oadrSignedObject'][0]['$$'][
|
|
|
|
|
+ 'oadrDistributeEvent'
|
|
|
|
|
+ ][0]['$$'];
|
|
|
|
|
+
|
|
|
|
|
+ const { code, description, requestId: responseRequestId } = parseEiResponse(
|
|
|
|
|
+ o['eiResponse'][0]['$$'],
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ const result = {
|
|
|
|
|
+ responseCode: code,
|
|
|
|
|
+ responseDescription: description,
|
|
|
|
|
+ responseRequestId: responseRequestId,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (code < 200 || code >= 300) {
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const payloadRequestId = childAttr(o, 'requestID');
|
|
|
|
|
+ if (payloadRequestId != null) result.requestId = payloadRequestId;
|
|
|
|
|
+
|
|
|
|
|
+ const vtnId = childAttr(o, 'vtnID');
|
|
|
|
|
+ if (vtnId != null) result.vtnId = vtnId;
|
|
|
|
|
+
|
|
|
|
|
+ const events = parseEvents(o['oadrEvent']);
|
|
|
|
|
+
|
|
|
|
|
+ result.events = events;
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEiResponse(code, description, requestId) {
|
|
|
|
|
+ const descriptionFrag =
|
|
|
|
|
+ description != null
|
|
|
|
|
+ ? fragment()
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:responseDescription')
|
|
|
|
|
+ .txt(description)
|
|
|
|
|
+ : fragment();
|
|
|
|
|
+ return fragment()
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:responseCode')
|
|
|
|
|
+ .txt(code)
|
|
|
|
|
+ .up()
|
|
|
|
|
+ .import(descriptionFrag)
|
|
|
|
|
+ .ele(energyInteropPayloadsNs, 'pyld:requestID')
|
|
|
|
|
+ .txt(requestId)
|
|
|
|
|
+ .up();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeDuration(duration) {
|
|
|
|
|
+ return duration != null
|
|
|
|
|
+ ? fragment()
|
|
|
|
|
+ .ele(calendarNs, 'cal:duration')
|
|
|
|
|
+ .txt(duration)
|
|
|
|
|
+ : fragment();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function validate(obj) {
|
|
|
|
|
+ if (!obj.responseCode) {
|
|
|
|
|
+ throw new Error('Missing responseCode');
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!obj.responseRequestId) {
|
|
|
|
|
+ throw new Error('Missing responseRequestId');
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeOadrEvent(event) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ const oadrEvent = result.ele(oadrNs, 'oadr2b:oadrEvent');
|
|
|
|
|
+ oadrEvent.import(serializeEiEvent(event));
|
|
|
|
|
+ oadrEvent
|
|
|
|
|
+ .ele(oadrNs, 'oadr2b:oadrResponseRequired')
|
|
|
|
|
+ .txt(event.responseRequired);
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serializeEvents(events) {
|
|
|
|
|
+ const result = fragment();
|
|
|
|
|
+ events.forEach(event => {
|
|
|
|
|
+ result.import(serializeOadrEvent(event));
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function serialize(obj) {
|
|
|
|
|
+ validate(obj);
|
|
|
|
|
+
|
|
|
|
|
+ const vtnId =
|
|
|
|
|
+ obj.vtnId != null
|
|
|
|
|
+ ? fragment()
|
|
|
|
|
+ .ele(energyInteropNs, 'ei:vtnID')
|
|
|
|
|
+ .txt(obj.vtnId)
|
|
|
|
|
+ : fragment();
|
|
|
|
|
+
|
|
|
|
|
+ const requestId =
|
|
|
|
|
+ obj.requestId != null
|
|
|
|
|
+ ? fragment()
|
|
|
|
|
+ .ele(oadrPayloadNs, 'pyld:requestID')
|
|
|
|
|
+ .txt(obj.requestId)
|
|
|
|
|
+ : fragment();
|
|
|
|
|
+
|
|
|
|
|
+ const doc = create({
|
|
|
|
|
+ namespaceAlias: {
|
|
|
|
|
+ ns: oadrPayloadNs,
|
|
|
|
|
+ oadr2b: oadrNs,
|
|
|
|
|
+ ei: energyInteropNs,
|
|
|
|
|
+ pyld: energyInteropPayloadsNs,
|
|
|
|
|
+ cal: calendarNs,
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
|
|
+ .ele('@oadr2b', 'oadr2b:oadrPayload')
|
|
|
|
|
+ .ele('oadr2b:oadrSignedObject')
|
|
|
|
|
+ .ele('oadr2b:oadrDistributeEvent')
|
|
|
|
|
+ .att('@ei', 'ei:schemaVersion', '2.0b')
|
|
|
|
|
+ .ele('@ei', 'ei:eiResponse')
|
|
|
|
|
+ .import(
|
|
|
|
|
+ serializeEiResponse(
|
|
|
|
|
+ obj.responseCode,
|
|
|
|
|
+ obj.responseDescription,
|
|
|
|
|
+ obj.responseRequestId,
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
+ .up()
|
|
|
|
|
+ .import(vtnId)
|
|
|
|
|
+ .import(requestId)
|
|
|
|
|
+ .import(serializeEvents(obj.events))
|
|
|
|
|
+ .doc();
|
|
|
|
|
+ return doc.end({ headless: true, prettyPrint: false });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+module.exports = {
|
|
|
|
|
+ parse,
|
|
|
|
|
+ serialize,
|
|
|
|
|
+};
|