From 8fb3bd40138b179ed9da9555bf57726370161df4 Mon Sep 17 00:00:00 2001 From: kchop Date: Thu, 9 Jan 2025 11:52:52 +0900 Subject: [PATCH] =?UTF-8?q?reference=20orbit=E3=82=9240=E5=80=8D=E9=80=9F?= =?UTF-8?q?=E3=81=8F=E8=A8=88=E7=AE=97=E3=81=A7=E3=81=8D=E3=82=8Brust=20cl?= =?UTF-8?q?ient=E3=82=92=E4=BD=BF=E3=81=88=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F=20(#50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Install ws * Install tsx * ChatGPTを頼りに作るwebsocket server/client * 計算クライアントとして登録できるようにした * エラー処理とかログとか追加 * blaTableは返さない * 計算用クライアントから値を受け取れるようになった が盛大に描画結果がぶっ壊れている * zeroが抜けてた * rust -> jsはJSONを経由するオーバーヘッドを減らすためバイナリで送る * NODE_ENVを読めるようにしておく * windows向けにcross-envしておく * websocketつなぐ部分を別メソッドに切り出した * 1回接続に失敗したらworker上での計算にfallbackする * 毎回ではなくworker立ち上げ時にwebsocket接続する * 途中で切断した場合などに対応 * README追加 * 邪魔 * なんか謎に2回wrapしてしまっていたのを修正 * workerの切り替え時にWebSocketを閉じるようにした --- .npmrc | 1 + package.json | 13 +- pnpm-lock.yaml | 314 ++++++++++++++++ rust_client/Cargo.lock | 611 ++++++++++++++++++++++++++++--- rust_client/Cargo.toml | 9 +- rust_client/README.md | 18 + rust_client/src/main.rs | 214 ++++++----- src/server/index.ts | 167 +++++++++ src/types.ts | 4 + src/worker-pool/pool-instance.ts | 43 ++- src/worker-pool/worker-facade.ts | 35 +- src/workers/calc-ref-orbit.ts | 266 ++++++++++---- vite.config.ts | 7 +- 13 files changed, 1453 insertions(+), 249 deletions(-) create mode 100644 .npmrc create mode 100644 rust_client/README.md create mode 100644 src/server/index.ts diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..cffe8cd --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +save-exact=true diff --git a/package.json b/package.json index 9ff8cf6..7cdc89e 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,11 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", - "build": "tsc && vite build", + "dev": "cross-env NODE_ENV=development vite", + "build": "cross-env NODE_ENV=production tsc && vite build", "preview": "vite preview", - "test": "vitest" + "test": "vitest", + "server": "tsx src/server/index.ts" }, "devDependencies": { "@radix-ui/colors": "2.1.0", @@ -20,12 +21,15 @@ "@types/p5": "1.7.6", "@types/react": "18.3.1", "@types/react-dom": "18.3.0", + "@types/ws": "8.5.12", "@vitejs/plugin-react": "4.2.1", "autoprefixer": "10.4.19", + "cross-env": "7.0.3", "postcss": "8.4.38", "prettier": "3.2.5", "prettier-plugin-tailwindcss": "0.5.14", "tailwindcss": "3.4.3", + "tsx": "4.19.0", "typescript": "5.4.5", "vite": "5.2.11", "vitest": "1.5.3", @@ -61,6 +65,7 @@ "react-dom": "18.3.1", "react-promise-suspense": "0.3.4", "tailwind-merge": "1.14.0", - "tailwindcss-animate": "1.0.7" + "tailwindcss-animate": "1.0.7", + "ws": "8.18.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef84a0b..4880794 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: tailwindcss-animate: specifier: 1.0.7 version: 1.0.7(tailwindcss@3.4.3) + ws: + specifier: 8.18.0 + version: 8.18.0 devDependencies: '@radix-ui/colors': specifier: 2.1.0 @@ -129,12 +132,18 @@ importers: '@types/react-dom': specifier: 18.3.0 version: 18.3.0 + '@types/ws': + specifier: 8.5.12 + version: 8.5.12 '@vitejs/plugin-react': specifier: 4.2.1 version: 4.2.1(vite@5.2.11(@types/node@20.12.8)) autoprefixer: specifier: 10.4.19 version: 10.4.19(postcss@8.4.38) + cross-env: + specifier: 7.0.3 + version: 7.0.3 postcss: specifier: 8.4.38 version: 8.4.38 @@ -147,6 +156,9 @@ importers: tailwindcss: specifier: 3.4.3 version: 3.4.3 + tsx: + specifier: 4.19.0 + version: 4.19.0 typescript: specifier: 5.4.5 version: 5.4.5 @@ -324,138 +336,282 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.20.2': resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} engines: {node: '>=12'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.20.2': resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} engines: {node: '>=12'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.20.2': resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} engines: {node: '>=12'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.20.2': resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.20.2': resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} engines: {node: '>=12'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.20.2': resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.20.2': resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.20.2': resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} engines: {node: '>=12'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.20.2': resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} engines: {node: '>=12'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.20.2': resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} engines: {node: '>=12'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.20.2': resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} engines: {node: '>=12'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.20.2': resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.20.2': resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.20.2': resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.20.2': resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} engines: {node: '>=12'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.20.2': resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} engines: {node: '>=12'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-x64@0.20.2': resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.20.2': resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.20.2': resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} engines: {node: '>=12'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.20.2': resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} engines: {node: '>=12'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.20.2': resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} engines: {node: '>=12'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.20.2': resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} engines: {node: '>=12'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@floating-ui/core@1.6.1': resolution: {integrity: sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==} @@ -1023,6 +1179,9 @@ packages: '@types/react@18.3.1': resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} + '@types/ws@8.5.12': + resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} + '@vitejs/plugin-react@4.2.1': resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -1201,6 +1360,11 @@ packages: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -1275,6 +1439,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -1344,6 +1513,9 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + get-tsconfig@4.8.0: + resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1782,6 +1954,9 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -1918,6 +2093,11 @@ packages: tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tsx@4.19.0: + resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} + engines: {node: '>=18.0.0'} + hasBin: true + type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -2044,6 +2224,18 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -2274,72 +2466,144 @@ snapshots: '@esbuild/aix-ppc64@0.20.2': optional: true + '@esbuild/aix-ppc64@0.23.1': + optional: true + '@esbuild/android-arm64@0.20.2': optional: true + '@esbuild/android-arm64@0.23.1': + optional: true + '@esbuild/android-arm@0.20.2': optional: true + '@esbuild/android-arm@0.23.1': + optional: true + '@esbuild/android-x64@0.20.2': optional: true + '@esbuild/android-x64@0.23.1': + optional: true + '@esbuild/darwin-arm64@0.20.2': optional: true + '@esbuild/darwin-arm64@0.23.1': + optional: true + '@esbuild/darwin-x64@0.20.2': optional: true + '@esbuild/darwin-x64@0.23.1': + optional: true + '@esbuild/freebsd-arm64@0.20.2': optional: true + '@esbuild/freebsd-arm64@0.23.1': + optional: true + '@esbuild/freebsd-x64@0.20.2': optional: true + '@esbuild/freebsd-x64@0.23.1': + optional: true + '@esbuild/linux-arm64@0.20.2': optional: true + '@esbuild/linux-arm64@0.23.1': + optional: true + '@esbuild/linux-arm@0.20.2': optional: true + '@esbuild/linux-arm@0.23.1': + optional: true + '@esbuild/linux-ia32@0.20.2': optional: true + '@esbuild/linux-ia32@0.23.1': + optional: true + '@esbuild/linux-loong64@0.20.2': optional: true + '@esbuild/linux-loong64@0.23.1': + optional: true + '@esbuild/linux-mips64el@0.20.2': optional: true + '@esbuild/linux-mips64el@0.23.1': + optional: true + '@esbuild/linux-ppc64@0.20.2': optional: true + '@esbuild/linux-ppc64@0.23.1': + optional: true + '@esbuild/linux-riscv64@0.20.2': optional: true + '@esbuild/linux-riscv64@0.23.1': + optional: true + '@esbuild/linux-s390x@0.20.2': optional: true + '@esbuild/linux-s390x@0.23.1': + optional: true + '@esbuild/linux-x64@0.20.2': optional: true + '@esbuild/linux-x64@0.23.1': + optional: true + '@esbuild/netbsd-x64@0.20.2': optional: true + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + '@esbuild/openbsd-x64@0.20.2': optional: true + '@esbuild/openbsd-x64@0.23.1': + optional: true + '@esbuild/sunos-x64@0.20.2': optional: true + '@esbuild/sunos-x64@0.23.1': + optional: true + '@esbuild/win32-arm64@0.20.2': optional: true + '@esbuild/win32-arm64@0.23.1': + optional: true + '@esbuild/win32-ia32@0.20.2': optional: true + '@esbuild/win32-ia32@0.23.1': + optional: true + '@esbuild/win32-x64@0.20.2': optional: true + '@esbuild/win32-x64@0.23.1': + optional: true + '@floating-ui/core@1.6.1': dependencies: '@floating-ui/utils': 0.2.2 @@ -2897,6 +3161,10 @@ snapshots: '@types/prop-types': 15.7.12 csstype: 3.1.3 + '@types/ws@8.5.12': + dependencies: + '@types/node': 20.12.8 + '@vitejs/plugin-react@4.2.1(vite@5.2.11(@types/node@20.12.8))': dependencies: '@babel/core': 7.24.5 @@ -3087,6 +3355,10 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.3 + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -3164,6 +3436,33 @@ snapshots: '@esbuild/win32-ia32': 0.20.2 '@esbuild/win32-x64': 0.20.2 + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + escalade@3.1.2: {} escape-string-regexp@1.0.5: {} @@ -3228,6 +3527,10 @@ snapshots: get-stream@8.0.1: {} + get-tsconfig@4.8.0: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -3559,6 +3862,8 @@ snapshots: resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve@1.22.8: dependencies: is-core-module: 2.13.1 @@ -3718,6 +4023,13 @@ snapshots: tslib@2.6.2: {} + tsx@4.19.0: + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.0 + optionalDependencies: + fsevents: 2.3.3 + type-detect@4.0.8: {} typescript@5.4.5: {} @@ -3834,6 +4146,8 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 + ws@8.18.0: {} + yallist@3.1.1: {} yaml@1.10.2: {} diff --git a/rust_client/Cargo.lock b/rust_client/Cargo.lock index d95e008..6c4d323 100644 --- a/rust_client/Cargo.lock +++ b/rust_client/Cargo.lock @@ -3,44 +3,82 @@ version = 3 [[package]] -name = "astro-float" -version = "0.9.4" +name = "addr2line" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ffbd469c925b479358981be1e1f1207ab20fecd8ed72734edcbcfc8e9374312" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ - "astro-float-macro", - "astro-float-num", + "gimli", ] [[package]] -name = "astro-float-macro" -version = "0.4.4" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac843ce8123b98c0da2667e1db6db6531d4dee133101a486afc6d8b1db34279" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ - "astro-float-num", - "proc-macro2", - "quote", - "syn 1.0.109", + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] [[package]] -name = "astro-float-num" -version = "0.3.5" +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf9647991ed5c8432085c326d70badf4d2480778434e657fb32a4fd353e441f" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "itertools", - "lazy_static", - "rand", - "serde", + "generic-array", ] [[package]] -name = "az" -version = "1.2.1" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cc" +version = "1.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93fe60e2fc87b6ba2c117f67ae14f66e3fc7d6a1e612a25adb238cc980eadb3" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -49,10 +87,153 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "either" -version = "1.11.0" +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] [[package]] name = "getrandom" @@ -65,6 +246,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + [[package]] name = "gmp-mpfr-sys" version = "1.6.4" @@ -76,25 +263,49 @@ dependencies = [ ] [[package]] -name = "itertools" -version = "0.10.5" +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ - "either", + "bytes", + "fnv", + "itoa", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "httparse" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libm" @@ -102,6 +313,99 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -156,6 +460,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags", +] + [[package]] name = "rug" version = "1.26.0" @@ -172,10 +485,34 @@ dependencies = [ name = "rust_client" version = "0.1.0" dependencies = [ - "astro-float", + "futures", + "futures-util", "rug", + "serde", + "serde_json", + "tokio", + "tokio-tungstenite", + "url", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.200" @@ -193,18 +530,70 @@ checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn", ] [[package]] -name = "syn" -version = "1.0.109" +name = "serde_json" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys", ] [[package]] @@ -218,12 +607,150 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/rust_client/Cargo.toml b/rust_client/Cargo.toml index a97ab33..8212026 100644 --- a/rust_client/Cargo.toml +++ b/rust_client/Cargo.toml @@ -6,5 +6,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -astro-float = "0.9.4" rug = "1.26" +tokio = { version = "1", features = ["full"] } +tokio-tungstenite = "0.23.1" +futures = "0.3" +futures-util = "0.3" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +url = "2.5.2" + diff --git a/rust_client/README.md b/rust_client/README.md new file mode 100644 index 0000000..ce17b6e --- /dev/null +++ b/rust_client/README.md @@ -0,0 +1,18 @@ +# Reference Orbit Calculation Client + +This Rust client enables high-speed calculation of reference orbits. + +With this client, calculations are approximately 40 times faster. wow. + +## How to Use + +- Launch the WebSocket server: + - `pnpm run server` +- Launch the reference orbit calculation client: + - `cd rust_client` + - `cargo run` + +## Restrictions + +- This client likely cannot run natively on Windows. + - Use WSL instead. diff --git a/rust_client/src/main.rs b/rust_client/src/main.rs index 226febe..3ec5eaf 100644 --- a/rust_client/src/main.rs +++ b/rust_client/src/main.rs @@ -1,144 +1,138 @@ +use futures_util::{SinkExt, StreamExt}; +use rug::{Complex, Float}; +use serde::{Deserialize, Serialize}; use std::time; +use tokio_tungstenite::tungstenite::client::IntoClientRequest; +use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; + +#[derive(Serialize, Deserialize)] +struct CalculationRequest { + r#type: String, + x: String, + y: String, + max_iter: u32, +} -use astro_float::ctx::Context; -use astro_float::expr; -use astro_float::BigFloat; -use astro_float::Consts; -use astro_float::Radix; -use astro_float::RoundingMode; -use astro_float::Sign; +#[derive(Serialize, Deserialize)] +struct CalculationResult { + r#type: String, + ref_orbit: Vec, +} -use rug::Complex; -use rug::Float; +#[derive(Serialize, Deserialize)] +struct ConnectionConfirmation { + r#type: String, + message: String, +} -fn main() { - astro_float(); - rug(); +#[derive(Serialize, Deserialize)] +struct RegisterMessage { + r#type: String, } -// [42129], (-2.69226756438447737599070014061584203959636738091161847602285848639761123410194460688365591129111950238937825917822133545062030386604782725746476089318437524247309293406301791962041207036106404987068099058831651440152659986060628237914104797847407455754446760853534983339722050909961416883759553792356752 -5.46135643926813444516672013346535484620971607690205358001033589997594833450480672732505512732181480038171452176139640005764563081044395780632149668971743799085475652710048431967203390608118740951107871375438161924651643805015533287275824560609450865881391103017112052644420969451909258233492629801012876e-1), 42129 -// Elapsed time: 55 ms -fn rug() { - let max_iteration = 50000; +const CALCULATION_RESULT: u8 = 0x03; - let now = time::Instant::now(); +#[tokio::main] +async fn main() -> Result<(), Box> { + let url = "ws://localhost:8080".into_client_request()?; + let (ws_stream, _) = connect_async(url).await?; + let (mut write, mut read) = ws_stream.split(); // ここで WebSocketStream を使う - let center_re_i = Float::parse("-1.408537400223374550983496866638703877765950056271735005951863022034495341910960396585990889377247354329184721916097300836171707822353071514393502045045428218599916142953125").unwrap(); - let center_im_i = Float::parse("0.1360385756605832424157267469300867279712448592945066056412400128811080204929672099044430962984916043688336280108617007855875705619618940154923777370644654005043363385").unwrap(); - let center = Complex::with_val( - 1000, - ( - Float::with_val(1000, center_re_i), - Float::with_val(1000, center_im_i), - ), - ); + println!("Connected to WebSocket server"); - let mut n = 0; + // サーバにcalculation_clientとして登録を通知 + let register_msg = RegisterMessage { + r#type: "register_calculation_client".to_string(), + }; + let register_msg_str = serde_json::to_string(®ister_msg)?; + write.send(Message::Text(register_msg_str)).await?; - let mut result: Vec = Vec::new(); + // 接続確認メッセージを待つ + if let Some(msg) = read.next().await { + let msg = msg?; - let mut z = Complex::with_val(1000, (0.0, 0.0)); - let mut z_norm = Float::with_val(1000, 0.0); + if msg.is_text() { + let confirmation: ConnectionConfirmation = serde_json::from_str(msg.to_text()?)?; - while n <= max_iteration && z_norm < 4.0 { - z = z.square() + center.clone(); + // 確認メッセージが"connection_confirmation"かどうか確認 + if confirmation.r#type == "connection_confirmation" { + println!("{}", confirmation.message); + } else { + println!("Failed to receive connection confirmation. Exiting."); + return Ok(()); + } + } + } else { + println!("Failed to receive any message. Exiting."); + return Ok(()); + } - result.push(z.real().to_f64()); - result.push(z.imag().to_f64()); + // ここから通常の処理を開始 + while let Some(msg) = read.next().await { + let msg = msg?; - z_norm = Float::with_val(53, z.norm_ref()); + if msg.is_text() { + let str = msg.to_text()?; + let request: CalculationRequest = serde_json::from_str(str)?; - n += 1; + if request.r#type == "calculation_request" { + println!( + "Received calculation request: x={}, y={}, max_iter={}", + request.x, request.y, request.max_iter + ); + let now = time::Instant::now(); + + let result = perform_calculation(request); + + let mut result_msg = vec![CALCULATION_RESULT]; + result_msg.extend(result.iter().flat_map(|f| f.to_le_bytes())); + + write.send(Message::Binary(result_msg)).await?; + + println!("Total elapsed time: {} ms", now.elapsed().as_millis()); + } + } } - println!("[{}], {}, {}", n, z, result.len() / 2); - println!("Elapsed time: {} ms", now.elapsed().as_millis()); + Ok(()) } -// [42129], -2.692267564384477375990700139235690639682249192220301141837896877354622969852955046474258185650781968202316948634501524813077171264645322593036692723718950230543305860214010564238442012933353125e+0, -5.461356439268134445166720127494617089573953495598505709703251404486120528760309805996442591162859166146263671500961915148334829886093231241298432413559442426346247314225775313008098636401168297e-1, 42129 -// Elapsed time: 1139 ms -fn astro_float() { - let p = 620; // 310 * 2 - let max_iteration = 50000; - +// 計算処理の関数 +fn perform_calculation(req: CalculationRequest) -> Vec { let now = time::Instant::now(); - let cc = Consts::new().expect("Constants cache initialized."); - let mut ctx = Context::new(p, RoundingMode::ToEven, cc, -10000, 10000); + let center_re_i = Float::parse(&req.x).unwrap(); + let center_im_i = Float::parse(&req.y).unwrap(); - let mut z_re = BigFloat::from_f64(0.0, p); - let mut z_im = BigFloat::from_f64(0.0, p); - let mut z_norm = BigFloat::from_f64(0.0, p); - - let bailout = BigFloat::from_f64(4.0, p); + let center = Complex::with_val( + 1000, + ( + Float::with_val(1000, center_re_i), + Float::with_val(1000, center_im_i), + ), + ); let mut n = 0; - let mut result: Vec = Vec::new(); + let mut z = Complex::with_val(1000, (0.0, 0.0)); + let mut z_norm = Float::with_val(1000, 0.0); - let center_re = BigFloat::parse("-1.408537400223374550983496866638703877765950056271735005951863022034495341910960396585990889377247354329184721916097300836171707822353071514393502045045428218599916142953125", Radix::Dec, p, RoundingMode::None, &mut ctx.consts()); - let center_im = BigFloat::parse("0.1360385756605832424157267469300867279712448592945066056412400128811080204929672099044430962984916043688336280108617007855875705619618940154923777370644654005043363385", Radix::Dec, p, RoundingMode::None, &mut ctx.consts()); - - // let a = expr!(center_re + center_im, &mut ctx); - - while n <= max_iteration && z_norm.cmp(&bailout).unwrap() < 0 { - let z_re2 = expr!(z_re * z_re, &mut ctx); - let z_im2 = expr!(z_im * z_im, &mut ctx); - let z_re_im = expr!(z_re * z_im, &mut ctx); - - z_re = expr!(z_re2 - z_im2 + center_re, &mut ctx); - z_im = expr!(2.0 * z_re_im + center_im, &mut ctx); + while n <= req.max_iter && z_norm < 4.0 { + result.push(z.real().to_f64()); + result.push(z.imag().to_f64()); - result.push(to_f64(&z_re, ctx.rounding_mode())); - result.push(to_f64(&z_im, ctx.rounding_mode())); + z = z.square() + center.clone(); - z_norm = expr!(z_re * z_re + z_im * z_im, &mut ctx); + z_norm = Float::with_val(1000, z.norm_ref()); n += 1; } - println!("[{}], {}, {}, {}", n, z_re, z_im, result.len() / 2); - println!("Elapsed time: {} ms", now.elapsed().as_millis()); -} + println!( + "Calculation elapsed time: {} ms (iter={})", + now.elapsed().as_millis(), + n + ); -// https://github.com/stencillogic/astro-float/issues/11#issuecomment-1998450484 -fn to_f64(big_float: &BigFloat, rounding_mode: RoundingMode) -> f64 { - let mut big_float = big_float.clone(); - big_float.set_precision(64, rounding_mode).unwrap(); - let sign = big_float.sign().unwrap(); - let exponent = big_float.exponent().unwrap(); - let mantissa = big_float.mantissa_digits().unwrap()[0]; - if mantissa == 0 { - return 0.0; - } - let mut exponent: isize = exponent as isize + 0b1111111111; - let mut ret = 0; - if exponent >= 0b11111111111 { - match sign { - Sign::Pos => f64::INFINITY, - Sign::Neg => f64::NEG_INFINITY, - } - } else if exponent <= 0 { - let shift = -exponent; - if shift < 52 { - ret |= mantissa >> (shift + 12); - if sign == Sign::Neg { - ret |= 0x8000000000000000u64; - } - f64::from_bits(ret) - } else { - 0.0 - } - } else { - let mantissa = mantissa << 1; - exponent -= 1; - if sign == Sign::Neg { - ret |= 1; - } - ret <<= 11; - ret |= exponent as u64; - ret <<= 52; - ret |= mantissa >> 12; - f64::from_bits(ret) - } + result } diff --git a/src/server/index.ts b/src/server/index.ts new file mode 100644 index 0000000..d30d967 --- /dev/null +++ b/src/server/index.ts @@ -0,0 +1,167 @@ +import { WebSocketServer, type WebSocket } from "ws"; + +interface CalculationClient { + ws: WebSocket; + busy: boolean; + queue: CalculationRequest[]; +} + +interface CalculationRequest { + ws: WebSocket; + data: { + x: number; + y: number; + maxIter: number; + }; +} + +const MAX_QUEUE_SIZE = 10; +const CALCULATION_RESULT = 0x03 as const; + +const wss = new WebSocketServer({ port: 8080 }); + +let calculationClients: CalculationClient[] = []; + +wss.on("connection", (ws: WebSocket) => { + console.log("New client connected"); + + ws.on("message", (message, isBinary) => { + let data: any; + if (isBinary && message instanceof Buffer) { + const type = message[0]; + + if (type === CALCULATION_RESULT) { + // ここでは何もしない + } else { + ws.send( + JSON.stringify({ + type: "error", + message: "Invalid binary message", + }), + ); + } + return; + } else { + try { + data = JSON.parse(message as any); + } catch (e) { + ws.send( + JSON.stringify({ + type: "error", + message: "Invalid JSON", + }), + ); + return; + } + + if (data.type === "register_calculation_client") { + // 計算を行うクライアントとして登録 + const newClient: CalculationClient = { ws: ws, busy: false, queue: [] }; + calculationClients.push(newClient); + console.log("Connected as calculation client"); + + ws.send( + JSON.stringify({ + type: "connection_confirmation", + message: "Connected as calculation client", + }), + ); + + // クライアント切断時の処理 + ws.on("close", () => { + handleClientDisconnection(newClient); + console.log( + `Calculation client disconnected. ${calculationClients.length} clients remaining`, + ); + }); + } else if (data.type === "calculation_request") { + // 計算要求クライアントからのリクエスト処理 + handleRequestClient(ws, data); + } + } + }); + + ws.on("close", () => { + console.log("Client disconnected"); + }); +}); + +const validateCalculationRequest = (data: any) => { + return ( + typeof data.x === "string" && + typeof data.y === "string" && + typeof data.maxIter === "number" + ); +}; + +function handleRequestClient(ws: WebSocket, data: any) { + if (!validateCalculationRequest(data)) { + ws.send( + JSON.stringify({ + type: "error", + message: "Invalid calculation request data", + }), + ); + return; + } + + const availableClient = calculationClients.find( + (client) => !client.busy || client.queue.length < MAX_QUEUE_SIZE, + ); + + if (availableClient) { + if (availableClient.busy) { + availableClient.queue.push({ ws: ws, data: data }); + } else { + availableClient.busy = true; + sendCalculationRequest(availableClient, ws, data); + } + } else { + ws.send( + JSON.stringify({ + type: "error", + message: + "All calculation clients are busy and queue is full. Please fallback to local calculation.", + }), + ); + } +} + +function sendCalculationRequest( + client: CalculationClient, + requestWs: WebSocket, + data: any, +) { + client.ws.send( + JSON.stringify({ + type: "calculation_request", + x: data.x, + y: data.y, + max_iter: data.maxIter, + }), + ); + + client.ws.once("message", (result: string) => { + requestWs.send(result); + + if (client.queue.length > 0) { + const nextRequest = client.queue.shift()!; + sendCalculationRequest(client, nextRequest.ws, nextRequest.data); + } else { + client.busy = false; + } + }); +} + +function handleClientDisconnection(client: CalculationClient) { + client.queue.forEach((request) => { + request.ws.send( + JSON.stringify({ + type: "error", + message: + "The calculation client has disconnected. Further waiting is futile.", + }), + ); + }); + calculationClients = calculationClients.filter((c) => c !== client); +} diff --git a/src/types.ts b/src/types.ts index 2ffe16c..a5cc3b2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,6 +39,10 @@ export interface RefOrbitProgress { progress: number; } +export interface RefOrbitShutdown { + type: "shutdown"; +} + export interface OffsetParams { x: number; y: number; diff --git a/src/worker-pool/pool-instance.ts b/src/worker-pool/pool-instance.ts index b085ca2..04ee6f0 100644 --- a/src/worker-pool/pool-instance.ts +++ b/src/worker-pool/pool-instance.ts @@ -1,23 +1,23 @@ -import { JobType, MandelbrotWorkerType } from "@/types"; -import { - CalcIterationWorker, - RefOrbitWorker, - MandelbrotFacadeLike, -} from "./worker-facade"; import { getStore, updateStore } from "@/store/store"; -import { clearTaskQueue } from "./task-queue"; -import { clearWorkerReference } from "./worker-reference"; +import { JobType, MandelbrotWorkerType } from "@/types"; import { - onIterationWorkerResult, onIterationWorkerIntermediateResult, onIterationWorkerProgress, + onIterationWorkerResult, } from "./callbacks/iteration-worker"; import { + onRefOrbitWorkerProgress, onRefOrbitWorkerResult, onRefOrbitWorkerTerminated, - onRefOrbitWorkerProgress, } from "./callbacks/ref-orbit-worker"; +import { clearTaskQueue } from "./task-queue"; +import { + CalcIterationWorker, + MandelbrotFacadeLike, + RefOrbitWorker, +} from "./worker-facade"; import { clearBatchContext, tickWorkerPool } from "./worker-pool"; +import { clearWorkerReference } from "./worker-reference"; type WorkerPool = MandelbrotFacadeLike[]; @@ -48,6 +48,19 @@ export const iterateAllWorker = (f: (worker: MandelbrotFacadeLike) => T) => { } }; +/** + * 全workerに対して非同期処理を行う + */ +export const iterateAllWorkerAsync = async ( + f: (worker: MandelbrotFacadeLike) => Promise, +) => { + const promises = []; + for (const workers of pool.values()) { + promises.push(...workers.map(f)); + } + return Promise.all(promises); +}; + /** * 全workerをリセットする */ @@ -85,7 +98,7 @@ export const calcNormalizedWorkerIndex = ( * WorkerPoolを再構築する * countやworkerTypeが変わった場合に呼ばれる */ -export function prepareWorkerPool( +export async function prepareWorkerPool( count: number = getStore("workerCount"), workerType: MandelbrotWorkerType = getStore("mode"), ) { @@ -96,7 +109,7 @@ export function prepareWorkerPool( updateStore("shouldReuseRefOrbit", false); } - resetWorkers(); + await resetWorkers(); fillIterationWorkerPool(count, workerType); fillRefOrbitWorkerPool(1 /* 仮 */, workerType); @@ -105,10 +118,10 @@ export function prepareWorkerPool( /** * WorkerPoolを溜まっていたJobごと全部リセットする */ -export function resetWorkers() { - iterateAllWorker((workerFacade) => { +export async function resetWorkers() { + await iterateAllWorkerAsync(async (workerFacade) => { workerFacade.clearCallbacks(); - workerFacade.terminate(); + await workerFacade.terminateAsync(); }); resetAllWorker(); diff --git a/src/worker-pool/worker-facade.ts b/src/worker-pool/worker-facade.ts index 4c076b9..a79f940 100644 --- a/src/worker-pool/worker-facade.ts +++ b/src/worker-pool/worker-facade.ts @@ -2,13 +2,14 @@ import { BatchContext, CalcIterationJob, CalcRefOrbitJob, + IterationIntermediateResult, + IterationProgress, + IterationResult, MandelbrotJob, MandelbrotWorkerType, RefOrbitProgress, RefOrbitResult, - IterationIntermediateResult, - IterationProgress, - IterationResult, + type RefOrbitShutdown, } from "@/types"; import { refOrbitWorkerPath, workerPaths } from "@/workers"; import { RefOrbitContext } from "@/workers/calc-ref-orbit"; @@ -95,12 +96,7 @@ export class CalcIterationWorker implements MandelbrotFacadeLike { case "result": { const { iterations, elapsed } = data; - const iterationsResult = new Uint32Array(iterations); - - this.resultCallback?.( - { type: "result", iterations: iterationsResult, elapsed }, - job, - ); + this.resultCallback?.({ type: "result", iterations, elapsed }, job); this.worker.removeEventListener("message", f); this.running = false; @@ -111,7 +107,7 @@ export class CalcIterationWorker implements MandelbrotFacadeLike { this.intermediateResultCallback?.( { type: "intermediateResult", - iterations: new Uint32Array(iterations), + iterations: iterations, resolution, }, job, @@ -285,6 +281,7 @@ export class RefOrbitWorker implements MandelbrotFacadeLike { Atomics.store(t, workerIdx, 0); this.worker.postMessage({ + type: "calc-reference-orbit", complexCenterX, complexCenterY, pixelWidth, @@ -304,10 +301,22 @@ export class RefOrbitWorker implements MandelbrotFacadeLike { }; terminateAsync = () => { - this.worker.terminate(); + const promise = new Promise((resolve) => { + const handler = (ev: MessageEvent) => { + if (ev.data.type === "shutdown") { + this.worker.terminate(); + this.running = false; + this.worker.removeEventListener("message", handler); + resolve(); + } + }; - this.running = false; - return Promise.resolve(); + this.worker.addEventListener("message", handler); + + this.worker.postMessage({ type: "request-shutdown" }); + }); + + return promise; }; cancel = ({ terminator }: BatchContext, { workerIdx }: MandelbrotJob) => { diff --git a/src/workers/calc-ref-orbit.ts b/src/workers/calc-ref-orbit.ts index d6c6d5e..6441a5f 100644 --- a/src/workers/calc-ref-orbit.ts +++ b/src/workers/calc-ref-orbit.ts @@ -1,5 +1,7 @@ /// +import { encodeBlaTableItems } from "@/lib/bla-table-item-buffer"; +import { encodeComplexArray } from "@/lib/xn-buffer"; import BigNumber from "bignumber.js"; import { BLATableItem, @@ -18,9 +20,7 @@ import { toComplex, } from "../math"; import { pixelToComplexCoordinateComplexArbitrary } from "../math/complex-plane"; -import { RefOrbitWorkerParams, XnBuffer, BLATableBuffer } from "../types"; -import { encodeComplexArray } from "@/lib/xn-buffer"; -import { encodeBlaTableItems } from "@/lib/bla-table-item-buffer"; +import { BLATableBuffer, RefOrbitWorkerParams, XnBuffer } from "../types"; export type RefOrbitContext = { xn: XnBuffer; @@ -33,6 +33,10 @@ export type RefOrbitContextPopulated = { blaTable: BLATableItem[][]; }; +// FIXME: 手抜き +let websocketServerConnected = false; +let ws: WebSocket | null = null; + function calcRefOrbit( center: ComplexArbitrary, maxIteration: number, @@ -136,76 +140,214 @@ function calcBLACoefficient(ref: Complex[], pixelSpacing: number) { return blaTable; } +/** + * 別serverでrefOrbitを計算する + * + * FIXME: serverが立っていない場合はとりあえず勝手に落ちるに任せている + */ +async function calcRefOrbitExternal( + referencePoint: ComplexArbitrary, + maxIteration: number, +): Promise { + if (ws == null || ws.readyState !== WebSocket.OPEN) { + return []; + } + + let xnn: number[] = []; + + const promise = new Promise((resolve) => { + ws?.addEventListener( + "message", + (ev) => { + const message = ev.data; + if (typeof message === "string") { + const data = JSON.parse(message); + if (data.type === "error") { + console.error("Received message:", data); + } else { + console.log("Received message:", data); + } + } else if (message instanceof ArrayBuffer) { + const view = new DataView(message); + const type = view.getUint8(0); + + if (type === 0x03) { + for (let i = 1; i < view.byteLength; i += 8) { + xnn.push(view.getFloat64(i, true)); + } + } + } + resolve(); + }, + { once: true }, + ); + }); + + ws.send( + JSON.stringify({ + type: "calculation_request", + x: referencePoint.re.toString(), + y: referencePoint.im.toString(), + maxIter: maxIteration, + }), + ); + + await promise; + + const n = Math.floor(xnn.length / 2); + let xn: Complex[] = []; + + for (let i = 0; i < n; i++) { + xn.push({ re: xnn[i * 2], im: xnn[i * 2 + 1] }); + } + + return xn; +} + +/** + * websocket serverに接続できるならしておく + */ +async function initWebsocketServer() { + ws = new WebSocket("ws://localhost:8080"); + ws.binaryType = "arraybuffer"; + + return new Promise((resolve, reject) => { + if (ws == null) return reject("WebSocket is not initialized"); + + ws.addEventListener("error", (event) => { + console.error("Failed to connect to websocket server!", event); + console.error( + "Check the server and refOrbit calculation rust client is running", + ); + reject(event); + }); + ws.addEventListener("open", () => { + console.log("Websocket connection established!"); + resolve(); + }); + // FIXME: 外からteminateされたときにWebSocketが閉じられない不具合がある + // MandelbrotFacadeLikeのterminateで即worker.terminateを呼ぶのではなくちゃんと後始末する + ws.addEventListener("close", () => { + websocketServerConnected = false; + ws = null; + console.log("Websocket connection closed."); + }); + }); +} + +/** + * worker起動時に呼ばれる + */ async function setup() { - // init here in future + if ( + process.env.NODE_ENV === "development" && + websocketServerConnected === false + ) { + try { + await initWebsocketServer(); + websocketServerConnected = true; + } catch {} + } self.postMessage({ type: "init" }); - self.addEventListener("message", (event) => { - const { - complexCenterX, - complexCenterY, - pixelHeight, - pixelWidth, - complexRadius: radiusStr, - maxIteration, - jobId, - terminator, - workerIdx, - } = event.data as RefOrbitWorkerParams; - - const startedAt = performance.now(); - console.debug(`${jobId}: start (ref)`); - - const terminateChecker = new Uint8Array(terminator); - - // 適当に中央のピクセルを参照点とする - const refPixelX = Math.floor(pixelWidth / 2); - const refPixelY = Math.floor(pixelHeight / 2); - - const center = complexArbitary(complexCenterX, complexCenterY); - const radius = new BigNumber(radiusStr); - - const referencePoint = pixelToComplexCoordinateComplexArbitrary( - refPixelX, - refPixelY, - center, - radius, - pixelWidth, - pixelHeight, - ); + self.addEventListener("message", async (event) => { + if (event.data.type === "calc-reference-orbit") { + const { + complexCenterX, + complexCenterY, + pixelHeight, + pixelWidth, + complexRadius: radiusStr, + maxIteration, + jobId, + terminator, + workerIdx, + } = event.data as RefOrbitWorkerParams; + + const startedAt = performance.now(); + console.debug(`${jobId}: start (ref)`); + + const terminateChecker = new Uint8Array(terminator); + + // 適当に中央のピクセルを参照点とする + const refPixelX = Math.floor(pixelWidth / 2); + const refPixelY = Math.floor(pixelHeight / 2); + + const center = complexArbitary(complexCenterX, complexCenterY); + const radius = new BigNumber(radiusStr); + + const referencePoint = pixelToComplexCoordinateComplexArbitrary( + refPixelX, + refPixelY, + center, + radius, + pixelWidth, + pixelHeight, + ); + + let xn: Complex[] = []; + + try { + // とりあえずrefOrbit計算serverは開発環境のみ使えるようにしておく + // worker起動のタイミングでwebsocket serverが立ち上がってなかったら、次からローカルで計算する + if ( + process.env.NODE_ENV === "development" && + websocketServerConnected + ) { + xn = await calcRefOrbitExternal(referencePoint, maxIteration); + } + } catch { + console.warn( + "Failed to calculate refOrbit on external server. Fallback.", + ); + } - const { xn } = calcRefOrbit( - referencePoint, - maxIteration, - terminateChecker, - workerIdx, - ); + if (xn.length === 0) { + // この時点で計算結果がない場合はローカルで計算する + const { xn: xn2 } = calcRefOrbit( + referencePoint, + maxIteration, + terminateChecker, + workerIdx, + ); + xn = xn2; + } - if (terminateChecker[workerIdx] !== 0) { - console.debug(`${jobId}: terminated (ref)`); - self.postMessage({ - type: "terminated", - }); - return; - } + if (terminateChecker[workerIdx] !== 0) { + console.debug(`${jobId}: terminated (ref)`); + self.postMessage({ + type: "terminated", + }); + return; + } - const pixelSpacing = radius.toNumber() / Math.max(pixelWidth, pixelHeight); - const blaTable = calcBLACoefficient(xn, pixelSpacing); + console.log("Reference orbit calculated", xn); - const xnConverted = encodeComplexArray(xn); - const blaTableConverted = encodeBlaTableItems(blaTable); + const pixelSpacing = + radius.toNumber() / Math.max(pixelWidth, pixelHeight); + const blaTable = calcBLACoefficient(xn, pixelSpacing); - const elapsed = performance.now() - startedAt; + const xnConverted = encodeComplexArray(xn); + const blaTableConverted = encodeBlaTableItems(blaTable); - self.postMessage({ - type: "result", - xn: xnConverted, - blaTable: blaTableConverted, - elapsed, - }); + const elapsed = performance.now() - startedAt; + + self.postMessage({ + type: "result", + xn: xnConverted, + blaTable: blaTableConverted, + elapsed, + }); - console.debug(`${jobId}: completed (ref)`); + console.debug(`${jobId}: completed (ref)`); + } else if (event.data.type === "request-shutdown") { + console.log("Shutdown requested"); + ws?.close(); + self.postMessage({ type: "shutdown" }); + } else { + console.error("Unknown message", event.data); + } }); } diff --git a/vite.config.ts b/vite.config.ts index f36e7a4..4775185 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -12,8 +12,11 @@ export default defineConfig({ }, server: { headers: { - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Embedder-Policy': 'require-corp' + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", }, }, + define: { + "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV), + }, });