using log4net; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using Microsoft.Owin.Hosting; using System.Net.Http; namespace bsmd.AIS2Service { internal static class AISManager { #region fields private static readonly List _tasks = new List(); private static readonly ConcurrentQueue _inputLines = new ConcurrentQueue(); private static readonly ConcurrentQueue _decodedClasses = new ConcurrentQueue(); private static readonly ILog _log = LogManager.GetLogger(typeof(AISManager)); private static readonly ConcurrentDictionary _sitRepList = new ConcurrentDictionary(); private static readonly ConcurrentQueue _dbSaveTargets = new ConcurrentQueue(); private static Timer _staleTargetTimer; // cleanup sitrep private static Timer _stalePosReportTimer; // clean db private static IDisposable _restAPISelfHost = null; #endregion #region public methods public static async void Start() { _tasks.Add(new SerialTCPReader(Properties.Settings.Default.DataSourceHost, Properties.Settings.Default.DataSourcePort, _inputLines)); _tasks.Add(new AISDecoder(_inputLines, _decodedClasses)); _tasks.Add(new SitRep(_decodedClasses, _sitRepList, _dbSaveTargets)); AIS_SQLiteStorage sqliteStorage = new AIS_SQLiteStorage(_dbSaveTargets); _tasks.Add(sqliteStorage); // preload sit rep Dictionary targets = await sqliteStorage.LoadTargets(); foreach(int key in targets.Keys) { _sitRepList.TryAdd(key, targets[key]); } foreach (var task in _tasks) { task.Start(); _log.InfoFormat("{0} started", task.Name); task.FatalErrorOccurred += Task_FatalErrorOccurred; } // init timer tasks _staleTargetTimer = new Timer(StaleTargetTimerCheck, null, 0, 60000); // check every minute, start immediately _stalePosReportTimer = new Timer(StalePosReportCheck, sqliteStorage, 0, 60000 * 10); // every ten minutes, // if required start self-hosted owin endpoint if(Properties.Settings.Default.EnableRestAPIEndpoint) { _restAPISelfHost = WebApp.Start(url: Properties.Settings.Default.RestAPIBaseAddress); } } public static void Stop() { foreach (var task in _tasks) { task.Stop(); _log.InfoFormat("{0} stopped", task.Name); } if (Properties.Settings.Default.EnableRestAPIEndpoint && (_restAPISelfHost != null)) _restAPISelfHost.Dispose(); _staleTargetTimer.Dispose(); _stalePosReportTimer.Dispose(); } #endregion #region Properties public static ConcurrentDictionary SitRep { get { return _sitRepList; } } #endregion #region private methods / timer callbacks private static void Task_FatalErrorOccurred(object sender, EventArgs e) { throw new NotImplementedException("TBD: shutdown the whole operation?"); } private static void StaleTargetTimerCheck(object state) { List removeKeyList = new List(); foreach (int key in _sitRepList.Keys) { if (!_sitRepList[key].LastUpdate.HasValue) { removeKeyList.Add(key); } else { if ((DateTime.Now - _sitRepList[key].LastUpdate.Value).TotalMinutes > Properties.Settings.Default.StaleTargetTimeoutMins) removeKeyList.Add(key); } } foreach(int key in removeKeyList) { _sitRepList.TryRemove(key, out _); } } private static void StalePosReportCheck(object state) { if (state is AIS_SQLiteStorage storage) { storage.RemoveOldPosReports(); } } #endregion } }