37 KiB
37 KiB
📊 Diagram Alur Kerja Printer Niimbot
🔄 Alur Lengkap: Dari UI ke Printer
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! 🎉