SmsProvider — راهنمای کلاینت (ESP32)

نسخه دِمو — امنیت و اعتبارسنجی عمداً ساده شده. این سند رفتار سرور و نحوه تعامل دستگاه‌های ESP32 با SignalR و API‌های موجود را به‌صورت خلاصه شرح می‌دهد.

خلاصهٔ سناریو

  • سرور همیشه حداکثر 10 پیامک ارسال‌نشده در جدول outbox نگهداری می‌کند.
  • هر بار که پیامک رَندوم تولید می‌شود، در صورت لزوم پیامک‌های فرستاده‌شده قدیمی حذف شده تا تعداد درون دیتابیس به 10 برسد.
  • پس از تولید پیامک(ها)، سرور از طریق Hub SignalR رویداد HasPendingMessages را به همه‌ی کلاینت‌ها (ESP32) ارسال می‌کند.
  • ESP32 پیام‌ها را می‌فرستد و نتیجه را با POST به /api/logs/create گزارش می‌دهد.
  • API ارسال پیامک (یکی‌به‌یکی) حذف شده — ارسال از طریق دستگاه‌ها انجام می‌شود.

چرخهٔ پیام

  1. سرور پیامک(های) جدید ایجاد می‌کند (Random generator).
  2. سرور رویداد SignalR با اطلاعات خلاصه برای همه ارسال می‌کند.
  3. دستگاه(ها) با Poll یا با استفاده از سرویس‌های تعریف‌شده پیامک‌ها را دریافت یا درخواست می‌کنند.
  4. پس از ارسال، دستگاه لاگ ارسال را به /api/logs/create می‌فرستد تا سرور شمارش لاگ‌ها را ثبت کند.

قانون دیتابیس

حداکثر پیامک در outbox: 10
ذخیرهٔ لاگ‌ها: شمارندهٔ کلی (totalDeletedMessages) در سرور ثبت می‌شود
دیتابیس: SQLite (کم‌حجم)

API‌های مرتبط (برای ESP32)

1) Poll دستورات (GET)

GET /api/commands/poll

کلاینت‌ها می‌توانند این مسیر را برای دریافت لیست پیامک‌های در صف فراخوانی کنند. پاسخ شامل آرایه‌ای از دستورات (پیامک) است:

{
  "commands": [
    {
      "outboxId": "guid",
      "phoneNumber": "09931234567",
      "unicodeHexMessage": "00610062...",
      "senderId": "TestLine1"
    }
  ]
}

2) ارسال لاگ‌ها (POST)

POST /api/logs/create

کلاینت‌ها پس از تلاش برای ارسال SMS باید نتایج را با فرمت زیر ارسال کنند (لیست):

[
  {
    "OutboxId": "GUID",
    "PhoneNumber": "09931234567",
    "StartDateTime": "2025-10-12T10:24:10Z",
    "EndDateTime": "2025-10-12T10:24:16Z",
    "ResponseCode": 0,
    "Description": "OK"
  }
]

سرور با ذخیرهٔ لاگ، آمار کلی (مانند totalDeletedMessages) را نگهداری می‌کند.

مثال سادهٔ کلاینت (JavaScript) — الگو برای ESP32

این مثال برای نشان دادن منطق کلی است. در ESP32 از کتابخانه‌های WebSocket یا SignalR client مناسب استفاده کنید.

// اتصال به SignalR hub (مثال JS برای فهم الگوریتم)
const connection = new signalR.HubConnectionBuilder()
  .withUrl("/hubs/sms")
  .withAutomaticReconnect()
  .build();

connection.on("HasPendingMessages", async (payload) => {
  console.log("HasPendingMessages:", payload);
  // وقتی payload.newMessages>0 -> poll یا endpoint مناسب را برای دریافت دستورات صدا بزن
  const resp = await fetch('/api/commands/poll');
  const json = await resp.json();
  for(const cmd of json.commands){
    // فرض: sendSms locally (توابع ارسال در ESP32)
    const result = await sendSmsToNetwork(cmd.phoneNumber, cmd.unicodeHexMessage, cmd.senderId);
    // سپس لاگ را به سرور بفرست
    await fetch('/api/logs/create', {
      method: 'POST',
      headers:{ 'Content-Type':'application/json' },
      body: JSON.stringify([{
        OutboxId: cmd.outboxId,
        PhoneNumber: cmd.phoneNumber,
        StartDateTime: new Date().toISOString(),
        EndDateTime: new Date().toISOString(),
        ResponseCode: result.code,
        Description: result.msg
      }])
    });
  }
});

connection.start().catch(e=>console.error(e));
    

در ESP32 از reconnection، محدودیت تلاش‌ها و backoff استفاده کنید. لاگ‌گیری محلی را هم فعال کنید تا در صورت قطعی شبکه، لاگ‌ها از بین نروند.

راهنمای عملیاتی کوتاه برای توسعه‌دهندهٔ ESP32

  • همیشه روی ارتباط پایدار با Hub کار کنید: reconnect و heartbeat پیاده کنید.
  • بعد از دریافت HasPendingMessages سریعا /api/commands/poll را فراخوانی کنید تا دستورات را بگیرید.
  • هر پیامک را تک‌تک ارسال و نتیجه را در یک آرایه لاگ POST کنید تا سرور آمار را ثبت کند.
  • به‌خاطر محدودیت SQLite و کم‌حجم نگه داشتن دیتابیس، سرور فقط 10 پیامک در outbox نگه می‌دارد؛ انتظار داشته باشید با تولید جدید، قدیمی‌ها حذف شوند.
  • این یک دِموست — هیچ مکانیزم احراز هویت یا رمزنگاری برای پیام‌ها وجود ندارد؛ برای تولید در محیط واقعی حتما امنیت اضافه کنید.