ip = $ip; $this->port = $port; $this->init(); } /** * Initialize Printer */ public function init(): self { $this->buffer .= self::ESC . "@"; // Reset return $this; } /** * Set text alignment * @param string $align 'left' | 'center' | 'right' */ public function setAlign(string $align): self { $val = 0; if ($align === 'center') $val = 1; if ($align === 'right') $val = 2; $this->buffer .= self::ESC . "a" . chr($val); return $this; } /** * Set text size * @param int $width 1-8 * @param int $height 1-8 */ public function setSize(int $width = 1, int $height = 1): self { $w = ($width - 1) << 4; $h = ($height - 1); $this->buffer .= self::GS . "!" . chr($w | $h); return $this; } /** * Set font emphasis (Bold) */ public function setBold(bool $enabled = true): self { $this->buffer .= self::ESC . "E" . chr($enabled ? 1 : 0); return $this; } /** * Print text line with Line Feed */ public function text(string $text): self { // ในระบบ Thermal Printer ไทยมักใช้ TIS-620 หรือ CP874 // แปลง UTF-8 เป็น CP874 $converted = @iconv('UTF-8', 'CP874//IGNORE', $text); $this->buffer .= ($converted !== false ? $converted : $text) . self::LF; return $this; } /** * Print Horizontal Line separator (80mm = 48 chars) */ public function line(): self { $this->text(str_repeat('-', 48)); return $this; } /** * Feed paper and cut */ public function cut(): self { $this->buffer .= self::LF . self::LF . self::LF; $this->buffer .= self::GS . "V" . chr(66) . chr(0); // Partial cut return $this; } /** * Get RAW buffer for WebUSB / Browser Print */ public function getBuffer(): string { return $this->buffer; } /** * Get Base64 encoded buffer for JSON API transfer to POS Frontend */ public function getBase64Buffer(): string { return base64_encode($this->buffer); } /** * Send command directly to Network LAN Printer via Socket * @return bool */ public function sendToPrinter(): bool { $fp = @fsockopen($this->ip, $this->port, $errno, $errstr, 2.0); if (!$fp) { error_log("ESC/POS Printer Connect Failed ({$this->ip}:{$this->port}) - {$errstr} ({$errno})"); return false; } fwrite($fp, $this->buffer); fclose($fp); return true; } /** * Build Queue Ticket Buffer (ใบคิวสำหรับผู้ป่วย) */ public static function buildQueueTicket(array $queueData): string { $printer = new self(); $printer->setAlign('center') ->setSize(2, 2)->setBold(true)->text("ใบคิวนวดแผนไทย") ->setSize(1, 1)->setBold(false)->text($queueData['branch_name'] ?? "TTMQMS Center") ->line() ->setSize(3, 3)->setBold(true)->text($queueData['queue_no']) ->setSize(1, 1)->setBold(false)->text("ประเภทคิว: " . ($queueData['priority'] ?? 'Normal')) ->line() ->setAlign('left') ->text("ผู้รับบริการ: " . ($queueData['patient_name'] ?? '-')) ->text("บริการ: " . ($queueData['service_name'] ?? '-')) ->text("หมอนวด: " . ($queueData['therapist_name'] ?? 'รอจัดสรร (AI Queue)')) ->text("ห้องนวด: " . ($queueData['room_no'] ?? '-')) ->line() ->setAlign('center') ->text("เวลาออกคิว: " . ($queueData['checkin_time'] ?? date('Y-m-d H:i:s'))) ->text("โปรดรอเรียกคิวที่หน้าจอ TV Display") ->text("*** ขอบพระคุณที่ใช้บริการ ***") ->cut(); return $printer->getBase64Buffer(); } }