פרויקט מעקב שליחים מתחיל לרוב במשימה פשוטה: להראות ללקוח היכן נמצאת המשלוח. אבל בסביבת production נדרש לפתור ניווט משוער (dead-reckoning) בעת אובדן GPS, התאמת נקודות למפת כבישים (map snapping), וסנכרון בין שלושה רכיבים: אפליקציית השליח, שרת ה-bus ואפליקציית הלקוח. בפרויקט עם 500 שליחים, צמצמנו את זמן ההשהיה לעדכון ל-2 שניות על ידי מעבר מ-WebSocket ל-MQTT והגדרת Redis עם TTL של 60 שניות. גישה זו חוסכת שעות פיתוח ומורידה עלויות תפעוליות בעד 30% בזכות הפחתת תעבורה. טעויות בבחירת פרוטוקול או בהגדרת מצב סוללה מובילות לשכתוב קוד — לכן קריטי להניח ארכיטקטורה נכונה מההתחלה. הזמינו ביקורת (audit) לפתרון הקיים שלכם — נבחר את התצורה האופטימלית.
למה מעקב שליחים הוא לא רק GPS?
מערכת production כוללת ארבעה רכיבים מרכזיים:
- ניווט משוער (Dead-reckoning) בעת אובדן אות GPS — אינטרפולציה של הקואורדינטות האחרונות באמצעות מהירות וכיוון.
- התאמה לכבישים (Map snapping) — נקודות GPS גולמיות קופצות יחסית לכביש; יש להתאים אותן למפת הכבישים.
- לוגיקת מצבי שליח: פנוי → שויך → איסוף → משלוח → הושלם.
- איזון סוללה בצד אפליקציית השליח: 5 שניות לעומת 15 שניות.
אפליקציית השליח: איסוף ושידור קואורדינטות
באנדרואיד אנו משתמשים ב-FusedLocationProviderClient מ-play-services-location בתוך Foreground Service. מרווח העדכון הוא פשרה: 5 שניות נותנות דיוק, 15 שניות חוסכות סוללה. לשליחים רגליים — Priority.PRIORITY_HIGH_ACCURACY + 5 שניות. לרכב — Priority.PRIORITY_BALANCED_POWER_ACCURACY + 10 שניות עם setMinUpdateDistanceMeters(20f).
ב-iOS — CLLocationManager עם desiredAccuracy: kCLLocationAccuracyBestForNavigation במצב פעיל ומעבר ל-Significant Location Changes ברקע. activityType = .automotiveNavigation מפעיל סינון רעשי GPS.
סינון נקודות חריגות הוא חובה. GPS ברחובות עיר מייצר קפיצות של 50–200 מטר. פילטר פשוט: discard נקודה אם horizontalAccuracy > 50 מטר או מהירות מחושבת > 200 קמ"ש.
func shouldAcceptLocation(_ location: CLLocation) -> Bool {
guard location.horizontalAccuracy > 0, location.horizontalAccuracy <= 50 else {
return false
}
if let lastLocation = lastAcceptedLocation {
let timeDelta = location.timestamp.timeIntervalSince(lastLocation.timestamp)
let distance = location.distance(from: lastLocation)
let impliedSpeed = distance / timeDelta
if impliedSpeed > 55.6 {
return false
}
}
return true
}בuffer במסד נתונים מקומי ושליחה בקבוצות (batch) בעת שחזור רשת היא תכנית סטנדרטית.
שרת ה-Bus: WebSocket או MQTT?
| תכונה | WebSocket (Socket.IO) | MQTT (EMQ X) |
|---|---|---|
| תעבורה | גבוהה יותר בגלל headers | נמוכה פי 3 בזכות binary frames |
| התנהגות ברשת סלולרית | גרועה יותר — ניתוקים דורשים חיבור מחדש | טובה יותר — QoS + מפגשים מתמשכים |
| סקלביליות | מתאים עד כמה אלפי שליחים | מתרחב בקלות לעשרות אלפים |
| טכנולוגיות | Node.js, FastAPI | Mosquitto, EMQ X, AWS IoT Core |
לעומסים קטנים אנו בוחרים ב-Socket.IO. לסקלה — MQTT. MQTT מגדיר פרוטוקול בינארי, ומשיג חיסכון של עד 70% בתעבורה ברשתות סלולריות.
אחסון מיקום נוכחי — Redis: func shouldAcceptLocation(_ location: CLLocation) -> Bool { guard location.horizontalAccuracy > 0, location.horizontalAccuracy <= 50 else { return false } if let lastLocation = lastAcceptedLocation { let timeDelta = location.timestamp.timeIntervalSince(lastLocation.timestamp) let distance = location.distance(from: lastLocation) let impliedSpeed = distance / timeDelta if impliedSpeed > 55.6 { return false } } return true } עם TTL של 60 שניות. היסטוריית מסלולים — TimescaleDB.
התאמה לכבישים. קואורדינטות GPS גולמיות מיושרות לכבישים דרך Google Roads API או OSRM self-hosted (חינמי, <10 ms).
איך להבטיח אנימציית Marker חלקה?
עדכונים כל 5–10 שניות. ללא אנימציה, ה-marker קופץ. אנו מבצעים אינטרפולציה של תנועה בין שתי נקודות.
באנדרואיד דרך SET courier:{id}:position:
val animator = ValueAnimator.ofFloat(0f, 1f).apply {
duration = 3000
interpolator = LinearInterpolator()
addUpdateListener { animation ->
val fraction = animation.animatedFraction
val lat = prevLat + (newLat - prevLat) * fraction
val lon = prevLon + (newLon - prevLon) * fraction
marker.position = LatLng(lat, lon)
}
}
animator.start()ב-iOS — ValueAnimator או val animator = ValueAnimator.ofFloat(0f, 1f).apply { duration = 3000 interpolator = LinearInterpolator() addUpdateListener { animation -> val fraction = animation.animatedFraction val lat = prevLat + (newLat - prevLat) * fraction val lon = prevLon + (newLon - prevLon) * fraction marker.position = LatLng(lat, lon) } } animator.start() עם תזמון מותאם אישית. זווית סיבוב ה-marker מחושבת דרך CADisplayLink.
ב-Flutter — UIView.animate עם atan2. עבור TweenAnimationBuilder אנו מעדכנים Tween<LatLng> ב-google_maps_flutter כל 16 ms.
מצבי שליח
| מצב | תיאור | ממשק לקוח |
|---|---|---|
| idle | שליח פנוי | "מחפש שליח" |
| assigned | שויך להזמנה | "השליח בדרך למסעדה" |
| picking_up | אוסף הזמנה | "השליח במסעדה" |
| delivering | מבצע משלוח | "השליח מבצע את המשלוח שלך" |
| completed | נמסר | "ההזמנה נמסרה" |
המעברים מנוהלים על ידי השרת. פיתוח הזמנה לתרחיש שלך — נתחשב בכל הניואנסים.
מה כלול
- עיצוב פרוטוקול (WebSocket/MQTT) וסכמת נתונים.
- פיתוח אפליקציית שליח עם מעקב ברקע.
- הטמעת שרת bus עם Redis ו-TimescaleDB.
- פיתוח ממשק לקוח עם אנימציה חלקה ומצבים.
- בדיקות עומס: 100/500/1000 שליחים.
- תיעוד והדרכה.
תהליך ולוח זמנים
- ניתוח: ביקורת תשתית קיימת, בחירת פרוטוקול.
- עיצוב: ארכיטקטורת bus, סכמות מצבים.
- פיתוח: הטמעת אפליקציה ושרת.
- בדיקות: יחידה, אינטגרציה, עומס.
- השקה: release ל-App Store ו-Google Play.
לוח זמנים: בין 2 ל-4 שבועות תלוי במספר הפלטפורמות ומוכנות ה-API של השרת.
למה לעבוד איתנו?
יישמנו מעקב ל-15+ פרויקטים לוגיסטיים. המהנדסים שלנו מוסמכים ב-iOS ואנדרואיד. אנו מבטיחים דיוק מעקב עד 5 מטרים ויציבות עם 10,000 מפגשים במקביל. קבלו ייעוץ למקרה שלכם — צרו קשר.







