From fa698164caec29d8d91696f96ee9d8b09933aeb3 Mon Sep 17 00:00:00 2001 From: Rama Priyanto Date: Wed, 1 Apr 2026 22:18:10 +0700 Subject: [PATCH] feat:socet --- app/dashboard/etle/page.tsx | 93 +++++++++++++++++++++++++++++-------- components/socket.tsx | 8 ++++ package-lock.json | 80 +++++++++++++++++++++++++++++++ package.json | 30 ++++++------ 4 files changed, 176 insertions(+), 35 deletions(-) create mode 100644 components/socket.tsx diff --git a/app/dashboard/etle/page.tsx b/app/dashboard/etle/page.tsx index df26d5e..dbf6cf8 100644 --- a/app/dashboard/etle/page.tsx +++ b/app/dashboard/etle/page.tsx @@ -13,18 +13,10 @@ import { DrawerTitle, DrawerTrigger, } from "@/components/ui/drawer" -import { - Sheet, - SheetClose, - SheetContent, - SheetDescription, - SheetFooter, - SheetHeader, - SheetTitle, - SheetTrigger, -} from "@/components/ui/sheet" import { Button } from "@/components/ui/button" import Image from "next/image" +import { useEffect, useState } from "react" +import { socket } from "@/components/socket" const dummy = [ { url: "/sample-1.jpg" }, @@ -40,6 +32,60 @@ const dummy = [ export default function Etle() { const router = useRouter() + const [sessionId, setSessionId] = useState(null) + const [imageSrc, setImageSrc] = useState("") + const [plate, setPlate] = useState("") + const [confidence, setConfidence] = useState(null) + const [history, setHistory] = useState([]) + + useEffect(() => { + if (!socket.connected) { + socket.connect() + } + + socket.on("connected", (data) => { + console.log("session", data.session_id) + setSessionId(data.session_id) + }) + + socket.emit("start_detection", { + stream_url: "/app/static/uploads/ds1", + }) + + socket.on("video_frame", (data) => { + console.log("frame", data.frame) + setImageSrc(`data:image/jpeg;base64,${data.frame}`) + }) + + socket.on("plate_detected", (data) => { + console.log("plate", data.plate_text) + setPlate(data.plate_text) + setConfidence(data.confidence ?? null) + + const thumb = `data:image/jpeg;base64,${data.frame_thumb}` + setHistory((prev) => [thumb, ...prev].slice(0, 20)) + }) + + socket.on("detection_stopped", (data) => { + console.log("Total:", data.total) + }) + + socket.on("error", (data) => { + console.error(data.message) + }) + + return () => { + socket.emit("stop_detection") + socket.removeAllListeners() + socket.disconnect() + } + }, []) + + // const startDetection = () => { + // socket.emit("start_detection", { + // stream_url: "/app/static/uploads/ds1", + // }) + // } return (
@@ -50,23 +96,25 @@ export default function Etle() {

TRAFFIC VIOLATIONS

- {"main-image"} + {/* */} + {imageSrc && ( + Realtime + )}

VIOLATIONS

- Plat yang terdeteksi berangka D1234PZ di 2026-01-08 jam 17:55:26 - berada di jalur 2 + {plate + ? `Plat ${plate} terdeteksi dengan confidence ${confidence}` + : "Menunggu deteksi..."}

@@ -91,6 +139,11 @@ export default function Etle() { />
))} + {history.map((img, index) => ( +
+ +
+ ))} diff --git a/components/socket.tsx b/components/socket.tsx new file mode 100644 index 0000000..82684ed --- /dev/null +++ b/components/socket.tsx @@ -0,0 +1,8 @@ +import { io, Socket } from "socket.io-client" + +const URL = "http://plate.koronyo.online/" +// const URL = "http://kubik.koroniyo.online:7272/" + +export const socket: Socket = io(URL, { + autoConnect: false, +}) diff --git a/package-lock.json b/package-lock.json index 576fd5f..ac8002a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "react-dom": "^19.2.4", "react-hook-form": "^7.71.2", "shadcn": "^4.0.8", + "socket.io-client": "^4.8.3", "tailwind-merge": "^3.5.0", "tailwindcss": "^4.2.1", "tw-animate-css": "^1.4.0", @@ -3184,6 +3185,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -5173,6 +5179,26 @@ "node": ">= 0.8" } }, + "node_modules/engine.io-client": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz", + "integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.20.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", @@ -9425,6 +9451,32 @@ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, + "node_modules/socket.io-client": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", + "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", + "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10410,6 +10462,26 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "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 + } + } + }, "node_modules/wsl-utils": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", @@ -10425,6 +10497,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 8f4337f..2975759 100644 --- a/package.json +++ b/package.json @@ -12,36 +12,36 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4.2.1", "@types/crypto-js": "^4.2.2", "@types/js-cookie": "^3.0.6", + "@types/node": "^25.5.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "crypto-js": "^4.2.0", "embla-carousel-react": "^8.6.0", + "eslint": "^9.39.4", + "eslint-config-next": "16.1.7", "js-cookie": "^3.0.5", "lucide-react": "^0.577.0", "next": "16.1.7", "next-themes": "^0.4.6", + "postcss": "^8", + "prettier": "^3.8.1", + "prettier-plugin-tailwindcss": "^0.7.2", "radix-ui": "^1.4.3", "react": "^19.2.4", "react-dom": "^19.2.4", "react-hook-form": "^7.71.2", "shadcn": "^4.0.8", + "socket.io-client": "^4.8.3", "tailwind-merge": "^3.5.0", - "tw-animate-css": "^1.4.0", - "vaul": "^1.1.2", - "@eslint/eslintrc": "^3", - "@tailwindcss/postcss": "^4.2.1", - "@types/node": "^25.5.0", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "eslint": "^9.39.4", - "eslint-config-next": "16.1.7", - "postcss": "^8", - "prettier": "^3.8.1", - "prettier-plugin-tailwindcss": "^0.7.2", "tailwindcss": "^4.2.1", - "typescript": "^5.9.3" - }, - "devDependencies": {} + "tw-animate-css": "^1.4.0", + "typescript": "^5.9.3", + "vaul": "^1.1.2" + } }