625 lines
22 KiB
Markdown
625 lines
22 KiB
Markdown
# راهنمای اتصال اپلیکیشن 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
|
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
```
|
|
|
|
## 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<Button>(R.id.btnLogin)
|
|
btnLogin.setOnClickListener { performLogin() }
|
|
}
|
|
|
|
private fun performLogin() {
|
|
// فراخوانی API لاگین
|
|
// فرض کنید response شامل workshopId است
|
|
|
|
// مثال ساده (باید از Retrofit یا کتابخانه مشابه استفاده کنید):
|
|
// val response = apiService.login(username, password)
|
|
// val workshopId = response.workshopId
|
|
|
|
val workshopId = 123L // این را از response دریافت کنید
|
|
|
|
// ذخیره workshopId
|
|
val prefs = getSharedPreferences("AppPrefs", Context.MODE_PRIVATE)
|
|
prefs.edit().putLong("workshopId", workshopId).apply()
|
|
|
|
// رفتن به صفحه اصلی
|
|
val intent = Intent(this, MainActivity::class.java)
|
|
startActivity(intent)
|
|
finish()
|
|
}
|
|
}
|
|
|
|
class MainActivity : AppCompatActivity() {
|
|
private lateinit var signalRClient: FaceEmbeddingSignalRClient
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.activity_main)
|
|
|
|
// دریافت workshopId از SharedPreferences
|
|
val prefs = getSharedPreferences("AppPrefs", Context.MODE_PRIVATE)
|
|
val workshopId = prefs.getLong("workshopId", 0L)
|
|
|
|
if (workshopId == 0L) {
|
|
// اگر workshopId وجود نداره، برگرد به صفحه لاگین
|
|
val intent = Intent(this, LoginActivity::class.java)
|
|
startActivity(intent)
|
|
finish()
|
|
return
|
|
}
|
|
|
|
// ایجاد و اتصال SignalR
|
|
signalRClient = object : FaceEmbeddingSignalRClient(workshopId) {
|
|
override fun onEmbeddingCreated(employeeId: Long, employeeFullName: String, timestamp: String) {
|
|
runOnUiThread {
|
|
// بروزرسانی UI
|
|
Toast.makeText(this@MainActivity,
|
|
"Embedding ایجاد شد برای: $employeeFullName",
|
|
Toast.LENGTH_SHORT).show()
|
|
|
|
// دریافت دادههای جدید از API
|
|
refreshEmployeeList()
|
|
}
|
|
}
|
|
|
|
override fun onEmbeddingDeleted(employeeId: Long, timestamp: String) {
|
|
runOnUiThread {
|
|
// بروزرسانی UI
|
|
refreshEmployeeList()
|
|
}
|
|
}
|
|
|
|
override fun onEmbeddingRefined(employeeId: Long, timestamp: String) {
|
|
runOnUiThread {
|
|
// بروزرسانی UI
|
|
refreshEmployeeList()
|
|
}
|
|
}
|
|
}
|
|
|
|
signalRClient.connect()
|
|
}
|
|
|
|
override fun onDestroy() {
|
|
super.onDestroy()
|
|
signalRClient.disconnect()
|
|
}
|
|
|
|
private fun refreshEmployeeList() {
|
|
// دریافت لیست جدید کارمندان از API
|
|
}
|
|
}
|
|
```
|
|
|
|
### مثال ساده بدون Login:
|
|
اگر workshopId را از قبل میدانید:
|
|
|
|
#### Java:
|
|
```java
|
|
public class MainActivity extends AppCompatActivity {
|
|
private FaceEmbeddingSignalRClient signalRClient;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setContentView(R.layout.activity_main);
|
|
|
|
long workshopId = 123; // شناسه کارگاه خود را وارد کنید
|
|
|
|
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();
|
|
});
|
|
}
|
|
};
|
|
|
|
signalRClient.connect();
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
super.onDestroy();
|
|
if (signalRClient != null) {
|
|
signalRClient.disconnect();
|
|
}
|
|
}
|
|
|
|
private void refreshEmployeeList() {
|
|
// دریافت لیست جدید کارمندان از API
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Kotlin:
|
|
```kotlin
|
|
class MainActivity : AppCompatActivity() {
|
|
private lateinit var signalRClient: FaceEmbeddingSignalRClient
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.activity_main)
|
|
|
|
val workshopId = 123L // شناسه کارگاه خود را وارد کنید
|
|
|
|
signalRClient = object : FaceEmbeddingSignalRClient(workshopId) {
|
|
override fun onEmbeddingCreated(employeeId: Long, employeeFullName: String, timestamp: String) {
|
|
runOnUiThread {
|
|
// بروزرسانی UI
|
|
Toast.makeText(this@MainActivity,
|
|
"Embedding ایجاد شد برای: $employeeFullName",
|
|
Toast.LENGTH_SHORT).show()
|
|
|
|
// دریافت دادههای جدید از API
|
|
refreshEmployeeList()
|
|
}
|
|
}
|
|
|
|
override fun onEmbeddingDeleted(employeeId: Long, timestamp: String) {
|
|
runOnUiThread {
|
|
// بروزرسانی UI
|
|
refreshEmployeeList()
|
|
}
|
|
}
|
|
}
|
|
|
|
signalRClient.connect()
|
|
}
|
|
|
|
override fun onDestroy() {
|
|
super.onDestroy()
|
|
signalRClient.disconnect()
|
|
}
|
|
|
|
private fun refreshEmployeeList() {
|
|
// دریافت لیست جدید کارمندان از API
|
|
}
|
|
}
|
|
```
|
|
|
|
## 5. نکات مهم
|
|
|
|
### آدرس سرور
|
|
- اگر روی شبیهساز اندروید تست میکنید و سرور روی localhost اجرا میشود، از آدرس `http://10.0.2.2:PORT` استفاده کنید
|
|
- اگر روی دستگاه فیزیکی تست میکنید، از آدرس IP شبکه محلی سرور استفاده کنید (مثل `http://192.168.1.100:PORT`)
|
|
- PORT پیشفرض معمولاً 5000 یا 5001 است (بسته به کانفیگ پروژه شما)
|
|
|
|
### دریافت WorkshopId از Login
|
|
بعد از login موفق، workshopId را از سرور دریافت کنید و در SharedPreferences یا یک Singleton ذخیره کنید:
|
|
|
|
```java
|
|
// بعد از login موفق
|
|
SharedPreferences prefs = getSharedPreferences("AppPrefs", MODE_PRIVATE);
|
|
prefs.edit().putLong("workshopId", workshopId).apply();
|
|
|
|
// استفاده در Activity
|
|
long workshopId = prefs.getLong("workshopId", 0);
|
|
```
|
|
|
|
یا در Kotlin:
|
|
|
|
```kotlin
|
|
// بعد از login موفق
|
|
val prefs = getSharedPreferences("AppPrefs", Context.MODE_PRIVATE)
|
|
prefs.edit().putLong("workshopId", workshopId).apply()
|
|
|
|
// استفاده در Activity
|
|
val workshopId = prefs.getLong("workshopId", 0L)
|
|
```
|
|
|
|
### مدیریت اتصال
|
|
برای reconnection خودکار:
|
|
|
|
```java
|
|
hubConnection.onClosed(exception -> {
|
|
Log.e(TAG, "Connection closed. Attempting to reconnect...");
|
|
new Handler().postDelayed(() -> connect(), 5000); // تلاش مجدد بعد از 5 ثانیه
|
|
});
|
|
```
|
|
|
|
### Thread Safety
|
|
همیشه UI updates را در main thread انجام دهید:
|
|
|
|
```java
|
|
runOnUiThread(() -> {
|
|
// UI updates here
|
|
});
|
|
```
|
|
|
|
## 6. تست اتصال
|
|
|
|
برای تست میتوانید:
|
|
1. اپلیکیشن را اجرا کنید
|
|
2. از طریق Postman یا Swagger یک Embedding ایجاد کنید
|
|
3. باید در Logcat پیام "Embedding Created" را ببینید
|
|
|
|
## 7. خطایابی (Debugging)
|
|
|
|
برای دیدن جزئیات بیشتر:
|
|
|
|
```java
|
|
hubConnection = HubConnectionBuilder
|
|
.create(serverUrl)
|
|
.withHttpConnectionOptions(options -> {
|
|
options.setLogging(LogLevel.TRACE);
|
|
})
|
|
.build();
|
|
```
|
|
|
|
---
|
|
|
|
## خلاصه Endpoints
|
|
|
|
| نوع رویداد | متد SignalR | پارامترهای دریافتی |
|
|
|-----------|-------------|---------------------|
|
|
| ایجاد Embedding | `EmbeddingCreated` | workshopId, employeeId, employeeFullName, timestamp |
|
|
| حذف Embedding | `EmbeddingDeleted` | workshopId, employeeId, timestamp |
|
|
| بهبود Embedding | `EmbeddingRefined` | workshopId, employeeId, timestamp |
|
|
|
|
| متد ارسالی | پارامتر | توضیحات |
|
|
|-----------|---------|---------|
|
|
| `JoinWorkshopGroup` | workshopId | عضویت در گروه کارگاه |
|
|
| `LeaveWorkshopGroup` | workshopId | خروج از گروه کارگاه |
|
|
|