'use strict'; const { childAttr, required, dateTime, duration, number, boolean, } = require('./parser'); const { create, fragment } = require('xmlbuilder2'); const oadrPayloadNs = 'http://www.w3.org/2000/09/xmldsig#'; const oadrNs = 'http://openadr.org/oadr-2.0b/2012/07'; 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 xsiNs = 'http://www.w3.org/2001/XMLSchema-instance'; const emixNs = 'http://docs.oasis-open.org/ns/emix/2011/06'; const powerNs = 'http://docs.oasis-open.org/ns/emix/2011/06/power'; const siScaleNs = 'http://docs.oasis-open.org/ns/emix/2011/06/siscale'; function serializeDateTime(dateTime) { return fragment() .ele(calendarNs, 'cal:date-time') .txt(dateTime) .up(); } function serializeDuration(duration) { return duration != null ? fragment() .ele(calendarNs, 'cal:duration') .txt(duration) : fragment(); } 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); } if (eventSignalInterval.signalPayloads != null) { serializeSignalPayloads(eventSignalInterval.signalPayloads).forEach( payload => { interval.import(payload); }, ); } if (eventSignalInterval.reportPayloads != null) { serializeReportPayloads(eventSignalInterval.reportPayloads).forEach( payload => { interval.import(payload); }, ); } return result; } function serializeEventSignalIntervals(eventSignalIntervals) { if (!eventSignalIntervals) return []; return eventSignalIntervals.map(x => serializeEventSignalInterval(x)); } function serializeSignalPayloads(signalPayloads) { return signalPayloads.map(x => serializeSignalPayload(x)); } function serializeReportPayloads(reportPayloads) { return reportPayloads.map(x => serializeReportPayload(x)); } 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 serializeLoadControlStateType(loadControlStateType) { return Object.keys(loadControlStateType).reduce((accum, cur) => { accum.ele('oadr2b', `oadr2b:${cur}`).txt(loadControlStateType[cur]); return accum; }, fragment()); } function serializeLoadControlState(loadControlState) { const result = fragment(); const resultChildren = Object.keys(loadControlState).reduce((accum, cur) => { accum .ele('oadr2b', `oadr2b:${cur}`) .import(serializeLoadControlStateType(loadControlState[cur])); return accum; }, fragment()); result.ele('oadr2b', 'oadr2b:oadrLoadControlState').import(resultChildren); return result; } function serializePayloadStatus(payloadStatus) { const result = fragment(); const payloadResourceStatus = result.ele( 'oadr2b', 'oadr2b:oadrPayloadResourceStatus', ); payloadResourceStatus .ele('oadr2b', 'oadr2b:oadrOnline') .txt(`${payloadStatus.online}`); payloadResourceStatus .ele('oadr2b', 'oadr2b:oadrManualOverride') .txt(`${payloadStatus.manualOverride}`); if (payloadStatus.loadControlState) { payloadResourceStatus.import( serializeLoadControlState(payloadStatus.loadControlState), ); } return result; } function serializeSignalPayload(signalPayload) { const result = fragment(); result .ele(energyInteropNs, 'ei:signalPayload') .import(serializePayloadFloat(signalPayload)); return result; } function serializeReportPayload(reportPayload) { const result = fragment(); const reportPayloadOut = result.ele(oadrNs, 'oadr2b:oadrReportPayload'); if (reportPayload.payloadFloat) { reportPayloadOut.import(serializePayloadFloat(reportPayload.payloadFloat)); } if (reportPayload.payloadStatus) { reportPayloadOut.import( serializePayloadStatus(reportPayload.payloadStatus), ); } reportPayloadOut.ele(energyInteropNs, 'ei:rID').txt(reportPayload.reportId); reportPayloadOut .ele(oadrNs, 'oadr2b:oadrDataQuality') .txt(reportPayload.dataQuality); return result; } function parseSignalPayloads(signalPayloads) { return signalPayloads.map(x => parsePayloadFloat(x['$$'])); } function parsePayloadStatusType(statusTypeContainer) { return Object.keys(statusTypeContainer).reduce((accum, cur) => { accum[cur] = required(number(statusTypeContainer[cur][0]), cur); return accum; }, {}); } function parsePayloadStatus(oadrPayloadResourceStatus) { const result = { online: required( boolean(childAttr(oadrPayloadResourceStatus, 'oadrOnline')), 'oadrOnline', ), manualOverride: required( boolean(childAttr(oadrPayloadResourceStatus, 'oadrManualOverride')), 'oadrManualOverride', ), }; if (oadrPayloadResourceStatus.oadrLoadControlState) { const container = oadrPayloadResourceStatus.oadrLoadControlState[0]['$$']; result.loadControlState = Object.keys(container).reduce((accum, cur) => { accum[cur] = parsePayloadStatusType(container[cur][0]['$$']); return accum; }, {}); } return result; } function parseReportPayload(reportPayload) { const result = { reportId: required(childAttr(reportPayload, 'rID'), 'rID'), }; if (reportPayload.payloadFloat) { result.payloadFloat = parsePayloadFloat(reportPayload); } if (reportPayload.oadrPayloadResourceStatus) { result.payloadStatus = parsePayloadStatus( reportPayload.oadrPayloadResourceStatus[0]['$$'], ); } const dataQuality = childAttr(reportPayload, 'oadrDataQuality'); if (dataQuality != null) { result.dataQuality = dataQuality; } return result; } function parseReportPayloads(oadrReportPayloads) { return oadrReportPayloads.map(x => parseReportPayload(x['$$'])); } function parseEventSignalInterval(eventSignalInterval) { const result = {}; if (eventSignalInterval.signalPayload != null) { result.signalPayloads = parseSignalPayloads( eventSignalInterval.signalPayload, ); } if (eventSignalInterval.oadrReportPayload != null) { result.reportPayloads = parseReportPayloads( eventSignalInterval.oadrReportPayload, ); } 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 parseEventSignalIntervals(intervals) { if (intervals.interval != null) { intervals = intervals.interval; } return intervals.map(x => parseEventSignalInterval(x['$$'])); } function serializeSamplingRate(samplingRate) { const result = fragment(); if (samplingRate == null) return result; result .ele(oadrNs, 'oadr2b:oadrSamplingRate') .ele(oadrNs, 'oadr2b:oadrMinPeriod') .txt(samplingRate.minPeriod) .up() .ele(oadrNs, 'oadr2b:oadrMaxPeriod') .txt(samplingRate.maxPeriod) .up() .ele(oadrNs, 'oadr2b:oadrOnChange') .txt(samplingRate.onChange) .up() .up(); return result; } function serializeReportDescription(description) { const result = fragment(); const oadrReportDescription = result.ele( oadrNs, 'oadr2b:oadrReportDescription', ); oadrReportDescription .ele(energyInteropNs, 'ei:rID') .txt(description.reportId) .up() .ele(energyInteropNs, 'ei:reportType') .txt(description.reportType) .up() .ele(energyInteropNs, 'ei:readingType') .txt(description.readingType) .up() .import(serializeSamplingRate(description.samplingRate)); return result; } function serializeReportDescriptions(descriptions) { const result = fragment(); descriptions.forEach(description => result.import(serializeReportDescription(description)), ); return result; } function serializeReport(report) { const result = fragment(); const oadrReport = result.ele(oadrNs, 'oadr2b:oadrReport'); oadrReport .ele(energyInteropNs, 'ei:reportRequestID') .txt(report.reportRequestId) .up() .ele(energyInteropNs, 'ei:reportSpecifierID') .txt(report.reportSpecifierId) .up() .ele(energyInteropNs, 'ei:createdDateTime') .txt(report.createdDateTime) .up(); if (report.reportName != null) { oadrReport.ele(energyInteropNs, 'ei:reportName').txt(report.reportName); } if (report.descriptions != null) { oadrReport.import(serializeReportDescriptions(report.descriptions)); } if (report.duration != null) { oadrReport .ele(calendarNs, 'cal:duration') .import(serializeDuration(report.duration)); } if (report.startDate != null) { oadrReport .ele(calendarNs, 'cal:dtstart') .import(serializeDateTime(report.startDate)); } if (report.intervals != null) { const intervals = oadrReport.ele(calendarStreamNs, 'strm:intervals'); serializeEventSignalIntervals(report.intervals).forEach(interval => intervals.import(interval), ); } return result; } function serializeReports(reports) { const result = fragment(); reports.forEach(report => { result.import(serializeReport(report)); }); return result; } function parseDescription(description) { const result = { reportId: required(childAttr(description, 'rID'), 'rID'), reportType: required(childAttr(description, 'reportType'), 'reportType'), readingType: required(childAttr(description, 'readingType'), 'readingType'), }; if (description['oadrSamplingRate']) { result.samplingRate = parseSamplingRate( description['oadrSamplingRate'][0]['$$'], ); } return result; } function parseSamplingRate(samplingRate) { return { minPeriod: required( childAttr(samplingRate, 'oadrMinPeriod'), 'oadrMinPeriod', ), maxPeriod: required( childAttr(samplingRate, 'oadrMaxPeriod'), 'oadrMaxPeriod', ), onChange: required( boolean(childAttr(samplingRate, 'oadrOnChange'), 'oadrOnChange'), ), }; } function parseDescriptions(descriptions) { return descriptions.map(x => parseDescription(x['$$'])); } function parseReport(report) { const result = { reportRequestId: required( childAttr(report, 'reportRequestID'), 'reportRequestID', ), reportSpecifierId: required( childAttr(report, 'reportSpecifierID'), 'reportSpecifierID', ), createdDateTime: required( childAttr(report, 'createdDateTime'), 'createdDateTime', ), }; const reportName = childAttr(report, 'reportName'); if (reportName != null) { result.reportName = reportName; } const descriptions = report['oadrReportDescription']; if (descriptions != null) { result.descriptions = parseDescriptions(descriptions); } const durationValue = duration(childAttr(report, 'duration'), 'duration'); if (durationValue != null) result.duration = durationValue; const dtStartValue = dateTime(childAttr(report, 'dtstart'), 'date-time'); if (dtStartValue != null) { result.startDate = dtStartValue; } const intervalsHolder = childAttr(report, 'intervals'); if (intervalsHolder != null && intervalsHolder['$$']) { const intervals = intervalsHolder['$$']['interval']; result.intervals = parseEventSignalIntervals(intervals); } return result; } function parseReports(reports) { return reports.map(x => parseReport(x['$$'])); } function serializeEiResponse(data) { const descriptionFrag = data.responseDescription != null ? fragment() .ele(energyInteropNs, 'ei:responseDescription') .txt(data.responseDescription) : fragment(); return fragment() .ele(energyInteropNs, 'ei:eiResponse') .ele(energyInteropNs, 'ei:responseCode') .txt(data.responseCode) .up() .import(descriptionFrag) .ele(energyInteropPayloadsNs, 'pyld:requestID') .txt(data.responseRequestId) .up() .up(); } function serializeReportRequests(reportRequests) { const result = fragment(); (reportRequests || []).forEach(x => result.import(serializeReportRequest(x))); return result; } function serializeReportRequest(reportRequest) { return fragment() .ele(oadrNs, 'oadr:oadrReportRequest') .ele(energyInteropNs, 'ei:reportRequestID') .txt(reportRequest.reportRequestId) .up() .import(serializeReportSpecifier(reportRequest)); } function serializeSpecifierPayloads(specifierPayloads) { return specifierPayloads.map(specifierPayload => { return fragment() .ele(energyInteropNs, 'ei:specifierPayload') .ele(energyInteropNs, 'ei:rID') .txt(specifierPayload.reportId) .up() .ele(energyInteropNs, 'ei:readingType') .txt(specifierPayload.readingType) .up() .up(); }); } function serializeReportSpecifier(reportRequest) { const result = fragment(); const specifier = result.ele(energyInteropNs, 'ei:reportSpecifier'); specifier .ele(energyInteropNs, 'ei:reportSpecifierID') .txt(reportRequest.reportSpecifierId) .up() .ele(calendarNs, 'cal:granularity') .ele(calendarNs, 'cal:duration') .txt(reportRequest.granularityDuration) .up() .up() .ele(energyInteropNs, 'ei:reportBackDuration') .ele(calendarNs, 'cal:duration') .txt(reportRequest.reportBackDuration) .up() .up() .ele(energyInteropNs, 'ei:reportInterval') .ele(calendarNs, 'cal:properties') .ele(calendarNs, 'cal:dtstart') .import(serializeDateTime(reportRequest.startDate)) .up() .ele(calendarNs, 'cal:duration') .import(serializeDuration(reportRequest.duration)) .up(); if (reportRequest.specifiers) { serializeSpecifierPayloads(reportRequest.specifiers).forEach(x => specifier.import(x), ); } return result; } function parseSpecifierPayload(specifierPayload) { return { reportId: required(childAttr(specifierPayload, 'rID'), 'rID'), readingType: required( childAttr(specifierPayload, 'readingType'), 'readingType', ), }; } function parseSpecifierPayloads(specifierPayloads) { return specifierPayloads.map(x => parseSpecifierPayload(x['$$'])); } function parseReportRequest(reportRequest) { const reportSpecifier = reportRequest['reportSpecifier'][0]['$$']; const reportInterval = reportSpecifier['reportInterval'][0]['$$']; const reportIntervalProperties = reportInterval['properties'][0]['$$']; const result = { reportRequestId: required( childAttr(reportRequest, 'reportRequestID'), 'reportRequestID', ), reportSpecifierId: required( childAttr(reportSpecifier, 'reportSpecifierID'), 'reportSpecifierID', ), granularityDuration: required( duration(childAttr(reportSpecifier, 'granularity'), 'duration'), 'granularity', ), reportBackDuration: required( duration(childAttr(reportSpecifier, 'reportBackDuration'), 'duration'), 'reportBackDuration', ), startDate: required( dateTime(childAttr(reportIntervalProperties, 'dtstart'), 'date-time'), 'dtstart', ), duration: required( duration(childAttr(reportIntervalProperties, 'duration'), 'duration'), 'duration', ), }; if (reportSpecifier['specifierPayload']) { result.specifiers = parseSpecifierPayloads( reportSpecifier['specifierPayload'], ); } return result; } function parseReportRequests(reportRequests) { return reportRequests.map(x => parseReportRequest(x['$$'])); } function parseEiResponse(response) { return { code: required(childAttr(response, 'responseCode'), 'responseCode'), description: childAttr(response, 'responseDescription'), requestId: required(childAttr(response, 'requestID'), 'requestID'), }; } function createDoc() { return create({ namespaceAlias: { ns: oadrPayloadNs, oadr2b: oadrNs, ei: energyInteropNs, pyld: energyInteropPayloadsNs, cal: calendarNs, strm: calendarStreamNs, }, }); } module.exports = { createDoc, oadrPayloadNs, oadrNs, energyInteropNs, energyInteropPayloadsNs, calendarNs, calendarStreamNs, xsiNs, emixNs, powerNs, siScaleNs, serializeDateTime, serializeDuration, parsePayloadFloat, serializePayloadFloat, serializeEventSignalIntervals, parseEventSignalIntervals, serializeReports, parseReports, serializeEiResponse, parseEiResponse, serializeReportRequests, parseReportRequests, };