﻿# AOT Printer Agent - Info / Installation Guide

هذا الملف يشرح تشغيل وتركيب `printer-agent` على جهاز Windows موجود في نفس شبكة الطابعة، أو متصل بالطابعة مباشرة.

## الفكرة باختصار

`printer-agent` هو برنامج Windows صغير يشتغل كـ Windows Service على جهاز العميل. البرنامج لا يحتاج أن Laravel يفتح اتصال داخل شبكة العميل. كل الاتصال من جهاز العميل للخارج:

1. الـ agent يرسل heartbeat إلى Laravel.
2. الـ agent يسأل Laravel كل فترة عن print jobs جديدة.
3. لو فيه job، Laravel يرجع payload بصيغة `escpos_base64`.
4. الـ agent يفك الـ base64 ويرسل bytes للطابعة.
5. الـ agent يرجع حالة job إلى Laravel: `sent` أو `failed`.

الـ API endpoints المستخدمة:

```text
POST /api/printer-agents/heartbeat
POST /api/printer-agents/jobs/claim
POST /api/printer-agents/jobs/{job}/status
```

## الحالة الحالية للدعم

### IP / LAN printer

مدعوم حاليا من الكود الموجود عن طريق adapter اسمه:

```text
network_raw
```

هذا يطبع عن طريق TCP raw على IP الطابعة، غالبا port `9100`.

### USB printer

الطباعة المباشرة على USB غير جاهزة حاليا في التنفيذ الحالي.

الكونفيج يقبل اسم `windows_spooler`، لكن الكود الحالي لا يسجل adapter لهذا النوع. لو تم إرسال job لطابعة `windows_spooler` حاليا، سيظهر فشل من نوع `unsupported_adapter`.

الحلول العملية لطابعة USB:

1. الأفضل: استخدم طابعة لها IP على الشبكة واضبطها `network_raw`.
2. أو استخدم print server / network adapter يحول طابعة USB إلى IP/RAW 9100.
3. أو يتم تنفيذ adapter جديد باسم `windows_spooler` يرسل للطابعة عن طريق Windows printer queue.

للتشغيل الموثوق الآن، اعتمد على IP/LAN printer.

## ماذا ننزل على جهاز العميل؟

لو الـ exe مبني مسبقا، جهاز العميل لا يحتاج Go.

انسخ فولدر مثلا:

```text
C:\AOTPrinterAgent
```

وضع داخله:

```text
C:\AOTPrinterAgent\aot-printer-agent.exe
C:\AOTPrinterAgent\scripts\install-service.ps1
C:\AOTPrinterAgent\scripts\uninstall-service.ps1
```

لو تريد تشغيل مرئي للمستخدم بأيقونة بجانب الساعة، استخدم:

```text
run-agent-tray.bat
```

هذا الملف يشغل الـ agent في الخلفية ويظهر أيقونة Tray. من كليك يمين على الأيقونة تقدر تعمل:

```text
Show Monitor
Restart Agent
Reload Config
Open Config
Open Log Folder
Exit (stop agent)
```

شاشة الـ Monitor تعرض حالة الاتصال `Connected / Waiting / Stopped` وتعرض أحداث الطباعة والـ statuses بدون إزعاج heartbeat المستمر. زر `Restart` يوقف ويشغل الـ agent من جديد، و `Reload Config` يعيد قراءة `config.json` ثم يعيد التشغيل.

## التركيب السريع بملف BAT

لو Symantec حجز `aot-printer-agent.exe` أو ظهر `Heur.AdvML.D`، لا تعمل whitelist مباشرة. اختبر الكود أولا بدون تشغيل الـ exe:

```text
printer-agent\run-local-code-test.bat
```

هذا الاختبار يعمل من سورس Go مباشرة:

1. يشغل Fake Laravel داخل test.
2. يشغل Fake Printer على TCP داخل test.
3. يشغل agent logic بدون ملف exe.
4. يتأكد أن Fake Laravel رجع print job.
5. يتأكد أن Fake Printer استقبل نص الأوردر.
6. يتأكد أن status رجع `sent`.

لو الاختبار نجح، يبقى الكود والـ flow سليمين، والمشكلة في executable packaging / antivirus reputation.

لاختبار Laravel الحقيقي مع fake printer، استخدم:

```text
printer-agent\run-laravel-fake-printer-test.bat
```

هذا الاختبار يعمل الآتي:

1. يشغل migrations الخاصة بتجربة printer agent.
2. يشغل Fake Printer على `127.0.0.1:19100`.
3. يشغل Laravel server محلي على `http://127.0.0.1:18081`.
4. ينشئ test agent/token/printer/job داخل Laravel بأمر Artisan.
5. يشغل الـ agent exe مرة واحدة ضد Laravel الحقيقي.
6. يتأكد أن fake printer استلم الأوردر من Laravel.

النتيجة الناجحة تكون مثل:

```text
Fake printer received an order from Laravel.
AOT LARAVEL TEST
Order #2001
status=sent
run-once complete
```

قبل التركيب، لو مضاد الفيروسات حذف أو حجز الـ exe الجاهز، ابنه من المصدر المحلي:

```text
printer-agent\build-agent.bat
```

هذا الملف يعمل `go build` ويطبع SHA256 للملف الناتج. لو Symantec حجز الملف المبني حديثا أيضا، لا تعمل whitelist مباشرة. ارفع الـ hash أو الملف لقسم الأمن/مسؤول Symantec لمراجعة false positive، أو استخدم code-signing certificate قبل النشر.

تم إضافة ملف جاهز:

```text
printer-agent\install-agent.bat
```

طريقة الاستخدام على جهاز العميل:

1. انسخ فولدر `printer-agent` أو باكدج يحتوي على `install-agent.bat` و `scripts` و `dist\aot-printer-agent.exe`.
2. اضغط كليك يمين على `install-agent.bat`.
3. اختر `Run as administrator`.
4. أدخل البيانات التي سيطلبها:
   - `Agent UUID`. لو Laravel لسه مفيهوش شاشة/سجل للـ agent، اضغط Enter وسيولد UUID تلقائيا.
   - `Agent token`. لو Laravel لسه مفيهوش token، اضغط Enter وسيولد token مؤقت تحفظه وتضيفه في Laravel لاحقا.
   - `Laravel base URL`.
   - `Local printer ID`.
   - اسم الطابعة.
   - IP الطابعة.
   - Port، غالبا `9100`.

الـ BAT سيعمل الآتي تلقائيا:

1. يطلب صلاحية Administrator لو لم يكن مفتوحا كمسؤول.
2. ينسخ الـ exe إلى:

```text
C:\AOTPrinterAgent\aot-printer-agent.exe
```

3. ينشئ:

```text
C:\ProgramData\AOTPrinterAgent\config.json
C:\ProgramData\AOTPrinterAgent\logs
```

4. يكتب الكونفيج بناء على البيانات التي أدخلتها.
5. يكتب ملف registration info يحتوي UUID/token هنا:

```text
C:\ProgramData\AOTPrinterAgent\agent-registration-info.txt
```

6. يختبر الاتصال بالطابعة على IP/port.
7. يشغل agent مرة واحدة بـ `-once` لاختبار Laravel والكونفيج.
8. يركب Windows Service باسم `AOTPrinterAgent`.
9. يشغل الخدمة ويعرض آخر سطور من اللوج.

لو Laravel printer-agent API لسه غير متنفذ، اختبار `-once` سيفشل وهذا طبيعي. وقتها اختار `YES` لو تريد تركيب الخدمة الآن، لكن الطباعة لن تعمل فعليا إلا بعد إضافة API والجداول والتوكن في Laravel.

ملاحظة: الـ BAT الحالي يجهز طابعة `network_raw` فقط، يعني IP/LAN printer. لا تستخدمه لطابعة USB مباشرة إلا بعد تنفيذ `windows_spooler` adapter.

الملف الجاهز في المشروع حاليا:

```text
printer-agent\dist\aot-printer-agent.exe
```

ملف الكونفيج يكون في:

```text
C:\ProgramData\AOTPrinterAgent\config.json
```

واللوج في:

```text
C:\ProgramData\AOTPrinterAgent\logs\agent.log
```

## تجهيز Laravel قبل التركيب

على لوحة Laravel أو من الداتا:

1. أنشئ printer agent جديد.
2. خذ `agent_uuid`.
3. أنشئ token معتمد للـ agent.
4. اربط الطابعة بهذا الـ agent.
5. تأكد أن print jobs ستتولد بصيغة:

```text
payload_format = escpos_base64
```

الـ agent الحالي لا يدعم HTML payload ولا PDF payload. يدعم ESC/POS bytes encoded base64 فقط.

## إعداد IP printer

على جهاز العميل، تأكد أن الطابعة لها IP ثابت. مثال:

```text
Printer IP: 192.168.1.50
Port: 9100
```

اختبر الوصول من PowerShell:

```powershell
Test-NetConnection 192.168.1.50 -Port 9100
```

لو `TcpTestSucceeded` = `True`، الاتصال متاح.

## إنشاء config.json

أنشئ المسار:

```powershell
New-Item -ItemType Directory -Force -Path C:\ProgramData\AOTPrinterAgent
New-Item -ItemType Directory -Force -Path C:\ProgramData\AOTPrinterAgent\logs
```

ثم أنشئ:

```text
C:\ProgramData\AOTPrinterAgent\config.json
```

مثال IP printer:

```json
{
  "agent_uuid": "PUT-AGENT-UUID-HERE",
  "agent_name": "Kitchen Agent",
  "cloud": {
    "base_url": "https://your-laravel-domain.com",
    "token": "PUT-APPROVED-AGENT-TOKEN-HERE",
    "timeout_seconds": 15
  },
  "runtime": {
    "heartbeat_interval_seconds": 30,
    "claim_interval_seconds": 5,
    "batch_size": 5,
    "lease_seconds": 60
  },
  "printers": [
    {
      "local_printer_id": "kitchen-lan-9100",
      "name": "Kitchen LAN",
      "connection_type": "network_raw",
      "ip_address": "192.168.1.50",
      "port": 9100
    }
  ],
  "logging": {
    "file": "C:\\ProgramData\\AOTPrinterAgent\\logs\\agent.log"
  }
}
```

مهم:

- `cloud.base_url` لازم يكون HTTPS في الإنتاج.
- `cloud.token` هو Bearer token الذي يقبله Laravel.
- `local_printer_id` لازم يطابق تعريف الطابعة في Laravel/print routing.
- `connection_type` حاليا استخدم `network_raw`.
- لو `port` غير مكتوب، الكود يضع `9100` تلقائيا لطابعات `network_raw`.

## اختبار قبل تركيب الخدمة

افتح PowerShell على جهاز العميل:

```powershell
cd C:\AOTPrinterAgent
.\aot-printer-agent.exe -config C:\ProgramData\AOTPrinterAgent\config.json -once
```

المتوقع:

1. البرنامج يقرأ الكونفيج.
2. يرسل heartbeat إلى Laravel.
3. يعمل claim مرة واحدة.
4. لو لا يوجد jobs، سيكتب في اللوج `claim returned no jobs`.

راجع اللوج:

```powershell
Get-Content C:\ProgramData\AOTPrinterAgent\logs\agent.log -Tail 50
```

## تركيب Windows Service

افتح PowerShell كـ Administrator:

```powershell
cd C:\AOTPrinterAgent
.\scripts\install-service.ps1 `
  -ExePath .\aot-printer-agent.exe `
  -ConfigPath C:\ProgramData\AOTPrinterAgent\config.json
```

اسم الخدمة:

```text
AOTPrinterAgent
```

تحقق من الخدمة:

```powershell
Get-Service AOTPrinterAgent
Get-Content C:\ProgramData\AOTPrinterAgent\logs\agent.log -Tail 50
```

أوامر التشغيل:

```powershell
Start-Service AOTPrinterAgent
Stop-Service AOTPrinterAgent
Restart-Service AOTPrinterAgent
```

## إلغاء التركيب

افتح PowerShell كـ Administrator:

```powershell
cd C:\AOTPrinterAgent
.\scripts\uninstall-service.ps1
```

لو تريد حذف الكونفيج واللوج أيضا:

```powershell
.\scripts\uninstall-service.ps1 -RemoveProgramData
```

## طريقة العمل بعد التشغيل

بعد Start Service:

1. كل `heartbeat_interval_seconds` يرسل agent status وبيانات الطابعات إلى Laravel.
2. كل `claim_interval_seconds` يسأل Laravel عن jobs مخصصة لهذا `agent_uuid`.
3. Laravel يرجع jobs بعدد أقصى `batch_size`.
4. كل job يحتوي على بيانات الطابعة و `payload_format` و `content`.
5. لو `payload_format` = `escpos_base64`، الـ agent يفك المحتوى إلى bytes.
6. لو الطابعة `network_raw`، يفتح socket إلى `ip_address:port`.
7. يكتب bytes للطابعة.
8. لو الكتابة تمت بالكامل، يرجع status `sent`.
9. لو حصل خطأ اتصال أو كتابة، يرجع status `failed` مع error code.

ملاحظة: status `sent` يعني أن bytes اتكتبت على socket بنجاح. هذا لا يضمن أن الورقة خرجت فعليا، لأن `network_raw` الحالي لا يقرأ تأكيد من الطابعة.

## Error codes مهمة

```text
unsupported_adapter
```

نوع الطابعة غير مدعوم في التنفيذ الحالي. غالبا تم استخدام `windows_spooler` أو نوع غير `network_raw`.

```text
unsupported_payload_format
```

الـ job ليس `escpos_base64`.

```text
payload_decode_failed
```

الـ base64 غير صحيح أو فارغ.

```text
printer_unreachable
```

لا يوجد اتصال إلى IP/port الطابعة.

```text
tcp_write_failed
```

الاتصال تم لكن الكتابة للطابعة فشلت.

```text
tcp_partial_write
```

الـ agent كتب جزء فقط من payload.

## Checklist للتركيب عند العميل

1. ثبت IP ثابت للطابعة أو احجزه من الراوتر/DHCP.
2. تأكد من `Test-NetConnection PRINTER_IP -Port 9100`.
3. جهز `agent_uuid` و token من Laravel.
4. انسخ `aot-printer-agent.exe` وسكربتات الخدمة إلى `C:\AOTPrinterAgent`.
5. أنشئ `C:\ProgramData\AOTPrinterAgent\config.json`.
6. شغل `-once` وتأكد من heartbeat في Laravel.
7. أنشئ test print job بصيغة `escpos_base64`.
8. ركب الخدمة وشغلها.
9. راقب اللوج وتأكد أن job تحول إلى `sent`.
10. تأكد من خروج الإيصال من الطابعة.

## لو لازم USB مباشر

المطلوب برمجيا قبل الاعتماد على USB مباشر:

1. إضافة adapter جديد في Go باسم `windows_spooler`.
2. تسجيله في `cmd/agent/main.go` بجانب `network_raw`.
3. استخدام `spooler_name` من الكونفيج.
4. إرسال raw ESC/POS bytes إلى Windows spooler.
5. اختبار الطباعة على طابعة USB معرفة في Windows.

شكل الكونفيج المتوقع بعد تنفيذ adapter:

```json
{
  "local_printer_id": "kitchen-usb",
  "name": "Kitchen USB",
  "connection_type": "windows_spooler",
  "spooler_name": "EPSON TM-T20"
}
```

لكن هذا الشكل غير قابل للاستخدام حاليا بدون تنفيذ adapter.

## بناء exe من المصدر

على جهاز المطور فقط:

```powershell
cd printer-agent
go mod download
go build -o .\dist\aot-printer-agent.exe .\cmd\agent
```

بعد البناء، انقل الـ exe إلى جهاز العميل. جهاز العميل لا يحتاج Go.
