| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- 'use strict';
- const logger = require('../logger');
- const nantum = require('../modules/nantum');
- const { v4 } = require('uuid');
- const reportSubscriptionParameters = {
- dataGranularitySeconds: 60,
- reportBackSeconds: 60,
- subscriptionDurationSeconds: 60 * 60,
- resubscribeDurationSeconds: 59 * 60,
- resubscribeAfterNoDataForSeconds: 90
- };
- function getSecondsSince(property, reportMetadata) {
- const value = reportMetadata[property];
- if (value != null) {
- const millisDiff = new Date().getTime() - new Date(value).getTime();
- if (millisDiff > 0) {
- return Math.round(millisDiff / 1000);
- }
- }
- }
- async function pollForReports(
- oadrPoll,
- clientCertificateCn,
- clientCertificateFingerprint,
- ) {
- logger.info(
- 'pollForReports',
- oadrPoll,
- clientCertificateCn,
- clientCertificateFingerprint,
- );
- const report = await nantum.fetchReport(clientCertificateFingerprint);
- const createRequests = [];
- if (report.venReportMetadata) {
- for (const reportMetadata of report.venReportMetadata) {
- let sendCreate = false;
- if (!reportMetadata.lastSentCreate) {
- // if we've never sent a subscription request, do it
- logger.info('sending create because we never have', reportMetadata.reportSpecifierId);
- sendCreate = true;
- } else {
- // have sent a create > 5s ago, not received a created
- if (
- !reportMetadata.lastReceivedCreated &&
- getSecondsSince('lastSentCreate', reportMetadata) > 5
- ) {
- logger.info('no reply to creation request, send another', reportMetadata.reportSpecifierId);
- sendCreate = true;
- }
- }
- if (
- getSecondsSince('lastReceivedUpdate', reportMetadata) >
- reportSubscriptionParameters.resubscribeAfterNoDataForSeconds
- ) {
- // previously received data, silent now
- sendCreate = true;
- }
- if (
- !reportMetadata.lastReceivedUpdate &&
- getSecondsSince('lastReceivedCreated', reportMetadata) >
- reportSubscriptionParameters.reportBackSeconds + 5
- ) {
- // if we haven't received any data but we've waited long enough for one data interval + 5 seconds
- logger.info('sending create because have not received data', reportMetadata.reportSpecifierId);
- sendCreate = true;
- }
- if (
- getSecondsSince('lastReceivedCreated', reportMetadata) >
- reportSubscriptionParameters.resubscribeDurationSeconds
- ) {
- // when we're close to the end of the subscription, trigger a resubscribe
- logger.info('sending create because close to end of subscription', reportMetadata.reportSpecifierId);
- sendCreate = true;
- }
- if (sendCreate) {
- const newReportRequestId = v4();
- // track the last 10 registration ids
- reportMetadata.reportRequestIds = [
- newReportRequestId,
- ...reportMetadata.reportRequestIds,
- ].slice(0, 10);
- createRequests.push({
- reportRequestId: newReportRequestId,
- reportSpecifierId: reportMetadata.reportSpecifierId,
- granularityDuration: `PT${reportSubscriptionParameters.dataGranularitySeconds}S`,
- reportBackDuration: `PT${reportSubscriptionParameters.reportBackSeconds}S`,
- startDate: new Date().toISOString(),
- duration: `PT${reportSubscriptionParameters.subscriptionDurationSeconds}S`,
- specifiers: reportMetadata.descriptions.map(description => ({
- reportId: description.reportId,
- readingType: 'x-notApplicable',
- })),
- });
- reportMetadata.lastSentCreate = new Date().toISOString();
- }
- }
- if (createRequests.length > 0) {
- const createReport = {
- _type: 'oadrCreateReport',
- requestId: v4(),
- requests: createRequests,
- };
- await nantum.updateReport(clientCertificateFingerprint, report);
- return createReport;
- }
- }
- }
- async function registerReports(
- oadrRegisterReport,
- clientCertificateCn,
- clientCertificateFingerprint,
- ) {
- logger.info(
- 'registerReports',
- oadrRegisterReport,
- clientCertificateCn,
- clientCertificateFingerprint,
- );
- const requestVenId = oadrRegisterReport.venId;
- validateVenId(requestVenId, clientCertificateFingerprint, false);
- const venReportMetadata = (oadrRegisterReport.reports || []).map(report => {
- const { reportSpecifierId, descriptions } = report;
- const lastReceivedRegister = new Date().toISOString();
- const reportRequestId = v4();
- return {
- reportRequestIds: [reportRequestId],
- reportSpecifierId,
- descriptions,
- lastReceivedRegister,
- };
- });
- //TODO: whitelist based off Nantum API sensors
- await nantum.updateReport(clientCertificateFingerprint, {
- venReportMetadata,
- });
- return {
- _type: 'oadrRegisteredReport',
- responseCode: '200',
- responseRequestId: oadrRegisterReport.requestId,
- responseDescription: 'OK',
- requests: [],
- };
- }
- async function createdReports(
- oadrCreatedReport,
- clientCertificateCn,
- clientCertificateFingerprint,
- ) {
- logger.info(
- 'createdReports',
- oadrCreatedReport,
- clientCertificateCn,
- clientCertificateFingerprint,
- );
- validateVenId(oadrCreatedReport.venId, clientCertificateFingerprint, false);
- if (oadrCreatedReport.pendingReports) {
- // flag reports as having been created
- const report = await nantum.fetchReport(clientCertificateFingerprint);
- if (report.venReportMetadata) {
- for (const pendingReport of oadrCreatedReport.pendingReports) {
- const reportRequestId = pendingReport['reportRequestId'];
- const match = report.venReportMetadata.filter(x =>
- x.reportRequestIds.includes(reportRequestId),
- )[0];
- if (match) {
- match.lastReceivedCreated = new Date().toISOString();
- } else {
- logger.info(
- 'could not match',
- reportRequestId,
- report.venReportMetadata,
- );
- }
- }
- }
- await nantum.updateReport(clientCertificateFingerprint, report);
- }
- return {
- _type: 'oadrResponse',
- responseCode: '200',
- responseDescription: 'OK',
- venId: clientCertificateFingerprint,
- };
- }
- async function receiveReportData(
- oadrUpdateReport,
- clientCertificateCn,
- clientCertificateFingerprint,
- ) {
- logger.info(
- 'receiveReportData',
- oadrUpdateReport,
- clientCertificateCn,
- clientCertificateFingerprint,
- );
- const requestVenId = oadrUpdateReport.venId;
- validateVenId(requestVenId, clientCertificateFingerprint, false);
- const report = await nantum.fetchReport(clientCertificateFingerprint);
- if (report.venReportMetadata) {
- for (const updateReport of oadrUpdateReport.reports) {
- const reportRequestId = updateReport.reportRequestId;
- const match = report.venReportMetadata.filter(x =>
- x.reportRequestIds.includes(reportRequestId),
- )[0];
- if (!match) {
- logger.info(
- 'could not match',
- reportRequestId,
- report.venReportMetadata,
- );
- continue;
- }
- match.lastReceivedUpdate = new Date().toISOString();
- for (const interval of updateReport.intervals || []) {
- const reportId = interval.reportPayloads[0].reportId;
- const date = interval.startDate;
- if (interval.reportPayloads[0].payloadFloat) {
- const value = interval.reportPayloads[0].payloadFloat;
- logger.info('received report', [
- date,
- clientCertificateFingerprint,
- updateReport.reportSpecifierId,
- updateReport.reportName,
- reportRequestId,
- reportId,
- value,
- ]);
- }
- if (
- interval.reportPayloads[0].payloadStatus &&
- interval.reportPayloads[0].payloadStatus.loadControlState
- ) {
- const loadControlState =
- interval.reportPayloads[0].payloadStatus.loadControlState;
- Object.keys(loadControlState).forEach(type => {
- const typeObj = loadControlState[type];
- Object.keys(typeObj).forEach(subType => {
- const value = typeObj[subType];
- logger.info('received report', [
- date,
- clientCertificateFingerprint,
- updateReport.reportSpecifierId,
- updateReport.reportName,
- reportRequestId,
- reportId,
- type,
- subType,
- value,
- ]);
- });
- });
- }
- }
- }
- }
- await nantum.updateReport(clientCertificateFingerprint, report);
- return {
- _type: 'oadrUpdatedReport',
- responseCode: '200',
- responseRequestId: oadrUpdateReport.requestId,
- responseDescription: 'OK',
- venId: clientCertificateFingerprint,
- };
- }
- function validateVenId(requestVenId, clientCertificateFingerprint, required) {
- if (requestVenId === clientCertificateFingerprint) {
- return;
- }
- if (!required && requestVenId == null) {
- return;
- }
- if (required && requestVenId == null) {
- const error = new Error('VenID is missing');
- error.responseCode = 452;
- throw error;
- }
- const error = new Error('VenID does not match certificate');
- error.responseCode = 452;
- throw error;
- }
- module.exports = {
- registerReports,
- createdReports,
- pollForReports,
- receiveReportData,
- };
|