shared.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. 'use strict';
  2. const {
  3. childAttr,
  4. required,
  5. dateTime,
  6. duration,
  7. number,
  8. boolean,
  9. } = require('./parser');
  10. const { create, fragment } = require('xmlbuilder2');
  11. const oadrPayloadNs = 'http://www.w3.org/2000/09/xmldsig#';
  12. const oadrNs = 'http://openadr.org/oadr-2.0b/2012/07';
  13. const energyInteropNs = 'http://docs.oasis-open.org/ns/energyinterop/201110';
  14. const energyInteropPayloadsNs =
  15. 'http://docs.oasis-open.org/ns/energyinterop/201110/payloads';
  16. const calendarNs = 'urn:ietf:params:xml:ns:icalendar-2.0';
  17. const calendarStreamNs = 'urn:ietf:params:xml:ns:icalendar-2.0:stream';
  18. const xsiNs = 'http://www.w3.org/2001/XMLSchema-instance';
  19. const emixNs = 'http://docs.oasis-open.org/ns/emix/2011/06';
  20. const powerNs = 'http://docs.oasis-open.org/ns/emix/2011/06/power';
  21. const siScaleNs = 'http://docs.oasis-open.org/ns/emix/2011/06/siscale';
  22. function serializeDateTime(dateTime) {
  23. return fragment()
  24. .ele(calendarNs, 'cal:date-time')
  25. .txt(dateTime)
  26. .up();
  27. }
  28. function serializeDuration(duration) {
  29. return duration != null
  30. ? fragment()
  31. .ele(calendarNs, 'cal:duration')
  32. .txt(duration)
  33. : fragment();
  34. }
  35. function serializeEventSignalInterval(eventSignalInterval) {
  36. const result = fragment();
  37. const interval = result.ele(energyInteropNs, 'ei:interval');
  38. if (eventSignalInterval.duration) {
  39. interval
  40. .ele(calendarNs, 'cal:duration')
  41. .import(serializeDuration(eventSignalInterval.duration));
  42. }
  43. if (eventSignalInterval.startDate) {
  44. interval
  45. .ele(calendarNs, 'cal:dtstart')
  46. .import(serializeDateTime(eventSignalInterval.startDate));
  47. }
  48. if (eventSignalInterval.uid) {
  49. interval
  50. .ele(calendarNs, 'cal:uid')
  51. .ele(calendarNs, 'cal:text')
  52. .txt(eventSignalInterval.uid);
  53. }
  54. if (eventSignalInterval.signalPayloads != null) {
  55. serializeSignalPayloads(eventSignalInterval.signalPayloads).forEach(
  56. payload => {
  57. interval.import(payload);
  58. },
  59. );
  60. }
  61. if (eventSignalInterval.reportPayloads != null) {
  62. serializeReportPayloads(eventSignalInterval.reportPayloads).forEach(
  63. payload => {
  64. interval.import(payload);
  65. },
  66. );
  67. }
  68. return result;
  69. }
  70. function serializeEventSignalIntervals(eventSignalIntervals) {
  71. if (!eventSignalIntervals) return [];
  72. return eventSignalIntervals.map(x => serializeEventSignalInterval(x));
  73. }
  74. function serializeSignalPayloads(signalPayloads) {
  75. return signalPayloads.map(x => serializeSignalPayload(x));
  76. }
  77. function serializeReportPayloads(reportPayloads) {
  78. return reportPayloads.map(x => serializeReportPayload(x));
  79. }
  80. function parsePayloadFloat(payloadFloatInput) {
  81. const payloadFloat = required(
  82. childAttr(payloadFloatInput, 'payloadFloat'),
  83. 'payloadFloat',
  84. )['$$'];
  85. return required(number(childAttr(payloadFloat, 'value')), 'value');
  86. }
  87. function serializePayloadFloat(payloadFloat) {
  88. const result = fragment();
  89. result
  90. .ele(energyInteropNs, 'ei:payloadFloat')
  91. .ele(energyInteropNs, 'ei:value')
  92. .txt(payloadFloat);
  93. return result;
  94. }
  95. function serializeLoadControlStateType(loadControlStateType) {
  96. return Object.keys(loadControlStateType).reduce((accum, cur) => {
  97. accum.ele('oadr2b', `oadr2b:${cur}`).txt(loadControlStateType[cur]);
  98. return accum;
  99. }, fragment());
  100. }
  101. function serializeLoadControlState(loadControlState) {
  102. const result = fragment();
  103. const resultChildren = Object.keys(loadControlState).reduce((accum, cur) => {
  104. accum
  105. .ele('oadr2b', `oadr2b:${cur}`)
  106. .import(serializeLoadControlStateType(loadControlState[cur]));
  107. return accum;
  108. }, fragment());
  109. result.ele('oadr2b', 'oadr2b:oadrLoadControlState').import(resultChildren);
  110. return result;
  111. }
  112. function serializePayloadStatus(payloadStatus) {
  113. const result = fragment();
  114. const payloadResourceStatus = result.ele(
  115. 'oadr2b',
  116. 'oadr2b:oadrPayloadResourceStatus',
  117. );
  118. payloadResourceStatus
  119. .ele('oadr2b', 'oadr2b:oadrOnline')
  120. .txt(`${payloadStatus.online}`);
  121. payloadResourceStatus
  122. .ele('oadr2b', 'oadr2b:oadrManualOverride')
  123. .txt(`${payloadStatus.manualOverride}`);
  124. if (payloadStatus.loadControlState) {
  125. payloadResourceStatus.import(
  126. serializeLoadControlState(payloadStatus.loadControlState),
  127. );
  128. }
  129. return result;
  130. }
  131. function serializeSignalPayload(signalPayload) {
  132. const result = fragment();
  133. result
  134. .ele(energyInteropNs, 'ei:signalPayload')
  135. .import(serializePayloadFloat(signalPayload));
  136. return result;
  137. }
  138. function serializeReportPayload(reportPayload) {
  139. const result = fragment();
  140. const reportPayloadOut = result.ele(oadrNs, 'oadr2b:oadrReportPayload');
  141. if (reportPayload.payloadFloat) {
  142. reportPayloadOut.import(serializePayloadFloat(reportPayload.payloadFloat));
  143. }
  144. if (reportPayload.payloadStatus) {
  145. reportPayloadOut.import(
  146. serializePayloadStatus(reportPayload.payloadStatus),
  147. );
  148. }
  149. reportPayloadOut.ele(energyInteropNs, 'ei:rID').txt(reportPayload.reportId);
  150. reportPayloadOut
  151. .ele(oadrNs, 'oadr2b:oadrDataQuality')
  152. .txt(reportPayload.dataQuality);
  153. return result;
  154. }
  155. function parseSignalPayloads(signalPayloads) {
  156. return signalPayloads.map(x => parsePayloadFloat(x['$$']));
  157. }
  158. function parsePayloadStatusType(statusTypeContainer) {
  159. return Object.keys(statusTypeContainer).reduce((accum, cur) => {
  160. accum[cur] = required(number(statusTypeContainer[cur][0]), cur);
  161. return accum;
  162. }, {});
  163. }
  164. function parsePayloadStatus(oadrPayloadResourceStatus) {
  165. const result = {
  166. online: required(
  167. boolean(childAttr(oadrPayloadResourceStatus, 'oadrOnline')),
  168. 'oadrOnline',
  169. ),
  170. manualOverride: required(
  171. boolean(childAttr(oadrPayloadResourceStatus, 'oadrManualOverride')),
  172. 'oadrManualOverride',
  173. ),
  174. };
  175. if (oadrPayloadResourceStatus.oadrLoadControlState) {
  176. const container = oadrPayloadResourceStatus.oadrLoadControlState[0]['$$'];
  177. result.loadControlState = Object.keys(container).reduce((accum, cur) => {
  178. accum[cur] = parsePayloadStatusType(container[cur][0]['$$']);
  179. return accum;
  180. }, {});
  181. }
  182. return result;
  183. }
  184. function parseReportPayload(reportPayload) {
  185. const result = {
  186. reportId: required(childAttr(reportPayload, 'rID'), 'rID'),
  187. };
  188. if (reportPayload.payloadFloat) {
  189. result.payloadFloat = parsePayloadFloat(reportPayload);
  190. }
  191. if (reportPayload.oadrPayloadResourceStatus) {
  192. result.payloadStatus = parsePayloadStatus(
  193. reportPayload.oadrPayloadResourceStatus[0]['$$'],
  194. );
  195. }
  196. const dataQuality = childAttr(reportPayload, 'oadrDataQuality');
  197. if (dataQuality != null) {
  198. result.dataQuality = dataQuality;
  199. }
  200. return result;
  201. }
  202. function parseReportPayloads(oadrReportPayloads) {
  203. return oadrReportPayloads.map(x => parseReportPayload(x['$$']));
  204. }
  205. function parseEventSignalInterval(eventSignalInterval) {
  206. const result = {};
  207. if (eventSignalInterval.signalPayload != null) {
  208. result.signalPayloads = parseSignalPayloads(
  209. eventSignalInterval.signalPayload,
  210. );
  211. }
  212. if (eventSignalInterval.oadrReportPayload != null) {
  213. result.reportPayloads = parseReportPayloads(
  214. eventSignalInterval.oadrReportPayload,
  215. );
  216. }
  217. const durationValue = duration(
  218. childAttr(eventSignalInterval, 'duration'),
  219. 'duration',
  220. );
  221. if (durationValue != null) result.duration = durationValue;
  222. const dtStartValue = dateTime(
  223. childAttr(eventSignalInterval, 'dtstart'),
  224. 'date-time',
  225. );
  226. if (dtStartValue != null) {
  227. result.startDate = dtStartValue;
  228. }
  229. const uidHolder = childAttr(eventSignalInterval, 'uid');
  230. if (uidHolder != null) {
  231. result.uid = required(childAttr(uidHolder['$$'], 'text'));
  232. }
  233. return result;
  234. }
  235. function parseEventSignalIntervals(intervals) {
  236. if (intervals.interval != null) {
  237. intervals = intervals.interval;
  238. }
  239. return intervals.map(x => parseEventSignalInterval(x['$$']));
  240. }
  241. function serializeSamplingRate(samplingRate) {
  242. const result = fragment();
  243. if (samplingRate == null) return result;
  244. result
  245. .ele(oadrNs, 'oadr2b:oadrSamplingRate')
  246. .ele(oadrNs, 'oadr2b:oadrMinPeriod')
  247. .txt(samplingRate.minPeriod)
  248. .up()
  249. .ele(oadrNs, 'oadr2b:oadrMaxPeriod')
  250. .txt(samplingRate.maxPeriod)
  251. .up()
  252. .ele(oadrNs, 'oadr2b:oadrOnChange')
  253. .txt(samplingRate.onChange)
  254. .up()
  255. .up();
  256. return result;
  257. }
  258. function serializeReportDescription(description) {
  259. const result = fragment();
  260. const oadrReportDescription = result.ele(
  261. oadrNs,
  262. 'oadr2b:oadrReportDescription',
  263. );
  264. oadrReportDescription
  265. .ele(energyInteropNs, 'ei:rID')
  266. .txt(description.reportId)
  267. .up()
  268. .ele(energyInteropNs, 'ei:reportType')
  269. .txt(description.reportType)
  270. .up()
  271. .ele(energyInteropNs, 'ei:readingType')
  272. .txt(description.readingType)
  273. .up()
  274. .import(serializeSamplingRate(description.samplingRate));
  275. return result;
  276. }
  277. function serializeReportDescriptions(descriptions) {
  278. const result = fragment();
  279. descriptions.forEach(description =>
  280. result.import(serializeReportDescription(description)),
  281. );
  282. return result;
  283. }
  284. function serializeReport(report) {
  285. const result = fragment();
  286. const oadrReport = result.ele(oadrNs, 'oadr2b:oadrReport');
  287. oadrReport
  288. .ele(energyInteropNs, 'ei:reportRequestID')
  289. .txt(report.reportRequestId)
  290. .up()
  291. .ele(energyInteropNs, 'ei:reportSpecifierID')
  292. .txt(report.reportSpecifierId)
  293. .up()
  294. .ele(energyInteropNs, 'ei:createdDateTime')
  295. .txt(report.createdDateTime)
  296. .up();
  297. if (report.reportName != null) {
  298. oadrReport.ele(energyInteropNs, 'ei:reportName').txt(report.reportName);
  299. }
  300. if (report.descriptions != null) {
  301. oadrReport.import(serializeReportDescriptions(report.descriptions));
  302. }
  303. if (report.duration != null) {
  304. oadrReport
  305. .ele(calendarNs, 'cal:duration')
  306. .import(serializeDuration(report.duration));
  307. }
  308. if (report.startDate != null) {
  309. oadrReport
  310. .ele(calendarNs, 'cal:dtstart')
  311. .import(serializeDateTime(report.startDate));
  312. }
  313. if (report.intervals != null) {
  314. const intervals = oadrReport.ele(calendarStreamNs, 'strm:intervals');
  315. serializeEventSignalIntervals(report.intervals).forEach(interval =>
  316. intervals.import(interval),
  317. );
  318. }
  319. return result;
  320. }
  321. function serializeReports(reports) {
  322. const result = fragment();
  323. reports.forEach(report => {
  324. result.import(serializeReport(report));
  325. });
  326. return result;
  327. }
  328. function parseDescription(description) {
  329. const result = {
  330. reportId: required(childAttr(description, 'rID'), 'rID'),
  331. reportType: required(childAttr(description, 'reportType'), 'reportType'),
  332. readingType: required(childAttr(description, 'readingType'), 'readingType'),
  333. };
  334. if (description['oadrSamplingRate']) {
  335. result.samplingRate = parseSamplingRate(
  336. description['oadrSamplingRate'][0]['$$'],
  337. );
  338. }
  339. return result;
  340. }
  341. function parseSamplingRate(samplingRate) {
  342. return {
  343. minPeriod: required(
  344. childAttr(samplingRate, 'oadrMinPeriod'),
  345. 'oadrMinPeriod',
  346. ),
  347. maxPeriod: required(
  348. childAttr(samplingRate, 'oadrMaxPeriod'),
  349. 'oadrMaxPeriod',
  350. ),
  351. onChange: required(
  352. boolean(childAttr(samplingRate, 'oadrOnChange'), 'oadrOnChange'),
  353. ),
  354. };
  355. }
  356. function parseDescriptions(descriptions) {
  357. return descriptions.map(x => parseDescription(x['$$']));
  358. }
  359. function parseReport(report) {
  360. const result = {
  361. reportRequestId: required(
  362. childAttr(report, 'reportRequestID'),
  363. 'reportRequestID',
  364. ),
  365. reportSpecifierId: required(
  366. childAttr(report, 'reportSpecifierID'),
  367. 'reportSpecifierID',
  368. ),
  369. createdDateTime: required(
  370. childAttr(report, 'createdDateTime'),
  371. 'createdDateTime',
  372. ),
  373. };
  374. const reportName = childAttr(report, 'reportName');
  375. if (reportName != null) {
  376. result.reportName = reportName;
  377. }
  378. const descriptions = report['oadrReportDescription'];
  379. if (descriptions != null) {
  380. result.descriptions = parseDescriptions(descriptions);
  381. }
  382. const durationValue = duration(childAttr(report, 'duration'), 'duration');
  383. if (durationValue != null) result.duration = durationValue;
  384. const dtStartValue = dateTime(childAttr(report, 'dtstart'), 'date-time');
  385. if (dtStartValue != null) {
  386. result.startDate = dtStartValue;
  387. }
  388. const intervalsHolder = childAttr(report, 'intervals');
  389. if (intervalsHolder != null && intervalsHolder['$$']) {
  390. const intervals = intervalsHolder['$$']['interval'];
  391. result.intervals = parseEventSignalIntervals(intervals);
  392. }
  393. return result;
  394. }
  395. function parseReports(reports) {
  396. return reports.map(x => parseReport(x['$$']));
  397. }
  398. function serializeEiResponse(data) {
  399. const descriptionFrag =
  400. data.responseDescription != null
  401. ? fragment()
  402. .ele(energyInteropNs, 'ei:responseDescription')
  403. .txt(data.responseDescription)
  404. : fragment();
  405. return fragment()
  406. .ele(energyInteropNs, 'ei:eiResponse')
  407. .ele(energyInteropNs, 'ei:responseCode')
  408. .txt(data.responseCode)
  409. .up()
  410. .import(descriptionFrag)
  411. .ele(energyInteropPayloadsNs, 'pyld:requestID')
  412. .txt(data.responseRequestId)
  413. .up()
  414. .up();
  415. }
  416. function serializeReportRequests(reportRequests) {
  417. const result = fragment();
  418. (reportRequests || []).forEach(x => result.import(serializeReportRequest(x)));
  419. return result;
  420. }
  421. function serializeReportRequest(reportRequest) {
  422. return fragment()
  423. .ele(oadrNs, 'oadr:oadrReportRequest')
  424. .ele(energyInteropNs, 'ei:reportRequestID')
  425. .txt(reportRequest.reportRequestId)
  426. .up()
  427. .import(serializeReportSpecifier(reportRequest));
  428. }
  429. function serializeSpecifierPayloads(specifierPayloads) {
  430. return specifierPayloads.map(specifierPayload => {
  431. return fragment()
  432. .ele(energyInteropNs, 'ei:specifierPayload')
  433. .ele(energyInteropNs, 'ei:rID')
  434. .txt(specifierPayload.reportId)
  435. .up()
  436. .ele(energyInteropNs, 'ei:readingType')
  437. .txt(specifierPayload.readingType)
  438. .up()
  439. .up();
  440. });
  441. }
  442. function serializeReportSpecifier(reportRequest) {
  443. const result = fragment();
  444. const specifier = result.ele(energyInteropNs, 'ei:reportSpecifier');
  445. specifier
  446. .ele(energyInteropNs, 'ei:reportSpecifierID')
  447. .txt(reportRequest.reportSpecifierId)
  448. .up()
  449. .ele(calendarNs, 'cal:granularity')
  450. .ele(calendarNs, 'cal:duration')
  451. .txt(reportRequest.granularityDuration)
  452. .up()
  453. .up()
  454. .ele(energyInteropNs, 'ei:reportBackDuration')
  455. .ele(calendarNs, 'cal:duration')
  456. .txt(reportRequest.reportBackDuration)
  457. .up()
  458. .up()
  459. .ele(energyInteropNs, 'ei:reportInterval')
  460. .ele(calendarNs, 'cal:properties')
  461. .ele(calendarNs, 'cal:dtstart')
  462. .import(serializeDateTime(reportRequest.startDate))
  463. .up()
  464. .ele(calendarNs, 'cal:duration')
  465. .import(serializeDuration(reportRequest.duration))
  466. .up();
  467. if (reportRequest.specifiers) {
  468. serializeSpecifierPayloads(reportRequest.specifiers).forEach(x =>
  469. specifier.import(x),
  470. );
  471. }
  472. return result;
  473. }
  474. function parseSpecifierPayload(specifierPayload) {
  475. return {
  476. reportId: required(childAttr(specifierPayload, 'rID'), 'rID'),
  477. readingType: required(
  478. childAttr(specifierPayload, 'readingType'),
  479. 'readingType',
  480. ),
  481. };
  482. }
  483. function parseSpecifierPayloads(specifierPayloads) {
  484. return specifierPayloads.map(x => parseSpecifierPayload(x['$$']));
  485. }
  486. function parseReportRequest(reportRequest) {
  487. const reportSpecifier = reportRequest['reportSpecifier'][0]['$$'];
  488. const reportInterval = reportSpecifier['reportInterval'][0]['$$'];
  489. const reportIntervalProperties = reportInterval['properties'][0]['$$'];
  490. const result = {
  491. reportRequestId: required(
  492. childAttr(reportRequest, 'reportRequestID'),
  493. 'reportRequestID',
  494. ),
  495. reportSpecifierId: required(
  496. childAttr(reportSpecifier, 'reportSpecifierID'),
  497. 'reportSpecifierID',
  498. ),
  499. granularityDuration: required(
  500. duration(childAttr(reportSpecifier, 'granularity'), 'duration'),
  501. 'granularity',
  502. ),
  503. reportBackDuration: required(
  504. duration(childAttr(reportSpecifier, 'reportBackDuration'), 'duration'),
  505. 'reportBackDuration',
  506. ),
  507. startDate: required(
  508. dateTime(childAttr(reportIntervalProperties, 'dtstart'), 'date-time'),
  509. 'dtstart',
  510. ),
  511. duration: required(
  512. duration(childAttr(reportIntervalProperties, 'duration'), 'duration'),
  513. 'duration',
  514. ),
  515. };
  516. if (reportSpecifier['specifierPayload']) {
  517. result.specifiers = parseSpecifierPayloads(
  518. reportSpecifier['specifierPayload'],
  519. );
  520. }
  521. return result;
  522. }
  523. function parseReportRequests(reportRequests) {
  524. return reportRequests.map(x => parseReportRequest(x['$$']));
  525. }
  526. function parseEiResponse(response) {
  527. return {
  528. code: required(childAttr(response, 'responseCode'), 'responseCode'),
  529. description: childAttr(response, 'responseDescription'),
  530. requestId: required(childAttr(response, 'requestID'), 'requestID'),
  531. };
  532. }
  533. function createDoc() {
  534. return create({
  535. namespaceAlias: {
  536. ns: oadrPayloadNs,
  537. oadr2b: oadrNs,
  538. ei: energyInteropNs,
  539. pyld: energyInteropPayloadsNs,
  540. cal: calendarNs,
  541. strm: calendarStreamNs,
  542. },
  543. });
  544. }
  545. module.exports = {
  546. createDoc,
  547. oadrPayloadNs,
  548. oadrNs,
  549. energyInteropNs,
  550. energyInteropPayloadsNs,
  551. calendarNs,
  552. calendarStreamNs,
  553. xsiNs,
  554. emixNs,
  555. powerNs,
  556. siScaleNs,
  557. serializeDateTime,
  558. serializeDuration,
  559. parsePayloadFloat,
  560. serializePayloadFloat,
  561. serializeEventSignalIntervals,
  562. parseEventSignalIntervals,
  563. serializeReports,
  564. parseReports,
  565. serializeEiResponse,
  566. parseEiResponse,
  567. serializeReportRequests,
  568. parseReportRequests,
  569. };