Kasir/Documentation/NiimbotPrinter-FlowChart.md
Baghaztra 8665584567 [Update] print label
Library niimblue tidak digunakan, namun potongan kode tetap disimpan
2025-10-15 14:33:47 +07:00

718 lines
37 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 📊 Diagram Alur Kerja Printer Niimbot
## 🔄 Alur Lengkap: Dari UI ke Printer
```mermaid
graph TD
A[User buka halaman Brankas] --> B{Printer sudah<br/>terhubung?}
B -->|Tidak| C[Klik 'Hubungkan Printer']
B -->|Ya| D[Status: Terhubung]
C --> E[Modal NiimbotConnector muncul]
E --> F{Pilih metode koneksi}
F -->|Bluetooth| G[Klik 'Hubungkan Printer']
F -->|USB/Serial| G
G --> H[Browser: Dialog pairing muncul]
H --> I[User pilih Niimbot device]
I --> J[Library: initClient & connect]
J --> K{Koneksi<br/>berhasil?}
K -->|Tidak| L[Tampilkan error]
K -->|Ya| M[Fetch printer info]
M --> N[Status: Terhubung ✓]
N --> D
D --> O[User klik item di Brankas]
O --> P[Modal item muncul]
P --> Q[Generate QR Code URL]
Q --> R[Tampilkan preview QR]
R --> S[User klik 'Cetak ke Niimbot']
S --> T{Printer<br/>terhubung?}
T -->|Tidak| U[Alert: Hubungkan printer]
U --> C
T -->|Ya| V[createQRLabelCanvas]
V --> W[Load QR image]
W --> X[Draw ke canvas:<br/>QR + Kode + Nama + Berat]
X --> Y[Convert canvas to DataURL]
Y --> Z[printQRCode composable]
Z --> AA[Stop heartbeat]
AA --> AB[Create PrintTask]
AB --> AC[ImageEncoder.encodeCanvas]
AC --> AD[printTask.printInit]
AD --> AE[printTask.printPage]
AE --> AF{Print<br/>sukses?}
AF -->|Tidak| AG[Tampilkan error]
AF -->|Ya| AH[printTask.printEnd]
AH --> AI[Start heartbeat]
AI --> AJ[Alert: Berhasil dicetak!]
AJ --> AK[Printer cetak label]
```
---
## 🏗️ Arsitektur Komponen
```
┌─────────────────────────────────────────────────────────────┐
│ BrankasList.vue │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ UI Layer │ │
│ │ - Tombol "Hubungkan Printer" │ │
│ │ - Tombol "Cetak ke Niimbot" │ │
│ │ - Modal item dengan QR Code │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Logic Layer │ │
│ │ - printQR() → Trigger print │ │
│ │ - createQRLabelCanvas() → Generate canvas dengan QR │ │
│ │ - handlePrinterConnected() → Event handler │ │
│ └────────────────────────────────────────────────────────┘ │
└───────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ useNiimbotPrinter.js (Composable) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ State Management │ │
│ │ - printerClient (reactive) │ │
│ │ - connectionState │ │
│ │ - isPrinting, printProgress │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Methods │ │
│ │ - initClient() → Buat NiimbotClient │ │
│ │ - connect() → Hubungkan ke printer │ │
│ │ - disconnect() → Putuskan koneksi │ │
│ │ - printQRCode() → Print image ke printer │ │
│ └────────────────────────────────────────────────────────┘ │
└───────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ @mmote/niimbluelib (Library) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ NiimbotBluetoothClient / NiimbotSerialClient │ │
│ │ - connect() → Web Bluetooth/Serial API │ │
│ │ - fetchPrinterInfo() → Get printer metadata │ │
│ │ - startHeartbeat() → Maintain connection │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ PrintTask │ │
│ │ - newPrintTask() → Buat task print │ │
│ │ - printInit() → Inisialisasi print │ │
│ │ - printPage() → Kirim data gambar │ │
│ │ - waitForFinished() → Tunggu selesai │ │
│ │ - printEnd() → Akhiri print │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ ImageEncoder │ │
│ │ - encodeCanvas() → Encode canvas ke binary │ │
│ └────────────────────────────────────────────────────────┘ │
└───────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Browser Web APIs │
│ - Web Bluetooth API (navigator.bluetooth) │
│ - Web Serial API (navigator.serial) │
└───────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Printer Niimbot (Hardware) │
│ - Terima perintah via BLE/USB │
│ - Decode binary image data │
│ - Print ke label thermal │
└─────────────────────────────────────────────────────────────┘
```
---
## 📡 Sequence Diagram: Proses Print
```
User BrankasList useNiimbot niimbluelib Browser API Printer
│ │ │ │ │ │
│ Klik item │ │ │ │ │
├───────────────>│ │ │ │ │
│ │ │ │ │ │
│ Modal muncul │ │ │ │ │
│<───────────────┤ │ │ │ │
│ │ │ │ │ │
│ Klik 'Cetak' │ │ │ │ │
├───────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ printQRCode()│ │ │ │
│ ├─────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ stopHeartbeat() │ │
│ │ ├─────────────>│ │ │
│ │ │ │ │ │
│ │ │ newPrintTask() │ │
│ │ ├─────────────>│ │ │
│ │ │ │ │ │
│ │ │ encodeCanvas() │ │
│ │ ├─────────────>│ │ │
│ │ │ │ │ │
│ │ │ │ BLE/Serial │ │
│ │ │ │ Write Data │ │
│ │ │ ├─────────────>│ │
│ │ │ │ │ │
│ │ │ │ │ Send Data │
│ │ │ │ ├───────────>│
│ │ │ │ │ │
│ │ │ │ │ Printing │
│ │ │ │ │ ........ │
│ │ │ │ │ │
│ │ │ │ Status ACK │ │
│ │ │ │<─────────────┤ │
│ │ │ │ │ │
│ │ │ printEnd() │ │ │
│ │ ├─────────────>│ │ │
│ │ │ │ │ │
│ │ │ startHeartbeat() │ │
│ │ ├─────────────>│ │ │
│ │ │ │ │ │
│ │ Alert sukses │ │ │ │
│ │<─────────────┤ │ │ │
│ │ │ │ │ │
│ Label tercetak│ │ │ │ │
│<───────────────┴──────────────┴──────────────┴──────────────┴────────────┤
│ │
```
---
## 🔌 Connection Flow Detail
### Bluetooth Connection
```
1. User Action
└─> Klik "Hubungkan Printer"
2. initClient('bluetooth')
└─> new NiimbotBluetoothClient()
3. connect()
└─> navigator.bluetooth.requestDevice({
filters: [{ namePrefix: 'Niimbot' }],
optionalServices: [SERVICE_UUID]
})
4. Browser Dialog
└─> User pilih device "Niimbot-XXXX"
5. device.gatt.connect()
└─> Establish BLE connection
6. getPrimaryService(SERVICE_UUID)
└─> getCharacteristic(TX_CHAR, RX_CHAR)
7. Event: 'connect' triggered
└─> connectionState = 'connected'
8. fetchPrinterInfo()
└─> Send command: GET_INFO
└─> Receive: model, serial, firmware version
9. startHeartbeat()
└─> Kirim ping setiap 1 detik
└─> Cek printer masih hidup
```
### USB/Serial Connection
```
1. User Action
└─> Klik "Hubungkan Printer"
2. initClient('serial')
└─> new NiimbotSerialClient()
3. connect()
└─> navigator.serial.requestPort({
filters: [{ usbVendorId: 0xXXXX }]
})
4. Browser Dialog
└─> User pilih USB device
5. port.open({ baudRate: 9600 })
└─> Establish serial connection
6. Setup reader/writer streams
└─> readable.getReader()
└─> writable.getWriter()
7. Event: 'connect' triggered
└─> connectionState = 'connected'
8. fetchPrinterInfo() & startHeartbeat()
└─> (sama dengan Bluetooth)
```
---
## 🖼️ Image Processing Pipeline
```
QR Code URL (dari API)
new Image()
img.onload
Create Canvas (384x384)
├─> Fill white background
├─> drawImage(qr, x, y, size, size)
├─> fillText(kode_item)
├─> fillText(nama_produk)
├─> fillText(berat)
canvas.toDataURL('image/png')
loadImageToCanvas(dataUrl)
[Optional] applyThreshold(canvas, 140)
├─> getImageData()
├─> for each pixel:
│ avg = (r+g+b)/3
│ if avg < 140: black
│ else: white
├─> putImageData()
ImageEncoder.encodeCanvas(canvas, 'top')
├─> Convert to 1-bit bitmap
├─> Rotate if needed (printDirection)
├─> Pack bits: 8 pixels = 1 byte
Binary Data (EncodedImage)
printTask.printPage(encoded, quantity)
Send to Printer via BLE/Serial
```
---
## 📊 State Machine Diagram
```
┌─────────────────┐
│ DISCONNECTED │◄────────┐
└────────┬────────┘ │
│ │
│ connect() │ disconnect()
│ │
▼ │
┌─────────────────┐ │
│ CONNECTING │ │
└────────┬────────┘ │
│ │
│ success │ error
│ │
▼ │
┌─────────────────┐ │
│ CONNECTED │─────────┤
└────────┬────────┘ │
│ │
│ printQRCode() │
│ │
▼ │
┌─────────────────┐ │
│ PRINTING │ │
│ [progress: X%] │ │
└────────┬────────┘ │
│ │
│ finished │
│ │
▼ │
┌─────────────────┐ │
│ CONNECTED │─────────┘
│ (heartbeat) │
└─────────────────┘
```
---
## 🎯 Data Flow: QR Code → Printed Label
```
┌────────────────────────────────────────────────────────────┐
│ 1. DATA SOURCE │
│ ─────────────────────────────────────────────────────────│
│ selectedItem = { │
│ kode_item: "BRN-001", │
│ produk: { │
│ nama: "Cincin Emas 24K", │
│ berat: 5.2 │
│ } │
│ } │
└────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ 2. QR CODE GENERATION (External API) │
│ ─────────────────────────────────────────────────────────│
│ URL: https://api.qrserver.com/v1/create-qr-code/ │
│ ?size=150x150&data=BRN-001 │
│ │
│ Returns: image/png (base64 or URL) │
└────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ 3. CANVAS CREATION │
│ ─────────────────────────────────────────────────────────│
│ createQRLabelCanvas(qrUrl, item) │
│ │
│ Canvas 384x384px: │
│ ┌──────────────────────┐ │
│ │ │ │
│ │ ┌──────────┐ │ ← QR Code (200x200) │
│ │ │ ▓▓ ▓▓▓▓│ │ │
│ │ │▓ ▓▓ ▓ │ │ │
│ │ │ ▓▓▓ ▓▓▓│ │ │
│ │ └──────────┘ │ │
│ │ │ │
│ │ BRN-001 │ ← Kode Item (bold 18px) │
│ │ Cincin Emas 24K │ ← Nama Produk (14px) │
│ │ 5.2g │ ← Berat (12px) │
│ │ │ │
│ └──────────────────────┘ │
└────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ 4. IMAGE ENCODING │
│ ─────────────────────────────────────────────────────────│
│ ImageEncoder.encodeCanvas(canvas, 'top') │
│ │
│ Process: │
│ - Convert RGB to grayscale │
│ - Apply threshold (< 140 = black, >= 140 = white) │
│ - Pack 8 pixels into 1 byte (1-bit bitmap) │
│ - Width: 384px = 48 bytes per row │
│ - Height: 384 rows │
│ - Total: 48 × 384 = 18,432 bytes │
│ │
│ Output: Uint8Array(18432) │
└────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ 5. PRINT PROTOCOL │
│ ─────────────────────────────────────────────────────────│
│ printTask.printInit() │
│ └─> Send: [CMD_INIT, density, labelType, ...] │
│ │
│ printTask.printPage(encoded, quantity) │
│ └─> Send in chunks: │
│ [CMD_IMAGE_HEADER, width, height] │
│ [CMD_IMAGE_DATA, chunk1...] │
│ [CMD_IMAGE_DATA, chunk2...] │
│ ... │
│ [CMD_IMAGE_END] │
│ │
│ printTask.waitForFinished() │
│ └─> Poll status every 100ms │
│ until status = FINISHED │
│ │
│ printTask.printEnd() │
│ └─> Send: [CMD_PRINT_END] │
└────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ 6. PHYSICAL OUTPUT │
│ ─────────────────────────────────────────────────────────│
│ Printer Niimbot: │
│ - Receives binary data via BLE/USB │
│ - Thermal head heats selected pixels │
│ - Paper feeds forward │
│ - Label printed with QR + text │
│ - Cut (automatic or manual) │
│ │
│ Result: Physical label 40x40mm dengan QR code │
└────────────────────────────────────────────────────────────┘
```
---
## 🧩 Component Interaction Map
```
┌──────────────────────┐
│ App.vue / Router │
└──────────┬───────────┘
┌──────────────────────┐
│ BrankasList.vue │
│ │
│ ┌────────────────┐ │
│ │ Header │ │
│ │ - Printer Btn │──┼──┐
│ └────────────────┘ │ │
│ │ │
│ ┌────────────────┐ │ │
│ │ Item List │ │ │
│ │ - Click item │──┼──┼──┐
│ └────────────────┘ │ │ │
│ │ │ │
│ ┌────────────────┐ │ │ │
│ │ Item Modal │ │ │ │
│ │ - QR Preview │ │ │ │
│ │ - Print Btn │──┼──┼──┼──┐
│ └────────────────┘ │ │ │ │
└──────────────────────┘ │ │ │
│ │ │
┌─────────────────────────────────────┘ │ │
│ │ │
▼ │ │
┌──────────────────┐ │ │
│ NiimbotConnector │ │ │
│ .vue │ │ │
│ │ │ │
│ - Connect UI │◄───────────────────────────┘ │
│ - Status display │ │
│ - Printer info │ │
└────────┬─────────┘ │
│ │
│ uses │
│ │
▼ │
┌──────────────────────────────────────────────────┼───┐
│ useNiimbotPrinter.js (Composable) │ │
│ │ │
│ - State: printerClient, connectionState, etc │◄──┘
│ - Methods: connect(), disconnect(), printQR() │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Event Listeners Setup │ │
│ │ - on('connect') │ │
│ │ - on('disconnect') │ │
│ │ - on('printprogress') │ │
│ │ - on('heartbeat') │ │
│ └─────────────────────────────────────────────┘ │
└──────────────────┬───────────────────────────────┘
│ imports
┌────────────────────────────────────────────────────┐
│ @mmote/niimbluelib │
│ │
│ - NiimbotBluetoothClient │
│ - NiimbotSerialClient │
│ - ImageEncoder │
│ - PrintTask abstraction │
│ - Utils │
└──────────────────┬─────────────────────────────────┘
│ uses
┌────────────────────────────────────────────────────┐
│ Browser APIs │
│ - navigator.bluetooth (Web Bluetooth API) │
│ - navigator.serial (Web Serial API) │
└────────────────────────────────────────────────────┘
```
---
## ⚡ Performance Timeline
```
Event Timeline (typical print job):
0ms │ User clicks "Cetak ke Niimbot"
10ms │ Check printer connection
│ └─> Already connected ✓
20ms │ createQRLabelCanvas() start
│ └─> Load QR image
150ms │ Image loaded, draw to canvas
│ └─> Draw QR, text, etc
170ms │ canvas.toDataURL()
200ms │ printQRCode() called
│ └─> stopHeartbeat()
220ms │ newPrintTask()
240ms │ ImageEncoder.encodeCanvas()
│ └─> RGB → grayscale
│ └─> Threshold
│ └─> Pack to 1-bit
450ms │ Encoding complete (18KB data)
470ms │ printTask.printInit()
│ └─> Send init command
490ms │ ACK received
500ms │ printTask.printPage()
│ └─> Send image header
520ms │ Send data chunk 1/10
540ms │ Send data chunk 2/10
... │ ...
740ms │ Send data chunk 10/10
750ms │ All data sent
780ms │ waitForFinished() polling start
800ms │ Status: PRINTING (0%)
900ms │ Status: PRINTING (25%)
1000ms │ Status: PRINTING (50%)
1100ms │ Status: PRINTING (75%)
1200ms │ Status: PRINTING (100%)
1220ms │ Status: FINISHED
1230ms │ printTask.printEnd()
1250ms │ startHeartbeat()
1260ms │ Alert: "QR Code berhasil dicetak!"
Total: ~1.3 seconds dari klik hingga selesai
```
---
## 🔐 Security & Privacy
```
Data Flow Security:
┌──────────────────┐
│ User's Browser │
└────────┬─────────┘
│ HTTPS (encrypted)
┌──────────────────┐
│ QR API Server │ ← External: api.qrserver.com
└────────┬─────────┘ (Kirim kode_item only)
│ Returns image URL
┌──────────────────┐
│ User's Browser │
│ - Generate │
│ canvas │
│ - Encode image │
└────────┬─────────┘
│ BLE/USB (direct, tidak via internet)
│ Encrypted jika BLE
┌──────────────────┐
│ Niimbot Printer │ ← Local device, tidak terkoneksi internet
└──────────────────┘
Privacy Notes:
- Data tidak lewat server backend aplikasi
- QR Code dibuat real-time di browser
- Gambar langsung dikirim ke printer lokal
- Tidak ada logging data item ke cloud
```
---
## 📈 Error Handling Flow
```
┌──────────────────┐
│ User Action │
└────────┬─────────┘
┌──────────────────┐
│ Try: connect() │
└────────┬─────────┘
┌────────────┴────────────┐
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ Success │ │ Error │
└──────┬──────┘ └──────┬──────┘
│ │
│ ▼
│ ┌────────────────┐
│ │ Catch error │
│ │ - Log to │
│ │ console │
│ │ - Set error │
│ │ message │
│ │ - Alert user │
│ └────────┬───────┘
│ │
│ ▼
│ ┌────────────────┐
│ │ connectionState│
│ │ = 'disconnected│
│ └────────────────┘
┌────────────────┐
│ fetchPrinterInfo│
└────────┬────────┘
┌──────┴──────┐
│ │
┌─────▼─────┐ ┌────▼────┐
│ Success │ │ Error │
└─────┬─────┘ └────┬────┘
│ │
│ └──> Log & continue (non-critical)
┌─────────────────┐
│ startHeartbeat()│
└─────────┬───────┘
└──> Connected & Ready
```
---
Semua diagram ini membantu memahami bagaimana sistem bekerja end-to-end! 🎉