Skip to content

Commit

Permalink
exit elegantly
Browse files Browse the repository at this point in the history
  • Loading branch information
timepp committed Jul 13, 2024
1 parent 918c60f commit 089a307
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 59 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.vite
/frontend/dist
45 changes: 7 additions & 38 deletions backend/api_impl.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,16 @@
import { BackendAPI } from '../api.ts'
import staticAssets from '../static_assets.json' with { type: "json" }

let scriptProcess: Deno.ChildProcess

const infoFile = Deno.env.get('TEMP') + '\\excel_info.txt'
function launchScript() {
// check if excel.js is present
let scriptFile = `./excel.js`
try {
Deno.statSync(scriptFile)
} catch (_e) {
scriptFile = Deno.env.get('TEMP') + '\\excel.js'
const content = staticAssets['excel.js']
Deno.writeTextFileSync(scriptFile, content)
}
const cmd = new Deno.Command('cscript.exe', {
args: ['//nologo', scriptFile, infoFile],
stdout: 'inherit',
stderr: 'inherit',
})
const p = cmd.spawn()
return p
}
import * as et from '../excel_tracker.ts'

export const apiImpl: BackendAPI = {
launchExcel: async () => {
if (scriptProcess) {
try {
scriptProcess.kill()
} catch (_e) {
// ignore
}
}
scriptProcess = launchScript()
await et.startNewTracker()
return ''
},
getActiveExcelRow: async () => {
// read output from script
const s = Deno.readTextFileSync(infoFile)
const [hs, vs] = s.split('_@@RS@@_')
return {
headings: hs.split('_@@HS@@_'),
data: vs.split('_@@VS@@_')
}
return await et.getActiveExcelRow()
}
}

export async function cleanUp() {
await et.stopTracker()
}
5 changes: 4 additions & 1 deletion build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// wrap some static assest to a single json file
// so that they are be imported to main app and can be created on demand on a http import use case

import * as vite from 'npm:[email protected]'

await vite.build()

const htmlContent = Deno.readTextFileSync('./frontend/dist/index.html')
const jsContent = Deno.readTextFileSync('./frontend/dist/assets/index.js')

Expand All @@ -15,7 +19,6 @@ const wshScriptContent = Deno.readTextFileSync('./excel.js')

const staticAssets = {
'/index.html': newHtmlContent,
'/index2.html': newHtmlContent,
'excel.js': wshScriptContent,
}

Expand Down
Binary file removed doc/screenshot.png
Binary file not shown.
5 changes: 3 additions & 2 deletions dwa/dwa_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { extname } from 'jsr:@std/[email protected]'
import staticAssets from '../static_assets.json' with { type: "json" }

export function startDenoWebApp(root: string, port: number, apiImpl: {[key: string]: Function}) {
const ac = new AbortController();
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
Expand All @@ -28,7 +29,7 @@ export function startDenoWebApp(root: string, port: number, apiImpl: {[key: stri
if (cmd === 'closeBackend') {
setTimeout(() => {
console.log('backend closed')
Deno.exit()
ac.abort()
}, 1000)
return new Response(JSON.stringify('OK'), { status: 200 });
}
Expand Down Expand Up @@ -63,6 +64,6 @@ export function startDenoWebApp(root: string, port: number, apiImpl: {[key: stri
}
};

Deno.serve({ port }, handlerCORS);
return Deno.serve({ port, signal: ac.signal }, handlerCORS);
}

15 changes: 9 additions & 6 deletions excel.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function saveActiveRow(excel, path) {
var headingsStr = headings.join('_@@HS@@_');
var rowValuesStr = rowValues.join('_@@VS@@_');
var content = [headingsStr, rowValuesStr].join('_@@RS@@_');
WScript.Echo(content);
// WScript.Echo(content);
WriteTextFile(content, path, 'utf-8');
}
}
Expand All @@ -44,15 +44,18 @@ excel.Visible = true;

var path = WScript.Arguments(0);

// for test, open a workbook
// excel.Workbooks.Open('d:\\src\\excelview\\test.xlsx');

for (;;) {
var cmd = WScript.StdIn.ReadLine();
if (cmd == "exit") {
WScript.Echo("received exit");
break;
}

try {
saveActiveRow(excel, path);
} catch (e) {
WScript.Echo(e.message);
}

WScript.Sleep(1000);
}

excel.Quit();
63 changes: 63 additions & 0 deletions excel_tracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Using "Excel.Application" through WScript to track active cell in Excel
// It's difficult to call COM (but possible) from FFI, so we use WScript for now
// However WScript(cscript.exe) has it's own problem:
// - It's impossible to receive unicode output from stdout pipe, we have to use file to communicate data
// - Need to create child process, make it a little bit complex

import staticAssets from './static_assets.json' with { type: "json" }

let scriptProcess: Deno.ChildProcess

const infoFile = Deno.env.get('TEMP') + '\\excel_info.txt'
function launchScript() {
// check if excel.js is present
let scriptFile = `./excel.js`
try {
Deno.statSync(scriptFile)
} catch (_e) {
scriptFile = Deno.env.get('TEMP') + '\\excel.js'
const content = staticAssets['excel.js']
Deno.writeTextFileSync(scriptFile, content)
}
const cmd = new Deno.Command('cscript.exe', {
args: ['//nologo', scriptFile, infoFile],
stdin: 'piped'
})
const p = cmd.spawn()
return p
}

async function sendCommand(cmd: string) {
if (scriptProcess) {
const writer = scriptProcess.stdin.getWriter()
await writer.write(new TextEncoder().encode(cmd + '\n'))
await writer.releaseLock()
}
}

export async function stopTracker() {
if (scriptProcess) {
sendCommand('exit')
await scriptProcess.status
}
}

export async function startNewTracker() {
await stopTracker()
scriptProcess = launchScript()
}

export async function getActiveExcelRow() {
if (scriptProcess) {
await sendCommand('get active row')
}
// wait the file to be created
await new Promise(resolve => setTimeout(resolve, 500))
// read output from script
const s = Deno.readTextFileSync(infoFile)
const [hs, vs] = s.split('_@@RS@@_')
return {
headings: hs.split('_@@HS@@_'),
data: vs.split('_@@VS@@_')
}
}
2 changes: 0 additions & 2 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
<head>
<title>Excel Navigate Helper</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.datatables.net/2.0.8/js/dataTables.js"></script>
<link href="https://cdn.datatables.net/2.0.8/css/dataTables.dataTables.css" rel="stylesheet">
<script src="./ui.ts" type="module"></script>
</head>
<body style="padding:10px;">
Expand Down
24 changes: 21 additions & 3 deletions frontend/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,27 @@ function updateStyle(app: HTMLElement, styleText: string) {
})
}

async function exit() {
try {
await api.closeBackend()
} catch (_e) {
// ignore
}
window.close()
}

async function getActiveExcelRow() {
try {
return await api.getActiveExcelRow()
} catch (e) {
console.error('backend exit', e)
window.close()
throw 'unreachable'
}
}

async function updateUI(app: HTMLElement, force: boolean = false) {
const activeRow = await api.getActiveExcelRow()
const activeRow = await getActiveExcelRow()
if (!force && currentRowData && JSON.stringify(currentRowData) === JSON.stringify(activeRow)) {
return
}
Expand Down Expand Up @@ -111,8 +130,7 @@ async function main() {
close.classList.add('btn', 'btn-danger')
close.textContent = 'Close';
close.onclick = async () => {
await api.closeBackend()
window.close()
await exit()
}
app.append(close)

Expand Down
2 changes: 1 addition & 1 deletion jsr.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@timepp/ev",
"version": "0.1.1",
"version": "0.1.2",
"exports": "./launch.ts"
}
24 changes: 21 additions & 3 deletions launch.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import * as vite from 'npm:[email protected]'
import { parseArgs } from "jsr:@std/[email protected]/parse-args";
import { apiImpl } from "./backend/api_impl.ts";
import * as apiImpl from "./backend/api_impl.ts";
import { startDenoWebApp } from "./dwa/dwa_service.ts";

async function main() {
const args = parseArgs(Deno.args)
const apiPort = 22311
startDenoWebApp('./frontend', apiPort, apiImpl);
const backend = startDenoWebApp('./frontend', apiPort, apiImpl.apiImpl);

let webPort = apiPort
let frontend: vite.ViteDevServer | null = null
// Use Vite for local development
if (!args.release && import.meta.url.startsWith('file://')) {
const frontend = await vite.createServer()
frontend = await vite.createServer()
webPort = 5173
frontend.listen(webPort)
}
Expand All @@ -22,6 +23,23 @@ async function main() {
})
const cp = cmd.spawn()
console.log('browser started, pid:', cp.pid)

const cleanUp = async () => {
console.log('cleaning up...')
if (frontend) {
await frontend.close()
}
await apiImpl.cleanUp()
}

Deno.addSignalListener('SIGINT', () => {
console.log('SIGINT received, shutting down backend')
backend.shutdown()
})

await backend.finished
await cleanUp()
console.log('App Exit')
}

await main()
Expand Down
Loading

0 comments on commit 089a307

Please sign in to comment.