react-native-mytatva-rn-sdk 1.2.67 → 1.2.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -49,6 +49,8 @@ import kotlinx.coroutines.SupervisorJob
|
|
|
49
49
|
import kotlinx.coroutines.delay
|
|
50
50
|
import kotlinx.coroutines.launch
|
|
51
51
|
import kotlinx.coroutines.withContext
|
|
52
|
+
import kotlinx.coroutines.sync.Mutex
|
|
53
|
+
import kotlinx.coroutines.sync.withLock
|
|
52
54
|
import org.json.JSONObject
|
|
53
55
|
import java.io.File
|
|
54
56
|
import java.text.SimpleDateFormat
|
|
@@ -56,6 +58,9 @@ import java.util.Date
|
|
|
56
58
|
import java.util.Locale
|
|
57
59
|
import java.util.Timer
|
|
58
60
|
import java.util.TimerTask
|
|
61
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
62
|
+
import java.util.concurrent.ConcurrentLinkedQueue
|
|
63
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
59
64
|
import kotlin.coroutines.resume
|
|
60
65
|
import kotlin.coroutines.suspendCoroutine
|
|
61
66
|
|
|
@@ -69,11 +74,14 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
69
74
|
var prefsHelper: SharedPreferencesLibraryUtil? = null
|
|
70
75
|
private val apiScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
|
71
76
|
private var debounceJob: Job? = null
|
|
72
|
-
private
|
|
77
|
+
private val isBatchProcessing = AtomicBoolean(false)
|
|
73
78
|
private var lastDeviceStatus: String? = null
|
|
74
79
|
private var glucoseObserver: Observer<PocGlucose?>? = null
|
|
75
80
|
private var isObserving = false
|
|
76
|
-
private
|
|
81
|
+
private val processedGlucoseIds = ConcurrentHashMap.newKeySet<Int>()
|
|
82
|
+
private val pendingDataQueue = ConcurrentLinkedQueue<PocGlucose>()
|
|
83
|
+
private val metadataLock = Mutex()
|
|
84
|
+
private var uploadJob: Job? = null
|
|
77
85
|
|
|
78
86
|
init {
|
|
79
87
|
mReactContext = reactContext
|
|
@@ -412,56 +420,42 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
412
420
|
)
|
|
413
421
|
return
|
|
414
422
|
}
|
|
415
|
-
|
|
423
|
+
|
|
424
|
+
// Don't prevent observation during batch processing - they can coexist
|
|
425
|
+
// Just log it for monitoring
|
|
426
|
+
if (isBatchProcessing.get()) {
|
|
416
427
|
Log.d(
|
|
417
|
-
"
|
|
418
|
-
"Batch processing in progress,
|
|
428
|
+
"observeGlucoseData",
|
|
429
|
+
"Batch processing in progress, but starting observer anyway"
|
|
419
430
|
)
|
|
420
|
-
return
|
|
421
431
|
}
|
|
422
432
|
|
|
423
433
|
// Remove existing observer if any to prevent memory leaks
|
|
424
|
-
|
|
434
|
+
if (glucoseObserver != null) {
|
|
435
|
+
stopObservingGlucoseDataInternal()
|
|
436
|
+
}
|
|
425
437
|
|
|
426
|
-
//
|
|
427
|
-
Handler(Looper.getMainLooper()).
|
|
438
|
+
// Create new observer immediately without delay to prevent data loss
|
|
439
|
+
Handler(Looper.getMainLooper()).post {
|
|
428
440
|
// Create new observer with explicit nullable parameter type
|
|
429
441
|
glucoseObserver = Observer<PocGlucose?> { pocGlucose ->
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
val lastSyncData = prefsHelper?.lastSyncData
|
|
434
|
-
val currentSyncTimeInMillis = lastSyncData?.timeInMillis
|
|
435
|
-
|
|
436
|
-
if (lastSyncData != null) {
|
|
437
|
-
dataAge = currentTime - lastSyncData.timeInMillis
|
|
442
|
+
if (pocGlucose == null) {
|
|
443
|
+
Log.d("observeGlucoseData", "Received null glucose data - skipping")
|
|
444
|
+
return@Observer
|
|
438
445
|
}
|
|
439
446
|
|
|
440
|
-
//
|
|
441
|
-
|
|
442
|
-
Log.
|
|
447
|
+
// Check for duplicate using glucoseId instead of timestamp
|
|
448
|
+
val glucoseId = pocGlucose.glucoseId ?: run {
|
|
449
|
+
Log.w("observeGlucoseData", "Glucose ID is null, skipping")
|
|
443
450
|
return@Observer
|
|
444
451
|
}
|
|
445
452
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
Log.d(
|
|
449
|
-
|
|
450
|
-
"Received glucose data - processing"
|
|
451
|
-
)
|
|
452
|
-
// Update last processed sync time
|
|
453
|
-
lastProcessedSyncTimeInMillis = currentSyncTimeInMillis
|
|
453
|
+
// Add to queue for processing - ensures no data is lost
|
|
454
|
+
if (pendingDataQueue.offer(pocGlucose)) {
|
|
455
|
+
Log.d("observeGlucoseData", "Added glucose data to queue: $glucoseId")
|
|
456
|
+
triggerDataUpload(envType)
|
|
454
457
|
} else {
|
|
455
|
-
|
|
456
|
-
Handler(Looper.getMainLooper()).postDelayed({
|
|
457
|
-
observeAllGlucoseData(userToken, isForClear, envType)
|
|
458
|
-
}, 100)
|
|
459
|
-
Log.d(
|
|
460
|
-
"observeGlucoseData",
|
|
461
|
-
"Received null glucose data - skipping processing"
|
|
462
|
-
)
|
|
463
|
-
// Update last processed sync time
|
|
464
|
-
lastProcessedSyncTimeInMillis = currentSyncTimeInMillis
|
|
458
|
+
Log.e("observeGlucoseData", "Failed to add data to queue: $glucoseId")
|
|
465
459
|
}
|
|
466
460
|
}
|
|
467
461
|
|
|
@@ -477,7 +471,7 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
477
471
|
isObserving = false
|
|
478
472
|
}
|
|
479
473
|
}
|
|
480
|
-
}
|
|
474
|
+
}
|
|
481
475
|
|
|
482
476
|
} catch (e: Exception) {
|
|
483
477
|
Log.e("observeGlucoseData", "observeGlucoseData: ${e.message}")
|
|
@@ -487,6 +481,56 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
487
481
|
}
|
|
488
482
|
}
|
|
489
483
|
|
|
484
|
+
// New method to handle data upload from queue
|
|
485
|
+
private fun triggerDataUpload(envType: String) {
|
|
486
|
+
// If upload is already in progress, just return
|
|
487
|
+
if (uploadJob?.isActive == true) {
|
|
488
|
+
return
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
uploadJob = apiScope.launch {
|
|
492
|
+
while (pendingDataQueue.isNotEmpty()) {
|
|
493
|
+
val pocGlucose = pendingDataQueue.poll() ?: continue
|
|
494
|
+
|
|
495
|
+
// Check for duplicate using glucoseId
|
|
496
|
+
val glucoseId = pocGlucose.glucoseId ?: continue
|
|
497
|
+
|
|
498
|
+
if (!processedGlucoseIds.add(glucoseId)) {
|
|
499
|
+
Log.d("triggerDataUpload", "Already processed: $glucoseId, skipping")
|
|
500
|
+
continue
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
val currentTime = System.currentTimeMillis()
|
|
504
|
+
val lastSyncData = prefsHelper?.lastSyncData
|
|
505
|
+
val dataAge = if (lastSyncData != null) {
|
|
506
|
+
currentTime - lastSyncData.timeInMillis
|
|
507
|
+
} else {
|
|
508
|
+
0L
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// If data is recent (within 4 minutes), upload immediately
|
|
512
|
+
if (lastSyncData == null || dataAge <= 4 * 60 * 1000L) {
|
|
513
|
+
handleGlucoseData(pocGlucose, envType)
|
|
514
|
+
Log.d("triggerDataUpload", "Uploaded recent data: $glucoseId")
|
|
515
|
+
} else {
|
|
516
|
+
// Data is old, need batch processing
|
|
517
|
+
// Put it back in queue for batch to handle
|
|
518
|
+
pendingDataQueue.offer(pocGlucose)
|
|
519
|
+
Log.d("triggerDataUpload", "Old data detected, triggering batch: $glucoseId")
|
|
520
|
+
|
|
521
|
+
// Trigger batch process on main thread
|
|
522
|
+
withContext(Dispatchers.Main) {
|
|
523
|
+
observeAllGlucoseData(userToken, false, envType)
|
|
524
|
+
}
|
|
525
|
+
break // Exit loop to let batch take over
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Small delay to avoid overwhelming the API
|
|
529
|
+
delay(100)
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
490
534
|
private fun handleGlucoseData(pocGlucose: PocGlucose, envType: String) {
|
|
491
535
|
try {
|
|
492
536
|
// Additional safety check
|
|
@@ -592,8 +636,9 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
592
636
|
fun observeAllGlucoseData(token: String, isForClear: Boolean = false, envType: String) {
|
|
593
637
|
Log.d("function call", "observeAllGlucoseData")
|
|
594
638
|
|
|
595
|
-
|
|
596
|
-
|
|
639
|
+
// Use atomic compareAndSet to prevent concurrent batch processing
|
|
640
|
+
if (!isBatchProcessing.compareAndSet(false, true)) {
|
|
641
|
+
Log.d("observeAllGlucoseData", "Batch processing already in progress, skipping duplicate call")
|
|
597
642
|
return
|
|
598
643
|
}
|
|
599
644
|
|
|
@@ -601,60 +646,94 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
601
646
|
env = envType
|
|
602
647
|
|
|
603
648
|
CoroutineScope(Dispatchers.IO).launch {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
649
|
+
try {
|
|
650
|
+
// Process any pending data from queue first to ensure nothing is lost
|
|
651
|
+
val pendingData = mutableListOf<PocGlucose>()
|
|
652
|
+
while (pendingDataQueue.isNotEmpty()) {
|
|
653
|
+
pendingDataQueue.poll()?.let { pendingData.add(it) }
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (pendingData.isNotEmpty()) {
|
|
657
|
+
Log.d("observeAllGlucoseData", "Found ${pendingData.size} items in pending queue")
|
|
658
|
+
}
|
|
609
659
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
stopObservingGlucoseData()
|
|
660
|
+
val lastSyncData = prefsHelper?.lastSyncData
|
|
661
|
+
Log.d("lastSyncData: ", Gson().toJson(lastSyncData).toString())
|
|
613
662
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
663
|
+
val currentTime = System.currentTimeMillis()
|
|
664
|
+
val dataAge = if (lastSyncData != null) currentTime - lastSyncData.timeInMillis else 0L
|
|
665
|
+
|
|
666
|
+
if (lastSyncData != null && dataAge > 3 * 60 * 1000L) {
|
|
667
|
+
val glucoseData = mModel.getGlucoseBetweenTime(lastSyncData.timeInMillis)
|
|
668
|
+
|
|
669
|
+
Log.d("observeAllGlucoseData", "Retrieved ${glucoseData?.size ?: 0} records from DB")
|
|
670
|
+
|
|
671
|
+
// Combine database data with pending queue data
|
|
672
|
+
val allData = mutableListOf<PocGlucose>()
|
|
673
|
+
glucoseData?.let { allData.addAll(it) }
|
|
674
|
+
allData.addAll(pendingData)
|
|
675
|
+
|
|
676
|
+
// Log BEFORE sorting to see database order
|
|
677
|
+
allData.take(5).forEachIndexed { i, data ->
|
|
678
|
+
Log.d("BEFORE Sort [$i]", "timeInMillis: ${data.timeInMillis}, glucoseId: ${data.glucoseId}")
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Sort by timestamp to ensure chronological order (oldest first)
|
|
682
|
+
// This is critical when device reconnects after being out of range
|
|
683
|
+
val sortedGlucoseData = allData.sortedBy { it.timeInMillis }
|
|
684
|
+
|
|
685
|
+
// Log AFTER sorting to verify chronological order
|
|
686
|
+
sortedGlucoseData.take(5).forEachIndexed { i, data ->
|
|
687
|
+
Log.d("AFTER Sort [$i]", "timeInMillis: ${data.timeInMillis}, glucoseId: ${data.glucoseId}")
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (sortedGlucoseData.isNotEmpty()) {
|
|
691
|
+
Log.d("observeAllGlucoseData", "✅ Sorted ${sortedGlucoseData.size} records chronologically")
|
|
692
|
+
Log.d("observeAllGlucoseData", "Oldest: ${sortedGlucoseData.first().timeInMillis}")
|
|
693
|
+
Log.d("observeAllGlucoseData", "Newest: ${sortedGlucoseData.last().timeInMillis}")
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (sortedGlucoseData.isNotEmpty()) {
|
|
697
|
+
processBatchDataAndStartObserver(
|
|
698
|
+
sortedGlucoseData,
|
|
699
|
+
isForClear,
|
|
700
|
+
envType
|
|
701
|
+
)
|
|
702
|
+
} else {
|
|
703
|
+
Log.d(
|
|
704
|
+
"observeAllGlucoseData",
|
|
705
|
+
"No historical data found, starting live observation"
|
|
706
|
+
)
|
|
707
|
+
withContext(Dispatchers.Main) {
|
|
708
|
+
if (!isObserving) {
|
|
709
|
+
observeGlucoseData(userToken, isForClear, envType)
|
|
648
710
|
}
|
|
649
|
-
}
|
|
650
|
-
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
} else {
|
|
714
|
+
// No batch needed, but process pending data if any
|
|
715
|
+
if (pendingData.isNotEmpty()) {
|
|
716
|
+
Log.d("observeAllGlucoseData", "Processing ${pendingData.size} pending items")
|
|
717
|
+
pendingData.forEach { data ->
|
|
718
|
+
val glucoseId = data.glucoseId
|
|
719
|
+
if (glucoseId != null && processedGlucoseIds.add(glucoseId)) {
|
|
720
|
+
handleGlucoseData(data, envType)
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
withContext(Dispatchers.Main) {
|
|
726
|
+
if (!isObserving) {
|
|
651
727
|
observeGlucoseData(userToken, isForClear, envType)
|
|
652
728
|
}
|
|
653
|
-
} catch (e: Exception) {
|
|
654
|
-
isBatchProcessing = false
|
|
655
|
-
observeGlucoseData(userToken, isForClear, envType)
|
|
656
729
|
}
|
|
657
|
-
}
|
|
730
|
+
}
|
|
731
|
+
} catch (e: Exception) {
|
|
732
|
+
Log.e("observeAllGlucoseData", "Error in batch processing: ${e.message}", e)
|
|
733
|
+
} finally {
|
|
734
|
+
// Always reset the flag
|
|
735
|
+
isBatchProcessing.set(false)
|
|
736
|
+
Log.d("observeAllGlucoseData", "Batch processing completed, flag reset")
|
|
658
737
|
}
|
|
659
738
|
}
|
|
660
739
|
}
|
|
@@ -679,8 +758,9 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
679
758
|
Log.w("processBatchDataAndStartObserver", "Batch processing had failures")
|
|
680
759
|
}
|
|
681
760
|
|
|
761
|
+
// Don't reset flag here - it's reset in observeAllGlucoseData's finally block
|
|
762
|
+
|
|
682
763
|
// Ensure we're on main thread and not already observing
|
|
683
|
-
isBatchProcessing = false
|
|
684
764
|
withContext(Dispatchers.Main) {
|
|
685
765
|
if (!isObserving) {
|
|
686
766
|
Log.d(
|
|
@@ -699,7 +779,6 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
699
779
|
} catch (e: Exception) {
|
|
700
780
|
Log.e("processBatchDataAndStartObserver", "Error in batch processing: ${e.message}")
|
|
701
781
|
// Start live observation even on error
|
|
702
|
-
isBatchProcessing = false
|
|
703
782
|
withContext(Dispatchers.Main) {
|
|
704
783
|
if (!isObserving) {
|
|
705
784
|
observeGlucoseData(userToken, isForClear, envType)
|
|
@@ -733,6 +812,9 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
733
812
|
// Cancel all coroutines
|
|
734
813
|
debounceJob?.cancel()
|
|
735
814
|
debounceJob = null
|
|
815
|
+
|
|
816
|
+
uploadJob?.cancel()
|
|
817
|
+
uploadJob = null
|
|
736
818
|
|
|
737
819
|
// Reset user token
|
|
738
820
|
userToken = ""
|
|
@@ -740,12 +822,16 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
740
822
|
// Reset device status
|
|
741
823
|
lastDeviceStatus = null
|
|
742
824
|
|
|
825
|
+
// Clear processed IDs and pending queue
|
|
826
|
+
processedGlucoseIds.clear()
|
|
827
|
+
pendingDataQueue.clear()
|
|
828
|
+
|
|
829
|
+
// Reset batch processing flag
|
|
830
|
+
isBatchProcessing.set(false)
|
|
831
|
+
|
|
743
832
|
// Clear any cached data if needed
|
|
744
833
|
prefsHelper?.clearQRInformation() // if you have such method
|
|
745
834
|
|
|
746
|
-
// Reset last processed sync time
|
|
747
|
-
lastProcessedSyncTimeInMillis = null
|
|
748
|
-
|
|
749
835
|
} catch (e: Exception) {
|
|
750
836
|
Log.e("resetCgmState", "Error resetting CGM state: ${e.message}")
|
|
751
837
|
}
|
|
@@ -814,13 +900,34 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
814
900
|
continue
|
|
815
901
|
}
|
|
816
902
|
|
|
903
|
+
// Log the actual timestamps being sent to verify order
|
|
904
|
+
transformedLogs.forEachIndexed { i, log ->
|
|
905
|
+
Log.d(
|
|
906
|
+
"Batch $index Record $i",
|
|
907
|
+
"timeInMillis: ${log.timeInMillis}, glucoseId: ${log.glucoseId}"
|
|
908
|
+
)
|
|
909
|
+
}
|
|
910
|
+
|
|
817
911
|
val allResult = AllCGMLogRequest(vendor = "GoodFlip", logs = transformedLogs)
|
|
818
912
|
val json = Gson().toJson(allResult)
|
|
819
913
|
|
|
914
|
+
// Check if logs array is empty - skip API call if so
|
|
915
|
+
if (transformedLogs.isEmpty()) {
|
|
916
|
+
Log.d(
|
|
917
|
+
"processBatchDataSynchronously",
|
|
918
|
+
"Batch $index skipped - logs array is empty, not hitting API"
|
|
919
|
+
)
|
|
920
|
+
continue
|
|
921
|
+
}
|
|
922
|
+
|
|
820
923
|
Log.d(
|
|
821
924
|
"Batch Upload",
|
|
822
925
|
"Processing batch $index with ${transformedLogs.size} records"
|
|
823
926
|
)
|
|
927
|
+
Log.d(
|
|
928
|
+
"Batch Upload",
|
|
929
|
+
"First timestamp: ${transformedLogs.firstOrNull()?.timeInMillis}, Last timestamp: ${transformedLogs.lastOrNull()?.timeInMillis}"
|
|
930
|
+
)
|
|
824
931
|
logLongJson("Batch $index JSON=>>> ", json)
|
|
825
932
|
|
|
826
933
|
val uploadSuccessful = uploadBatchSynchronously(json, index, envType)
|
|
@@ -864,6 +971,11 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
864
971
|
|
|
865
972
|
@ReactMethod
|
|
866
973
|
fun stopObservingGlucoseData() {
|
|
974
|
+
stopObservingGlucoseDataInternal()
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// Internal method that doesn't clear the queue (used during restarts)
|
|
978
|
+
private fun stopObservingGlucoseDataInternal() {
|
|
867
979
|
Log.d("stopObservingGlucoseData", "Stopping glucose data observation")
|
|
868
980
|
|
|
869
981
|
try {
|
|
@@ -890,9 +1002,6 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
890
1002
|
// Reset last device status
|
|
891
1003
|
lastDeviceStatus = null
|
|
892
1004
|
|
|
893
|
-
// Reset last processed sync time
|
|
894
|
-
lastProcessedSyncTimeInMillis = null
|
|
895
|
-
|
|
896
1005
|
} catch (e: Exception) {
|
|
897
1006
|
Log.e("stopObservingGlucoseData", "Error stopping observer: ${e.message}")
|
|
898
1007
|
}
|
|
@@ -929,21 +1038,28 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
|
|
|
929
1038
|
|
|
930
1039
|
private fun updateSyncMetadata(lastRecord: PocGlucose?) {
|
|
931
1040
|
lastRecord?.let {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1041
|
+
apiScope.launch {
|
|
1042
|
+
metadataLock.withLock {
|
|
1043
|
+
try {
|
|
1044
|
+
val syncData = SyncMeta(
|
|
1045
|
+
System.currentTimeMillis(),
|
|
1046
|
+
it.timeInMillis,
|
|
1047
|
+
it.deviceId,
|
|
1048
|
+
it.glucoseId
|
|
1049
|
+
)
|
|
1050
|
+
prefsHelper?.lastSyncData = syncData
|
|
1051
|
+
|
|
1052
|
+
// Mark this glucose ID as processed
|
|
1053
|
+
it.glucoseId?.let { id -> processedGlucoseIds.add(id) }
|
|
1054
|
+
|
|
1055
|
+
Log.d(
|
|
1056
|
+
"Sync Metadata",
|
|
1057
|
+
"Sync metadata updated: glucoseId=${it.glucoseId}, time=${it.timeInMillis}"
|
|
1058
|
+
)
|
|
1059
|
+
} catch (e: Exception) {
|
|
1060
|
+
Log.e("updateSyncMetadata", "Error updating sync metadata: ${e.message}")
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
947
1063
|
}
|
|
948
1064
|
}
|
|
949
1065
|
}
|
|
@@ -429,6 +429,14 @@ class FinalViewModel: NSObject {
|
|
|
429
429
|
let cgmLogs = filteredBatch.map { ReceiveDataToLog(data: $0) }
|
|
430
430
|
let payload = Payload(logs: cgmLogs)
|
|
431
431
|
|
|
432
|
+
// Check if logs array is empty - skip API call if so
|
|
433
|
+
if cgmLogs.isEmpty {
|
|
434
|
+
print("Batch \(index + 1) skipped - logs array is empty, not hitting API")
|
|
435
|
+
// Move to next batch
|
|
436
|
+
self.uploadBatch(batches: batches, index: index + 1)
|
|
437
|
+
return
|
|
438
|
+
}
|
|
439
|
+
|
|
432
440
|
print("====================================> called uploadBatch")
|
|
433
441
|
API.shared.postCGMData(data: payload, environment: .stage) {
|
|
434
442
|
print("====================================> uploaded successfully")
|