kopia lustrzana https://github.com/dl2alf/AirScout
501 wiersze
21 KiB
C#
501 wiersze
21 KiB
C#
|
|
using System;
|
|
using System.ComponentModel;
|
|
using System.Windows.Forms;
|
|
using System.Net;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Diagnostics;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Globalization;
|
|
using System.Drawing;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using ScoutBase;
|
|
using ScoutBase.Core;
|
|
using ScoutBase.Stations;
|
|
using System.Data.SQLite;
|
|
using System.Text;
|
|
|
|
namespace ScoutBase.Stations
|
|
{
|
|
|
|
#region StationDatabaseUpdater
|
|
|
|
public class StationDatabaseUpdaterStartOptions
|
|
{
|
|
public string Name;
|
|
public bool RestrictToAreaOfInterest;
|
|
public double MinLat;
|
|
public double MinLon;
|
|
public double MaxLat;
|
|
public double MaxLon;
|
|
public string InstanceID;
|
|
public string SessionKey;
|
|
public string GetKeyURL;
|
|
public BACKGROUNDUPDATERSTARTOPTIONS Options;
|
|
}
|
|
|
|
|
|
// Background worker for station database update
|
|
[DefaultPropertyAttribute("Name")]
|
|
public class StationDatabaseUpdater : BackgroundWorker
|
|
{
|
|
string Password = "";
|
|
StationDatabaseUpdaterStartOptions StartOptions;
|
|
|
|
// Temp directory to save downloaded files
|
|
string TmpDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName, "Tmp").TrimEnd(Path.DirectorySeparatorChar);
|
|
|
|
public StationDatabaseUpdater() : base()
|
|
{
|
|
this.WorkerReportsProgress = true;
|
|
this.WorkerSupportsCancellation = true;
|
|
// create temp directory if not exists
|
|
if (!Directory.Exists(TmpDirectory))
|
|
Directory.CreateDirectory(TmpDirectory);
|
|
}
|
|
|
|
private DOWNLOADFILESTATUS GetUpdateFromURL(string url, string zipfilename, string filename)
|
|
{
|
|
AutoDecompressionWebClient cl = new AutoDecompressionWebClient();
|
|
DOWNLOADFILESTATUS status = cl.DownloadFileIfNewer(url, zipfilename, true, true, Password);
|
|
return status;
|
|
}
|
|
|
|
private bool ReadLocationsFromURL(string url, string zipfilename, string filename)
|
|
{
|
|
try
|
|
{
|
|
DOWNLOADFILESTATUS status = GetUpdateFromURL(url, zipfilename, filename);
|
|
if (((status & DOWNLOADFILESTATUS.ERROR) > 0) && ((status & DOWNLOADFILESTATUS.NOTFOUND) > 0))
|
|
{
|
|
this.ReportProgress(-1, "Error while downloading and extracting " + filename);
|
|
return false;
|
|
}
|
|
else if (((status & DOWNLOADFILESTATUS.NEWER) > 0) || ((status & DOWNLOADFILESTATUS.NOTNEWER) > 0))
|
|
{
|
|
string json = "";
|
|
using (StreamReader sr = new StreamReader(filename))
|
|
json = sr.ReadToEnd();
|
|
List<LocationDesignator> tmp = StationData.Database.LocationFromJSON(json);
|
|
List<LocationDesignator> lds = new List<LocationDesignator>();
|
|
foreach (LocationDesignator ld in tmp)
|
|
{
|
|
// skip locations outsid area of interest if option set
|
|
if (StartOptions.RestrictToAreaOfInterest && ((ld.Lat < StartOptions.MinLat) || (ld.Lat > StartOptions.MaxLat) || (ld.Lon < StartOptions.MinLon) || (ld.Lon > StartOptions.MaxLon)))
|
|
continue;
|
|
lds.Add(ld);
|
|
if (this.CancellationPending)
|
|
return false;
|
|
// reduce CPU load
|
|
Thread.Sleep(1);
|
|
}
|
|
// check for empty database
|
|
if (StationData.Database.LocationCount() == 0)
|
|
{
|
|
// do bulk insert
|
|
StationData.Database.LocationBulkInsert(lds);
|
|
}
|
|
else
|
|
{
|
|
// do bulk update
|
|
StationData.Database.LocationBulkInsertOrUpdateIfNewer(lds);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Error loading database
|
|
this.ReportProgress(-1, "[" + url + "]: " + ex.ToString());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool ReadQRVFromURL(string url, string zipfilename, string filename)
|
|
{
|
|
try
|
|
{
|
|
DOWNLOADFILESTATUS status = GetUpdateFromURL(url, zipfilename, filename);
|
|
if (((status & DOWNLOADFILESTATUS.ERROR) > 0) && ((status & DOWNLOADFILESTATUS.NOTFOUND) > 0))
|
|
{
|
|
this.ReportProgress(-1, "Error while downloading and extracting " + filename);
|
|
return false;
|
|
}
|
|
else if (((status & DOWNLOADFILESTATUS.NEWER) > 0) || ((status & DOWNLOADFILESTATUS.NOTNEWER) > 0))
|
|
{
|
|
string json = "";
|
|
using (StreamReader sr = new StreamReader(filename))
|
|
json = sr.ReadToEnd();
|
|
List<QRVDesignator> tmp = StationData.Database.QRVFromJSON(json);
|
|
List<QRVDesignator> qrvs = new List<QRVDesignator>();
|
|
foreach (QRVDesignator qrv in tmp)
|
|
{
|
|
// skip locations outsid area of interest if option set
|
|
if (StartOptions.RestrictToAreaOfInterest)
|
|
{
|
|
LocationDesignator ld = StationData.Database.LocationFind(qrv.Call, qrv.Loc);
|
|
if ((ld == null) || ((ld.Lat < StartOptions.MinLat) || (ld.Lat > StartOptions.MaxLat) || (ld.Lon < StartOptions.MinLon) || (ld.Lon > StartOptions.MaxLon)))
|
|
continue;
|
|
qrvs.Add(qrv);
|
|
}
|
|
else
|
|
qrvs.Add(qrv);
|
|
if (this.CancellationPending)
|
|
return false;
|
|
// reduce CPU load
|
|
Thread.Sleep(1);
|
|
}
|
|
// chek for empty database
|
|
if (StationData.Database.QRVCount() == 0)
|
|
{
|
|
// do bulk insert
|
|
StationData.Database.QRVBulkInsert(qrvs);
|
|
}
|
|
else
|
|
{
|
|
// do bulk update
|
|
StationData.Database.QRVBulkInsertOrUpdateIfNewer(qrvs);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Error loading database
|
|
this.ReportProgress(-1, "[" + url + "]: " + ex.ToString());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool ReadLocationsFromV1_2 (string filename)
|
|
{
|
|
List<LocationDesignator> tmp = StationData.Database.LocationFromV1_2(filename);
|
|
List<LocationDesignator> lds = new List<LocationDesignator>();
|
|
foreach (LocationDesignator ld in tmp)
|
|
{
|
|
// skip locations outsid area of interest if option set
|
|
if (StartOptions.RestrictToAreaOfInterest && ((ld.Lat < StartOptions.MinLat) || (ld.Lat > StartOptions.MaxLat) || (ld.Lon < StartOptions.MinLon) || (ld.Lon > StartOptions.MaxLon)))
|
|
continue;
|
|
lds.Add(ld);
|
|
}
|
|
try
|
|
{
|
|
foreach (LocationDesignator ld in lds)
|
|
{
|
|
if (StationData.Database.LocationInsertOrUpdateIfNewer(ld) > 0)
|
|
this.ReportProgress(0, "Importing locations from V1.2 database: " + ld.Call + ", " + ld.Loc + ", " + ld.Source.ToString());
|
|
if (this.CancellationPending)
|
|
return false;
|
|
// reduce CPU load
|
|
Thread.Sleep(1);
|
|
}
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
this.ReportProgress(-1, ex.ToString());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private DateTime GetDatabaseTimeStamp()
|
|
{
|
|
string filename = StationData.Database.GetDBLocation();
|
|
DateTime dt = File.GetLastWriteTimeUtc(filename).ToUniversalTime();
|
|
// convert to YYYY:MM:DD hh:mm:ss only as this is stored in settings
|
|
dt = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, DateTimeKind.Utc);
|
|
return dt;
|
|
}
|
|
|
|
private DATABASESTATUS GetDatabaseStatus()
|
|
{
|
|
return StationData.Database.GetDBStatus();
|
|
}
|
|
|
|
private DateTime GetLocationtUpdateTimeStamp()
|
|
{
|
|
string filename = Path.Combine(TmpDirectory, "locations.json");
|
|
DateTime dt = File.GetLastWriteTimeUtc(filename).ToUniversalTime();
|
|
// convert to YYYY:MM:DD hh:mm:ss only as this is stored in settings
|
|
dt = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, DateTimeKind.Utc);
|
|
return dt;
|
|
}
|
|
|
|
private DateTime GetQRVUpdateTimeStamp()
|
|
{
|
|
string filename = Path.Combine(TmpDirectory, "qrv.json");
|
|
DateTime dt = File.GetLastWriteTimeUtc(filename).ToUniversalTime();
|
|
// convert to YYYY:MM:DD hh:mm:ss only as this is stored in settings
|
|
dt = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, DateTimeKind.Utc);
|
|
return dt;
|
|
}
|
|
|
|
private DateTime GetSavedDatabaseTimeStamp()
|
|
{
|
|
DateTime dt = DateTime.MinValue;
|
|
dt = Properties.Settings.Default.Stations_TimeStamp;
|
|
// change kind to UTC as it is not specified in settings
|
|
dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
|
|
return dt;
|
|
}
|
|
|
|
private DateTime GetSavedLocationUpdateTimeStamp()
|
|
{
|
|
DateTime dt = DateTime.MinValue;
|
|
dt = Properties.Settings.Default.Locations_Update_TimeStamp;
|
|
// change kind to UTC as it is not specified in settings
|
|
dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
|
|
return dt;
|
|
}
|
|
|
|
private DateTime GetSavedQRVUpdateTimeStamp()
|
|
{
|
|
DateTime dt = DateTime.MinValue;
|
|
dt = Properties.Settings.Default.QRV_Update_TimeStamp;
|
|
// change kind to UTC as it is not specified in settings
|
|
dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
|
|
return dt;
|
|
}
|
|
|
|
private DATABASESTATUS GetSavedDatabaseStatus()
|
|
{
|
|
return Properties.Settings.Default.Stations_Status;
|
|
}
|
|
|
|
private void SaveDatabaseTimeStamp()
|
|
{
|
|
Properties.Settings.Default.Stations_TimeStamp = GetDatabaseTimeStamp();
|
|
}
|
|
|
|
private void SaveDatabaseStatus()
|
|
{
|
|
Properties.Settings.Default.Stations_Status = GetDatabaseStatus();
|
|
}
|
|
|
|
private void SaveLocationUpdateTimeStamp()
|
|
{
|
|
Properties.Settings.Default.Locations_Update_TimeStamp = GetLocationtUpdateTimeStamp();
|
|
}
|
|
|
|
private void SaveQRVUpdateTimeStamp()
|
|
{
|
|
Properties.Settings.Default.QRV_Update_TimeStamp = GetQRVUpdateTimeStamp();
|
|
}
|
|
|
|
private bool HasDatabaseChanged()
|
|
{
|
|
try
|
|
{
|
|
DateTime dt1 = GetSavedDatabaseTimeStamp();
|
|
DateTime dt2 = GetDatabaseTimeStamp();
|
|
return dt1 != dt2;
|
|
}
|
|
catch
|
|
{
|
|
// do nothing
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool HasLocationUpdateChanged()
|
|
{
|
|
try
|
|
{
|
|
DateTime dt1 = GetSavedLocationUpdateTimeStamp();
|
|
DateTime dt2 = GetLocationtUpdateTimeStamp();
|
|
return dt1 != dt2;
|
|
}
|
|
catch
|
|
{
|
|
// do nothing
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool HasQRVUpdateChanged()
|
|
{
|
|
try
|
|
{
|
|
DateTime dt1 = GetSavedQRVUpdateTimeStamp();
|
|
DateTime dt2 = GetQRVUpdateTimeStamp();
|
|
return dt1 != dt2;
|
|
}
|
|
catch
|
|
{
|
|
// do nothing
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
protected override void OnDoWork(DoWorkEventArgs e)
|
|
{
|
|
StartOptions = (StationDatabaseUpdaterStartOptions)e.Argument;
|
|
this.ReportProgress(0, StartOptions.Name + " started.");
|
|
// name the thread for debugging
|
|
if (String.IsNullOrEmpty(Thread.CurrentThread.Name))
|
|
Thread.CurrentThread.Name = nameof(StationDatabaseUpdater);
|
|
this.ReportProgress(0, "Updating station database...");
|
|
// get current AirScout password phrase for Unzip from website
|
|
try
|
|
{
|
|
WebClient client = new WebClient();
|
|
string result = client.DownloadString(StartOptions.GetKeyURL +
|
|
"?id=" + StartOptions.InstanceID +
|
|
"&key=zip");
|
|
if (!result.StartsWith("Error:"))
|
|
{
|
|
result = result.Trim('\"');
|
|
Password = Encryption.OpenSSLDecrypt(result, StartOptions.SessionKey);
|
|
}
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
this.ReportProgress(-1, ex.ToString());
|
|
}
|
|
// get update interval
|
|
int interval = (int)Properties.Settings.Default.Database_BackgroundUpdate_Period * 60;
|
|
do
|
|
{
|
|
try
|
|
{
|
|
int errors = 0;
|
|
// check if any kind of update is enabled
|
|
if ((StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE) || (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY))
|
|
{
|
|
// reset database status
|
|
StationData.Database.SetDBStatus(DATABASESTATUS.UNDEFINED);
|
|
this.ReportProgress(1, StationData.Database.GetDBStatus());
|
|
Stopwatch st = new Stopwatch();
|
|
st.Start();
|
|
// clear temporary files
|
|
try
|
|
{
|
|
SupportFunctions.DeleteFilesFromDirectory(TmpDirectory, new string[] { "*.tmp", "*.PendingOverwrite" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
this.ReportProgress(-1, ex.ToString());
|
|
}
|
|
// set status to updating
|
|
StationData.Database.SetDBStatus(DATABASESTATUS.UPDATING);
|
|
this.ReportProgress(1, StationData.Database.GetDBStatus());
|
|
// update location database
|
|
this.ReportProgress(0, "Updating locations from web database...");
|
|
// get update from url
|
|
GetUpdateFromURL(Properties.Settings.Default.Stations_UpdateURL + "locations.zip", Path.Combine(TmpDirectory, "locations.zip"), Path.Combine(TmpDirectory, "locations.json"));
|
|
if (HasDatabaseChanged() || HasLocationUpdateChanged())
|
|
{
|
|
// database and/or update changed --> do full check
|
|
if (!ReadLocationsFromURL(Properties.Settings.Default.Stations_UpdateURL + "locations.zip", Path.Combine(TmpDirectory, "locations.zip"), Path.Combine(TmpDirectory, "locations.json")))
|
|
errors++;
|
|
// save status & timestamps
|
|
SaveDatabaseTimeStamp();
|
|
SaveDatabaseStatus();
|
|
SaveLocationUpdateTimeStamp();
|
|
}
|
|
else
|
|
{
|
|
// dabase and update not changed --> nothing to do
|
|
// restore database status
|
|
StationData.Database.SetDBStatus(GetDatabaseStatus());
|
|
}
|
|
if (this.CancellationPending)
|
|
break;
|
|
// update qrv database
|
|
this.ReportProgress(0, "Updating qrv info from web database...");
|
|
// get update from url
|
|
GetUpdateFromURL(Properties.Settings.Default.Stations_UpdateURL + "qrv.zip", Path.Combine(TmpDirectory, "qrv.zip"), Path.Combine(TmpDirectory, "qrv.json"));
|
|
if (HasDatabaseChanged() || HasQRVUpdateChanged())
|
|
{
|
|
if (!ReadQRVFromURL(Properties.Settings.Default.Stations_UpdateURL + "qrv.zip", Path.Combine(TmpDirectory, "qrv.zip"), Path.Combine(TmpDirectory, "qrv.json")))
|
|
errors++;
|
|
// save status & timestamps
|
|
SaveDatabaseTimeStamp();
|
|
SaveDatabaseStatus();
|
|
SaveQRVUpdateTimeStamp();
|
|
}
|
|
else
|
|
{
|
|
// dabase and update not changed --> nothing to do
|
|
// restore database status
|
|
StationData.Database.SetDBStatus(GetDatabaseStatus());
|
|
}
|
|
// silently update locations from V1.2 database if found
|
|
string appdatadir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName).TrimEnd(Path.DirectorySeparatorChar);
|
|
string oldfile = Path.Combine(appdatadir.Replace("AirScout", "ScoutBase"), "Database", "ScoutBase.db3");
|
|
if (File.Exists(oldfile))
|
|
{
|
|
this.ReportProgress(0, "Import locations from V1.2 database...");
|
|
if (!ReadLocationsFromV1_2(oldfile))
|
|
errors++;
|
|
}
|
|
st.Stop();
|
|
// display status
|
|
if (errors == 0)
|
|
{
|
|
StationData.Database.SetDBStatus(DATABASESTATUS.UPTODATE);
|
|
this.ReportProgress(1, StationData.Database.GetDBStatus());
|
|
this.ReportProgress(0, " Station database update completed: " + st.Elapsed.ToString(@"hh\:mm\:ss"));
|
|
}
|
|
else
|
|
{
|
|
StationData.Database.SetDBStatus(DATABASESTATUS.ERROR);
|
|
this.ReportProgress(1, StationData.Database.GetDBStatus());
|
|
this.ReportProgress(0, " Station database update completed with errors[" + errors.ToString() + "]: " + st.Elapsed.ToString(@"hh\:mm\:ss"));
|
|
}
|
|
// sleep once to get all messages to main thread
|
|
Thread.Sleep(1000);
|
|
// sleep when running periodically
|
|
if (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY)
|
|
{
|
|
int i = 0;
|
|
while (!this.CancellationPending && (i < interval))
|
|
{
|
|
Thread.Sleep(1000);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
this.ReportProgress(-1, ex.ToString());
|
|
}
|
|
}
|
|
while (!this.CancellationPending && (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY));
|
|
if (this.CancellationPending)
|
|
this.ReportProgress(0, "Cancelled.");
|
|
else
|
|
this.ReportProgress(0, "Finished.");
|
|
}
|
|
|
|
#endregion
|
|
|
|
// legacy!!!
|
|
public class OldLocationDesignator: SQLiteEntry
|
|
{
|
|
public string Call { get; set; }
|
|
public double Lat { get; set; }
|
|
public double Lon { get; set; }
|
|
public GEOSOURCE Source { get; set; }
|
|
|
|
public OldLocationDesignator()
|
|
{
|
|
Call = "";
|
|
Lat = 0;
|
|
Lon = 0;
|
|
Source = GEOSOURCE.UNKONWN;
|
|
LastUpdated = DateTime.MinValue;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|