SafeDispatch/Safedispatch_4_0/maptab/MapHandler.cs
2024-02-22 18:43:59 +02:00

1928 lines
80 KiB
C#

using System;
using System.Collections.Generic;
using SafeMobileLib;
using Safedispatch_4_0;
using System.Threading.Tasks;
using System.Threading;
using System.Text;
using System.Windows.Forms;
using System.Linq;
namespace Dispatcher.maptab
{
/// <summary>
/// - MapHashName : is the Live Tab instance name (each instance has an unique name) in order to find the state of an unit on that tab
/// - HasLabels : provide indicator if the markers will have labels or they will be hidden
/// - IsMetric : speed unit will be set to metric if true, otherwise it will be in miles
/// - IsConsoleVisible : provides indicator if the console needs to be displayed for debug reasons
/// - IconType : pass the style of units that is used in order to change the labels position and color accordingly
/// - Center Position : default position to which the map needs to be center at initialization(usualy is the last visible position)
/// - HideTimeInactiveUnits : indicator if the units needs to be removed from the map if the GPS is older than a predefined amount of minutes
/// - HideNonEmergUnits : indicator if the units need to be removed from the map if they are not in emergency
/// - InactiveTimeutMinutes : number of minutes after which a unit can be considere inactive
/// - IsNeverReportedWaning : raise events which time you try to display a unit that had never reported any gps
/// - MaxDisplayedNrOfUnits : set dataset limit after which the units will be skipped by the map js library
/// </summary>
public partial class MapHandler : UserControl, IMap
{
private String mapUrl = "";
private int NeverReportedTime = 1262304000;
private int GPSHandleIntervalSec = 7;
private readonly int TimeoutBetweenCmdMs = 700;
private readonly bool SEQ_ID_REQ = false;
private readonly String UNASSIGNED = "unASSigned";
#region Map commands
private readonly string ShowConsole = "showconsole";
private readonly string SetLegacyIcon = "setLegacyIcons";
private readonly string CenterZoom = "centerzoom";
private readonly string SetDatasetLimit = "setdatasetlimit";
private readonly string UpdateDataSet = "updatedataset";
private readonly string RemoveDataSet = "removedataset";
private readonly string DeleteFromDataset = "deletefromdataset";
private readonly string PutLabels = "putlabels";
private readonly string RemoveLabels = "removelabels";
private readonly string PutOnMap = "putOnMap";
private readonly string OpenInfo = "openInfo";
private readonly string MapLoaded = "map-loaded";
private readonly string Nearest = "nearest";
private readonly string UnitDisplayReq = "request-unit-display";
private readonly string UpdateTime = "exec-time";
private readonly string FastCommand = "fast-command";
private readonly string TextCommand = "text-command";
private readonly string PTTCommand = "ptt-command";
private readonly string StreetViewOpened = "street-view-open";
private readonly string Landmark = "landmark";
private readonly string ShowLandmark = "updatedataset";
private readonly string ShowGeofence = "setPolygon";
private readonly string DeleteGeofence = "deletePolygon";
private readonly string Done = "done";
private readonly string Bounds = "bounds";
#endregion
private CefSharpChromiumWebBrowser mapGoogles;
private Dictionary<Int64, Vehicle> InactiveScIdsVehicle = new Dictionary<long, Vehicle>();
private Dictionary<Int64, Vehicle> displayedScIdsVehicle = new Dictionary<long, Vehicle>();
private Dictionary<Int64, Vehicle> newGPSScIdsVehicle = new Dictionary<long, Vehicle>();
private List<String> dispalyedDatasets = new List<string>();
private Int64 SeqId = 1;
private bool hideTimeInactiveUnits = false;
private bool showEmergencyForTimeInactiveUnits = true;
private bool hideNonEmergUnits = false;
private int inactiveTimeoutMinutes = Int32.MaxValue;
private bool isNeverReportedWarning = true;
private bool isConsoleVisible = false;
private IconTheme iconTheme = IconTheme.PIN;
private IconType iconType = IconType.ARROWS;
private LatLngZoom centerPosition = new LatLngZoom();
private Int32 maxDisplayedNrOfUnits = 20000;
private bool isMetric = true;
private bool hasLabels = true;
private int datasetChunkSize = 70;
private int lastResponseMs = 0;
private bool mapDead = false;
private Int32 TimeoutMapDeadMs = 5000;
private Object mapLock = new Object();
#region SETTERS AND GETTERS
private String mapHashName;
public string MapHashName
{
get { return mapHashName; }
set { mapHashName = value; }
}
public bool ConsoleVisible
{
get { return isConsoleVisible; }
set
{
isConsoleVisible = value;
ChangeConsoleState(isConsoleVisible);
//(this as IMap).changeConsoleState(value);
}
}
public bool HideNonEmergUnits
{
get { return hideNonEmergUnits; }
set
{
hideNonEmergUnits = value;
}
}
public bool HideTimeInactiveUnits
{
get { return hideTimeInactiveUnits; }
set
{
hideTimeInactiveUnits = value;
}
}
public bool ShowEmergencyForTimeInactiveUnits
{
get { return showEmergencyForTimeInactiveUnits; }
set
{
showEmergencyForTimeInactiveUnits = value;
}
}
public bool IsNeverReportedWarning
{
get { return isNeverReportedWarning; }
set
{
isNeverReportedWarning = value;
}
}
public int InactiveTimeoutMinutes
{
get { return inactiveTimeoutMinutes; }
set { inactiveTimeoutMinutes = value; }
}
public IconTheme IconTheme
{
get { return iconTheme; }
set
{
iconTheme = value;
//(this as IMap).changeMarkersType(value);
}
}
public IconType IconType
{
get { return iconType; }
set
{
iconType = value;
//(this as IMap).changeMarkersType(value);
}
}
public LatLngZoom CenterPosition
{
get { return centerPosition; }
set
{
centerPosition = value;
}
}
public int MaxDisplayedNrOfUnits
{
get { return maxDisplayedNrOfUnits; }
set { maxDisplayedNrOfUnits = value; }
}
public bool IsMetric
{
get { return isMetric; }
set { isMetric = value; }
}
public bool HasLabels
{
get { return hasLabels; }
set { hasLabels = value; }
}
#endregion
// thread lock so only one update will have access to the map at a time
private object _lock = 0;
private long numberOfRunningTasks = 0;
private bool mapLoaded = false;
// Create a semaphore that can satisfy up to three
// concurrent requests. Use an initial count of zero,
// so that the entire semaphore count is initially
// owned by the main program thread.
//
private Semaphore semaphore = new Semaphore(0, 1);
private int semaphoreTimeoutMs = 2000;
public MapHandler(String url)
{
InitializeComponent();
mapUrl = url;
//this.Dock = DockStyle.Fill;
// create a task taht will handle any map changes
Task.Factory.StartNew(() => MapWorker());
// initialize the map control
InitializeMap();
}
/// <summary>
/// Initialize the map by creating the control and adding event handlers
/// </summary>
private void InitializeMap()
{
mapGoogles = new CefSharpChromiumWebBrowser(mapUrl);
mapGoogles.Dock = DockStyle.Fill;
// create event handler for when the map is loaded
mapGoogles.OnWebPageLoaded += OnWePageLoadedHandler;
mapGoogles.OnURLResponseReceived += OnMapMessageReceivedHandler;
// add map control to the mai form controll
this.Controls.Add(mapGoogles);
}
/// <summary>
/// Restart the map by removing the previous control and handlers
/// and the by initializing it again
/// </summary>
public void RestartMap()
{
// remove previous handlers
if (mapGoogles != null)
{
this.Controls.Remove(mapGoogles);
mapGoogles.OnWebPageLoaded -= OnWePageLoadedHandler;
mapGoogles.OnURLResponseReceived -= OnMapMessageReceivedHandler;
mapGoogles.Dispose();
}
// reinitialize the map
InitializeMap();
}
/// <summary>
/// Handle all map request as of unit removing and adding, position updates and icon updates
/// </summary>
private void MapWorker()
{
int countInactive = 0;
int count = 0;
while (MainForm2.isRunning)
{
Thread.Sleep(100);
//Task.WaitAll
if (count++ > GPSHandleIntervalSec * 10 && mapLoaded)
{
count = 0;
long runningTasks = Interlocked.Read(ref numberOfRunningTasks);
Utils.WriteLine("Hash count active: " + displayedScIdsVehicle.Count + " | inactive: " + InactiveScIdsVehicle.Count, ConsoleColor.Cyan);
if (runningTasks != 0)
continue;
lock (mapLock)
{
if (newGPSScIdsVehicle.Count == 0)
continue;
else if (newGPSScIdsVehicle.Count < 100)
GPSHandleIntervalSec = 5;
else if (newGPSScIdsVehicle.Count < 300)
GPSHandleIntervalSec = 15;
else if (newGPSScIdsVehicle.Count < 500)
GPSHandleIntervalSec = 30;
else
GPSHandleIntervalSec = 50;
List<Vehicle> displayedWithGPS = new List<Vehicle>();
lock (InactiveScIdsVehicle)
{
lock (displayedScIdsVehicle)
{
displayedWithGPS = newGPSScIdsVehicle.Values.ToList()
.Where(d => (displayedScIdsVehicle.Values.ToList().Select(w => w.sc_id)).Contains(d.sc_id)).ToList();
Utils.WriteLine("GPS updates for " + displayedWithGPS.Count + " displayed units", ConsoleColor.Yellow);
// hide units that are inactive
if (HideTimeInactiveUnits || HideNonEmergUnits)
{
List<Vehicle> displayFromInactiveListGPS = newGPSScIdsVehicle.Values.ToList()
.Where(d => (InactiveScIdsVehicle.Values.ToList().Select(w => w.sc_id)).Contains(d.sc_id)).ToList();
// move unit from inactive list to the displayed list
foreach (Vehicle veh in displayFromInactiveListGPS)
{
if (InactiveScIdsVehicle.ContainsKey(veh.sc_id))
InactiveScIdsVehicle.Remove(veh.sc_id);
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
displayedScIdsVehicle.Add(veh.sc_id, veh);
}
Utils.WriteLine("GPS updates for " + displayFromInactiveListGPS.Count + " inactive units", ConsoleColor.Green);
// add units that were reactivated to the display list
if (displayFromInactiveListGPS.Count > 0)
displayedWithGPS.AddRange(displayFromInactiveListGPS);
}
}
}
// update the units on the map
if (displayedWithGPS.Count > 0)
AddVehicles(displayedWithGPS, false);
newGPSScIdsVehicle.Clear();
}
}
if (HideTimeInactiveUnits || HideNonEmergUnits)
{
// remove inactive units once at 30 seconds
if (countInactive++ > 30 * 10 && mapLoaded)
{
lock (InactiveScIdsVehicle)
{
lock (displayedScIdsVehicle)
{
// detect units that needs to be removed due to innactive
int positionTimeNewerThan = (DateTime.UtcNow.AddMinutes(-1 * inactiveTimeoutMinutes)).GetSecondsFromDT();
//int nonEmergUnits = ;
List<Vehicle> toRemoveInactiveMinutesVehicles;
if (showEmergencyForTimeInactiveUnits)
toRemoveInactiveMinutesVehicles =
displayedScIdsVehicle.Values.ToList().Where(d => d.lastValidPositionTime < positionTimeNewerThan
&& d.lastValidPositionTime > NeverReportedTime && d.actual_status != Status_for_tab.EMERG).ToList();
else
toRemoveInactiveMinutesVehicles =
displayedScIdsVehicle.Values.ToList().Where(d => d.lastValidPositionTime < positionTimeNewerThan
&& d.lastValidPositionTime > NeverReportedTime).ToList();
List<Vehicle> toRemoveNonEmergencyVehicles =
displayedScIdsVehicle.Values.ToList().Where(d => d.actual_status != Status_for_tab.EMERG).ToList();
if (HideNonEmergUnits)
{
foreach (Vehicle veh in toRemoveNonEmergencyVehicles)
{
if (InactiveScIdsVehicle.ContainsKey(veh.sc_id))
InactiveScIdsVehicle.Remove(veh.sc_id);
InactiveScIdsVehicle.Add(veh.sc_id, veh);
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
}
if (toRemoveNonEmergencyVehicles.Count > 0)
{
removeVehicles(toRemoveNonEmergencyVehicles, false);
if (toRemoveNonEmergencyVehicles.Where(d => d.actual_status != Status_for_tab.NOGPSFIX).ToList().Count > 0)
OnInactiveUnitsWarning?.Invoke(toRemoveNonEmergencyVehicles.Count);
}
}
if (HideTimeInactiveUnits)
{
// add all units to the inactive minutes list
foreach (Vehicle veh in toRemoveInactiveMinutesVehicles)
{
if (InactiveScIdsVehicle.ContainsKey(veh.sc_id))
InactiveScIdsVehicle.Remove(veh.sc_id);
InactiveScIdsVehicle.Add(veh.sc_id, veh);
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
}
// remove units from the dataset
if (toRemoveInactiveMinutesVehicles.Count > 0)
{
removeVehicles(toRemoveInactiveMinutesVehicles, false);
if (toRemoveInactiveMinutesVehicles.Where(d => d.actual_status != Status_for_tab.NOGPSFIX).ToList().Count > 0)
OnInactiveUnitsWarning?.Invoke(toRemoveInactiveMinutesVehicles.Count);
}
}
}
}
}
}
}
}
#region MAP HANDLERS
/// <summary>
/// Handler for when a response is received from the map. This is done by a change in the url of the map
/// </summary>
/// <param name="response">The response received from the map</param>
private void OnMapMessageReceivedHandler(string response)
{
if (response.IndexOf("app://") > -1)
{
//Utils.WriteLine("RESPONSE FROM MAP : " + response, ConsoleColor.Magenta);
if (response.IndexOf(MapLoaded) > -1)
{
mapLoaded = true;
ChangeConsoleState(isConsoleVisible);
SetDataSetLimit(MaxDisplayedNrOfUnits);
// raise the event
OnMapLoaded?.Invoke();
}
else if(response.IndexOf(Nearest) > -1)
{
// remove command name and slash
String tmpstr = response.Remove(0, "app://".Length + Nearest.Length + 1);
tmpstr = tmpstr.Trim();
String[] split = tmpstr.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
Int32 x = 0;
Int32 y = 0;
Double latitude = 0;
Double longitude = 0;
Int32.TryParse(split[0], out x);
Int32.TryParse(split[1], out y);
Double.TryParse(split[2], out latitude);
Double.TryParse(split[3], out longitude);
// fire event
OnNearestRequest?.Invoke(x, y, latitude, longitude);
}
else if (response.IndexOf(UnitDisplayReq) > -1)
{
// remove command name and slash
String tmpstr = response.Remove(0, "app://".Length + Nearest.Length + 1);
tmpstr = tmpstr.Trim();
String[] split = tmpstr.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
// fire event
OnUnitDisplayReq?.Invoke(split[1]);
}
// check response from map
else if (response.IndexOf(UpdateTime) > -1)
{
// remove command name and slash
String tmpstr = response.Remove(0, "app://".Length + UpdateTime.Length + 1);
tmpstr = tmpstr.Trim();
String[] split = tmpstr.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
// expected two items
if (split.Length < 2)
return;
String cmd = split[0];
int time = 0;
Int32.TryParse(split[1], out time);
mapDead = false;
try
{
//if(!cmd.StartsWith("set-bounds"))
semaphore.Release();
}
catch (Exception ex)
{
Utils.WriteLine("Exception semaphore: " + ex.ToString(), ConsoleColor.Red);
}
//Utils.WriteLine("semaphore Release for " + cmd.ToUpper(), ConsoleColor.Green);
// store the last response
lastResponseMs = time;
if (time > 1000)
datasetChunkSize = (int)(0.8 * datasetChunkSize);
else if (time < 500)
datasetChunkSize = (int)(1.2 * datasetChunkSize);
// prevent chunks smaller that 20
if (datasetChunkSize < 70)
datasetChunkSize = 70;
else if (datasetChunkSize > 100)
datasetChunkSize = 100;
}
// fast command was pressed
else if (response.IndexOf(FastCommand) > -1)
{
String tmpstr = response.Remove(0, response.IndexOf(FastCommand) + 13);
tmpstr = tmpstr.Trim();
}
// intercept text command received from the map
else if (response.IndexOf(TextCommand) > -1)
{
String tmpstr = response.Remove(0, response.IndexOf(TextCommand) + 13);
tmpstr = tmpstr.Trim();
//string urldecode = HttpServerUtility
// get unit name and imei
string unitName = tmpstr.Split(new char[] { ',' })[0];
string textMessage = System.Web.HttpUtility.UrlDecode(tmpstr.Split(new char[] { ',' })[1]);
// raise the event
OnTextMessageRequest?.Invoke(unitName, textMessage);
}
// intercept ptt command received from the map
else if (response.IndexOf(PTTCommand) > -1)
{
String tmpstr = response.Remove(0, response.IndexOf(PTTCommand) + 12);
tmpstr = tmpstr.Trim();
// get unit name and imei
string unitName = tmpstr.Split(new char[] { ',' })[0];
// state can be 'on' or 'off'
string state = tmpstr.Split(new char[] { ',' })[1];
// raise the event
OnPTTButtonPressChanged?.Invoke(unitName, state.ToLowerInvariant().Equals("on") ? true : false);
}
// street view was opened or closed
else if (response.IndexOf(StreetViewOpened) > -1)
{
String tmpstr = response.Remove(0, response.IndexOf(StreetViewOpened) + 17);
tmpstr = tmpstr.Trim();
bool isOpened = tmpstr.ToLowerInvariant().Equals("true");
// raise the event
OnStreetViewStateChanged?.Invoke(isOpened);
}
else if (response.IndexOf(Landmark) > -1)
{
;
}
else if (response.IndexOf(Done) > -1)
{
;
}
else if (response.IndexOf(Bounds) > -1)
{
;
}
}
}
private void OnWePageLoadedHandler()
{
ChangeConsoleState(isConsoleVisible);
ChangeMarkersType(iconTheme);
CenterMapToPosition(centerPosition);
}
#endregion
private int mapDeadCount = 0;
/// <summary>
/// Display a list of units on the map
/// </summary>
/// <param name="vehicles">List containing the units that needs to be added on the map</param>
public void AddVehicles(List<Vehicle> vehicles, bool scaleViewBounds)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
lock (InactiveScIdsVehicle)
{
lock (displayedScIdsVehicle)
{
foreach (Vehicle veh in vehicles)
{
// check to see if the unit had reported gps which is not desired to be displayed on the map
DateTime positionTime = ((veh).lastValidPositionTime.ConvertGMTToLocal()).GetDTFromSeconds();
// check to see if the unit had reported gps which is not desired to be displayed on the map
bool isInactive = !(positionTime.AddMinutes(InactiveTimeoutMinutes) > DateTime.Now) && hideTimeInactiveUnits;
// update the reference if active, or inactive and requested emergency
if((HideNonEmergUnits && veh.actual_status == Status_for_tab.EMERG) || (!isInactive && !hideNonEmergUnits) ||
(hideTimeInactiveUnits && showEmergencyForTimeInactiveUnits && veh.actual_status == Status_for_tab.EMERG) )
{
if (InactiveScIdsVehicle.ContainsKey(veh.sc_id))
InactiveScIdsVehicle.Remove(veh.sc_id);
// remove old instance of vehicle from map
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
// add new instance for vehicle on the map
displayedScIdsVehicle.Add(veh.sc_id, veh);
}
else
//if ((!isInactive) || (HideNonEmergUnits && !veh.is_emergency))
{
// remove old instance of vehicle from map
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
if (InactiveScIdsVehicle.ContainsKey(veh.sc_id))
InactiveScIdsVehicle.Remove(veh.sc_id);
InactiveScIdsVehicle.Add(veh.sc_id, veh);
}
/*
else
{
// remove old instance of vehicle from map
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
// add new instance for vehicle on the map
displayedScIdsVehicle.Add(veh.sc_id, veh);
}*/
}
}
}
// store number of units that never reported
int totalNumberOfNeverReportedGPS = 0;
// store number of units that were removed from dataset due to inactive minutes
int totalNumberOfUnitsWithInactiveMinutes = 0;
// store number of units that were removed from dataset due to not being in emergency
int totalNumberOfUnitsNotInEmergency = 0;
IEnumerable<IGrouping<int, Vehicle>> query = vehicles.GroupBy(d => d.group.Id, d => d);
// Iterate over each IGrouping in the collection.
foreach (IGrouping<int, Vehicle> group in query)
{
String groupName = group.FirstOrDefault(d => d.IMEI.Length > 0).group.Name;
// create unassigned group if the units are part of the groupId 0
groupName = groupName.Trim().Length == 0 ? UNASSIGNED : groupName;
List<Vehicle> dataSetVehicles = new List<Vehicle>();
dataSetVehicles.AddRange(group);
//Utils.WriteLine($"Displaying group {groupName}");
int vehiclesWithValidGPSCount = 0;
int vehiclesWithInactiveMinutes = 0;
int vehiclesNotInEmergency = 0;
List<Vehicle> list = new List<Vehicle>();
list.AddRange(group);
while (list.Count > 0)
{
int toFetchCount = list.Count > datasetChunkSize ? datasetChunkSize : list.Count;
// notify number of units displayed until now from total number
OnMapProgressChanged?.Invoke(dataSetVehicles.Count - list.Count, dataSetVehicles.Count, "Displaying selected units from " + groupName);
// get number of units that had valid gps
vehiclesWithValidGPSCount += addToDataSet(groupName, list.GetRange(0, toFetchCount), ref vehiclesWithInactiveMinutes, ref vehiclesNotInEmergency);
//Utils.WriteLine($"[vehiclesWithValidGPSCount: {vehiclesWithValidGPSCount}] ");
// remove the items passed to the map
list.RemoveRange(0, toFetchCount);
if (mapDeadCount > 3)
;// list.Clear();
}
// force the map to display the group with each update but not pan zoom
if (!dispalyedDatasets.Contains(groupName))
putDataSetOnMap(PutOnMapOption.NONE, groupName);
// put dataset on map only if valid gps were available
if (vehiclesWithValidGPSCount > 0 && !dispalyedDatasets.Contains(groupName))
{
// add dataset to the displayed ones
dispalyedDatasets.Add(groupName);
//putDataSetOnMap(PutOnMapOption.NONE, groupName);
}
// add the units that weren't displayed due to inactive minutes
totalNumberOfUnitsWithInactiveMinutes += vehiclesWithInactiveMinutes;
// add the units that weren't displayed due to not being in emergency
totalNumberOfUnitsNotInEmergency += vehiclesNotInEmergency;
// add number of units that never reported
totalNumberOfNeverReportedGPS += (dataSetVehicles.Count - vehiclesWithValidGPSCount - vehiclesWithInactiveMinutes - vehiclesNotInEmergency);
OnMapProgressChanged?.Invoke(0, 0, "done");
// first time remove the labels
if (scaleViewBounds)
SetLabelVisibility(HasLabels, groupName);
}
// scale the entire
if (scaleViewBounds)
{
// force the map to scale once and fit all units
putDataSetOnMap(MapHandler.PutOnMapOption.ONCE, dispalyedDatasets);
}
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Update a unit that changed it's gps coordinates
/// </summary>
/// <param name="vehicle">The unit object with the new location</param>
public void GPSUpdate(Vehicle vehicle)
{
// skip units that are not displayed
if (!displayedScIdsVehicle.ContainsKey(vehicle.sc_id) && !InactiveScIdsVehicle.ContainsKey(vehicle.sc_id))
{
Utils.WriteLine($"GPS updare for units {vehicle.busName} not in the lists");
return;
}
if (newGPSScIdsVehicle.ContainsKey(vehicle.sc_id))
newGPSScIdsVehicle.Remove(vehicle.sc_id);
Utils.WriteLine($"GPS from {vehicle.busName} added to the new GPS queue");
newGPSScIdsVehicle.Add(vehicle.sc_id, vehicle);
}
/// <summary>
/// Delete a list of units on the map
/// </summary>
/// <param name="vehicles">List containing the units that needs to be removed from the map</param>
public void RemoveVehicles(List<Vehicle> vehicles)
{
removeVehicles(vehicles, true);
}
/// <summary>
/// Delete a list of units on the map
/// </summary>
/// <param name="vehicles">List containing the units that needs to be removed from the map</param>
/// <param name="removedByUser">Specify if the unit was removed by the user or if the unit was removed by the inactive timer</param>
private void removeVehicles(List<Vehicle> vehicles, bool removedByUser)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
IEnumerable<IGrouping<int, Vehicle>> query = vehicles.GroupBy(d => d.group.Id, d => d);
lock (InactiveScIdsVehicle)
{
lock (displayedScIdsVehicle)
{
// Iterate over each IGrouping in the collection.
foreach (IGrouping<int, Vehicle> group in query)
{
String groupName = group.FirstOrDefault(d => d.IMEI.Length > 0).group.Name;
// create unassigned group if the units are part of the groupId 0
groupName = groupName.Trim().Length == 0 ? UNASSIGNED : groupName;
// remove dataset if not vehicle from this group is displayed
int countDisplayedVehiclesForThisGroup = displayedScIdsVehicle.Values.ToList().Where(d => d.group.Name.Equals(groupName)
&& d.lastValidPositionTime > NeverReportedTime).Count();
bool datasetRemoved = false;
if ((countDisplayedVehiclesForThisGroup == 0 && !removedByUser)
|| (countDisplayedVehiclesForThisGroup - group.Count() == 0 && removedByUser))
{
RemoveDataset(groupName, false);
datasetRemoved = true;
}
foreach (Vehicle veh in group)
{
if (removedByUser)
{
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
if (InactiveScIdsVehicle.ContainsKey(veh.sc_id))
InactiveScIdsVehicle.Remove(veh.sc_id);
}
//if (countDisplayedVehiclesForThisGroup - group.Count() != 0)
if (!datasetRemoved)
removeFromDataSet(groupName, veh.busName, !datasetRemoved, false);
}
}
}
}
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Add all units inside this group to the map
/// </summary>
/// <param name="groupName">Group name that needs to be added</param>
/// <param name="vehicles">Vehicles that are inside the group</param>
/// <param name="scaleViewBounds">Tell if the map needs to scale and adjus viewbounds to the group</param>
public void AddGroup(String groupName, List<Vehicle> vehicles, bool scaleViewBounds)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
lock (InactiveScIdsVehicle)
{
lock (displayedScIdsVehicle)
{
foreach (Vehicle veh in vehicles)
{
// check to see if the unit had reported gps which is not desired to be displayed on the map
DateTime positionTime = ((veh).lastValidPositionTime.ConvertGMTToLocal()).GetDTFromSeconds();
if ((!(positionTime.AddMinutes(InactiveTimeoutMinutes) > DateTime.Now) && HideTimeInactiveUnits)
|| (((HideNonEmergUnits) && (veh).actual_status != Status_for_tab.EMERG)))
{
if (InactiveScIdsVehicle.ContainsKey(veh.sc_id))
InactiveScIdsVehicle.Remove(veh.sc_id);
InactiveScIdsVehicle.Add(veh.sc_id, veh);
}
else
{
// remove old instance of vehicle from map
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
// add new instance for vehicle on the map
displayedScIdsVehicle.Add(veh.sc_id, veh);
}
}
}
}
// create unassigned group if the units are part of the groupId 0
groupName = groupName.Trim().Length == 0 ? UNASSIGNED : groupName;
int vehiclesWithValidGPSCount = 0;
int vehiclesWithInactiveMinutes = 0;
int vehiclesNotInEmergency = 0;
List<Vehicle> list = new List<Vehicle>();
list.AddRange(vehicles);
while (list.Count > 0)
{
int toFetchCount = list.Count > datasetChunkSize ? datasetChunkSize : list.Count;
// notify number of units displayed until now from total number
OnMapProgressChanged?.Invoke(vehicles.Count - list.Count, vehicles.Count, "Displaying selected units from " + groupName);
// get number of units that had valid gps
vehiclesWithValidGPSCount += addToDataSet(groupName, list.GetRange(0, toFetchCount), ref vehiclesWithInactiveMinutes, ref vehiclesNotInEmergency);
// remove the items passed to the map
list.RemoveRange(0, toFetchCount);
if (mapDeadCount > 3)
;// list.Clear();
// force the map to display the group with each update but not pan zoom
if (!dispalyedDatasets.Contains(groupName))
putDataSetOnMap(PutOnMapOption.NONE, groupName);
}
if (vehiclesWithValidGPSCount > 0)
{
// add group to dataset lists and then
if (!dispalyedDatasets.Contains(groupName))
{
dispalyedDatasets.Add(groupName);
// display dataSet
//putDataSetOnMap(PutOnMapOption.NONE, groupName);
}
SetLabelVisibility(hasLabels, groupName);
}
// trigger event with warning for inactive units
if (vehiclesWithInactiveMinutes > 0)
OnInactiveUnitsWarning?.Invoke(vehiclesWithInactiveMinutes);
if (vehiclesNotInEmergency > 0)
OnInactiveUnitsWarning?.Invoke(vehiclesNotInEmergency);
// trigger event with nerver reported units if any
if (vehicles.Count - vehiclesWithValidGPSCount > 0)
OnNeverReportedWarning?.Invoke(vehicles.Count - vehiclesWithValidGPSCount - vehiclesWithInactiveMinutes - vehiclesNotInEmergency);
OnMapProgressChanged?.Invoke(0, 0, "done");
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Delete all units inside this group
/// </summary>
/// <param name="groupName">Group name that needs to be removed</param>
public void RemoveGroup(String groupName)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
lock (InactiveScIdsVehicle)
{
lock (displayedScIdsVehicle)
{
// get vehicles from this group that are displayed
List<Vehicle> vehicles = displayedScIdsVehicle.Values.Where(d => d.group.Name == groupName).ToList();
vehicles.AddRange(InactiveScIdsVehicle.Values.Where(d => d.group.Name == groupName).ToList());
// remove all vehicles from this group that are displayed
foreach (Vehicle veh in vehicles)
{
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
if (InactiveScIdsVehicle.ContainsKey(veh.sc_id))
InactiveScIdsVehicle.Remove(veh.sc_id);
}
}
}
// create unassigned group if the units are part of the groupId 0
groupName = groupName.Trim().Length == 0 ? UNASSIGNED : groupName;
RemoveDataset(groupName, true);
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Delete all units inside all the specified groups
/// </summary>
/// <param name="groupName">List containung all group name that needs to be removed</param>
public void RemoveGroups(List<String> groupNames)
{
// skip empty lists
if (groupNames.Count == 0)
return;
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
foreach (String groupName in groupNames)
{
lock (InactiveScIdsVehicle)
{
lock (displayedScIdsVehicle)
{
// get vehicles from this group that are displayed
List<Vehicle> vehicles = displayedScIdsVehicle.Values.Where(d => d.group.Name == groupName).ToList();
vehicles.AddRange(InactiveScIdsVehicle.Values.Where(d => d.group.Name == groupName).ToList());
// remove all vehicles from this group that are displayed
foreach (Vehicle veh in vehicles)
{
if (displayedScIdsVehicle.ContainsKey(veh.sc_id))
displayedScIdsVehicle.Remove(veh.sc_id);
if (InactiveScIdsVehicle.ContainsKey(veh.sc_id))
InactiveScIdsVehicle.Remove(veh.sc_id);
}
}
}
}
RemoveDatasets(groupNames);
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
public void NearestUnitResponse(int x, int y, double lat, double lng, List<Vehicle> vehicles)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
//Send the response to the map
int count = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < vehicles.Count; i++)
{
String cmd = getDataSetCmdValuesForVehicle(vehicles[i]);
// skip units that are not having any dataset cmd because they didn't reported any Fixed GPS
// or ar having gps to older
if (cmd.Trim().Length == 0)
continue;
count++;
sb.Append(cmd + ",");
}
// remove last comma
if (sb.ToString().EndsWith(","))
sb.Remove(sb.Length - 1, 1);
// execute cmd if at least a unit has Fixed GPS
if (sb.ToString().Trim().Length != 0)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{Nearest},{(SEQ_ID_REQ ? SeqId + "," : "")}{x},{y},{lat},{lng},{sb.ToString()}');");
//Utils.WriteLine("addToDataSet WAIT");
bool res = semaphore.WaitOne(semaphoreTimeoutMs);
//Utils.WriteLine("addToDataSet ENDED " + res);
}
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Disable or enable the map console
/// </summary>
/// <param name="isConsoleVisible">Boolean telling if the console needs to be displayed or hidden</param>
public void ChangeConsoleState(bool isConsoleVisible)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{ShowConsole},{(SEQ_ID_REQ ? SeqId + "," : "")}{isConsoleVisible.ToString().ToLower()}');");
}
/// <summary>
/// Disable or enable the labels for the markers
/// </summary>
/// <param name="hasLabels">Boolean telling if the markers should have labels displayed or hidden</param>
public void SetLabelVisibility(bool hasLabels, String dataSetName)
{
if (!dispalyedDatasets.Contains(dataSetName))
return;
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
if (hasLabels)
mapGoogles?.ExecuteScript($"window.GWTcallback('{PutLabels},{(SEQ_ID_REQ ? SeqId + "," : "")}{dataSetName}');");
else
mapGoogles?.ExecuteScript($"window.GWTcallback('{RemoveLabels},{(SEQ_ID_REQ ? SeqId + "," : "")}{dataSetName}');");
}
//Utils.WriteLine("SetLabelVisibility WAIT");
bool res = semaphore.WaitOne(semaphoreTimeoutMs);
//Utils.WriteLine("SetLabelVisibility ENDED " + res);
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Update the emergency status for an unit
/// </summary>
/// <param name="vehicle">Vehicle that changed it's emergency state</param>
public void EmergencyStateUpdate(Vehicle vehicle)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
// skip units that are not displayed
if (!displayedScIdsVehicle.ContainsKey(vehicle.sc_id) && !InactiveScIdsVehicle.ContainsKey(vehicle.sc_id))
{
//Utils.WriteLine("Skip position for " + vehicle.sc_id);
return;
}
DateTime positionTime = ((vehicle).lastValidPositionTime.ConvertGMTToLocal()).GetDTFromSeconds();
// check to see if the unit had reported gps which is not desired to be displayed on the map
bool isInactive = !(positionTime.AddMinutes(InactiveTimeoutMinutes) > DateTime.Now) && hideTimeInactiveUnits;
// move the unit from inactive to active if required to show only emergency units
if (vehicle.actual_status == Status_for_tab.EMERG &&
(hideNonEmergUnits || (hideTimeInactiveUnits && showEmergencyForTimeInactiveUnits)))
{
lock (InactiveScIdsVehicle)
{
if (InactiveScIdsVehicle.ContainsKey(vehicle.sc_id))
InactiveScIdsVehicle.Remove(vehicle.sc_id);
}
lock (displayedScIdsVehicle)
{
if (displayedScIdsVehicle.ContainsKey(vehicle.sc_id))
displayedScIdsVehicle.Remove(vehicle.sc_id);
displayedScIdsVehicle.Add(vehicle.sc_id, vehicle);
}
}
// hide non emergency unit if required or emergency one that is inactive by timer
else if ((vehicle.actual_status == Status_for_tab.EMERG && hideTimeInactiveUnits && !showEmergencyForTimeInactiveUnits && isInactive)
|| (vehicle.actual_status != Status_for_tab.EMERG && (hideNonEmergUnits || (hideTimeInactiveUnits && isInactive))))
{
lock (InactiveScIdsVehicle)
{
if (InactiveScIdsVehicle.ContainsKey(vehicle.sc_id))
InactiveScIdsVehicle.Remove(vehicle.sc_id);
InactiveScIdsVehicle.Add(vehicle.sc_id, vehicle);
}
lock (displayedScIdsVehicle)
{
if (displayedScIdsVehicle.ContainsKey(vehicle.sc_id))
displayedScIdsVehicle.Remove(vehicle.sc_id);
}
}
AddVehicles(new List<Vehicle> { vehicle }, false);
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Change the way markers are displayed by the map. This will force the map to change the background color
/// of the labels and also their position
/// </summary>
/// <param name="type">Markers type as displayed now</param>
public void ChangeMarkersType(IconTheme type)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
mapGoogles.ExecuteScript($"window.GWTcallback('{SetLegacyIcon},{(SEQ_ID_REQ ? SeqId + "," : "")}{(type == IconTheme.CLASSIC ? "true" : "false")}');");
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Change the center of the map to the desired coordinates and also change the zoom level
/// </summary>
/// <param name="latLngZoom">Object containing the desired latitude, longitude and zoom level</param>
public void CenterMapToPosition(LatLngZoom latLngZoom)
{
CenterMapToPosition(latLngZoom.Lat, latLngZoom.Lng, latLngZoom.ZoomLevel);
}
/// <summary>
/// Change the center of the map to the desired coordinates, keeping the same zoom level
/// </summary>
/// <param name="lat">Latitude where the map needs to be centered</param>
/// <param name="lng">Longitude where the map needs to be centered</param>
public void CenterMapToPosition(double lat, double lng)
{
CenterMapToPosition(lat, lng, -1);
}
/// <summary>
/// Change the center of the map to the desired coordinates, and zoom level in case it is different than -1
/// </summary>
/// <param name="lat">Latitude where the map needs to be centered</param>
/// <param name="lng">Longitude where the map needs to be centered</param>
/// <param name="zoomLevel">Zoom level at which the map needs to be focused.
/// In case of -1 value, the zoom level will be left unchanged</param>
public void CenterMapToPosition(double lat, double lng, Int32 zoomLevel)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{CenterZoom},{(SEQ_ID_REQ ? SeqId + "," : "")}{lat},{lng},{zoomLevel}');");
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Set the limit of displayed units on the map
/// </summary>
/// <param name="limit">Integer value that represents the maximum number of units that will be displayed</param>
public void SetDataSetLimit(int limit)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{SetDatasetLimit},{(SEQ_ID_REQ ? SeqId + "," : "")}{limit}');");
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Remove an entire dataset from the map
/// </summary>
/// <param name="dataSetName">Group dataSet that needs to be removed from the map</param>
/// <param name="vehicles">List of maximum 100 units that needs to be added to the map</param>
private int addToDataSet(String dataSetName, List<Vehicle> vehicles, ref int vehiclesWithInactiveMinutes, ref int vehiclesNotInEmergency)
{
int count = 0;
StringBuilder sb = new StringBuilder();
ReasonSkip reasonSkip = ReasonSkip.NOT_SKIPPED;
for (int i = 0; i < vehicles.Count; i++)
{
reasonSkip = ReasonSkip.NOT_SKIPPED;
String cmd = getDataSetCmdValuesForVehicle(vehicles[i], ref reasonSkip, HideTimeInactiveUnits, HideNonEmergUnits);
//Utils.WriteLine("addToDataSet " + cmd);
// skip units that are not having any dataset cmd because they didn't reported any Fixed GPS
// or ar having gps to older
if (cmd.Trim().Length == 0)
{
// count unit to those that were skipped
if (reasonSkip == ReasonSkip.INACTIVE_MINUTES)
{
vehiclesWithInactiveMinutes++;
Utils.WriteLine($"addToDataSet {dataSetName} SKIP unit {vehicles[i].busName}", ConsoleColor.DarkYellow);
}
else if (reasonSkip == ReasonSkip.NO_EMERGENCY)
{
vehiclesNotInEmergency++;
Utils.WriteLine($"addToDataSet {dataSetName} SKIP unit {vehicles[i].busName}", ConsoleColor.DarkYellow);
}
continue;
}
else
Utils.WriteLine($"addToDataSet {dataSetName} unit {vehicles[i].busName}",ConsoleColor.Yellow);
count++;
sb.Append(cmd + ",");
}
// remove last comma
if (sb.ToString().EndsWith(","))
sb.Remove(sb.Length - 1, 1);
// execute cmd if at least a unit has Fixed GPS
if (sb.ToString().Trim().Length != 0)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{UpdateDataSet},{(SEQ_ID_REQ ? SeqId + "," : "")}{dataSetName},{sb.ToString()}');");
//Utils.WriteLine("addToDataSet WAIT");
bool res = semaphore.WaitOne(semaphoreTimeoutMs);
//Utils.WriteLine("addToDataSet ENDED " + res);
}
return count;
}
/// <summary>
/// Remove an unit from the dataset
/// </summary>
/// <param name="dataSetName">Group dataSet from which an unit needs to be removed</param>
/// <param name="unitName">Unit name that needs to be removed from this dataset</param>
/// <param name="hasTimeout">This will force the command to not wait for an answer due to the fact that
/// the dataset was already removed and no answer will be received</param>
public void removeFromDataSet(String dataSetName, String unitName, bool hasTimeout, bool lockNeeded)
{
Utils.WriteLine($"removeFromDataSet {dataSetName} unit {unitName}", ConsoleColor.Green);
if (lockNeeded)
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
removeFromDataSet(dataSetName, unitName, hasTimeout);
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
else
removeFromDataSet(dataSetName, unitName, hasTimeout);
}
private void removeFromDataSet(String dataSetName, String unitName, bool hasTimeout)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{DeleteFromDataset},{(SEQ_ID_REQ ? SeqId + "," : "")}{dataSetName},{unitName}');");
//Utils.WriteLine("removeFromDataSet WAIT");
bool res = semaphore.WaitOne(hasTimeout ? semaphoreTimeoutMs : 100);
//Utils.WriteLine("removeFromDataSet ENDED " + res);
}
/// <summary>
/// Remove an entire dataset from the map
/// </summary>
/// <param name="dataSetName">Group dataSet that needs to be removed from the map</param>
public void RemoveDataset(String dataSetName, bool lockNeeded)
{
if (lockNeeded)
Task.Factory.StartNew((Action)delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
RemoveDataset(dataSetName);
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
else
RemoveDataset(dataSetName); ;
}
private void RemoveDataset(String dataSetName)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{this.RemoveDataSet},{(SEQ_ID_REQ ? SeqId + "," : "")}{dataSetName}');");
//Utils.WriteLine("RemoveDataset WAIT");
bool res = semaphore.WaitOne(semaphoreTimeoutMs);
//Utils.WriteLine("RemoveDataset ENDED " + res);
// delete group from dataset lists
if (dispalyedDatasets.Contains(dataSetName))
dispalyedDatasets.Remove(dataSetName);
}
/// <summary>
/// Remove an entire dataset from the map
/// </summary>
/// <param name="dataSetName">Group dataSet that needs to be removed from the map</param>
public void RemoveDatasets(List<String> dataSetNames)
{
Task.Factory.StartNew((Action)delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
StringBuilder sb = new StringBuilder();
foreach (String dataSetName in dataSetNames)
{
sb.Append(dataSetName + ",");
// delete group from dataset lists
if (dispalyedDatasets.Contains(dataSetName))
dispalyedDatasets.Remove(dataSetName);
}
// remove last comma
if (sb.ToString().EndsWith(","))
sb.Remove(sb.Length - 1, 1);
mapGoogles?.ExecuteScript($"window.GWTcallback('{this.RemoveDataSet},{(SEQ_ID_REQ ? SeqId + "," : "")}{sb.ToString()}');");
foreach (String dataSetName in dataSetNames)
{
//Utils.WriteLine($"RemoveDataset {dataSetName} WAIT");
bool res = semaphore.WaitOne(semaphoreTimeoutMs);
//Utils.WriteLine($"RemoveDataset {dataSetName} ENDED " + res);
}
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Display units on map that are in a dataset with option to zoom in order to accomodate all units or not
/// </summary>
/// <param name="option">Option if the command needs to adjust the viewbounds once, always or not</param>
/// <param name="dataSetName">DataSet name which needs to be added on map and scaled</param>
public void putDataSetOnMap(PutOnMapOption option, String dataSetName)
{
putDataSetOnMap(option, new List<String>() { dataSetName });
}
/// <summary>
/// Display units on map that are in a dataset with option to zoom in order to accomodate all units or not
/// </summary>
/// <param name="option">Option if the command needs to adjust the viewbounds once, always or not</param>
/// <param name="dataSetNames">DataSet names which needs to be added on map and scaled</param>
public void putDataSetOnMap(PutOnMapOption option, List<String> dataSetNames)
{
// do not process if empty dataset names
if (dataSetNames.Count == 0)
return;
// create the list of dataset names divided by comma
StringBuilder sb = new StringBuilder();
for (int i = 0; i < dataSetNames.Count; i++)
sb.Append(dataSetNames[i] + (i == dataSetNames.Count - 1 ? "" : ","));
mapGoogles?.ExecuteScript($"window.GWTcallback('{PutOnMap},{(SEQ_ID_REQ ? SeqId + "," : "")}{option.ToString().ToLower()},{sb.ToString()}');");
//Utils.WriteLine("putDataSetOnMap WAIT");
bool res = semaphore.WaitOne(semaphoreTimeoutMs);
//Utils.WriteLine("putDataSetOnMap ENDED " + res);
}
/// <summary>
/// Display an info bubble for the selected unit as it's location
/// </summary>
/// <param name="unitName">Name of the units for which the info bubble needs to be oppened</param>
public void OpenInfoBubble(String unitName)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{OpenInfo},{(SEQ_ID_REQ ? SeqId + "," : "")}{unitName}');");
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
private enum ReasonSkip { NO_GPS_FIX, INACTIVE_MINUTES, NOT_SKIPPED, NO_EMERGENCY }
/// <summary>
/// Get the command required by the JS library in order to display a vehicle on the map
/// </summary>
/// <param name="veh">Vehicle that needs to be displayed</param>
/// skipped </param>
/// <returns>A string command formated according to the documentation or empty string if the vehicle needs to be skipped
/// due to Never reported or due to inactive minutes</returns>
private String getDataSetCmdValuesForVehicle(Vehicle veh)
{
ReasonSkip reasonSkip = ReasonSkip.NOT_SKIPPED;
return getDataSetCmdValuesForVehicle(veh, ref reasonSkip, false, false);
}
/// <summary>
/// Get the command required by the JS library in order to display a vehicle on the map
/// </summary>
/// <param name="veh">Vehicle that needs to be displayed</param>
/// <param name="reasonSkip">reference to an object that will specify the reason why this vehicle was
/// <param name="hideTimeInactiveUnits">Set if the units needs to be checked for inactive time</param>
/// <param name="hideNonEmergUnits">Set if the units need to be checked for not being in emergency</param>
/// skipped </param>
/// <returns>A string command formated according to the documentation or empty string if the vehicle needs to be skipped
/// due to Never reported or due to inactive minutes</returns>
private String getDataSetCmdValuesForVehicle(Vehicle veh, ref ReasonSkip reasonSkip, bool hideTimeInactiveUnits , bool hideNonEmergUnits)
{
int addressMaxLength = 55;
int mapCallTimeout = 3;
int smsLength = 141;
Boolean hasVoice = veh.has_voice;
Boolean hasText = veh.has_text;
Boolean isInCall = veh.inCall;
String iconName = veh.IconName;
int count = 0;
//Utils.WriteLine($"getDataSetCmdValuesForVehicle 000 {count++}");
bool isEmergency = veh.actual_status == Status_for_tab.EMERG;
switch(IconType)
{
case IconType.ARROWS: iconName = $"images/arrow{(isEmergency ? "r" : (veh.vehicleSpeed > 0 ? "g" : "b"))}_{veh.GetHeadingDirection()}.png"; break;
case IconType.EMERG: iconName = isEmergency ? "images/peoplepink.png" : iconName;
// long version to change also the regular icons
//$"images/people{(veh.is_emergency ? "pink" : (veh.vehicleSpeed > 0 ? "green" : "blue"))}.png";
break;
case IconType.ICONS:
// flag for nearest units to have an opaque icon if not displayed
if (!hideTimeInactiveUnits && !((MapElement)veh.MapsHT[MapHashName]).itemCheck)
iconName = iconName.Replace("_emerg", "").Replace(".png", "_50o.png");
if (!hideNonEmergUnits && !((MapElement)veh.MapsHT[MapHashName]).itemCheck)
iconName = iconName.Replace("_emerg", "").Replace(".png", "_50o.png");
break;
}
String address = veh.GetLastAddress().Replace(',', ';');
String trimmedAddress = address.Substring(0, address.Length > addressMaxLength ? addressMaxLength : address.Length);
if (address.Length != trimmedAddress.Length)
address = trimmedAddress + "...";
//Utils.WriteLine($"getDataSetCmdValuesForVehicle 1111 {count++}");
DateTime positionTime = ((veh).lastValidPositionTime.ConvertGMTToLocal()).GetDTFromSeconds();
Utils.WriteLine($"Unit position is : {veh.lastValidPositionTime} | {(veh).lastValidPositionTime.ConvertGMTToLocal()} | {positionTime.ToString()}");
if (positionTime.Year < 2000)// || (Math.Abs(veh.GetLng()) <= 0.1 && Math.Abs(veh.GetLat()) <= 0.1))
{
reasonSkip = ReasonSkip.NO_GPS_FIX;
//Utils.WriteLine($"getDataSetCmdValuesForVehicle 222 {count++}");
return "";
}
// check to see if the unit had reported gps which is not desired to be displayed on the map
if (!(positionTime.AddMinutes(InactiveTimeoutMinutes) > DateTime.Now) && hideTimeInactiveUnits)
{
if (!showEmergencyForTimeInactiveUnits
|| (showEmergencyForTimeInactiveUnits && veh.actual_status != Status_for_tab.EMERG))
{
if (veh.actual_status == Status_for_tab.NOGPSFIX)
reasonSkip = ReasonSkip.NO_GPS_FIX;
else
reasonSkip = ReasonSkip.INACTIVE_MINUTES;
return "";
}
}
if (hideNonEmergUnits && veh.actual_status != Status_for_tab.EMERG)
{
reasonSkip = ReasonSkip.NO_EMERGENCY;
return "";
}
//Utils.WriteLine($"getDataSetCmdValuesForVehicle 333 {count++}");
reasonSkip = ReasonSkip.NOT_SKIPPED;
return $"{MainForm2.FixComma(veh.GetLat() + "")},{MainForm2.FixComma(veh.GetLng() + "")},{positionTime.ToString("yyyy-MM-dd HH:mm:ss")},{veh.busName}," +
$"{veh.GetSpeed()}{(IsMetric ? "kph" : "mph")},{""},{address}," +
$"{iconName},{(veh.has_voice ? mapCallTimeout : 0)}, {(veh.has_text ? smsLength : 0)},{veh.inCall}";
}
private String landmarkDataSetName = "LandView";
public void ShowLandmarks(List<LandmarkUI> landmarks)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{RemoveDataSet},LandView');");
List<LandmarkUI> list = new List<LandmarkUI>();
list.AddRange(landmarks);
while (list.Count > 0)
{
int toFetchCount = list.Count > datasetChunkSize ? datasetChunkSize : list.Count;
// notify number of units displayed until now from total number
OnMapProgressChanged?.Invoke(landmarks.Count - list.Count, landmarks.Count, "Displaying landmarks");
StringBuilder sb = new StringBuilder("");
foreach (LandmarkUI land in list.GetRange(0, toFetchCount))
{
String cmd = getDataSetCmdValuesForLandmark(land);
sb.Append(cmd + ",");
}
// remove last comma
if (sb.ToString().EndsWith(","))
sb.Remove(sb.Length - 1, 1);
mapGoogles?.ExecuteScript($"window.GWTcallback('{ShowLandmark},{(SEQ_ID_REQ ? SeqId + "," : "")}{landmarkDataSetName},{sb.ToString()}');");
Utils.WriteLine("ShowLandmarks WAIT");
bool res = semaphore.WaitOne(semaphoreTimeoutMs);
Utils.WriteLine("ShowLandmarks ENDED " + res);
// remove the items passed to the map
list.RemoveRange(0, toFetchCount);
}
// raise event
if (landmarks.Count > 0)
{
OnMapProgressChanged?.Invoke(0, 0, "done");
OnLandmarksDisplayed?.Invoke(landmarks.Count);
}
Thread.Sleep(100);
putDataSetOnMap(PutOnMapOption.NONE, landmarkDataSetName);
SetLabelVisibility(hasLabels, landmarkDataSetName);
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Remove all landmarks from the map
/// </summary>
public void RemoveLandmarks()
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
RemoveDataset(landmarkDataSetName, true);
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Get the command required by the JS library in order to display a landmark on the map
/// </summary>
/// <param name="landmark">Landmark that needs to be displayed</param>
/// <returns>A string command formated according to the documentation in order to display the landmark</returns>
private String getDataSetCmdValuesForLandmark(LandmarkUI landmark)
{
return $"{MainForm2.FixDoubleLAT(landmark.Latitude + "")},{MainForm2.FixDoubleLNG(landmark.Longitude + "")},{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}," +
$"{MainForm2.FixComma(landmark.Name)},{"speed"},{"alt"},{MainForm2.FixComma(landmark.Description).Replace(',', ' ')}," +
$"{"landmarks/" + LandZoneCfg.GetLandmarkIcon(landmark.TypeIdx, landmark.Color, landmark.Size)},{"false"}, {"false"},{"false"}";
}
public void ShowGeofences(List<GeofenceUI> geofences)
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{DeleteGeofence},{(SEQ_ID_REQ ? SeqId + "," : "")}{"ALL"}');");
foreach (GeofenceUI geofence in geofences)
{
String cmd = getDataSetCmdValuesForGeofence(geofence);
mapGoogles?.ExecuteScript($"window.GWTcallback('{ShowGeofence},{(SEQ_ID_REQ ? SeqId + "," : "")}{cmd}');");
}
// raise event for geofences displayed
if (geofences.Count > 0)
OnGeofencesDisplayed?.Invoke(geofences.Count);
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
public void RemoveGeofences()
{
Task.Factory.StartNew(delegate ()
{
Interlocked.Increment(ref numberOfRunningTasks);
lock (mapLock)
{
mapGoogles?.ExecuteScript($"window.GWTcallback('{DeleteGeofence},{(SEQ_ID_REQ ? SeqId + "," : "")}{"ALL"}');");
}
Interlocked.Decrement(ref numberOfRunningTasks);
});
}
/// <summary>
/// Get the command required by the JS library in order to display a geofence on the map
/// </summary>
/// <param name="geofence">Geofence that needs to be displayed</param>
/// <returns>A string command formated according to the documentation in order to display the geofence</returns>
private String getDataSetCmdValuesForGeofence(GeofenceUI geofence)
{
String datasetCMD = "";
foreach (GeofencePoint point in geofence.points)
{
datasetCMD = datasetCMD
+ MainForm2.FixDoubleLAT(point.Latitude + "") + ","
+ MainForm2.FixDoubleLNG(point.Longitude + "") + ",";
}
// get geofence fill color
String hexFillColor = "#" + geofence.FillColor.R.ToString("X2") + geofence.FillColor.G.ToString("X2") + geofence.FillColor.B.ToString("X2");
String hexBorderColor = "#" + geofence.BorderColor.R.ToString("X2") + geofence.BorderColor.G.ToString("X2") + geofence.BorderColor.B.ToString("X2");
try
{
datasetCMD = datasetCMD.Remove(datasetCMD.Length - 1);
}
catch (Exception ex) { SM.Debug("Ex:" + ex.ToString()); }
return
geofence.Name + ","
+ hexFillColor + ","
+ MainForm2.FixDoubleLAT(geofence.FillOpacity.ToString()) + ","
+ hexBorderColor + ","
+ MainForm2.FixDoubleLAT(geofence.BorderOpacity.ToString()) + ","
+ MainForm2.FixDoubleLAT(geofence.BorderWidth.ToString()) + ","
+ datasetCMD;
}
/// <summary>
/// Class equal with an enum containing a string as a returned value
/// This class is used to store the options awailable for the put on map command
/// </summary>
public sealed class PutOnMapOption
{
private readonly String name;
private readonly int value;
public static readonly PutOnMapOption NONE = new PutOnMapOption(1, "none");
public static readonly PutOnMapOption ONCE = new PutOnMapOption(2, "once");
public static readonly PutOnMapOption ALWAYS = new PutOnMapOption(3, "always");
private PutOnMapOption(int value, String name)
{
this.name = name;
this.value = value;
}
public override String ToString()
{
return name;
}
}
/// <summary>
/// Returnes true if the unit is displayed on map, otherweise returns false
/// </summary>
/// <param name="unitName"></param>
/// <returns></returns>
public Boolean IsUnitDisplaydOnMap(string unitName)
{
if (InactiveScIdsVehicle.ContainsValue((Vehicle)MainForm2.vehicleHT[unitName]))
return false;
else
return true;
}
#region EVENTS
public delegate void MapLoadedEvent();
public event MapLoadedEvent OnMapLoaded;
public delegate void NearestRequest(int x, int y, double lat, double lng);
public event NearestRequest OnNearestRequest;
public delegate void UnitDisplayReqDel(String unitName);
public event UnitDisplayReqDel OnUnitDisplayReq;
public delegate void LandmarksDisplayed(int number);
public event LandmarksDisplayed OnLandmarksDisplayed;
public delegate void GeofencesDisplayed(int number);
public event GeofencesDisplayed OnGeofencesDisplayed;
public delegate void MapProgressChanged(int currentStep, int totalSteps, String message);
public event MapProgressChanged OnMapProgressChanged;
public delegate void TextMessageRequestEvent(String unitName, String message);
public event TextMessageRequestEvent OnTextMessageRequest;
public delegate void PTTButtonPressChangedEvent(String unitName, bool isPressed);
public event PTTButtonPressChangedEvent OnPTTButtonPressChanged;
public delegate void StreetViewStateChangedEvent(bool isOpened);
public event StreetViewStateChangedEvent OnStreetViewStateChanged;
public delegate void NeverReportedWarningEvent(int count);
public event NeverReportedWarningEvent OnNeverReportedWarning;
public delegate void InactiveMinuteWarningEvent(int count);
public event InactiveMinuteWarningEvent OnInactiveUnitsWarning;
public delegate void MapDeadEvent();
public event MapDeadEvent OnMapDead;
#endregion
}
}