diff --git a/0_Framework/Application/FaceEmbedding/IFaceEmbeddingNotificationService.cs b/0_Framework/Application/FaceEmbedding/IFaceEmbeddingNotificationService.cs new file mode 100644 index 00000000..0c00577f --- /dev/null +++ b/0_Framework/Application/FaceEmbedding/IFaceEmbeddingNotificationService.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; + +namespace _0_Framework.Application.FaceEmbedding; + +/// +/// سرویس اطلاع‌رسانی تغییرات Face Embedding +/// +public interface IFaceEmbeddingNotificationService +{ + /// + /// اطلاع‌رسانی ایجاد یا به‌روزرسانی Embedding + /// + Task NotifyEmbeddingCreatedAsync(long workshopId, long employeeId, string employeeFullName); + + /// + /// اطلاع‌رسانی حذف Embedding + /// + Task NotifyEmbeddingDeletedAsync(long workshopId, long employeeId); + + /// + /// اطلاع‌رسانی بهبود Embedding + /// + Task NotifyEmbeddingRefinedAsync(long workshopId, long employeeId); +} + diff --git a/0_Framework/InfraStructure/FaceEmbeddingService.cs b/0_Framework/InfraStructure/FaceEmbeddingService.cs index 402e4cc7..0b709ed5 100644 --- a/0_Framework/InfraStructure/FaceEmbeddingService.cs +++ b/0_Framework/InfraStructure/FaceEmbeddingService.cs @@ -20,12 +20,15 @@ public class FaceEmbeddingService : IFaceEmbeddingService { private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; + private readonly IFaceEmbeddingNotificationService _notificationService; private readonly string _apiBaseUrl; - public FaceEmbeddingService(IHttpClientFactory httpClientFactory, ILogger logger) + public FaceEmbeddingService(IHttpClientFactory httpClientFactory, ILogger logger, + IFaceEmbeddingNotificationService notificationService = null) { _httpClientFactory = httpClientFactory; _logger = logger; + _notificationService = notificationService; _apiBaseUrl = "http://localhost:8000"; } @@ -81,6 +84,12 @@ public class FaceEmbeddingService : IFaceEmbeddingService _logger.LogInformation("Embeddings generated successfully for Employee {EmployeeId}, Workshop {WorkshopId}", employeeId, workshopId); + // ارسال اطلاع‌رسانی به سایر سیستم‌ها + if (_notificationService != null) + { + await _notificationService.NotifyEmbeddingCreatedAsync(workshopId, employeeId, employeeFullName); + } + return new OperationResult { IsSuccedded = true, @@ -151,6 +160,13 @@ public class FaceEmbeddingService : IFaceEmbeddingService if (response.IsSuccessStatusCode) { _logger.LogInformation("Embeddings generated successfully from streams for Employee {EmployeeId}", employeeId); + + // ارسال اطلاع‌رسانی به سایر سیستم‌ها + if (_notificationService != null) + { + await _notificationService.NotifyEmbeddingCreatedAsync(workshopId, employeeId, employeeFullName); + } + return new OperationResult { IsSuccedded = true, Message = "Embedding با موفقیت ایجاد شد" }; } else @@ -200,6 +216,13 @@ public class FaceEmbeddingService : IFaceEmbeddingService if (response.IsSuccessStatusCode) { _logger.LogInformation("Embedding refined successfully for Employee {EmployeeId}", employeeId); + + // ارسال اطلاع‌رسانی به سایر سیستم‌ها + if (_notificationService != null) + { + await _notificationService.NotifyEmbeddingRefinedAsync(workshopId, employeeId); + } + return new OperationResult { IsSuccedded = true, Message = "Embedding بهبود یافت" }; } else @@ -239,6 +262,13 @@ public class FaceEmbeddingService : IFaceEmbeddingService if (response.IsSuccessStatusCode) { _logger.LogInformation("Embedding deleted successfully for Employee {EmployeeId}", employeeId); + + // ارسال اطلاع‌رسانی به سایر سیستم‌ها + if (_notificationService != null) + { + await _notificationService.NotifyEmbeddingDeletedAsync(workshopId, employeeId); + } + return new OperationResult { IsSuccedded = true, Message = "Embedding حذف شد" }; } else diff --git a/0_Framework/InfraStructure/NullFaceEmbeddingNotificationService.cs b/0_Framework/InfraStructure/NullFaceEmbeddingNotificationService.cs new file mode 100644 index 00000000..c760d2cd --- /dev/null +++ b/0_Framework/InfraStructure/NullFaceEmbeddingNotificationService.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using _0_Framework.Application.FaceEmbedding; + +namespace _0_Framework.InfraStructure; + +/// +/// پیاده‌سازی پیش‌فرض (بدون عملیات) برای IFaceEmbeddingNotificationService +/// این کلاس زمانی استفاده می‌شود که SignalR در دسترس نباشد +/// +public class NullFaceEmbeddingNotificationService : IFaceEmbeddingNotificationService +{ + public Task NotifyEmbeddingCreatedAsync(long workshopId, long employeeId, string employeeFullName) + { + // هیچ عملیاتی انجام نمی‌دهد + return Task.CompletedTask; + } + + public Task NotifyEmbeddingDeletedAsync(long workshopId, long employeeId) + { + // هیچ عملیاتی انجام نمی‌دهد + return Task.CompletedTask; + } + + public Task NotifyEmbeddingRefinedAsync(long workshopId, long employeeId) + { + // هیچ عملیاتی انجام نمی‌دهد + return Task.CompletedTask; + } +} + diff --git a/ANDROID_SIGNALR_GUIDE.md b/ANDROID_SIGNALR_GUIDE.md new file mode 100644 index 00000000..576aba02 --- /dev/null +++ b/ANDROID_SIGNALR_GUIDE.md @@ -0,0 +1,624 @@ +# راهنمای اتصال اپلیکیشن Android به SignalR برای Face Embedding + +## 1. افزودن کتابخانه SignalR به پروژه Android + +در فایل `build.gradle` (Module: app) خود، dependency زیر را اضافه کنید: + +```gradle +dependencies { + // SignalR for Android + implementation 'com.microsoft.signalr:signalr:7.0.0' + + // اگر از Kotlin استفاده می‌کنید: + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1' + + // برای JSON پردازش: + implementation 'com.google.code.gson:gson:2.10.1' +} +``` + +## 2. اضافه کردن Permission در AndroidManifest.xml + +```xml + + +``` + +## 3. کد Java/Kotlin برای اتصال به SignalR + +### نسخه Java: + +```java +import com.microsoft.signalr.HubConnection; +import com.microsoft.signalr.HubConnectionBuilder; +import com.microsoft.signalr.HubConnectionState; +import com.google.gson.JsonObject; +import android.util.Log; + +public class FaceEmbeddingSignalRClient { + private static final String TAG = "FaceEmbeddingHub"; + private HubConnection hubConnection; + private String serverUrl = "http://YOUR_SERVER_IP:PORT/trackingFaceEmbeddingHub"; // آدرس سرور خود را وارد کنید + private long workshopId; + + public FaceEmbeddingSignalRClient(long workshopId) { + this.workshopId = workshopId; + initializeSignalR(); + } + + private void initializeSignalR() { + // ایجاد اتصال SignalR + hubConnection = HubConnectionBuilder + .create(serverUrl) + .build(); + + // دریافت رویداد ایجاد Embedding + hubConnection.on("EmbeddingCreated", (data) -> { + JsonObject jsonData = (JsonObject) data; + long employeeId = jsonData.get("employeeId").getAsLong(); + String employeeFullName = jsonData.get("employeeFullName").getAsString(); + String timestamp = jsonData.get("timestamp").getAsString(); + + Log.d(TAG, "Embedding Created - Employee: " + employeeFullName + " (ID: " + employeeId + ")"); + + // اینجا می‌توانید داده‌های جدید را از سرور بگیرید یا UI را بروزرسانی کنید + onEmbeddingCreated(employeeId, employeeFullName, timestamp); + + }, JsonObject.class); + + // دریافت رویداد حذف Embedding + hubConnection.on("EmbeddingDeleted", (data) -> { + JsonObject jsonData = (JsonObject) data; + long employeeId = jsonData.get("employeeId").getAsLong(); + String timestamp = jsonData.get("timestamp").getAsString(); + + Log.d(TAG, "Embedding Deleted - Employee ID: " + employeeId); + onEmbeddingDeleted(employeeId, timestamp); + + }, JsonObject.class); + + // دریافت رویداد بهبود Embedding + hubConnection.on("EmbeddingRefined", (data) -> { + JsonObject jsonData = (JsonObject) data; + long employeeId = jsonData.get("employeeId").getAsLong(); + String timestamp = jsonData.get("timestamp").getAsString(); + + Log.d(TAG, "Embedding Refined - Employee ID: " + employeeId); + onEmbeddingRefined(employeeId, timestamp); + + }, JsonObject.class); + } + + public void connect() { + if (hubConnection.getConnectionState() == HubConnectionState.DISCONNECTED) { + hubConnection.start() + .doOnComplete(() -> { + Log.d(TAG, "Connected to SignalR Hub"); + joinWorkshopGroup(); + }) + .doOnError(error -> { + Log.e(TAG, "Error connecting to SignalR: " + error.getMessage()); + }) + .subscribe(); + } + } + + private void joinWorkshopGroup() { + // عضویت در گروه مخصوص این کارگاه + hubConnection.send("JoinWorkshopGroup", workshopId); + Log.d(TAG, "Joined workshop group: " + workshopId); + } + + public void disconnect() { + if (hubConnection.getConnectionState() == HubConnectionState.CONNECTED) { + // خروج از گروه + hubConnection.send("LeaveWorkshopGroup", workshopId); + + hubConnection.stop(); + Log.d(TAG, "Disconnected from SignalR Hub"); + } + } + + // این متدها را در Activity/Fragment خود override کنید + protected void onEmbeddingCreated(long employeeId, String employeeFullName, String timestamp) { + // اینجا UI را بروزرسانی کنید یا داده جدید را بگیرید + } + + protected void onEmbeddingDeleted(long employeeId, String timestamp) { + // اینجا UI را بروزرسانی کنید + } + + protected void onEmbeddingRefined(long employeeId, String timestamp) { + // اینجا UI را بروزرسانی کنید + } +} +``` + +### نسخه Kotlin: + +```kotlin +import com.microsoft.signalr.HubConnection +import com.microsoft.signalr.HubConnectionBuilder +import com.microsoft.signalr.HubConnectionState +import com.google.gson.JsonObject +import android.util.Log +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class FaceEmbeddingSignalRClient(private val workshopId: Long) { + + companion object { + private const val TAG = "FaceEmbeddingHub" + } + + private lateinit var hubConnection: HubConnection + private val serverUrl = "http://YOUR_SERVER_IP:PORT/trackingFaceEmbeddingHub" // آدرس سرور خود را وارد کنید + + init { + initializeSignalR() + } + + private fun initializeSignalR() { + hubConnection = HubConnectionBuilder + .create(serverUrl) + .build() + + // دریافت رویداد ایجاد Embedding + hubConnection.on("EmbeddingCreated", { data: JsonObject -> + val employeeId = data.get("employeeId").asLong + val employeeFullName = data.get("employeeFullName").asString + val timestamp = data.get("timestamp").asString + + Log.d(TAG, "Embedding Created - Employee: $employeeFullName (ID: $employeeId)") + onEmbeddingCreated(employeeId, employeeFullName, timestamp) + }, JsonObject::class.java) + + // دریافت رویداد حذف Embedding + hubConnection.on("EmbeddingDeleted", { data: JsonObject -> + val employeeId = data.get("employeeId").asLong + val timestamp = data.get("timestamp").asString + + Log.d(TAG, "Embedding Deleted - Employee ID: $employeeId") + onEmbeddingDeleted(employeeId, timestamp) + }, JsonObject::class.java) + + // دریافت رویداد بهبود Embedding + hubConnection.on("EmbeddingRefined", { data: JsonObject -> + val employeeId = data.get("employeeId").asLong + val timestamp = data.get("timestamp").asString + + Log.d(TAG, "Embedding Refined - Employee ID: $employeeId") + onEmbeddingRefined(employeeId, timestamp) + }, JsonObject::class.java) + } + + fun connect() { + if (hubConnection.connectionState == HubConnectionState.DISCONNECTED) { + CoroutineScope(Dispatchers.IO).launch { + try { + hubConnection.start().blockingAwait() + Log.d(TAG, "Connected to SignalR Hub") + joinWorkshopGroup() + } catch (e: Exception) { + Log.e(TAG, "Error connecting to SignalR: ${e.message}") + } + } + } + } + + private fun joinWorkshopGroup() { + hubConnection.send("JoinWorkshopGroup", workshopId) + Log.d(TAG, "Joined workshop group: $workshopId") + } + + fun disconnect() { + if (hubConnection.connectionState == HubConnectionState.CONNECTED) { + hubConnection.send("LeaveWorkshopGroup", workshopId) + hubConnection.stop() + Log.d(TAG, "Disconnected from SignalR Hub") + } + } + + // این متدها را override کنید + open fun onEmbeddingCreated(employeeId: Long, employeeFullName: String, timestamp: String) { + // اینجا UI را بروزرسانی کنید یا داده جدید را بگیرید + } + + open fun onEmbeddingDeleted(employeeId: Long, timestamp: String) { + // اینجا UI را بروزرسانی کنید + } + + open fun onEmbeddingRefined(employeeId: Long, timestamp: String) { + // اینجا UI را بروزرسانی کنید + } +} +``` + +## 4. استفاده در Activity یا Fragment + +### مثال با Login و دریافت WorkshopId + +#### Java: +```java +public class LoginActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + + Button btnLogin = findViewById(R.id.btnLogin); + btnLogin.setOnClickListener(v -> performLogin()); + } + + private void performLogin() { + // فراخوانی API لاگین + // فرض کنید response شامل workshopId است + + // مثال ساده (باید از Retrofit یا کتابخانه مشابه استفاده کنید): + // LoginResponse response = apiService.login(username, password); + // long workshopId = response.getWorkshopId(); + + long workshopId = 123; // این را از response دریافت کنید + + // ذخیره workshopId + SharedPreferences prefs = getSharedPreferences("AppPrefs", MODE_PRIVATE); + prefs.edit().putLong("workshopId", workshopId).apply(); + + // رفتن به صفحه اصلی + Intent intent = new Intent(this, MainActivity.class); + startActivity(intent); + finish(); + } +} + +public class MainActivity extends AppCompatActivity { + private FaceEmbeddingSignalRClient signalRClient; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + // دریافت workshopId از SharedPreferences + SharedPreferences prefs = getSharedPreferences("AppPrefs", MODE_PRIVATE); + long workshopId = prefs.getLong("workshopId", 0); + + if (workshopId == 0) { + // اگر workshopId وجود نداره، برگرد به صفحه لاگین + Intent intent = new Intent(this, LoginActivity.class); + startActivity(intent); + finish(); + return; + } + + // ایجاد و اتصال SignalR + signalRClient = new FaceEmbeddingSignalRClient(workshopId) { + @Override + protected void onEmbeddingCreated(long employeeId, String employeeFullName, String timestamp) { + runOnUiThread(() -> { + // بروزرسانی UI + Toast.makeText(MainActivity.this, + "Embedding ایجاد شد برای: " + employeeFullName, + Toast.LENGTH_SHORT).show(); + + // دریافت داده‌های جدید از API + refreshEmployeeList(); + }); + } + + @Override + protected void onEmbeddingDeleted(long employeeId, String timestamp) { + runOnUiThread(() -> { + // بروزرسانی UI + refreshEmployeeList(); + }); + } + + @Override + protected void onEmbeddingRefined(long employeeId, String timestamp) { + runOnUiThread(() -> { + // بروزرسانی UI + refreshEmployeeList(); + }); + } + }; + + signalRClient.connect(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (signalRClient != null) { + signalRClient.disconnect(); + } + } + + private void refreshEmployeeList() { + // دریافت لیست جدید کارمندان از API + } +} +``` + +#### Kotlin: +```kotlin +class LoginActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_login) + + val btnLogin = findViewById