public bool PrioritizeAction(Action action) { ICancellationToken priorityToken = new GSF.Threading.CancellationToken(); ICancellationToken normalToken = null; bool cancelled = false; priorityToken.Cancel(); while (Interlocked.CompareExchange(ref m_cancellationToken, priorityToken, normalToken) != normalToken) { normalToken = Interlocked.CompareExchange(ref m_cancellationToken, null, null); cancelled = normalToken?.Cancel() ?? true; if (!cancelled) { return(false); } } TaskThread.Push(HighPriority, () => { try { action(); } finally { Interlocked.Exchange(ref m_cancellationToken, null); } }); return(true); }
/// <summary> /// Read historian data from server. /// </summary> /// <param name="instanceName">Historian instance name.</param> /// <param name="startTime">Start time of query.</param> /// <param name="stopTime">Stop time of query.</param> /// <param name="measurementIDs">Measurement IDs to query - or <c>null</c> for all available points.</param> /// <param name="resolution">Resolution for data query.</param> /// <param name="seriesLimit">Maximum number of points per series.</param> /// <param name="forceLimit">Flag that determines if series limit should be strictly enforced.</param> /// <param name="timestampType">Type of timestamps.</param> /// <returns>Enumeration of <see cref="TrendValue"/> instances read for time range.</returns> public IEnumerable <TrendValue> GetHistorianData(string instanceName, DateTime startTime, DateTime stopTime, ulong[] measurementIDs, Resolution resolution, int seriesLimit, bool forceLimit, TimestampType timestampType = TimestampType.UnixMilliseconds) { // Cancel any running operation CancellationToken cancellationToken = new CancellationToken(); Interlocked.Exchange(ref m_readCancellationToken, cancellationToken)?.Cancel(); SnapServer server = GetServer(instanceName)?.Host; IEnumerable <TrendValue> values = TrendValueAPI.GetHistorianData(server, instanceName, startTime, stopTime, measurementIDs, resolution, seriesLimit, forceLimit, cancellationToken); switch (timestampType) { case TimestampType.Ticks: return(values.Select(value => { value.Timestamp = value.Timestamp * 10000.0D + 621355968000000000.0D; return value; })); case TimestampType.UnixSeconds: return(values.Select(value => { value.Timestamp = value.Timestamp / 1000.0D; return value; })); default: return(values); } }
public async Task EnumerateAsync(FileEnumerationStrategy fileEnumerationStrategy) { if (m_disposed) { return; } CancellationToken cancellationToken = new CancellationToken(); Interlocked.Exchange(ref m_cancellationToken, cancellationToken); DirectoryInfo directory = new DirectoryInfo(Path); await EnumerateDirectoryAsync(directory, fileEnumerationStrategy, cancellationToken); }
public bool QueueAction(Action action) { ICancellationToken cancellationToken = new GSF.Threading.CancellationToken(); if (Interlocked.CompareExchange(ref m_cancellationToken, cancellationToken, null) != null) { return(false); } TaskThread.Push(NormalPriority, () => { if (!cancellationToken.Cancel()) { return; } try { action(); } finally { Interlocked.CompareExchange(ref m_cancellationToken, null, cancellationToken); } }); return(true); }
/// <summary> /// Read historian data from server. /// </summary> /// <param name="server">The server to use for the query.</param> /// <param name="instanceName">Name of the archive to be queried.</param> /// <param name="startTime">Start time of query.</param> /// <param name="stopTime">Stop time of query.</param> /// <param name="measurementIDs">Measurement IDs to query - or <c>null</c> for all available points.</param> /// <param name="resolution">Resolution for data query.</param> /// <param name="seriesLimit">Maximum number of points per series.</param> /// <param name="forceLimit">Flag that determines if series limit should be strictly enforced.</param> /// <param name="cancellationToken">Cancellation token for query.</param> /// <returns>Enumeration of <see cref="TrendValue"/> instances read for time range.</returns> public static IEnumerable <TrendValue> GetHistorianData(SnapServer server, string instanceName, DateTime startTime, DateTime stopTime, ulong[] measurementIDs, Resolution resolution, int seriesLimit, bool forceLimit, ICancellationToken cancellationToken = null) { if (cancellationToken == null) { cancellationToken = new CancellationToken(); } if (server == null) { yield break; } // Setting series limit to zero requests full resolution data, which overrides provided parameter if (seriesLimit < 1) { resolution = Resolution.Full; } TimeSpan resolutionInterval = resolution.GetInterval(); SeekFilterBase <HistorianKey> timeFilter; MatchFilterBase <HistorianKey, HistorianValue> pointFilter = null; HistorianKey key = new HistorianKey(); HistorianValue value = new HistorianValue(); // Set data scan resolution if (resolution == Resolution.Full) { timeFilter = TimestampSeekFilter.CreateFromRange <HistorianKey>(startTime, stopTime); } else { BaselineTimeInterval interval = BaselineTimeInterval.Second; if (resolutionInterval.Ticks < Ticks.PerMinute) { interval = BaselineTimeInterval.Second; } else if (resolutionInterval.Ticks < Ticks.PerHour) { interval = BaselineTimeInterval.Minute; } else if (resolutionInterval.Ticks == Ticks.PerHour) { interval = BaselineTimeInterval.Hour; } startTime = startTime.BaselinedTimestamp(interval); stopTime = stopTime.BaselinedTimestamp(interval); timeFilter = TimestampSeekFilter.CreateFromIntervalData <HistorianKey>(startTime, stopTime, resolutionInterval, new TimeSpan(TimeSpan.TicksPerMillisecond)); } Dictionary <ulong, DataRow> metadata = null; using (SnapClient connection = SnapClient.Connect(server)) using (ClientDatabaseBase <HistorianKey, HistorianValue> database = connection.GetDatabase <HistorianKey, HistorianValue>(instanceName)) { if (database == null) { yield break; } if (LocalOutputAdapter.Instances.TryGetValue(database.Info?.DatabaseName ?? DefaultInstanceName, out LocalOutputAdapter historianAdapter)) { metadata = historianAdapter?.Measurements; } if (metadata == null) { yield break; } // Setup point ID selections if (measurementIDs != null) { pointFilter = PointIdMatchFilter.CreateFromList <HistorianKey, HistorianValue>(measurementIDs); } else { measurementIDs = metadata.Keys.ToArray(); } // Start stream reader for the provided time window and selected points Dictionary <ulong, long> pointCounts = new Dictionary <ulong, long>(measurementIDs.Length); Dictionary <ulong, long> intervals = new Dictionary <ulong, long>(measurementIDs.Length); Dictionary <ulong, ulong> lastTimes = new Dictionary <ulong, ulong>(measurementIDs.Length); double range = (stopTime - startTime).TotalSeconds; ulong pointID, timestamp, resolutionSpan = (ulong)resolutionInterval.Ticks, baseTicks = (ulong)UnixTimeTag.BaseTicks.Value; long pointCount; if (resolutionSpan <= 1UL) { resolutionSpan = Ticks.PerSecond; } if (seriesLimit < 1) { seriesLimit = 1; } // Estimate total measurement counts per point so decimation intervals for each series can be calculated foreach (ulong measurementID in measurementIDs) { if (resolution == Resolution.Full) { pointCounts[measurementID] = metadata.TryGetValue(measurementID, out DataRow row) ? (long)(int.Parse(row["FramesPerSecond"].ToString()) * range) : 2; } else { pointCounts[measurementID] = (long)(range / resolutionInterval.TotalSeconds.NotZero(1.0D)); } } foreach (ulong measurementID in pointCounts.Keys) { intervals[measurementID] = (pointCounts[measurementID] / seriesLimit).NotZero(1L); } using (TreeStream <HistorianKey, HistorianValue> stream = database.Read(SortedTreeEngineReaderOptions.Default, timeFilter, pointFilter)) { while (stream.Read(key, value) && !cancellationToken.IsCancelled) { pointID = key.PointID; timestamp = key.Timestamp; pointCount = pointCounts[pointID]; if (pointCount++ % intervals[pointID] == 0 || !forceLimit && timestamp - lastTimes.GetOrAdd(pointID, 0UL) > resolutionSpan) { yield return new TrendValue { ID = (long)pointID, Timestamp = (timestamp - baseTicks) / (double)Ticks.PerMillisecond, Value = value.AsSingle } } ; pointCounts[pointID] = pointCount; lastTimes[pointID] = timestamp; } } } } }
public ContourAnimationInfo getContourAnimations(ContourQuery contourQuery) { List<List<TrendingDataLocation>> frames = GetFramesFromHistorian(contourQuery); PiecewiseLinearFunction colorScale = GetColorScale(contourQuery); Func<double, double> colorFunc = colorScale; // The actual startDate is the timestamp of the // first frame after contourQuery.GetStartDate() DateTime startDate = contourQuery.GetStartDate(); int stepSize = contourQuery.StepSize; int startTimeOffset = (int)Math.Ceiling((startDate - startDate.Date).TotalMinutes / stepSize); startDate = startDate.Date.AddMinutes(startTimeOffset * stepSize); double minLat = frames.Min(frame => frame.Min(location => location.Latitude)) - GetLatFromMiles(50.0D); double maxLat = frames.Min(frame => frame.Max(location => location.Latitude)) + GetLatFromMiles(50.0D); double minLng = frames.Min(frame => frame.Min(location => location.Longitude)) - GetLngFromMiles(50.0D, 0.0D); double maxLng = frames.Min(frame => frame.Max(location => location.Longitude)) + GetLngFromMiles(50.0D, 0.0D); GeoCoordinate topLeft = new GeoCoordinate(maxLat, minLng); GeoCoordinate bottomRight = new GeoCoordinate(minLat, maxLng); GSF.Drawing.Point topLeftPoint = s_crs.Translate(topLeft, contourQuery.Resolution); GSF.Drawing.Point bottomRightPoint = s_crs.Translate(bottomRight, contourQuery.Resolution); topLeftPoint = new GSF.Drawing.Point(Math.Floor(topLeftPoint.X), Math.Floor(topLeftPoint.Y)); bottomRightPoint = new GSF.Drawing.Point(Math.Ceiling(bottomRightPoint.X), Math.Ceiling(bottomRightPoint.Y)); topLeft = s_crs.Translate(topLeftPoint, contourQuery.Resolution); bottomRight = s_crs.Translate(bottomRightPoint, contourQuery.Resolution); int width = (int)(bottomRightPoint.X - topLeftPoint.X + 1); int height = (int)(bottomRightPoint.Y - topLeftPoint.Y + 1); int animationID; string timeZoneID = null; using (AdoDataConnection connection = new AdoDataConnection(connectionstring, typeof(SqlConnection), typeof(SqlDataAdapter))) { connection.ExecuteNonQuery("INSERT INTO ContourAnimation(ColorScaleName, StartTime, EndTime, StepSize) VALUES({0}, {1}, {2}, {3})", contourQuery.ColorScaleName, contourQuery.GetStartDate(), contourQuery.GetEndDate(), contourQuery.StepSize); animationID = connection.ExecuteScalar<int>("SELECT @@IDENTITY"); if (contourQuery.IncludeWeather) timeZoneID = connection.ExecuteScalar<string>("SELECT Value FROM Setting WHERE Name = 'XDATimeZone'"); } GSF.Threading.CancellationToken cancellationToken = new GSF.Threading.CancellationToken(); s_cancellationTokens[animationID] = cancellationToken; ProgressCounter progressCounter = new ProgressCounter(frames.Count); s_progressCounters[animationID] = progressCounter; Action<int> createFrame = i => { List<TrendingDataLocation> frame = frames[i]; IDWFunc idwFunction = GetIDWFunction(contourQuery, frame); uint[] pixelData; if (contourQuery.IncludeWeather) { TimeZoneInfo tzInfo = !string.IsNullOrEmpty(timeZoneID) ? TimeZoneInfo.FindSystemTimeZoneById(timeZoneID) : TimeZoneInfo.Local; // Weather data is only available in 5-minute increments DateTime frameTime = TimeZoneInfo.ConvertTimeToUtc(startDate.AddMinutes(stepSize * i), tzInfo); double minutes = (frameTime - frameTime.Date).TotalMinutes; int weatherMinutes = (int)Math.Ceiling(minutes / 5) * 5; NameValueCollection queryString = HttpUtility.ParseQueryString(string.Empty); queryString["service"] = "WMS"; queryString["request"] = "GetMap"; queryString["layers"] = "nexrad-n0r-wmst"; queryString["format"] = "image/png"; queryString["transparent"] = "true"; queryString["version"] = "1.1.1"; queryString["time"] = frameTime.Date.AddMinutes(weatherMinutes).ToString("o"); queryString["height"] = height.ToString(); queryString["width"] = width.ToString(); queryString["srs"] = "EPSG:3857"; GSF.Drawing.Point topLeftProjected = s_crs.Projection.Project(topLeft); GSF.Drawing.Point bottomRightProjected = s_crs.Projection.Project(bottomRight); queryString["bbox"] = string.Join(",", topLeftProjected.X, bottomRightProjected.Y, bottomRightProjected.X, topLeftProjected.Y); string weatherURL = "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi?" + queryString.ToString(); using (WebClient client = new WebClient()) using (MemoryStream stream = new MemoryStream(client.DownloadData(weatherURL))) using (Bitmap bitmap = new Bitmap(stream)) { pixelData = bitmap.ToPixelData(); } } else { pixelData = new uint[width * height]; } if (cancellationToken.IsCancelled) return; for (int x = 0; x < width; x++) { if (cancellationToken.IsCancelled) return; for (int y = 0; y < height; y++) { if (cancellationToken.IsCancelled) return; if (pixelData[y * width + x] > 0) continue; GSF.Drawing.Point offsetPixel = new GSF.Drawing.Point(topLeftPoint.X + x, topLeftPoint.Y + y); GeoCoordinate pixelCoordinate = s_crs.Translate(offsetPixel, contourQuery.Resolution); double interpolatedValue = idwFunction(pixelCoordinate.Longitude, pixelCoordinate.Latitude); pixelData[y * width + x] = (uint)colorFunc(interpolatedValue); } } if (cancellationToken.IsCancelled) return; using (Bitmap bitmap = BitmapExtensions.FromPixelData(width, pixelData)) using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream, ImageFormat.Png); using (AdoDataConnection connection = new AdoDataConnection(connectionstring, typeof(SqlConnection), typeof(SqlDataAdapter))) { connection.ExecuteNonQuery("INSERT INTO ContourAnimationFrame VALUES({0}, {1}, {2})", animationID, i, stream.ToArray()); } } progressCounter.Increment(); }; Task.Run(() => { ICancellationToken token; ProgressCounter counter; Parallel.For(0, frames.Count, createFrame); s_cancellationTokens.TryRemove(animationID, out token); s_progressCounters.TryRemove(animationID, out counter); if (cancellationToken.IsCancelled) { using (AdoDataConnection connection = new AdoDataConnection(connectionstring, typeof(SqlConnection), typeof(SqlDataAdapter))) { connection.ExecuteNonQuery("DELETE FROM ContourAnimationFrame WHERE ContourAnimationID = {0}", animationID); connection.ExecuteNonQuery("DELETE FROM ContourAnimation WHERE ID = {0}", animationID); } } }); s_cleanUpAnimationOperation.TryRunOnceAsync(); return new ContourAnimationInfo() { AnimationID = animationID, ColorDomain = colorScale.Domain, ColorRange = colorScale.Range, MinLatitude = bottomRight.Latitude, MaxLatitude = topLeft.Latitude, MinLongitude = topLeft.Longitude, MaxLongitude = bottomRight.Longitude, Infos = frames.Select((frame, index) => new ContourInfo() { Locations = frame, URL = string.Format("./mapService.asmx/getContourAnimationFrame?animation={0}&frame={1}", animationID, index), Date = contourQuery.GetStartDate().AddMinutes(index * contourQuery.StepSize).ToString() }).ToList() }; }
private async Task EnumerateDirectoryAsync(DirectoryInfo directory, FileEnumerationStrategy fileEnumerationStrategy, CancellationToken cancellationToken) { bool parallelSubdirectories = fileEnumerationStrategy == FileEnumerationStrategy.ParallelSubdirectories; LogicalThread thread = LogicalThread.CurrentThread; if (parallelSubdirectories) { LogicalThreadScheduler scheduler = m_fileProcessor.m_threadScheduler; thread = scheduler.CreateThread(); await thread.Join(); } async Task ForEach(IEnumerable <Task> tasks) { foreach (Task task in tasks) { if (cancellationToken.IsCancelled) { return; } await task; await thread.Yield(); } } LogicalThread processingThread = m_fileProcessor.m_processingThread; string activePath = null; async Task VisitSubdirectoryAsync(DirectoryInfo subdirectory) { activePath = subdirectory.FullName; if (cancellationToken.IsCancelled) { return; } if (m_fileProcessor.MatchesFolderExclusion(subdirectory.FullName)) { return; } await EnumerateDirectoryAsync(subdirectory, fileEnumerationStrategy, cancellationToken); } async Task <Task> VisitFileAsync(FileInfo file) { activePath = file.FullName; if (cancellationToken.IsCancelled) { return(Task.CompletedTask); } async Task InvokeOnProcessingThread(Action action) { await processingThread.Join(); action(); } DateTime lastWriteTime = file.LastWriteTimeUtc; void Process() => m_fileProcessor.TouchAndProcess(file.FullName, lastWriteTime, false); void Skip() => m_fileProcessor.TouchAndSkip(file.FullName, lastWriteTime); if (!m_fileProcessor.MatchesFilter(file.FullName)) { return(InvokeOnProcessingThread(Skip)); } Task processTask = InvokeOnProcessingThread(Process); await thread.Yield(); return(processTask); } async Task EnumerateSubdirectoriesAsync() { Func <IEnumerable <Task>, Task> whenAll = parallelSubdirectories ? Task.WhenAll : ForEach; IEnumerable <Task> subdirectoryTasks = FilePath .EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly, m_fileProcessor.OnError) .Select(VisitSubdirectoryAsync); await whenAll(subdirectoryTasks); } async Task EnumerateFilesAsync() { IEnumerable <Task <Task> > fileTasks = FilePath .EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly) .Select(VisitFileAsync); List <Task> processTasks = new List <Task>(); foreach (Task <Task> fileTask in fileTasks) { processTasks.Add(await fileTask); } await Task.WhenAll(processTasks); } EventHandler <EventArgs <List <string> > > handler = (sender, args) => { if (!(activePath is null)) { args.Argument.Add(activePath); } }; try { ActivelyVisitedPathsRequested += handler; if (parallelSubdirectories) { Task subdirectoriesTask = EnumerateSubdirectoriesAsync(); await EnumerateFilesAsync(); ActivelyVisitedPathsRequested -= handler; await subdirectoriesTask; } else { await EnumerateFilesAsync(); await EnumerateSubdirectoriesAsync(); } } finally { ActivelyVisitedPathsRequested -= handler; } }
public void CancelEnumeration() { CancellationToken cancellationToken = Interlocked.CompareExchange(ref m_cancellationToken, null, null); cancellationToken?.Cancel(); }
public ContourAnimationInfo getContourAnimations(ContourQuery contourQuery) { List <List <TrendingDataLocation> > frames = GetFramesFromHistorian(contourQuery); PiecewiseLinearFunction colorScale = GetColorScale(contourQuery); Func <double, double> colorFunc = colorScale; // The actual startDate is the timestamp of the // first frame after contourQuery.GetStartDate() DateTime startDate = contourQuery.GetStartDate(); int stepSize = contourQuery.StepSize; int startTimeOffset = (int)Math.Ceiling((startDate - startDate.Date).TotalMinutes / stepSize); startDate = startDate.Date.AddMinutes(startTimeOffset * stepSize); double latDif = frames.Min(frame => frame.Max(location => location.Latitude)) - frames.Min(frame => frame.Min(location => location.Latitude)); double lonDif = frames.Min(frame => frame.Max(location => location.Longitude)) - frames.Min(frame => frame.Min(location => location.Longitude)); double minLat = frames.Min(frame => frame.Min(location => location.Latitude)) - (latDif * 0.1D); double maxLat = frames.Min(frame => frame.Max(location => location.Latitude)) + (latDif * 0.1D); double minLng = frames.Min(frame => frame.Min(location => location.Longitude)) - (lonDif * 0.1D); double maxLng = frames.Min(frame => frame.Max(location => location.Longitude)) + (lonDif * 0.1D); GeoCoordinate topLeft = new GeoCoordinate(maxLat, minLng); GeoCoordinate bottomRight = new GeoCoordinate(minLat, maxLng); GSF.Drawing.Point topLeftPoint = s_crs.Translate(topLeft, contourQuery.Resolution); GSF.Drawing.Point bottomRightPoint = s_crs.Translate(bottomRight, contourQuery.Resolution); topLeftPoint = new GSF.Drawing.Point(Math.Floor(topLeftPoint.X), Math.Floor(topLeftPoint.Y)); bottomRightPoint = new GSF.Drawing.Point(Math.Ceiling(bottomRightPoint.X), Math.Ceiling(bottomRightPoint.Y)); topLeft = s_crs.Translate(topLeftPoint, contourQuery.Resolution); bottomRight = s_crs.Translate(bottomRightPoint, contourQuery.Resolution); int width = (int)(bottomRightPoint.X - topLeftPoint.X + 1); int height = (int)(bottomRightPoint.Y - topLeftPoint.Y + 1); int animationID; string timeZoneID = null; using (AdoDataConnection connection = new AdoDataConnection(connectionstring, typeof(SqlConnection), typeof(SqlDataAdapter))) { connection.ExecuteNonQuery("INSERT INTO ContourAnimation(ColorScaleName, StartTime, EndTime, StepSize) VALUES({0}, {1}, {2}, {3})", contourQuery.ColorScaleName, contourQuery.GetStartDate(), contourQuery.GetEndDate(), contourQuery.StepSize); animationID = connection.ExecuteScalar <int>("SELECT @@IDENTITY"); if (contourQuery.IncludeWeather) { timeZoneID = connection.ExecuteScalar <string>("SELECT Value FROM Setting WHERE Name = 'XDATimeZone'"); } } GSF.Threading.CancellationToken cancellationToken = new GSF.Threading.CancellationToken(); s_cancellationTokens[animationID] = cancellationToken; ProgressCounter progressCounter = new ProgressCounter(frames.Count); s_progressCounters[animationID] = progressCounter; Action <int> createFrame = i => { List <TrendingDataLocation> frame = frames[i]; IDWFunc idwFunction = GetIDWFunction(contourQuery, frame); uint[] pixelData; if (contourQuery.IncludeWeather) { TimeZoneInfo tzInfo = !string.IsNullOrEmpty(timeZoneID) ? TimeZoneInfo.FindSystemTimeZoneById(timeZoneID) : TimeZoneInfo.Local; // Weather data is only available in 5-minute increments DateTime frameTime = TimeZoneInfo.ConvertTimeToUtc(startDate.AddMinutes(stepSize * i), tzInfo); double minutes = (frameTime - frameTime.Date).TotalMinutes; int weatherMinutes = (int)Math.Ceiling(minutes / 5) * 5; NameValueCollection queryString = HttpUtility.ParseQueryString(string.Empty); queryString["service"] = "WMS"; queryString["request"] = "GetMap"; queryString["layers"] = "nexrad-n0r-wmst"; queryString["format"] = "image/png"; queryString["transparent"] = "true"; queryString["version"] = "1.1.1"; queryString["time"] = frameTime.Date.AddMinutes(weatherMinutes).ToString("o"); queryString["height"] = height.ToString(); queryString["width"] = width.ToString(); queryString["srs"] = "EPSG:3857"; GSF.Drawing.Point topLeftProjected = s_crs.Projection.Project(topLeft); GSF.Drawing.Point bottomRightProjected = s_crs.Projection.Project(bottomRight); queryString["bbox"] = string.Join(",", topLeftProjected.X, bottomRightProjected.Y, bottomRightProjected.X, topLeftProjected.Y); string weatherURL = "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi?" + queryString.ToString(); using (WebClient client = new WebClient()) using (MemoryStream stream = new MemoryStream(client.DownloadData(weatherURL))) using (Bitmap bitmap = new Bitmap(stream)) { pixelData = bitmap.ToPixelData(); } } else { pixelData = new uint[width * height]; } if (cancellationToken.IsCancelled) { return; } for (int x = 0; x < width; x++) { if (cancellationToken.IsCancelled) { return; } for (int y = 0; y < height; y++) { if (cancellationToken.IsCancelled) { return; } if (pixelData[y * width + x] > 0) { continue; } GSF.Drawing.Point offsetPixel = new GSF.Drawing.Point(topLeftPoint.X + x, topLeftPoint.Y + y); GeoCoordinate pixelCoordinate = s_crs.Translate(offsetPixel, contourQuery.Resolution); double interpolatedValue = idwFunction(pixelCoordinate.Longitude, pixelCoordinate.Latitude); pixelData[y * width + x] = (uint)colorFunc(interpolatedValue); } } if (cancellationToken.IsCancelled) { return; } using (Bitmap bitmap = BitmapExtensions.FromPixelData(width, pixelData)) using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream, ImageFormat.Png); using (AdoDataConnection connection = new AdoDataConnection(connectionstring, typeof(SqlConnection), typeof(SqlDataAdapter))) { connection.ExecuteNonQuery("INSERT INTO ContourAnimationFrame VALUES({0}, {1}, {2})", animationID, i, stream.ToArray()); } } progressCounter.Increment(); }; Task.Run(() => { ICancellationToken token; ProgressCounter counter; Parallel.For(0, frames.Count, createFrame); s_cancellationTokens.TryRemove(animationID, out token); s_progressCounters.TryRemove(animationID, out counter); if (cancellationToken.IsCancelled) { using (AdoDataConnection connection = new AdoDataConnection(connectionstring, typeof(SqlConnection), typeof(SqlDataAdapter))) { connection.ExecuteNonQuery("DELETE FROM ContourAnimationFrame WHERE ContourAnimationID = {0}", animationID); connection.ExecuteNonQuery("DELETE FROM ContourAnimation WHERE ID = {0}", animationID); } } }); s_cleanUpAnimationOperation.TryRunOnceAsync(); return(new ContourAnimationInfo() { AnimationID = animationID, ColorDomain = colorScale.Domain, ColorRange = colorScale.Range, MinLatitude = bottomRight.Latitude, MaxLatitude = topLeft.Latitude, MinLongitude = topLeft.Longitude, MaxLongitude = bottomRight.Longitude, Infos = frames.Select((frame, index) => new ContourInfo() { Locations = frame, URL = string.Format("./mapService.asmx/getContourAnimationFrame?animation={0}&frame={1}", animationID, index), Date = contourQuery.GetStartDate().AddMinutes(index * contourQuery.StepSize).ToString() }).ToList() }); }
/// <summary> /// Read historian data from server. /// </summary> /// <param name="server">The server to use for the query.</param> /// <param name="instanceName">Name of the archive to be queried.</param> /// <param name="startTime">Start time of query.</param> /// <param name="stopTime">Stop time of query.</param> /// <param name="measurementIDs">Measurement IDs to query - or <c>null</c> for all available points.</param> /// <param name="resolution">Resolution for data query.</param> /// <param name="seriesLimit">Maximum number of points per series.</param> /// <param name="forceLimit">Flag that determines if series limit should be strictly enforced.</param> /// <param name="cancellationToken">Cancellation token for query.</param> /// <returns>Enumeration of <see cref="TrendValue"/> instances read for time range.</returns> public static IEnumerable <TrendValue> GetHistorianData(SnapServer server, string instanceName, DateTime startTime, DateTime stopTime, ulong[] measurementIDs, Resolution resolution, int seriesLimit, bool forceLimit, ICancellationToken cancellationToken = null) { if (cancellationToken == null) { cancellationToken = new CancellationToken(); } if (server == null) { yield break; } // Setting series limit to zero requests full resolution data, which overrides provided parameter if (seriesLimit < 1) { resolution = Resolution.Full; forceLimit = false; } TimeSpan resolutionInterval = resolution.GetInterval(); MatchFilterBase <HistorianKey, HistorianValue> pointFilter = null; HistorianKey key = new HistorianKey(); HistorianValue value = new HistorianValue(); bool subFullResolution = false; // Set data scan resolution if (resolution != Resolution.Full) { subFullResolution = true; BaselineTimeInterval interval = BaselineTimeInterval.Second; if (resolutionInterval.Ticks < Ticks.PerMinute) { interval = BaselineTimeInterval.Second; } else if (resolutionInterval.Ticks < Ticks.PerHour) { interval = BaselineTimeInterval.Minute; } else if (resolutionInterval.Ticks == Ticks.PerHour) { interval = BaselineTimeInterval.Hour; } startTime = startTime.BaselinedTimestamp(interval); stopTime = stopTime.BaselinedTimestamp(interval); } SeekFilterBase <HistorianKey> timeFilter = TimestampSeekFilter.CreateFromRange <HistorianKey>(startTime, stopTime); Dictionary <ulong, DataRow> metadata = null; using (SnapClient connection = SnapClient.Connect(server)) using (ClientDatabaseBase <HistorianKey, HistorianValue> database = connection.GetDatabase <HistorianKey, HistorianValue>(instanceName)) { if (database == null) { yield break; } if (LocalOutputAdapter.Instances.TryGetValue(database.Info?.DatabaseName ?? DefaultInstanceName, out LocalOutputAdapter historianAdapter)) { metadata = historianAdapter?.Measurements; } if (metadata == null) { yield break; } // Setup point ID selections if (measurementIDs != null) { pointFilter = PointIdMatchFilter.CreateFromList <HistorianKey, HistorianValue>(measurementIDs); } else { measurementIDs = metadata.Keys.ToArray(); } Dictionary <ulong, long> pointCounts = new Dictionary <ulong, long>(measurementIDs.Length); Dictionary <ulong, ulong> lastTimes = new Dictionary <ulong, ulong>(measurementIDs.Length); Dictionary <ulong, Tuple <float, float> > extremes = new Dictionary <ulong, Tuple <float, float> >(measurementIDs.Length); ulong pointID, timestamp, resolutionSpan = (ulong)resolutionInterval.Ticks, baseTicks = (ulong)UnixTimeTag.BaseTicks.Value; long pointCount; float pointValue, min = 0.0F, max = 0.0F; foreach (ulong measurementID in measurementIDs) { pointCounts[measurementID] = 0L; } // Start stream reader for the provided time window and selected points using (TreeStream <HistorianKey, HistorianValue> stream = database.Read(SortedTreeEngineReaderOptions.Default, timeFilter, pointFilter)) { while (stream.Read(key, value) && !cancellationToken.IsCancelled) { pointID = key.PointID; timestamp = key.Timestamp; pointCount = pointCounts[pointID]; pointValue = value.AsSingle; if (subFullResolution) { Tuple <float, float> stats = extremes.GetOrAdd(pointID, _ => new Tuple <float, float>(float.MaxValue, float.MinValue)); min = stats.Item1; max = stats.Item2; if (pointValue < min) { min = pointValue; } if (pointValue > max) { max = pointValue; } if (min != float.MaxValue && max != float.MinValue) { pointValue = Math.Abs(max) > Math.Abs(min) ? max : min; } else if (min != float.MaxValue) { pointValue = min; } else if (max != float.MinValue) { pointValue = max; } } if (timestamp - lastTimes.GetOrAdd(pointID, 0UL) > resolutionSpan) { pointCount++; if (forceLimit && pointCount > seriesLimit) { break; } yield return(new TrendValue { ID = (long)pointID, Timestamp = (timestamp - baseTicks) / (double)Ticks.PerMillisecond, Value = pointValue }); lastTimes[pointID] = timestamp; // Reset extremes at each point publication if (subFullResolution) { extremes[pointID] = new Tuple <float, float>(float.MaxValue, float.MinValue); } } else if (subFullResolution) { // Track extremes over interval extremes[pointID] = new Tuple <float, float>(min, max); } pointCounts[pointID] = pointCount; } } } }
/// <summary> /// Initiates enumeration of the watch directories. /// </summary> /// <param name="directory">The directory to be enumerated.</param> public void Enumerate(string directory) { CancellationToken cancellationToken; lock (m_cancellationTokens) { if (m_cancellationTokens.TryGetValue(directory, out cancellationToken)) cancellationToken.Cancel(); cancellationToken = new CancellationToken(); m_cancellationTokens[directory] = cancellationToken; } switch (m_fileProcessor.EnumerationStrategy) { // Sequential enumeration strategy kicks // off all its processing on a single thread case FileEnumerationStrategy.Sequential: m_sequentialEnumerationThread.Push(() => { // Create the enumerable wrappers for file and directory enumeration EnumerableWrapper fileWrapper = new EnumerableWrapper(Directory.EnumerateFiles(directory), cancellationToken); EnumerableWrapper directoryWrapper = new EnumerableWrapper(Directory.EnumerateDirectories(directory), cancellationToken); if (m_isActive.Value) { // If the thread is already active, // push processing into the directory queue m_directoryQueue.Value.Enqueue(() => { m_wrapperStack.Value.Push(() => EnumerateNextFile(fileWrapper)); EnumerateNextDirectory(directoryWrapper); }); } else { // If the thread is inactive, mark it as active // and then begin processing the directory wrapper ActivateThread(); m_wrapperStack.Value.Push(() => EnumerateNextFile(fileWrapper)); EnumerateNextDirectory(directoryWrapper); } }); break; // Parallel processing kicks off new // enumerations on their own thread case FileEnumerationStrategy.ParallelWatchDirectories: case FileEnumerationStrategy.ParallelSubdirectories: m_fileProcessor.m_threadScheduler.CreateThread().Push(() => { EnumerableWrapper fileWrapper = new EnumerableWrapper(Directory.EnumerateFiles(directory), cancellationToken); EnumerableWrapper directoryWrapper = new EnumerableWrapper(Directory.EnumerateDirectories(directory), cancellationToken); m_wrapperStack.Value.Push(() => EnumerateNextFile(fileWrapper)); LogicalThread.CurrentThread.Push(() => EnumerateNextDirectory(directoryWrapper)); ActivateThread(); }); break; } // Initiate the process to clean up the files // in the collection of processed files m_cleanProcessedFilesOperation.RunOnceAsync(); }
/// <summary> /// Creates a new instance of the <see cref="EnumerableWrapper"/> class. /// </summary> /// <param name="enumerable">The enumerable to be wrapped.</param> /// <param name="cancellationToken">The token used to cancel the enumeration operation.</param> public EnumerableWrapper(IEnumerable<string> enumerable, CancellationToken cancellationToken) { if ((object)enumerable == null) throw new ArgumentNullException(nameof(enumerable)); m_enumerable = enumerable; m_enumerator = enumerable.GetEnumerator(); m_cancellationToken = cancellationToken; }
/// <summary> /// Read historian data from server. /// </summary> /// <param name="database">Client database to use for query.</param> /// <param name="startTime">Start time of query.</param> /// <param name="stopTime">Stop time of query.</param> /// <param name="measurementIDs">Measurement IDs to query - or <c>null</c> for all available points.</param> /// <param name="resolution">Resolution for data query.</param> /// <param name="seriesLimit">Maximum number of points per series.</param> /// <param name="forceLimit">Flag that determines if series limit should be strictly enforced.</param> /// <param name="cancellationToken">Cancellation token for query.</param> /// <returns>Enumeration of <see cref="TrendValue"/> instances read for time range.</returns> public static IEnumerable<TrendValue> GetHistorianData(ClientDatabaseBase<HistorianKey, HistorianValue> database, DateTime startTime, DateTime stopTime, ulong[] measurementIDs, Resolution resolution, int seriesLimit, bool forceLimit, ICancellationToken cancellationToken = null) { if ((object)cancellationToken == null) cancellationToken = new CancellationToken(); if ((object)database == null) yield break; TimeSpan resolutionInterval = resolution.GetInterval(); SeekFilterBase<HistorianKey> timeFilter; MatchFilterBase<HistorianKey, HistorianValue> pointFilter = null; HistorianKey key = new HistorianKey(); HistorianValue value = new HistorianValue(); // Set data scan resolution if (resolution == Resolution.Full) { timeFilter = TimestampSeekFilter.CreateFromRange<HistorianKey>(startTime, stopTime); } else { BaselineTimeInterval interval = BaselineTimeInterval.Second; if (resolutionInterval.Ticks < Ticks.PerMinute) interval = BaselineTimeInterval.Second; else if (resolutionInterval.Ticks < Ticks.PerHour) interval = BaselineTimeInterval.Minute; else if (resolutionInterval.Ticks == Ticks.PerHour) interval = BaselineTimeInterval.Hour; startTime = startTime.BaselinedTimestamp(interval); stopTime = stopTime.BaselinedTimestamp(interval); timeFilter = TimestampSeekFilter.CreateFromIntervalData<HistorianKey>(startTime, stopTime, resolutionInterval, new TimeSpan(TimeSpan.TicksPerMillisecond)); } Dictionary<ulong, DataRow> metadata = null; LocalOutputAdapter historianAdapter; if (LocalOutputAdapter.Instances.TryGetValue(database.Info?.DatabaseName ?? DefaultInstanceName, out historianAdapter)) metadata = historianAdapter?.Measurements; if ((object)metadata == null) yield break; // Setup point ID selections if ((object)measurementIDs != null) pointFilter = PointIdMatchFilter.CreateFromList<HistorianKey, HistorianValue>(measurementIDs); else measurementIDs = metadata.Keys.ToArray(); // Start stream reader for the provided time window and selected points Dictionary<ulong, long> pointCounts = new Dictionary<ulong, long>(measurementIDs.Length); Dictionary<ulong, long> intervals = new Dictionary<ulong, long>(measurementIDs.Length); Dictionary<ulong, ulong> lastTimes = new Dictionary<ulong, ulong>(measurementIDs.Length); double range = (stopTime - startTime).TotalSeconds; ulong pointID, timestamp, resolutionSpan = (ulong)resolutionInterval.Ticks, baseTicks = (ulong)UnixTimeTag.BaseTicks.Value; long pointCount; DataRow row; if (resolutionSpan <= 1UL) resolutionSpan = Ticks.PerSecond; if (seriesLimit < 1) seriesLimit = 1; // Estimate total measurement counts per point so decimation intervals for each series can be calculated foreach (ulong measurementID in measurementIDs) { if (resolution == Resolution.Full) pointCounts[measurementID] = metadata.TryGetValue(measurementID, out row) ? (long)(int.Parse(row["FramesPerSecond"].ToString()) * range) : 2; else pointCounts[measurementID] = (long)(range / resolutionInterval.TotalSeconds.NotZero(1.0D)); } foreach (ulong measurementID in pointCounts.Keys) intervals[measurementID] = (pointCounts[measurementID] / seriesLimit).NotZero(1L); lock (database) { TreeStream<HistorianKey, HistorianValue> stream = database.Read(SortedTreeEngineReaderOptions.Default, timeFilter, pointFilter); while (stream.Read(key, value) && !cancellationToken.IsCancelled) { pointID = key.PointID; timestamp = key.Timestamp; pointCount = pointCounts[pointID]; if (pointCount++ % intervals[pointID] == 0 || (!forceLimit && timestamp - lastTimes.GetOrAdd(pointID, 0UL) > resolutionSpan)) yield return new TrendValue { ID = (long)pointID, Timestamp = (timestamp - baseTicks) / (double)Ticks.PerMillisecond, Value = value.AsSingle }; pointCounts[pointID] = pointCount; lastTimes[pointID] = timestamp; } } }