blob: 3f42077c0a30b5bf5294ca511f35bada29ee8d2e [file] [log] [blame]
/**
*
* Copyright (c) 2020 Silicon Labs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This module provides IPC Server functionality.
*
* @module IPC Server API: Inter-process communication
*/
const ipc = require('node-ipc')
const env = require('../util/env')
const path = require('path')
const os = require('os')
const util = require('../util/util.js')
const watchdog = require('../main-process/watchdog')
const httpServer = require('./http-server.js')
const startup = require('../main-process/startup.js')
const queryPackage = require('../db/query-package.js')
const dbEnum = require('../../src-shared/db-enum.js')
const eventType = {
ping: 'ping', // Receiver responds with pong, returning the object.
pong: 'pong', // Return of the ping data, no response required.
over: 'over', // Sent from server to client as an intermediate printout.
overAndOut: 'overAndOut', // Sent from server to client as a final answer.
convert: 'convert', // Sent from client to server when requesting to convert files
generate: 'generate', // Sent from client to server when requesting generation.
serverStatus: 'serverStatus', // Sent from client to ask for server URL
stop: 'stop' // Sent from client to ask for server to shut down
}
const server = {
ipc: new ipc.IPC(),
serverStarted: false
}
/**
* Returns the socket path for the IPC.
*/
function socketPath() {
let defaultSocketPath =
process.platform == 'win32'
? 'zap-ipc-sock'
: path.join(os.tmpdir(), 'zap-ipc.sock')
return defaultSocketPath
}
/**
* Log IPC message.
*
* @param {*} msg
*/
function log(msg) {
env.logIpc(`Ipc server: ${msg}`)
}
/**
* Ping IPC server.
*
* @param {*} context
* @param {*} data
*/
function handlerPing(context, data) {
server.ipc.server.emit(context.socket, eventType.pong, data)
}
/**
* Get IPC server status.
*
* @param {*} context
*/
function handlerServerStatus(context) {
let svr = httpServer.httpServerStartupMessage()
svr.zapServerStatus = 'running'
server.ipc.server.emit(context.socket, eventType.overAndOut, svr)
}
/**
* Convert zap files.
*
* @param {*} context
* @param {*} data
*/
function handlerConvert(context, data) {
let zapFiles = data.files
server.ipc.server.emit(context.socket, eventType.over, 'Convert')
zapFiles.forEach((element) => {
server.ipc.server.emit(context.socket, eventType.over, `File: ${element}`)
})
server.ipc.server.emit(context.socket, eventType.overAndOut, 'Done.')
}
/**
* Shut down IPC server.
*
* @param {*} context
* @param {*} data
*/
function handlerStop(context, data) {
console.log('Shutting down because of remote client request.')
server.ipc.server.emit(
context.socket,
eventType.overAndOut,
'Shutting down server.'
)
startup.shutdown()
util.waitFor(1000).then(() => startup.quit())
}
/**
* Data contains: zapFileArray, outputPattern, zcl, template
*
* @param {*} context
* @param {*} data
* @returns Promise of generation
*/
async function handlerGenerate(context, data) {
let ps = []
let packages = await queryPackage.getPackagesByType(
context.db,
dbEnum.packageType.genTemplatesJson
)
let templatePackageId = packages[0].id
data.zapFileArray.forEach((zapFile, index) => {
ps.push(
startup.generateSingleFile(
context.db,
zapFile,
templatePackageId,
data.outputPattern,
index,
{
logger: (x) =>
server.ipc.server.emit(context.socket, eventType.over, x),
zcl: env.builtinSilabsZclMetafile(),
template: env.builtinTemplateMetafile()
}
)
)
})
return Promise.all(ps).then(() => {
server.ipc.server.emit(
context.socket,
eventType.overAndOut,
'Generation done.'
)
})
}
const handlers = [
{
eventType: eventType.ping,
handler: handlerPing
},
{
eventType: eventType.serverStatus,
handler: handlerServerStatus
},
{
eventType: eventType.convert,
handler: handlerConvert
},
{
eventType: eventType.generate,
handler: handlerGenerate
},
{
eventType: eventType.stop,
handler: handlerStop
}
]
/**
* Runs just before every time IPC request is processed.
*/
function preHandler() {
watchdog.reset()
}
/**
* IPC initialization.
*
* @parem {*} isServer 'true' if this is a server, 'false' for client.
* @param {*} options
*/
async function initServer(db = null, httpPort = 0) {
return new Promise((resolve, reject) => {
server.ipc.config.logger = log
server.ipc.config.id = 'main'
server.ipc.serve(socketPath(), () => {
env.logIpc('IPC server started.')
server.serverStarted = true
// Register top-level handlers
server.ipc.server.on('error', (err) => {
env.logIpc('IPC error', err)
})
server.ipc.server.on('connect', () => {
env.logIpc('New connection.')
watchdog.reset()
})
server.ipc.server.on('destroy', () => {
env.logIpc('IPC server destroyed.')
})
// Register individual type handlers
handlers.forEach((handlerRecord) => {
server.ipc.server.on(handlerRecord.eventType, (data, socket) => {
preHandler()
handlerRecord.handler(
{
db: db,
socket: socket,
httpPort: httpPort
},
data
)
})
})
resolve('')
})
server.ipc.server.start()
})
}
/**
* Returns true if server is running.
*
* @returns true if server is running.
*/
function isServerRunning() {
return server.serverStarted === true
}
/**
* Shuts down the IPC server.
*
* @param {*} isServer
*/
function shutdownServerSync() {
env.logIpc('Shutting down the server.')
if (server.ipc.server) {
server.ipc.server.stop()
server.serverStarted = false
} else {
env.logIpc('There is no server.')
}
}
exports.socketPath = socketPath
exports.initServer = initServer
exports.shutdownServerSync = shutdownServerSync
exports.isServerRunning = isServerRunning
exports.eventType = eventType