diff --git a/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt b/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt --- a/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt +++ b/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt @@ -17,6 +17,8 @@ import java.lang.ref.WeakReference import java.util.* import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock typealias PositionsMap = HashMap > @@ -31,6 +33,7 @@ private val respondersMap = HashMap>>() private val currentPositions = PositionsMap() + private val positionsLock = ReentrantLock() private lateinit var lifecycle: LifecycleOwner //TODO: remove class reference to context (always require context in all methods) @@ -177,9 +180,7 @@ } Log.d(DEBUG_TAG, "Removed: $removed, respondersMap: $respondersMap") } - fun getPositions(): PositionsMap{ - return currentPositions - } + /** * Cancel the notification @@ -201,7 +202,9 @@ else { val resp = wrD.get()!! resp.onStatusUpdate(LivePositionsServiceStatus.OK) - resp.onUpdateReceived(currentPositions) + positionsLock.withLock { + resp.onUpdateReceived(currentPositions) + } //sent = true count++ } @@ -316,12 +319,14 @@ ) //add update - var valid = false - if(!currentPositions.contains(lineId)) - currentPositions[lineId] = HashMap() - currentPositions[lineId]?.let{ - it[vehicleId] = posUpdate - valid = true + //var valid = false + positionsLock.withLock { + if (!currentPositions.contains(lineId)) + currentPositions[lineId] = HashMap() + currentPositions[lineId]!!.let { + it[vehicleId] = posUpdate + //valid = true + } } //sending //Log.d(DEBUG_TAG, "Parsed update on topic $topic, line $lineId, responders $respondersMap") @@ -369,11 +374,13 @@ fun clearOldPositions(timeMins: Int){ val currentTimeStamp = makeUnixTimestamp() var c = 0 - for((k, manyp) in currentPositions.entries){ - for ((t, p) in manyp.entries){ - if (currentTimeStamp - p.timestamp > timeMins*60){ - manyp.remove(t) - c+=1 + positionsLock.withLock{ + for ((k, manyp) in currentPositions.entries) { + for ((t, p) in manyp.entries) { + if (currentTimeStamp - p.timestamp > timeMins * 60) { + manyp.remove(t) + c += 1 + } } } } diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt --- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt @@ -42,6 +42,7 @@ import androidx.core.view.ViewCompat import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -69,6 +70,8 @@ import it.reyboz.bustorino.viewmodels.LinesViewModel import it.reyboz.bustorino.viewmodels.LivePositionsViewModel import kotlinx.coroutines.Runnable +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.maplibre.android.camera.CameraPosition import org.maplibre.android.camera.CameraUpdateFactory import org.maplibre.android.geometry.LatLng @@ -531,11 +534,7 @@ //if(!stopsLayerStarted) initStopsPolyLineLayers(style, FeatureCollection.fromFeatures(ArrayList()), null, null) - /*if(!stopsLayerStarted) { - Log.d(DEBUG_TAG, "Stop layer is not started yet") - initStopsPolyLineLayers(style, FeatureCollection.fromFeatures(ArrayList()), null) - } - */ + setupBusLayer(style) symbolManager = SymbolManager(mapView,mapReady,style) @@ -1225,6 +1224,7 @@ Log.d(DEBUG_TAG, "In fragment, have ${incomingData.size} updates to show") var countUpds = 0 + var createdVehs = 0 //val symbolsToUpdate = ArrayList() for (upsWithTrp in incomingData.values){ val newPos = upsWithTrp.first @@ -1256,6 +1256,10 @@ val samePosition = oldPos?.let { (it.latitude==newPos.latitude)&&(it.longitude == newPos.longitude) }?:false val setPattern = (oldPattern==null) && (patternStops!=null) + if(newPos.bearing == null && oldPos?.bearing != null){ + //copy old bearing + newPos.bearing = oldPos.bearing + } if((!samePosition)|| setPattern) { val newOrOldPosInBounds = isPointInsideVisibleRegion( @@ -1291,6 +1295,7 @@ //createLabelForVehicle(pos) //if(vehShowing==vehID) // map?.animateCamera(CameraUpdateFactory.newLatLng(LatLng(pos.latitude, pos.longitude)),500) + createdVehs +=1 } if (vehID == vehShowing){ //update the data @@ -1299,7 +1304,7 @@ } //symbolManager.update(symbolsToUpdate) //remove old positions - Log.d(DEBUG_TAG, "Updated $countUpds vehicles") + Log.d(DEBUG_TAG, "Updated $countUpds vehicles, created $createdVehs vehicles") vehsOld.removeAll(vehsNew) //now vehsOld contains the vehicles id for those that have NOT been updated val currentTimeStamp = System.currentTimeMillis() /1000 @@ -1342,10 +1347,11 @@ latLng = animation.animatedValue as LatLng //update position on animation val update = updatesByVehDict[positionUpdate.vehicle] - if(update!=null) latLng?.let { ll-> + if(update!=null){ latLng?.let { ll -> update.posUpdate.latitude = ll.latitude update.posUpdate.longitude = ll.longitude updatePositionsIcons(false) + } } else{ //The update is null Log.w(DEBUG_TAG, "The bus position to animate has been removed, but the animator is still running!") @@ -1395,7 +1401,7 @@ //updatesByVehDict[positionUpdate.vehicle] = positionUpdate } } - + //TODO: MERGE THIS CODE WITH MapLibreFragment ONE /** * Update the bus positions displayed on the map, from the existing data */ @@ -1404,6 +1410,10 @@ val currentTime = System.currentTimeMillis() if(!forced && currentTime - lastUpdateTime < 60){ //DO NOT UPDATE THE MAP + viewLifecycleOwner.lifecycleScope.launch { + delay(200) + updatePositionsIcons(forced) + } return } diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt --- a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt @@ -842,18 +842,18 @@ val symbolsToUpdate = ArrayList() for (upsWithTrp in incomingData.values){ - val pos = upsWithTrp.first - val vehID = pos.vehicle - var animate = false + val newPos = upsWithTrp.first + val vehID = newPos.vehicle + //var animate = false if (vehsOld.contains(vehID)){ //update position only if the starting or the stopping position of the animation are in the view val oldPos = positionsByVehDict[vehID] var avoidShowingUpdateBecauseIsImpossible = false oldPos?.let{ - if(oldPos.routeID!=pos.routeID) { - val dist = LatLng(it.latitude, it.longitude).distanceTo(LatLng(pos.latitude, pos.longitude)) - val speed = dist*3.6 / (pos.timestamp - it.timestamp) //this should be in km/h - Log.w(DEBUG_TAG, "Vehicle $vehID changed route from ${oldPos.routeID} to ${pos.routeID}, distance: $dist, speed: $speed") + if(oldPos.routeID!=newPos.routeID) { + val dist = LatLng(it.latitude, it.longitude).distanceTo(LatLng(newPos.latitude, newPos.longitude)) + val speed = dist*3.6 / (newPos.timestamp - it.timestamp) //this should be in km/h + Log.w(DEBUG_TAG, "Vehicle $vehID changed route from ${oldPos.routeID} to ${newPos.routeID}, distance: $dist, speed: $speed") if (speed > 120 || speed < 0){ avoidShowingUpdateBecauseIsImpossible = true } @@ -865,17 +865,23 @@ continue } - val samePosition = oldPos?.let { (oldPos.latitude==pos.latitude)&&(oldPos.longitude == pos.longitude) }?:false + val samePosition = oldPos?.let { (oldPos.latitude==newPos.latitude)&&(oldPos.longitude == newPos.longitude) }?:false + if(!samePosition) { val isPositionInBounds = isInsideVisibleRegion( - pos.latitude, pos.longitude, false + newPos.latitude, newPos.longitude, false ) || (oldPos?.let { isInsideVisibleRegion(it.latitude,it.longitude, false) } ?: false) + if ((newPos.bearing==null && oldPos?.bearing!=null)){ + //copy old bearing + newPos.bearing = oldPos.bearing + } if (isPositionInBounds) { //animate = true //this moves both the icon and the label - moveVehicleToNewPosition(pos) + moveVehicleToNewPosition(newPos) } else { - positionsByVehDict[vehID] = pos + + positionsByVehDict[vehID] = newPos /*busLabelSymbolsByVeh[vehID]?.let { it.latLng = LatLng(pos.latitude, pos.longitude) symbolsToUpdate.add(it) @@ -885,13 +891,13 @@ } } } - else if(pos.latitude>0 && pos.longitude>0) { + else if(newPos.latitude>0 && newPos.longitude>0) { //we should not have to check for this // update it simply - positionsByVehDict[vehID] = pos + positionsByVehDict[vehID] = newPos //createLabelForVehicle(pos) }else{ - Log.w(DEBUG_TAG, "Update ignored for veh $vehID on line ${pos.routeID}, lat: ${pos.latitude}, lon ${pos.longitude}") + Log.w(DEBUG_TAG, "Update ignored for veh $vehID on line ${newPos.routeID}, lat: ${newPos.latitude}, lon ${newPos.longitude}") } } @@ -907,7 +913,7 @@ //removeVehicleLabel(vehID) } } - //update UI + //finally, update UI updatePositionsIcons() } diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt --- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt +++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt @@ -270,11 +270,15 @@ val filteredLocationUpdates = MediatorLiveData>>() init { filteredLocationUpdates.addSource(updatesWithTripAndPatterns){ - filteredLocationUpdates.value = filterUpdatesForGtfsLine(it, gtfsLineToFilterPos.value!!) + filteredLocationUpdates.postValue(filterUpdatesForGtfsLine(it, gtfsLineToFilterPos.value!!)) } filteredLocationUpdates.addSource(gtfsLineToFilterPos){ - updatesWithTripAndPatterns.value?.let{ ups-> filteredLocationUpdates.value = filterUpdatesForGtfsLine(ups, it)} + //Log.d(DEBUG_TI, "line to filter change to: ${gtfsLineToFilterPos.value}") + updatesWithTripAndPatterns.value?.let{ + ups-> filteredLocationUpdates.postValue(filterUpdatesForGtfsLine(ups, it)) + //Log.d(DEBUG_TI, "Set ${ups.size} updates as new value for filteredLocation") + } } } @@ -284,41 +288,52 @@ Pair, List>{ val gtfsLineId = linePatt.first val pattern = linePatt.second - - - val filtdLineID = GtfsUtils.stripGtfsPrefix(gtfsLineId) - //filter buses with direction, show those only with the same direction val updsForTripId = HashMap>() - val directionId = pattern?.directionId ?: -100 - val numUpds = updates.entries.size - Log.d(DEBUG_TI, "Got $numUpds updates, current pattern is: ${pattern?.name}, directionID: ${pattern?.directionId}") - // cannot understand where this is used - //val patternsDirections = HashMap() val vehicleOnWrongDirection = mutableListOf() - for((tripId, pair) in updates.entries){ - //remove trips with wrong line - val posUp = pair.first - val vehicle = pair.first.vehicle - if(pair.first.routeID!=filtdLineID) - continue - - if(directionId!=-100 && pair.second!=null && pair.second?.pattern !=null){ - val dir = pair.second!!.pattern!!.directionId - - if(dir == directionId){ - //add the trip + + //supporting the eventual null case when there is no need to filter + if (gtfsLineId == "ALL"){ + //copy the dict + for ((tripId, pair) in updates.entries) { + updsForTripId[tripId] = pair + } + } else { + + val filtdLineID = GtfsUtils.stripGtfsPrefix(gtfsLineId) + //filter buses with direction, show those only with the same direction + val directionId = pattern?.directionId ?: -100 + val numUpds = updates.entries.size + Log.d( + DEBUG_TI, + "Got $numUpds updates, current pattern is: ${pattern?.name}, directionID: ${pattern?.directionId}" + ) + // cannot understand where this is used + //val patternsDirections = HashMap() + for ((tripId, pair) in updates.entries) { + //remove trips with wrong line + val posUp = pair.first + val vehicle = pair.first.vehicle + if (pair.first.routeID != filtdLineID) + continue + + if (directionId != -100 && pair.second != null && pair.second?.pattern != null) { + val dir = pair.second!!.pattern!!.directionId + + if (dir == directionId) { + //add the trip + updsForTripId[tripId] = pair + } else { + vehicleOnWrongDirection.add(vehicle) + } + //patternsDirections[tripId] = dir ?: -10 + } else { updsForTripId[tripId] = pair - } else{ - vehicleOnWrongDirection.add(vehicle) + //Log.d(DEBUG_TAG, "No pattern for tripID: $tripId") + //patternsDirections[tripId] = -10 } - //patternsDirections[tripId] = dir ?: -10 - } else{ - updsForTripId[tripId] = pair - //Log.d(DEBUG_TAG, "No pattern for tripID: $tripId") - //patternsDirections[tripId] = -10 } } - Log.d(DEBUG_TI, " Filtered updates are ${updsForTripId.keys.size}") // Original updates directs: $patternsDirections\n + Log.d(DEBUG_TI, "Filtered updates are ${updsForTripId.keys.size}") // Original updates directs: $patternsDirections\n return Pair(updsForTripId, vehicleOnWrongDirection) }