From 0fe96a7d4bc39f95d1e95054a6fdff92a2389f8a Mon Sep 17 00:00:00 2001
From: Timotej Ecimovic <timotej.ecimovic@silabs.com>
Date: Wed, 13 Jul 2022 13:57:58 -0400
Subject: [PATCH] Clean up server-mode for concurrent use (#598)

* Share the ipc socket same way as single-instance shares sockets.

* Some code cleanup.

* Increase feature level to trigger IDE tests.

* Make server load both zigbee and matter data.

* Fix the hang of the generation in the client/server case.
---
 apack.json                           |  2 +-
 package.json                         |  2 +-
 src-electron/main-process/main.ts    | 14 +++----
 src-electron/main-process/startup.js | 60 ++++++++++++----------------
 src-electron/server/ipc-server.ts    |  7 +++-
 src-electron/ui/main-ui.ts           | 21 +++++-----
 6 files changed, 50 insertions(+), 56 deletions(-)

diff --git a/apack.json b/apack.json
index 639875be..7f9a9747 100644
--- a/apack.json
+++ b/apack.json
@@ -4,7 +4,7 @@
   "description": "Graphical configuration tool for application and libraries based on Zigbee Cluster Library.",
   "path": [".", "node_modules/.bin/", "ZAP.app/Contents/MacOS"],
   "requiredFeatureLevel": "apack.core:9",
-  "featureLevel": 75,
+  "featureLevel": 76,
   "uc.triggerExtension": "zap",
   "executable": {
     "zap:win32.x86_64": {
diff --git a/package.json b/package.json
index 6f2ef0ca..1788fd7b 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,7 @@
     "zaphelp": "node src-script/zap-start.js --help",
     "zap-dotdot": "node src-script/zap-start.js --logToStdout --zcl ./zcl-builtin/dotdot/library.xml",
     "zap-devserver": "node src-script/zap-start.js server --allowCors --logToStdout --gen ./test/gen-template/zigbee/gen-templates.json --reuseZapInstance",
-    "server": "node src-script/zap-start.js server --logToStdout --gen ./test/gen-template/zigbee/gen-templates.json --reuseZapInstance",
+    "server": "node src-script/zap-start.js server --logToStdout --zcl ./zcl-builtin/silabs/zcl.json  --zcl ./zcl-builtin/matter/zcl.json --gen ./test/gen-template/zigbee/gen-templates.json --reuseZapInstance",
     "stop": "node src-script/zap-start.js stop --reuseZapInstance",
     "status": "node src-script/zap-start.js status --reuseZapInstance",
     "self-check": "node src-script/zap-start.js selfCheck -g ./test/gen-template/zigbee/gen-templates.json",
diff --git a/src-electron/main-process/main.ts b/src-electron/main-process/main.ts
index a8da168f..22b0eb9b 100644
--- a/src-electron/main-process/main.ts
+++ b/src-electron/main-process/main.ts
@@ -27,20 +27,16 @@ env.versionsCheck()
 env.setProductionEnv()
 
 let argv = args.processCommandLineArguments(process.argv)
-
 util.mainOrSecondaryInstance(
   argv.reuseZapInstance,
   () => {
-    startup.startUpMainInstance(
-      {
-        quitFunction: null,
-        uiEnableFunction: null,
-      },
-      argv
-    )
+    startup.startUpMainInstance(argv, {
+      quitFunction: () => process.exit(0),
+      uiEnableFunction: null,
+    })
   },
   () => {
-    startup.startUpSecondaryInstance(null, argv)
+    startup.startUpSecondaryInstance(argv, { quitFunction: null })
   }
 )
 
diff --git a/src-electron/main-process/startup.js b/src-electron/main-process/startup.js
index 92688b61..23d4123c 100644
--- a/src-electron/main-process/startup.js
+++ b/src-electron/main-process/startup.js
@@ -323,35 +323,28 @@ async function startServer(argv, quitFunction) {
     }
   })
   mainDatabase = db
-
-  await zclLoader.loadZclMetafiles(db, argv.zclProperties)
-  return generatorEngine
-    .loadTemplates(db, argv.generationTemplate)
-    .then((ctx) => {
-      if (ctx.error) {
-        env.logWarning(ctx.error)
+  try {
+    await zclLoader.loadZclMetafiles(db, argv.zclProperties)
+    let ctx = await generatorEngine.loadTemplates(db, argv.generationTemplate)
+    if (ctx.error) {
+      env.logWarning(ctx.error)
+    }
+    await httpServer.initHttpServer(
+      ctx.db,
+      argv.httpPort,
+      argv.studioHttpPort,
+      {
+        zcl: argv.zclProperties,
+        template: argv.generationTemplate,
+        allowCors: argv.allowCors,
       }
-      return ctx
-    })
-    .then((ctx) => {
-      return httpServer
-        .initHttpServer(ctx.db, argv.httpPort, argv.studioHttpPort, {
-          zcl: argv.zclProperties,
-          template: argv.generationTemplate,
-          allowCors: argv.allowCors,
-        })
-        .then(() => {
-          ipcServer.initServer(ctx.db, argv.httpPort)
-        })
-        .then(() => ctx)
-    })
-    .then((ctx) => {
-      logRemoteData(httpServer.httpServerStartupMessage())
-    })
-    .catch((err) => {
-      env.logError(err)
-      throw err
-    })
+    )
+    await ipcServer.initServer(ctx.db, argv.httpPort)
+    logRemoteData(httpServer.httpServerStartupMessage())
+  } catch (err) {
+    env.logError(err)
+    throw err
+  }
 }
 
 /**
@@ -554,12 +547,12 @@ function logRemoteData(data) {
  *
  * @param {*} argv
  */
-function startUpSecondaryInstance(quitFunction, argv) {
+function startUpSecondaryInstance(argv, callbacks) {
   console.log('🧐 Existing instance of zap will service this request.')
   ipcClient.initAndConnectClient().then(() => {
     ipcClient.on(ipcServer.eventType.overAndOut, (data) => {
       logRemoteData(data)
-      if (quitFunction != null) quitFunction()
+      if (callbacks.quitFunction != null) callbacks.quitFunction()
       else process.exit(0)
     })
 
@@ -598,7 +591,7 @@ function quit() {
  * @param {*} quitFunction
  * @param {*} argv
  */
-async function startUpMainInstance(callbacks, argv) {
+async function startUpMainInstance(argv, callbacks) {
   let quitFunction = callbacks.quitFunction
   let uiFunction = callbacks.uiEnableFunction
   if (quitFunction != null) {
@@ -671,7 +664,7 @@ async function startUpMainInstance(callbacks, argv) {
     })
   } else {
     // If we run with node only, we force no UI as it won't work.
-    if (quitFunction == null) {
+    if (uiEnableFunction == null) {
       argv.noUi = true
       argv.showUrl = true
       argv.standalone = false
@@ -681,11 +674,10 @@ async function startUpMainInstance(callbacks, argv) {
     let uiEnabled = !argv.noUi
     let zapFiles = argv.zapFiles
     let port = await startNormal(quitFunction, argv)
-    let showUrl = argv.showUrl
     if (uiEnabled && uiFunction != null) {
       uiFunction(port, zapFiles, argv.uiMode, argv.standalone)
     } else {
-      if (showUrl) {
+      if (argv.showUrl) {
         // NOTE: this is parsed/used by Studio as the default landing page.
         logRemoteData(httpServer.httpServerStartupMessage())
       }
diff --git a/src-electron/server/ipc-server.ts b/src-electron/server/ipc-server.ts
index a65aa10a..105ef149 100644
--- a/src-electron/server/ipc-server.ts
+++ b/src-electron/server/ipc-server.ts
@@ -20,6 +20,7 @@ import ipc from 'node-ipc'
 import * as env from '../util/env'
 import * as ipcTypes from '../../src-shared/types/ipc-types'
 const path = require('path')
+const os = require('os')
 const util = require('../util/util.js')
 const watchdog = require('../main-process/watchdog')
 const httpServer = require('../server/http-server.js')
@@ -47,7 +48,11 @@ const server: ipcTypes.Server = {
  * Returns the socket path for the IPC.
  */
 function socketPath() {
-  return path.join(env.appDirectory(), 'main.ipc')
+  var defaultSocketPath =
+    process.platform == 'win32'
+      ? '\\\\.\\pipe\\' + 'zap-ipc' + '-sock'
+      : path.join(os.tmpdir(), 'zap-ipc' + '.sock')
+  return defaultSocketPath
 }
 
 function log(msg: string) {
diff --git a/src-electron/ui/main-ui.ts b/src-electron/ui/main-ui.ts
index 4d24bb10..3bd8f584 100644
--- a/src-electron/ui/main-ui.ts
+++ b/src-electron/ui/main-ui.ts
@@ -15,11 +15,11 @@
  *    limitations under the License.
  */
 
-const { app } = require('electron')
-
 // enable stack trace to be mapped back to the correct line number in TypeScript source files.
 require('source-map-support').install()
 
+const { app } = require('electron')
+
 import * as args from '../util/args'
 const env = require('../util/env')
 const windowJs = require('./window')
@@ -31,7 +31,11 @@ env.versionsCheck()
 env.setProductionEnv()
 
 function hookSecondInstanceEvents(argv: args.Arguments) {
-  app.whenReady().then(() => startup.startUpSecondaryInstance(app.quit, argv))
+  app
+    .whenReady()
+    .then(() =>
+      startup.startUpSecondaryInstance(argv, { quitFunction: app.quit })
+    )
 }
 
 /**
@@ -41,13 +45,10 @@ function hookMainInstanceEvents(argv: args.Arguments) {
   app
     .whenReady()
     .then(() =>
-      startup.startUpMainInstance(
-        {
-          quitFunction: app.quit,
-          uiEnableFunction: uiUtil.enableUi,
-        },
-        argv
-      )
+      startup.startUpMainInstance(argv, {
+        quitFunction: app.quit,
+        uiEnableFunction: uiUtil.enableUi,
+      })
     )
     .catch((err) => {
       console.log(err)
-- 
GitLab