isatdatapro-microservices
Version:
A library for creating microservices to access Inmarsat's IsatData Pro satellite IoT system
171 lines (165 loc) • 6.97 kB
JavaScript
/**
* submitForwardMessages module
* @module submitForwardMessages
*/
;
const logger = require('../infra/logging').loggerProxy(__filename);
const { submitForwardMessages } = require('isatdatapro-api');
const DatabaseContext = require('../infra/database/repositories');
const { getMobileMailbox, getMailboxGateway, handleApiResponse, handleApiFailure } = require('../infra/database/utilities');
const { ApiCallLog, MessageForward, Mobile } = require('../infra/database/models');
const supportedCommands = require('../infra/messageCodecs/coreModem').commandMessages;
const event = require('../infra/eventHandler');
/**
* A command structure shorthand for standard core modem operations
* @typedef {Object} ModemCommand
* @property {string} command The shorthand command name
* <br> 'ping' (no params)
* <br> 'getLocation' (no params)
* <br> 'setWakeupPeriod' (string)
* <br> 'getConfiguration' (no params)
* <br> 'reset' (optional string)
* <br> 'setTxMute' (boolean)
* <br> 'getBroadcastIds' (no params)
* @property {*} [params] Parameter(s) specific to the command
*/
/**
* A JSON message structure
* @typedef {Object} PayloadJson
* @property {number} codecServiceId A service definition number [16..255]
* @property {number} codecMessageId A message definition number [0..255]
* @property {string} [name] The message name
* @property {Object[]} fields A list of Field objects (https://github.com/Inmarsat/isatdatapro-api)
*/
/**
* Submits a command/message to a specified remote modem.
*
* Emits events:
* * ``NewForwardMessage``
* * ``NewMobile``
* * ``ApiError``
* * ``ApiOutage``
* * ``ApiRecovery``
* @param {string} mobileId The destination of the message
* @param {Object} message A wrapper for the command/message
* @param {Object} [message.payloadJson]
* @param {number[]} [message.payloadRaw] An array of decimal bytes [0..255]
* @param {ModemCommand} [message.modemCommand] A supported modem command shorthand
* @param {number} [userMessageId] an optional user message ID number
*/
module.exports = async function(mobileId, message, userMessageId) {
const thisFunction = {name: logger.getModuleName(__filename)};
logger.debug(`>>>> ${thisFunction.name} entry`);
const database = new DatabaseContext();
await database.initialize();
/**
* Submits a single message to a mobile/broadcast group and stores the result
* @private
* @param {MessageForward} forwardMessage A forward message entity
* @returns {number} the message ID
*/
async function submitMessage(forwardMessage) {
const operation = 'submitFowardMessages';
const mailbox = await getMobileMailbox(database, forwardMessage.mobileId);
const idpGateway = await getMailboxGateway(database, mailbox);
const auth = {
accessId: mailbox.accessId,
password: mailbox.passwordGet(),
};
const callTimeUtc = new Date().toISOString();
const apiCallLog = new ApiCallLog(operation, idpGateway.name,
mailbox.mailboxId, callTimeUtc);
await Promise.resolve(submitForwardMessages(auth, [forwardMessage.submit()],
idpGateway.url))
.then(async function (result) {
logger.debug(`${operation} result: ${JSON.stringify(result)}`);
const success = await handleApiResponse(database, result.errorId,
apiCallLog, idpGateway);
if (success) {
if (result.submissions.length > 0) {
for (let s = 0; s < result.submissions.length; s++) {
await forwardMessage.fromApi(result.submissions[s]);
forwardMessage.mailboxId = mailbox.mailboxId;
forwardMessage.mailboxTimeUtc = forwardMessage.stateTimeUtc;
forwardMessage.codecServiceId = forwardMessage.getCodecServiceId();
forwardMessage.codecMessageId = forwardMessage.getCodecMessageId();
const { messageId, mobileId } = forwardMessage;
if (forwardMessage.errorId !== 0) {
logger.debug(`Submission error: ${forwardMessage.error}`);
} else {
logger.debug(`Forward Message ID ${messageId} assigned` +
` by ${idpGateway.name} gateway`);
const messageFilter = { messageId: messageId };
const { id: messageDbId, created: newMessage } =
await database.upsert(forwardMessage, messageFilter);
if (newMessage) {
logger.debug(`Added forward message ${messageId} to database` +
` (${messageDbId})`);
event.newForwardMessage(forwardMessage);
let mobile = new Mobile();
mobile.mobileId = mobileId;
mobile.mailboxId = mailbox.mailboxId;
mobile.mobileWakeupPeriod = forwardMessage.wakeupPeriodEnum();
const mobileFilter = { mobileId: mobileId };
const { id: mailboxDbId, created: newMobile } =
await database.upsert(mobile, mobileFilter);
if (newMobile) {
logger.debug(`Mobile ${mobileId} added to database` +
` (${mailboxDbId})`);
event.newMobile(mobile);
}
}
}
}
} else {
logger.warn(`No submission accepted`);
}
}
})
.catch(async function (err) {
let apiOutage =
await handleApiFailure(database, err, idpGateway, operation);
if (!apiOutage) {
logger.error(err.stack);
throw err;
}
});
await database.upsert(apiCallLog);
// return message.messageId;
return forwardMessage;
}
try {
if (!mobileId || !message) {
throw new Error('Invalid arguments');
}
let forwardMessage = new MessageForward();
forwardMessage.mobileId = mobileId;
if (userMessageId) forwardMessage.userMessageId = userMessageId;
if (message.modemCommand) {
if (message.modemCommand.command in supportedCommands) {
const command = message.modemCommand.command;
const params = message.modemCommand.params;
forwardMessage.payloadJson = supportedCommands[command](params);
} else {
throw new Error(`Unsupported modem command:`
+ ` ${message.modemCommand}`);
}
} else if (message.payloadJson) {
//TODO: validate payload structure
forwardMessage.payloadJson = message.payloadJson;
} else if (message.payloadRaw) {
forwardMessage.payloadRaw = message.payloadRaw;
} else {
throw new Error('Invalid payload definition');
}
const messageMeta = await submitMessage(forwardMessage);
if (messageMeta) return messageMeta.messageId;
return null;
} catch (err) {
logger.error(err.stack);
throw err;
} finally {
await database.close();
logger.debug(`<<<< ${thisFunction.name} exit`);
}
}