1928 lines
80 KiB
C#
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
|
|||
|
}
|
|||
|
}
|