Exemple #1
0
        public List <TimeSeriesPoint> GeneratePoints()
        {
            var points = new List <TimeSeriesPoint>();

            var isXValueSelected = !string.IsNullOrWhiteSpace(Context.WaveFormTextX);

            var text = isXValueSelected
                ? Context.WaveFormTextX
                : Context.WaveFormTextY;

            text = text.Trim();

            if (string.IsNullOrEmpty(text))
            {
                throw new ExpectedException($"You must set the /{nameof(Context.WaveFormTextX)}= or /{nameof(Context.WaveFormTextY)}= to a non-empty value.");
            }

            var invalidChars = text
                               .Where(ch => !VectorFont.Symbols.ContainsKey(ch))
                               .ToList();

            if (invalidChars.Any())
            {
                throw new ExpectedException($"Only printable ASCII characters are supported. {"invalid character".ToQuantity(invalidChars.Count)}' detected.");
            }

            var x = 0.0;
            var y = 0.0 + Context.WaveformOffset;

            foreach (var symbol in text.Select(ch => VectorFont.Symbols[ch]))
            {
                var startX = x;
                var startY = y;

                foreach (var point in symbol.Lines.SelectMany(RenderLine))
                {
                    points.Add(new TimeSeriesPoint
                    {
                        Time  = Context.StartTime.PlusTicks(points.Count * Duration.FromTimeSpan(Context.PointInterval).BclCompatibleTicks),
                        Value = isXValueSelected
                            ? startX + Context.WaveformScalar * point.X
                            : startY + Context.WaveformScalar * point.Y,
                        GradeCode  = Context.GradeCode,
                        Qualifiers = Context.Qualifiers
                    });
                }

                x += symbol.Width * Context.WaveformScalar;
            }

            Log.Info($"Generated {PointSummarizer.Summarize(points, "vector-text point")}. Scatter-plot X vs Y to read the message.");

            return(points);
        }
        public void AppendPoints()
        {
            Log.Info(Context.ExecutingFileVersion);

            (Points, Notes) = GetPoints();

            AdjustNotes();

            if (Points.All(p => p.Type != PointType.Gap))
            {
                Points = Points
                         .OrderBy(p => p.Time)
                         .ToList();
            }

            ThrowIfInvalidGapInterval();

            AdjustGradesAndQualifiers(Points);

            if (!string.IsNullOrEmpty(Context.SaveCsvPath))
            {
                new CsvWriter(Context)
                .WritePoints(Points, Notes);

                if (Context.StopAfterSavingCsv)
                {
                    return;
                }
            }

            Log.Info($"Connecting to {Context.Server} ...");

            using (var client = CreateConnectedClient())
            {
                Log.Info($"Connected to {Context.Server} ({client.ServerVersion})");

                ThrowIfGapsNotSupported(client);

                if (Context.CreateMode != CreateMode.Never)
                {
                    new TimeSeriesCreator
                    {
                        Context = Context,
                        Client  = client
                    }.CreateMissingTimeSeries(Context.TimeSeries);
                }

                var timeSeries = client.GetTimeSeriesInfo(Context.TimeSeries);

                var isReflected  = Context.Command == CommandType.Reflected || timeSeries.TimeSeriesType == TimeSeriesType.Reflected;
                var hasTimeRange = isReflected || DeleteCommands.Contains(Context.Command) || Context.Command == CommandType.OverwriteAppend;

                if (hasTimeRange)
                {
                    var timeRange = GetTimeRange();

                    if (Notes.Any(note => note.TimeRange != null && (!timeRange.Contains(note.TimeRange.Value.Start) ||
                                                                     !timeRange.Contains(note.TimeRange.Value.End))))
                    {
                        throw new ExpectedException($"All notes to append must be completely within the {timeRange} interval.");
                    }
                }

                Log.Info(Context.Command == CommandType.DeleteAllPoints
                    ? $"Deleting all existing points from {timeSeries.Identifier} ({timeSeries.TimeSeriesType}) ..."
                    : hasTimeRange
                        ? $"Appending {PointSummarizer.Summarize(Points)} and {"note".ToQuantity(Notes.Count)} within TimeRange={GetTimeRange()} to {timeSeries.Identifier} ({timeSeries.TimeSeriesType}) ..."
                        : $"Appending {PointSummarizer.Summarize(Points)} and {"note".ToQuantity(Notes.Count)} to {timeSeries.Identifier} ({timeSeries.TimeSeriesType}) ...");

                var numberOfPointsAppended = 0;
                var numberOfPointsDeleted  = 0;
                var numberOfNotesAppended  = 0;
                var numberOfNotesDeleted   = 0;
                var stopwatch = Stopwatch.StartNew();

                var pointBatches = GetPointBatches(Points).ToList();
                var isBatched    = pointBatches.Count > 1;
                var batchIndex   = 1;

                foreach (var batch in pointBatches)
                {
                    if (isBatched)
                    {
                        var batchSummary =
                            $"Appending batch #{batchIndex}: {PointSummarizer.Summarize(batch.Points)}";

                        Log.Info(hasTimeRange
                            ? $"{batchSummary} within TimeRange={batch.TimeRange} ..."
                            : $"{batchSummary} ...");
                    }

                    var result = AppendPointBatch(client, timeSeries, batch.Points, batch.TimeRange, isReflected, hasTimeRange);
                    numberOfPointsAppended += result.NumberOfPointsAppended;
                    numberOfPointsDeleted  += result.NumberOfPointsDeleted;
                    ++batchIndex;

                    if (!ValidStatusCodesByWaitMode[Context.Wait].Contains(result.AppendStatus))
                    {
                        throw new ExpectedException($"Unexpected append status={result.AppendStatus}");
                    }
                }

                if (DeleteCommands.Contains(Context.Command))
                {
                    numberOfNotesDeleted += DeleteNotesWithinTimeRange(client, timeSeries, GetTimeRange());
                }
                else
                {
                    numberOfNotesAppended += AppendNotes(client, timeSeries);
                }

                var batchText = isBatched ? $" using {"append".ToQuantity(pointBatches.Count)}" : "";
                var waitText  = Context.Wait ? string.Empty : " (without waiting for appends to complete)";

                Log.Info($"Appended {"point".ToQuantity(numberOfPointsAppended)} and {"note".ToQuantity(numberOfNotesAppended)} (deleting {"point".ToQuantity(numberOfPointsDeleted)} and {"note".ToQuantity(numberOfNotesDeleted)}) in {stopwatch.ElapsedMilliseconds / 1000.0:F1} seconds{batchText}{waitText}.");
            }
        }
        public void WritePoints(List <TimeSeriesPoint> points, List <TimeSeriesNote> notes)
        {
            var timeSeriesIdentifier = CreateTimeSeriesIdentifier();

            var csvPath = Directory.Exists(Context.SaveCsvPath)
                ? Path.Combine(Context.SaveCsvPath, SanitizeFilename($"{timeSeriesIdentifier.Identifier}.{CreatePeriod(Context.SourceQueryFrom, Context.SourceQueryTo)}.csv"))
                : Context.SaveCsvPath;

            Log.Info($"Saving {PointSummarizer.Summarize(points, "extracted point")} to '{csvPath}' ...");

            var dir = Path.GetDirectoryName(csvPath);

            if (!string.IsNullOrEmpty(dir))
            {
                Directory.CreateDirectory(dir);
            }

            var publishNotes = notes
                               .Select(Convert)
                               .ToList();

            var notesLookup = new MetadataLookup <PublishNote>(publishNotes);

            using (var writer = new StreamWriter(csvPath))
            {
                var offsetPattern = OffsetPattern.CreateWithInvariantCulture("m");
                var utcOffsetText = $"UTC{offsetPattern.Format(Context.UtcOffset ?? Offset.Zero)}";
                var period        = CreatePeriod(Context.SourceQueryFrom ?? Instant.MinValue, Context.SourceQueryTo ?? Instant.MaxValue);

                writer.WriteLine($"# {Path.GetFileName(csvPath)} generated by {Context.ExecutingFileVersion}");
                writer.WriteLine($"#");
                writer.WriteLine($"# Time series identifier: {timeSeriesIdentifier.Identifier}");
                writer.WriteLine($"# Location: {timeSeriesIdentifier.LocationIdentifier}");
                writer.WriteLine($"# UTC offset: ({utcOffsetText})");
                writer.WriteLine($"# Value units: {Context.Unit}");
                writer.WriteLine($"# Value parameter: {timeSeriesIdentifier.Parameter}");
                writer.WriteLine($"# Interpolation type: {Context.InterpolationType}");
                writer.WriteLine($"# Time series type: {Context.TimeSeriesType}");
                writer.WriteLine($"#");
                writer.WriteLine($"# Export options: Corrected signal from {period.StartText} to {period.EndText}");
                writer.WriteLine($"#");
                writer.WriteLine($"# CSV data starts at line 15.");
                writer.WriteLine($"#");

                var optionalNotesHeader = Context.SaveNotesMode == SaveNotesMode.WithPoints
                    ? ", Notes"
                    : string.Empty;

                writer.WriteLine($"ISO 8601 UTC, Value, Grade, Qualifiers{optionalNotesHeader}");

                foreach (var point in points)
                {
                    var time = point.Time ?? Instant.MinValue;

                    var line = $"{InstantPattern.ExtendedIso.Format(time)}, {point.Value:G12}, {point.GradeCode}, {FormatQualifiers(point.Qualifiers)}";

                    if (Context.SaveNotesMode == SaveNotesMode.WithPoints)
                    {
                        var pointNotes = string.Join("\r\n", notesLookup.GetMany(time.ToDateTimeOffset()).Select(note => note.NoteText));

                        line += $", {CsvEscapedColumn(pointNotes)}";
                    }

                    writer.WriteLine(line);
                }

                if (Context.SaveNotesMode == SaveNotesMode.SeparateCsv)
                {
                    var notesCsvPath = Path.ChangeExtension(csvPath, ".Notes.csv");

                    Log.Info($"Saving {"extracted note".ToQuantity(notes.Count)} to '{notesCsvPath}' ...");

                    // ReSharper disable once AssignNullToNotNullAttribute
                    using (var notesWriter = new StreamWriter(notesCsvPath))
                    {
                        notesWriter.WriteLine($"# {Path.GetFileName(notesCsvPath)} generated by {Context.ExecutingFileVersion}");
                        notesWriter.WriteLine($"#");
                        notesWriter.WriteLine($"# Time series identifier: {timeSeriesIdentifier.Identifier}");
                        notesWriter.WriteLine($"# Location: {timeSeriesIdentifier.LocationIdentifier}");
                        notesWriter.WriteLine($"# UTC offset: ({utcOffsetText})");
                        notesWriter.WriteLine($"#");
                        notesWriter.WriteLine($"# Export options: Corrected signal notes from {period.StartText} to {period.EndText}");
                        notesWriter.WriteLine($"#");
                        notesWriter.WriteLine($"# CSV data starts at line 11.");
                        notesWriter.WriteLine($"#");
                        notesWriter.WriteLine($"StartTime, EndTime, NoteText");

                        foreach (var note in notes)
                        {
                            if (!note.TimeRange.HasValue)
                            {
                                continue;
                            }

                            notesWriter.WriteLine($"{InstantPattern.ExtendedIso.Format(note.TimeRange.Value.Start)}, {InstantPattern.ExtendedIso.Format(note.TimeRange.Value.End)}, {CsvEscapedColumn(note.NoteText)}");
                        }
                    }
                }
            }
        }