Connect Flutter phone auth and Android platform
This commit is contained in:
@@ -7,10 +7,10 @@ import '../utils/geo_utils.dart';
|
||||
/// خدمة التحليل الذكي (Smart Analytics Service)
|
||||
/// مسؤولة عن تقسيم المسار إلى أجزاء، كشف التوقفات، وتحليل أنماط الأداء.
|
||||
class AnalyticsService extends GetxService {
|
||||
|
||||
/// تحليل المسار وتقسيمه إلى أجزاء (Segments)
|
||||
/// يعتمد على تغيرات السرعة، الارتفاع، أو الاتجاه.
|
||||
List<WorkoutSegment> analyzeSegments(List<GpsCoordinate> coords, WorkoutType type) {
|
||||
List<WorkoutSegment> analyzeSegments(
|
||||
List<GpsCoordinate> coords, WorkoutType type) {
|
||||
if (coords.length < 5) return [];
|
||||
|
||||
List<WorkoutSegment> segments = [];
|
||||
@@ -20,35 +20,38 @@ class AnalyticsService extends GetxService {
|
||||
// 1. تحديد مرحلة الإحماء (Warm-up)
|
||||
// نعتبر أول 10% من النقاط أو أول 500 متر إحماء إذا كانت السرعة متدرجة
|
||||
int warmupEnd = (coords.length * 0.1).toInt().clamp(5, 50);
|
||||
segments.add(_createSegment(coords, 0, warmupEnd, segmentCounter++, SegmentType.warmup));
|
||||
segments.add(_createSegment(
|
||||
coords, 0, warmupEnd, segmentCounter++, SegmentType.warmup));
|
||||
startIdx = warmupEnd;
|
||||
|
||||
// 2. تحليل النقاط الوسطى لكشف التوقفات أو التسارعات
|
||||
for (int i = startIdx + 1; i < coords.length - 5; i++) {
|
||||
final current = coords[i];
|
||||
final prev = coords[i - 1];
|
||||
|
||||
// كشف التوقف (Stop detection)
|
||||
// إذا كانت السرعة شبه صفرية لمسافة معينة
|
||||
if (current.speed != null && current.speed! < 0.3) {
|
||||
int stopEnd = i;
|
||||
while (stopEnd < coords.length - 1 &&
|
||||
(coords[stopEnd].speed ?? 0) < 0.3 &&
|
||||
stopEnd - i < 20) { // توقف بحد أقصى 20 نقطة لتجنب المقاطع الضخمة
|
||||
while (stopEnd < coords.length - 1 &&
|
||||
(coords[stopEnd].speed ?? 0) < 0.3 &&
|
||||
stopEnd - i < 20) {
|
||||
// توقف بحد أقصى 20 نقطة لتجنب المقاطع الضخمة
|
||||
stopEnd++;
|
||||
}
|
||||
|
||||
if (stopEnd - i > 3) { // توقف حقيقي لأكثر من 10 ثواني تقريباً
|
||||
|
||||
if (stopEnd - i > 3) {
|
||||
// توقف حقيقي لأكثر من 10 ثواني تقريباً
|
||||
// إغلاق المقطع السابق إذا كان هناك مسافة
|
||||
if (i > startIdx) {
|
||||
segments.add(_createSegment(coords, startIdx, i, segmentCounter++, SegmentType.main));
|
||||
segments.add(_createSegment(
|
||||
coords, startIdx, i, segmentCounter++, SegmentType.main));
|
||||
}
|
||||
segments.add(_createSegment(coords, i, stopEnd, segmentCounter++, SegmentType.stop));
|
||||
segments.add(_createSegment(
|
||||
coords, i, stopEnd, segmentCounter++, SegmentType.stop));
|
||||
i = stopEnd;
|
||||
startIdx = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// كشف الميول (Elevation detection)
|
||||
// يمكن التوسع هنا لكشف uphill/downhill
|
||||
}
|
||||
@@ -56,36 +59,37 @@ class AnalyticsService extends GetxService {
|
||||
// 3. تحديد مرحلة التبريد (Cool-down)
|
||||
int cooldownStart = (coords.length * 0.9).toInt();
|
||||
if (cooldownStart > startIdx) {
|
||||
segments.add(_createSegment(coords, startIdx, cooldownStart, segmentCounter++, SegmentType.main));
|
||||
segments.add(_createSegment(coords, cooldownStart, coords.length - 1, segmentCounter++, SegmentType.cooldown));
|
||||
segments.add(_createSegment(
|
||||
coords, startIdx, cooldownStart, segmentCounter++, SegmentType.main));
|
||||
segments.add(_createSegment(coords, cooldownStart, coords.length - 1,
|
||||
segmentCounter++, SegmentType.cooldown));
|
||||
} else {
|
||||
segments.add(_createSegment(coords, startIdx, coords.length - 1, segmentCounter++, SegmentType.main));
|
||||
segments.add(_createSegment(coords, startIdx, coords.length - 1,
|
||||
segmentCounter++, SegmentType.main));
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
WorkoutSegment _createSegment(List<GpsCoordinate> coords, int start, int end, int index, SegmentType type) {
|
||||
WorkoutSegment _createSegment(List<GpsCoordinate> coords, int start, int end,
|
||||
int index, SegmentType type) {
|
||||
double dist = 0;
|
||||
double maxSpd = 0;
|
||||
double sumSpd = 0;
|
||||
double elevChange = 0;
|
||||
|
||||
for (int i = start + 1; i <= end; i++) {
|
||||
dist += GeoUtils.haversineDistance(
|
||||
coords[i-1].latitude, coords[i-1].longitude,
|
||||
coords[i].latitude, coords[i].longitude
|
||||
);
|
||||
dist += GeoUtils.haversineDistance(coords[i - 1].latitude,
|
||||
coords[i - 1].longitude, coords[i].latitude, coords[i].longitude);
|
||||
double s = coords[i].speed ?? 0;
|
||||
if (s > maxSpd) maxSpd = s;
|
||||
sumSpd += s;
|
||||
|
||||
if (coords[i].altitude != null && coords[i-1].altitude != null) {
|
||||
elevChange += (coords[i].altitude! - coords[i-1].altitude!);
|
||||
|
||||
if (coords[i].altitude != null && coords[i - 1].altitude != null) {
|
||||
elevChange += (coords[i].altitude! - coords[i - 1].altitude!);
|
||||
}
|
||||
}
|
||||
|
||||
int duration = coords[end].timestamp.difference(coords[start].timestamp).inSeconds;
|
||||
int duration =
|
||||
coords[end].timestamp.difference(coords[start].timestamp).inSeconds;
|
||||
|
||||
return WorkoutSegment(
|
||||
segmentIndex: index,
|
||||
@@ -104,16 +108,18 @@ class AnalyticsService extends GetxService {
|
||||
/// مثال: "سرعتك في أول 2 كم تحسنت بنسبة 12%"
|
||||
String generateInsight(List<Workout> history) {
|
||||
if (history.length < 2) return "ابدأ تمرينك الأول للحصول على تحليلات!";
|
||||
|
||||
|
||||
// منطق بسيط للمقارنة بين آخر تمرينين
|
||||
final latest = history[0];
|
||||
final previous = history[1];
|
||||
|
||||
if (latest.avgSpeedMps > previous.avgSpeedMps) {
|
||||
double diff = ((latest.avgSpeedMps - previous.avgSpeedMps) / previous.avgSpeedMps) * 100;
|
||||
|
||||
if (latest.avgSpeedMps > previous.avgSpeedMps && previous.avgSpeedMps > 0) {
|
||||
double diff =
|
||||
((latest.avgSpeedMps - previous.avgSpeedMps) / previous.avgSpeedMps) *
|
||||
100;
|
||||
return "أداء رائع! سرعتك المتوسطة تحسنت بنسبة ${diff.toStringAsFixed(1)}% مقارنة بآخر مرة.";
|
||||
}
|
||||
|
||||
|
||||
return "استمر في التدريب! الاستمرارية هي مفتاح التطور.";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user