/// <summary> /// Initalizes the fix archiver instance. /// </summary> /// <param name="node">The parent <see cref="GeoTrackerNode" /> instance.</param> /// <param name="args"> /// This implementation's settings come directly from the application /// configuration as described below. /// </param> /// <remarks> /// <note> /// <para> /// Note that some of the application log settings are loaded directly from the application's <b>LillTek.AppLog</b> /// configuration settings section as described in <see cref="AppLog" />. The valid arguments passed to the /// <see cref="Start" /> are: /// </para> /// <list type="table"> /// <item> /// <term><b>LogName</b></term> /// <description> /// The name of the application log. This defaults to <b>GeoTrackerFixes</b>. /// </description> /// </item> /// <item> /// <term><b>MaxSize</b></term> /// <description> /// The approximate maximum size of the log on disk or zero for no limit. /// This defaults to <b>5GB</b>. /// </description> /// </item> /// <item> /// <term><b>PurgeInterval</b></term> /// <description> /// The interval at which the log will purge old log files so that the /// total log size remains within the <b>MaxSize</b> limit. /// This defaults to <b>5 minutes</b>. /// </description> /// </item> /// </list> /// <see cref="IGeoFixArchiver" /> implementations must silently handle any internal /// error conditions. <see cref="GeoTrackerNode" /> does not expect to see any /// exceptions raised from calls to any of these methods. Implementations should /// catch any exceptions thrown internally and log errors or warnings as necessary. /// </note> /// </remarks> public void Start(GeoTrackerNode node, ArgCollection args) { lock (syncLock) { try { if (this.logWriter != null) { throw new InvalidOperationException("AppLogGeoFixArchiver: Archiver has already been started."); } if (this.isStopped) { throw new InvalidOperationException("AppLogGeoFixArchiver: Cannot restart a stopped archiver."); } this.node = node; this.logName = args.Get("LogName", "GeoTrackerFixes"); this.maxSize = args.Get("MaxSize", 5L * 1024L * 1024L * 1024L); this.purgeInterval = args.Get("PurgeInterval", TimeSpan.FromMinutes(5)); this.logWriter = new AppLogWriter(logName, SchemaName, SchemaVersion, maxSize); this.logWriter.PurgeInterval = purgeInterval; } catch (Exception e) { SysLog.LogException(e); } } }
/// <summary> /// Initalizes the fix archiver instance. /// </summary> /// <param name="node">The parent <see cref="GeoTrackerNode" /> instance.</param> /// <param name="args">This implementation recognizes special arguments (see the remarks).</param> /// <remarks> /// <para> /// This archiver recognizes the following arguments: /// </para> /// <list type="table"> /// <item> /// <term><b>ConnectionString</b></term> /// <description> /// <b>Required:</b> This specifies the SQL connection string the archiver will use /// to connect to the database server. /// </description> /// </item> /// <item> /// <term><b>BufferSize</b></term> /// <description> /// <b>Optional:</b> This specifies the number of fixes the archiver will buffer before /// submitting the fixes to the database in a batch. (defaults to <b>1000</b>). /// </description> /// </item> /// <item> /// <term><b>BufferInterval</b></term> /// <description> /// <b>Optional: </b> The maximum length of time the archiver will buffer fixes before /// flushing to the database, regardless of how full thbe buffer is. (defaults to <b>5 minutes</b>). /// </description> /// </item> /// <item> /// <term><b>AddScript</b></term> /// <description> /// <para> /// The SQL script template the archiver will use for submitting fixes to the database. /// This script will include macros where the archiver will substitute the values /// from the <see cref="GeoFix" /> being persisted. Here are the supported macros: /// </para> /// <list type="table"> /// <item> /// <term><b>@(TimeUtc)</b></term> /// <description> /// The time (UTC) the fix was taken. /// </description> /// </item> /// <item> /// <term><b>@(EntityID)</b></term> /// <description> /// The ID of the entity being tracked. /// </description> /// </item> /// <item> /// <term><b>@(GroupID)</b></term> /// <description> /// <b>Nullable:</b> The ID of the group the entity belongs to. /// </description> /// </item> /// <item> /// <term><b>@(Technology)</b></term> /// <description> /// The technology used to obtain the position. /// </description> /// </item> /// <item> /// <term><b>@(Latitude)</b></term> /// <description> /// The latitude. /// </description> /// </item> /// <item> /// <term><b>@(Longitude)</b></term> /// <description> /// The longitude. /// </description> /// </item> /// <item> /// <term><b>@(Altitude)</b></term> /// <description> /// <b>Nullable:</b> The altitude in meters. /// </description> /// </item> /// <item> /// <term><b>@(Course)</b></term> /// <description> /// <b>Nullable:</b> The direction of travel in degrees from true north. /// </description> /// </item> /// <item> /// <term><b>@(Speed)</b></term> /// <description> /// <b>Nullable:</b> The speed in kilometers per hour. /// </description> /// </item> /// <item> /// <term><b>@(HorizontalAccuracy)</b></term> /// <description> /// <b>Nullable:</b> The estimated horizontal accuracy of the fix in meters. /// </description> /// </item> /// <item> /// <term><b>@(VerticalAccurancy)</b></term> /// <description> /// <b>Nullable:</b> The estimated vertical accuracy of the fix in meters. /// </description> /// </item> /// <item> /// <term><b>@(NetworkStatus)</b></term> /// <description> /// Identifies how or if the entity was connected to the Internet when the fix was taken. /// </description> /// </item> /// </list> /// <note> /// We're using the <b>"@"</b> rather than the usual <b>"$"</b> character to mark the /// macro name to avoid conflicting with environment variable expansions performed /// when loading the application configuration. /// </note> /// </description> /// </item> /// </list> /// <para> /// The most important archiver parameter is <b>AddScript</b>. This is the template the archiver /// will use to generate the SQL statements that will add <see cref="GeoFix" />es to the database. /// The script can be as simple as an ad-hoc table <b>insert</b> or a call to a stored procedure. /// Add one or more of the macro identifiers listed above to the script. The archiver will replace /// these macros with actual fields from the <see cref="GeoFix" />, quoting them as necessary. /// Null <see cref="GeoFix" /> fields or numeric fields set to <see cref="double.NaN" /> will /// be generated as <c>null</c>. See the <b>Nullable</b> tags in the macro definitions above. /// </para> /// <para> /// Here's an exmple of a simple sample script that will insert the <see cref="GeoFix.TimeUtc" />, /// <b>entity ID</b>, <see cref="GeoFix.Latitude" />, and <see cref="GeoFix.Longitude" /> /// <see cref="GeoFix" /> fields into a table. /// </para> /// <code language="none"> /// insert into FixArchive(Time,Entity,Lat,Lon) values (@(TimeUtc),@(EntityID),@(Latitude),@(longitude)) /// </code> /// <note> /// <see cref="IGeoFixArchiver" /> implementations must silently handle any internal /// error conditions. <see cref="GeoTrackerNode" /> does not expect to see any /// exceptions raised from calls to any of these methods. Implementations should /// catch any exceptions thrown internally and log errors or warnings as necessary. /// </note> /// </remarks> public void Start(GeoTrackerNode node, ArgCollection args) { lock (syncLock) { try { if (isRunning) { throw new InvalidProgramException("SqlGeoFixArchiver: Archiver has already been started."); } if (isStopped) { throw new InvalidOperationException("SqlGeoFixArchiver: Cannot restart a stopped archiver."); } this.node = node; this.settings = node.Settings; this.bufferedFixes = new List <FixRecord>(); this.conString = args.Get("ConnectionString", string.Empty); this.bufferSize = args.Get("BufferSize", 1000); this.bufferInterval = args.Get("BufferInterval", TimeSpan.FromMinutes(5)); this.addScript = args.Get("AddScript", string.Empty).Replace("@(", "$("); if (string.IsNullOrWhiteSpace(this.conString)) { throw new ArgumentException("SqlGeoFixArchiver: The [ConnectionString] argument is required."); } if (string.IsNullOrWhiteSpace(this.addScript)) { throw new ArgumentException("SqlGeoFixArchiver: The [AddScript] argument is required."); } this.isRunning = true; this.flushTimer = new PolledTimer(bufferInterval, false); this.flushThread = new Thread(new ThreadStart(FlushThread)); this.flushThread.Start(); } catch (Exception e) { SysLog.LogException(e); } } }
/// <summary> /// Constructor. /// </summary> /// <param name="node">The parent GeoTracker node.</param> public MercatorIndex(GeoTrackerNode node) { // Initialize the top-level blocks. this.topLevelBlocks = new MercatorBlock[TopLevelColumnCount, TopLevelRowCount]; for (int row = 0; row < TopLevelRowCount; row++) { for (int col = 0; col < TopLevelColumnCount; col++) { var neCorner = new GeoCoordinate(90.0 - row * 10, -180.0 + (col + 1) * 10.0); var swCorner = new GeoCoordinate(90.0 - (row + 1) * 10.0, -180.0 + col * 10.0); var bounds = new GeoRectangle(neCorner, swCorner); topLevelBlocks[row, col] = new MercatorBlock(node.Settings, bounds, 0); } } }
/// <summary> /// Constructs the instance and starts the background downloading thread. /// </summary> /// <param name="node">The parent <see cref="GeoTrackerNode" /> instance.</param> /// <remarks> /// <note> /// The constructor will not start a background thread if IP geocoding is disabled. /// </note> /// </remarks> public IPGeocoder(GeoTrackerNode node) { settings = node.Settings; if (!settings.IPGeocodeEnabled) { return; } // Initialize the service including loading the MaxMind database if present. running = true; stopPending = false; pollDataNow = false; maxMind = null; try { if (File.Exists(dataPath)) { maxMind = new LookupService(dataPath, LookupService.GEOIP_MEMORY_CACHE); maxMind.close(); } } catch (Exception e) { // Assume that the database file is corrupted if there's an exception // and delete it so the download thread will download a new copy. SysLog.LogException(e); SysLog.LogError("GeoTracker: The MaxMind database file [{0}] appears to be corrupted. This will be deleted so the downloader can get a fresh copy.", dataPath); Helper.DeleteFile(dataPath); } // Start the background downloader thread. downloadThread = new Thread(new ThreadStart(DownloadThread)); downloadThread.Name = "GeoTracker: GeoData Downloader"; downloadThread.Start(); }
/// <summary> /// Initalizes the fix archiver instance. /// </summary> /// <param name="node">The parent <see cref="GeoTrackerNode" /> instance.</param> /// <param name="args">This implementation defines no special arguments.</param> /// <remarks> /// <note> /// <see cref="IGeoFixArchiver" /> implementations must silently handle any internal /// error conditions. <see cref="GeoTrackerNode" /> does not expect to see any /// exceptions raised from calls to any of these methods. Implementations should /// catch any exceptions thrown internally and log errors or warnings as necessary. /// </note> /// </remarks> public void Start(GeoTrackerNode node, ArgCollection args) { }