示例#1
0
        private static Context ParseArgs(string[] args)
        {
            var context = new Context();

            var resolvedArgs = args
                               .SelectMany(ResolveOptionsFromFile)
                               .ToArray();

            var options = new[]
            {
                new Option {
                    Description = "Export configuration settings. Changes will trigger a full resync:"
                },
                new Option
                {
                    Key         = nameof(context.Config.AquariusServer),
                    Setter      = value => context.Config.AquariusServer = value,
                    Getter      = () => context.Config.AquariusServer,
                    Description = "AQTS server name"
                },
                new Option
                {
                    Key         = nameof(context.Config.AquariusUsername),
                    Setter      = value => context.Config.AquariusUsername = value,
                    Getter      = () => context.Config.AquariusUsername,
                    Description = "AQTS username"
                },
                new Option
                {
                    Key         = nameof(context.Config.AquariusPassword),
                    Setter      = value => context.Config.AquariusPassword = value,
                    Getter      = () => context.Config.AquariusPassword,
                    Description = "AQTS password"
                },
                new Option
                {
                    Key         = nameof(context.Config.SosServer),
                    Setter      = value => context.Config.SosServer = value,
                    Getter      = () => context.Config.SosServer,
                    Description = "SOS server name"
                },
                new Option
                {
                    Key         = nameof(context.Config.SosUsername),
                    Setter      = value => context.Config.SosUsername = value,
                    Getter      = () => context.Config.SosUsername,
                    Description = "SOS username"
                },
                new Option
                {
                    Key         = nameof(context.Config.SosPassword),
                    Setter      = value => context.Config.SosPassword = value,
                    Getter      = () => context.Config.SosPassword,
                    Description = "SOS password"
                },

                new Option(), new Option {
                    Description = "/Publish/v2/GetTimeSeriesUniqueIdList settings. Changes will trigger a full resync:"
                },
                new Option
                {
                    Key         = nameof(context.Config.LocationIdentifier),
                    Setter      = value => context.Config.LocationIdentifier = value,
                    Getter      = () => context.Config.LocationIdentifier,
                    Description = "Optional location filter."
                },
                new Option
                {
                    Key         = nameof(context.Config.Publish),
                    Setter      = value => context.Config.Publish = string.IsNullOrEmpty(value) ? (bool?)null : bool.Parse(value),
                    Getter      = () => context.Config.Publish?.ToString(),
                    Description = "Optional publish filter."
                },
                new Option
                {
                    Key         = nameof(context.Config.ChangeEventType),
                    Setter      = value => context.Config.ChangeEventType = (ChangeEventType)Enum.Parse(typeof(ChangeEventType), value, true),
                    Getter      = () => context.Config.ChangeEventType?.ToString(),
                    Description = $"Optional change event type filter. One of {string.Join(", ", Enum.GetNames(typeof(ChangeEventType)))}"
                },
                new Option
                {
                    Key         = nameof(context.Config.Parameter),
                    Setter      = value => context.Config.Parameter = value,
                    Getter      = () => context.Config.Parameter,
                    Description = "Optional parameter filter."
                },
                new Option
                {
                    Key         = nameof(context.Config.ComputationIdentifier),
                    Setter      = value => context.Config.ComputationIdentifier = value,
                    Getter      = () => context.Config.ComputationIdentifier,
                    Description = "Optional computation filter."
                },
                new Option
                {
                    Key         = nameof(context.Config.ComputationPeriodIdentifier),
                    Setter      = value => context.Config.ComputationPeriodIdentifier = value,
                    Getter      = () => context.Config.ComputationPeriodIdentifier,
                    Description = "Optional computation period filter."
                },
                new Option
                {
                    Key    = nameof(context.Config.ExtendedFilters),
                    Setter = value =>
                    {
                        var split = value.Split('=');

                        if (split.Length != 2)
                        {
                            throw new ExpectedException($"Can't parse '{value}' as Name=Value extended attribute filter");
                        }

                        context.Config.ExtendedFilters.Add(new ExtendedAttributeFilter
                        {
                            FilterName  = split[0],
                            FilterValue = split[1]
                        });
                    },
                    Getter      = () => string.Empty,
                    Description = "Extended attribute filter in Name=Value format. Can be set multiple times."
                },

                new Option(), new Option {
                    Description = "Aggressive time-series filtering. Changes will trigger a full resync:"
                },
                new Option
                {
                    Key         = nameof(context.Config.TimeSeries),
                    Setter      = value => context.Config.TimeSeries.Add(ParseTimeSeriesFilter(value)),
                    Getter      = () => string.Empty,
                    Description = "Time-series identifier regular expression filter. Can be specified multiple times."
                },
                new Option
                {
                    Key         = "TimeSeriesDescription",
                    Setter      = value => context.Config.TimeSeriesDescriptions.Add(ParseTimeSeriesFilter(value)),
                    Getter      = () => string.Empty,
                    Description = "Time-series description regular expression filter. Can be specified multiple times."
                },
                new Option
                {
                    Key         = nameof(context.Config.Approvals),
                    Setter      = value => context.Config.Approvals.Add(ParseApprovalFilter(value)),
                    Getter      = () => string.Empty,
                    Description = "Filter points by approval level or name. Can be specified multiple times."
                },
                new Option
                {
                    Key         = nameof(context.Config.Grades),
                    Setter      = value => context.Config.Grades.Add(ParseGradeFilter(value)),
                    Getter      = () => string.Empty,
                    Description = "Filter points by grade code or name. Can be specified multiple times."
                },
                new Option
                {
                    Key         = nameof(context.Config.Qualifiers),
                    Setter      = value => context.Config.Qualifiers.Add(ParseQualifierFilter(value)),
                    Getter      = () => string.Empty,
                    Description = "Filter points by qualifier. Can be specified multiple times."
                },

                new Option(),
                new Option {
                    Description = "Maximum time range of points to upload: Changes will trigger a full resync:"
                },
                new Option
                {
                    Key    = nameof(context.Config.MaximumPointDays),
                    Setter = value =>
                    {
                        var components = value.Split('=');
                        if (components.Length == 2)
                        {
                            var periodText = components[0];
                            var daysText   = components[1];

                            var period = (ComputationPeriod)Enum.Parse(typeof(ComputationPeriod), periodText, true);
                            var days   = daysText.Equals("All", StringComparison.InvariantCultureIgnoreCase)
                                ? -1
                                : int.Parse(daysText);

                            context.Config.MaximumPointDays[period] = days;
                        }
                    },
                    Getter = () => "\n    " + string.Join("\n    ", context.Config.MaximumPointDays.Select(kvp =>
                    {
                        var daysText = kvp.Value > 0
                            ? kvp.Value.ToString()
                            : "All";

                        return($"{kvp.Key,-8} = {daysText}");
                    })) + "\n  ",
                    Description = "Days since the last point to upload, in Frequency=Value format."
                },

                new Option(),
                new Option {
                    Description = "Other options: (Changing these values won't trigger a full resync)"
                },
                new Option
                {
                    Key         = nameof(context.ConfigurationName),
                    Setter      = value => context.ConfigurationName = value,
                    Getter      = () => context.ConfigurationName,
                    Description = "The name of the export configuration, to be saved in the AQTS global settings."
                },
                new Option
                {
                    Key         = nameof(context.DryRun),
                    Setter      = value => context.DryRun = bool.Parse(value),
                    Getter      = () => context.DryRun.ToString(),
                    Description = "When true, don't export to SOS. Only log the changes that would have been performed."
                },
                new Option
                {
                    Key         = nameof(context.ForceResync),
                    Setter      = value => context.ForceResync = bool.Parse(value),
                    Getter      = () => context.ForceResync.ToString(),
                    Description = "When true, force a full resync of all time-series."
                },
                new Option
                {
                    Key         = nameof(context.NeverResync),
                    Setter      = value => context.NeverResync = bool.Parse(value),
                    Getter      = () => context.NeverResync.ToString(),
                    Description = "When true, avoid full time-series resync, even when the algorithm recommends it."
                },
                new Option
                {
                    Key         = nameof(context.ChangesSince),
                    Setter      = value => context.ChangesSince = DateTimeOffset.Parse(value),
                    Getter      = () => string.Empty,
                    Description = "The starting changes-since time in ISO 8601 format. Defaults to the saved AQTS global setting value."
                },
                new Option
                {
                    Key         = nameof(context.ApplyRounding),
                    Setter      = value => context.ApplyRounding = bool.Parse(value),
                    Getter      = () => context.ApplyRounding.ToString(),
                    Description = "When true, export the rounded point values."
                },
                new Option
                {
                    Key         = nameof(context.MaximumPointsPerObservation),
                    Setter      = value => context.MaximumPointsPerObservation = int.Parse(value),
                    Getter      = () => context.MaximumPointsPerObservation.ToString(),
                    Description = "The maximum number of points per SOS observation"
                },
                new Option
                {
                    Key         = nameof(context.MaximumExportDuration),
                    Setter      = value => context.MaximumExportDuration = TimeSpan.Parse(value, CultureInfo.InvariantCulture),
                    Getter      = () => context.MaximumExportDuration?.Humanize(2),
                    Description = "The maximum duration before polling AQTS for more changes, in hh:mm:ss format. Defaults to the AQTS global setting."
                },
                new Option
                {
                    Key         = nameof(context.Timeout),
                    Setter      = value => context.Timeout = TimeSpan.Parse(value, CultureInfo.InvariantCulture),
                    Getter      = () => context.Timeout.Humanize(2),
                    Description = "The timeout used for all web requests, in hh:mm:ss format."
                }
            };

            var usageMessage
                = $"Export time-series changes in AQTS time-series to an OGC SOS server."
                  + $"\n"
                  + $"\nusage: {GetProgramName()} [-option=value] [@optionsFile] ..."
                  + $"\n"
                  + $"\nSupported -option=value settings (/option=value works too):\n\n  {string.Join("\n  ", options.Select(o => o.UsageText()))}"
                  + $"\n"
                  + $"\nISO 8601 timestamps use a yyyy'-'mm'-'dd'T'HH':'mm':'ss'.'fffffffzzz format."
                  + $"\n"
                  + $"\n  The 7 fractional seconds digits are optional."
                  + $"\n  The zzz timezone can be 'Z' for UTC, or +HH:MM, or -HH:MM"
                  + $"\n"
                  + $"\n  Eg: 2017-04-01T00:00:00Z represents April 1st, 2017 in UTC."
                  + $"\n"
                  + $"\nUse the @optionsFile syntax to read more options from a file."
                  + $"\n"
                  + $"\n  Each line in the file is treated as a command line option."
                  + $"\n  Blank lines and leading/trailing whitespace are ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

            foreach (var arg in resolvedArgs)
            {
                var match = ArgRegex.Match(arg);

                if (!match.Success)
                {
                    if (HelpKeywords.Contains(arg))
                    {
                        throw new ExpectedException($"Showing help page\n\n{usageMessage}");
                    }

                    throw new ExpectedException($"Unknown command line argument: {arg}");
                }

                var key   = match.Groups["key"].Value.ToLower();
                var value = match.Groups["value"].Value;

                var option =
                    options.FirstOrDefault(o => o.Key != null && o.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));

                if (option == null)
                {
                    throw new ExpectedException($"Unknown -option=value: {arg}\n\n{usageMessage}");
                }

                option.Setter(value);
            }

            if (string.IsNullOrWhiteSpace(context.Config.AquariusServer) ||
                string.IsNullOrEmpty(context.Config.AquariusUsername) ||
                string.IsNullOrEmpty(context.Config.AquariusPassword))
            {
                throw new ExpectedException($"Ensure your AQTS server credentials are set.");
            }

            if (string.IsNullOrWhiteSpace(context.Config.SosServer) ||
                string.IsNullOrEmpty(context.Config.SosUsername) ||
                string.IsNullOrEmpty(context.Config.SosPassword))
            {
                throw new ExpectedException($"Ensure your SOS server credentials are set.");
            }

            return(context);
        }
示例#2
0
        private void ParseArgs(string[] args)
        {
            var resolvedArgs = args
                               .SelectMany(ResolveOptionsFromFile)
                               .ToArray();

            var options = new[]
            {
                new Option {
                    Key = nameof(Context.Server), Setter = value => Context.Server = value, Getter = () => Context.Server, Description = "The AQTS app server from which time-series data will be retrieved."
                },
                new Option {
                    Key = nameof(Context.Username), Setter = value => Context.Username = value, Getter = () => Context.Username, Description = "AQTS username."
                },
                new Option {
                    Key = nameof(Context.Password), Setter = value => Context.Password = value, Getter = () => Context.Password, Description = "AQTS credentials."
                },
                new Option {
                    Key = nameof(Context.TemplatePath), Setter = value => Context.TemplatePath = value, Getter = () => Context.TemplatePath, Description = "Path of the SharpShooter Report template file (*.RST)"
                },
                new Option {
                    Key = nameof(Context.OutputPath), Setter = value => Context.OutputPath = value, Getter = () => Context.OutputPath, Description = "Path to the generated report output. Only PDF output is supported."
                },
                new Option {
                    Key = nameof(Context.LaunchReportDesigner), Setter = value => Context.LaunchReportDesigner = bool.Parse(value), Getter = () => Context.LaunchReportDesigner.ToString(), Description = "When true, launch the SharpShooter Report Designer."
                },
                new Option {
                    Key = "TimeSeries", Setter = value => Context.TimeSeries.Add(ParseTimeSeries(value)), Getter = () => string.Empty, Description = "Load the specified time-series as a dataset."
                },
                new Option {
                    Key = "RatingModel", Setter = value => Context.RatingModels.Add(ParseRatingModel(value)), Getter = () => string.Empty, Description = "Load the specified rating-model as a dataset."
                },
                new Option {
                    Key = "ExternalDataSet", Setter = value => Context.ExternalDataSets.Add(ParseExternalDataSet(value)), Getter = () => string.Empty, Description = "Load the external DataSet XML file."
                },
                new Option {
                    Key = nameof(Context.UploadedReportLocation), Setter = value => Context.UploadedReportLocation = value, Getter = () => Context.UploadedReportLocation, Description = "Upload the generated report to this AQTS location identifier."
                },
                new Option {
                    Key = nameof(Context.UploadedReportTitle), Setter = value => Context.UploadedReportTitle = value, Getter = () => Context.UploadedReportTitle, Description = "Upload the generated report with this title. Defaults to the -OutputPath value."
                },
            };

            var usageMessage
                = $"Run a SharpShooter Reports template with AQTS data."
                  + $"\n"
                  + $"\nusage: {GetProgramName()} [-option=value] [@optionsFile] ..."
                  + $"\n"
                  + $"\nSupported -option=value settings (/option=value works too):\n\n  -{string.Join("\n  -", options.Select(o => o.UsageText()))}"
                  + $"\n"
                  + $"\nRetrieving time-series data from AQTS: (more than one -TimeSeries=value option can be specified)"
                  + $"\n"
                  + $"\n  -TimeSeries=identifierOrUniqueId[,From=date][,To=date][,Unit=outputUnit][,GroupBy=option]"
                  + $"\n"
                  + $"\n     =identifierOrUniqueId - Use either the uniqueId or the <parameter>.<label>@<location> syntax."
                  + $"\n     ,From=date            - Retrieve data from this date. [default: Beginning of record]"
                  + $"\n     ,To=date              - Retrieve data until this date. [default; End of record]"
                  + $"\n     ,Unit=outputUnit      - Convert the values to the unit. [default: The default unit of the time-series]"
                  + $"\n     ,GroupBy=option       - Groups data by {string.Join("|", Enum.GetNames(typeof(GroupBy)))} [default: Year]"
                  + $"\n"
                  + $"\n  Dates specified as yyyy-MM-ddThh:mm:ss.fff. Only the year component is required."
                  + $"\n"
                  + $"\nRetrieving rating model info from AQTS: (more than one -RatingModel=value option can be specified)"
                  + $"\n"
                  + $"\n  -RatingModel=identifierOrUniqueId[,From=date][,To=date][,Unit=outputUnit][,GroupBy=option]"
                  + $"\n"
                  + $"\n     =identifierOrUniqueId - Use either the uniqueId or the <InputParameter>-<OutputParameter>.<label>@<location> syntax."
                  + $"\n     ,From=date            - Retrieve data from this date. [default: Beginning of record]"
                  + $"\n     ,To=date              - Retrieve data until this date. [default: End of record]"
                  + $"\n     ,StepSize=increment   - Set the expanded table step size. [default: 0.1]"
                  + $"\n"
                  + $"\n  Dates specified as yyyy-MM-ddThh:mm:ss.fff. Only the year component is required."
                  + $"\n"
                  + $"\nUsing external data sets: (more than one -ExternalDataSet=value option can be specified)"
                  + $"\n"
                  + $"\n  -ExternalDataSet=pathToXml[,Name=datasetName]"
                  + $"\n"
                  + $"\n     =pathToXml            - A standard .NET DataSet, serialized to XML."
                  + $"\n     ,Name=datasetName     - Override the name of the dataset. [default: The name stored within the XML]"
                  + $"\n"
                  + $"\nUnknown -name=value options will be merged with the appropriate data set and table."
                  + $"\n"
                  + $"\n  Simple -name=value options like -MySetting=MyValue will be added to the Common.CommandLineParameters table."
                  + $"\n"
                  + $"\n  Dotted -name=value options like -ReportParameters.Parameters.Description=MyValue will be merged into the named dataset.table.column."
                  + $"\n"
                  + $"\nUse the @optionsFile syntax to read more options from a file."
                  + $"\n"
                  + $"\n  Each line in the file is treated as a command line option."
                  + $"\n  Blank lines and leading/trailing whitespace is ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

            foreach (var arg in resolvedArgs)
            {
                var match = ArgRegex.Match(arg);

                if (!match.Success)
                {
                    if (HelpKeywords.Contains(arg))
                    {
                        throw new ExpectedException($"Showing help.\n\n{usageMessage}");
                    }

                    throw new ExpectedException($"Unknown argument '{arg}'.");
                }

                var key   = match.Groups["key"].Value.ToLower();
                var value = match.Groups["value"].Value;

                var option =
                    options.FirstOrDefault(o => o.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));

                if (option == null)
                {
                    AddReportParameter(match.Groups["key"].Value, value);
                    continue;
                }

                option.Setter(value);
            }
        }
示例#3
0
        private static void ParseArgsIntoContext(Context context, string[] resolvedArgs)
        {
            var options = new[]
            {
                new Option
                {
                    Key         = nameof(context.Files),
                    Description = "Parse the NEM12 file. Can be set multiple times.",
                    Setter      = value => context.Files.Add(value),
                    Getter      = () => string.Empty,
                },
            };

            var usageMessage
                = $"Converts the NEM12 CSV file into a single CSV row per point."
                  + $"\n"
                  + $"\nusage: {GetProgramName()} [-option=value] [@optionsFile] NEM12File1 NEM12File2 ..."
                  + $"\n"
                  + $"\nIf no NEM12 CSV file is specified, the standard input will be used."
                  + $"\n"
                  + $"\nSupported -option=value settings (/option=value works too):\n\n  {string.Join("\n  ", options.Select(o => o.UsageText()))}"
                  + $"\n"
                  + $"\nUse the @optionsFile syntax to read more options from a file."
                  + $"\n"
                  + $"\n  Each line in the file is treated as a command line option."
                  + $"\n  Blank lines and leading/trailing whitespace are ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

            foreach (var arg in resolvedArgs)
            {
                var match = ArgRegex.Match(arg);

                if (!match.Success)
                {
                    if (HelpKeywords.Contains(arg))
                    {
                        throw new ExpectedException($"Showing help page\n\n{usageMessage}");
                    }

                    if (File.Exists(arg))
                    {
                        context.Files.Add(arg);
                        continue;
                    }

                    throw new ExpectedException($"Unknown command line argument: {arg}");
                }

                var key   = match.Groups["key"].Value.ToLower();
                var value = match.Groups["value"].Value;

                var option =
                    options.FirstOrDefault(o =>
                                           o.Key != null && o.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));

                if (option == null)
                {
                    throw new ExpectedException($"Unknown -option=value: {arg}\n\n{usageMessage}");
                }

                option.Setter(value);
            }
        }
示例#4
0
        private static Context ParseArgs(string[] args)
        {
            var context = new Context();

            var resolvedArgs = args
                               .SelectMany(ResolveOptionsFromFile)
                               .ToArray();

            var options = new[]
            {
                new Option
                {
                    Key         = nameof(context.Server),
                    Setter      = value => context.Server = value,
                    Getter      = () => context.Server,
                    Description = "AQTS server name"
                },
                new Option
                {
                    Key         = nameof(context.Username),
                    Setter      = value => context.Username = value,
                    Getter      = () => context.Username,
                    Description = "AQTS username"
                },
                new Option
                {
                    Key         = nameof(context.Password),
                    Setter      = value => context.Password = value,
                    Getter      = () => context.Password,
                    Description = "AQTS password"
                },
                new Option
                {
                    Key    = nameof(context.SessionToken),
                    Setter = value => context.SessionToken = value,
                    Getter = () => context.SessionToken,
                },
                new Option
                {
                    Key         = nameof(context.LocationIdentifier),
                    Setter      = value => context.LocationIdentifier = value,
                    Getter      = () => context.LocationIdentifier,
                    Description = "Optional location filter."
                },
                new Option
                {
                    Key         = nameof(context.Publish),
                    Setter      = value => context.Publish = bool.Parse(value),
                    Getter      = () => context.Publish?.ToString(),
                    Description = "Optional publish filter."
                },
                new Option
                {
                    Key         = nameof(context.ChangeEventType),
                    Setter      = value => context.ChangeEventType = (ChangeEventType)Enum.Parse(typeof(ChangeEventType), value, true),
                    Getter      = () => context.ChangeEventType?.ToString(),
                    Description = $"Optional change event type filter. One of {string.Join(", ", Enum.GetNames(typeof(ChangeEventType)))}"
                },
                new Option
                {
                    Key         = nameof(context.Parameter),
                    Setter      = value => context.Parameter = value,
                    Getter      = () => context.Parameter,
                    Description = "Optional parameter filter."
                },
                new Option
                {
                    Key         = nameof(context.ComputationIdentifier),
                    Setter      = value => context.ComputationIdentifier = value,
                    Getter      = () => context.ComputationIdentifier,
                    Description = "Optional computation filter."
                },
                new Option
                {
                    Key         = nameof(context.ComputationPeriodIdentifier),
                    Setter      = value => context.ComputationPeriodIdentifier = value,
                    Getter      = () => context.ComputationPeriodIdentifier,
                    Description = "Optional computation period filter."
                },
                new Option
                {
                    Key    = nameof(context.ExtendedFilters),
                    Setter = value =>
                    {
                        var split = value.Split('=');

                        if (split.Length != 2)
                        {
                            throw new ExpectedException($"Can't parse '{value}' as Name=Value extended attribute filter");
                        }

                        context.ExtendedFilters.Add(new ExtendedAttributeFilter
                        {
                            FilterName  = split[0],
                            FilterValue = split[1]
                        });
                    },
                    Getter      = () => string.Empty,
                    Description = "Optional extended attribute filter in Name=Value format. Can be set multiple times."
                },
                new Option
                {
                    Key         = nameof(context.TimeSeries),
                    Setter      = value => context.TimeSeries.Add(value),
                    Getter      = () => string.Empty,
                    Description = "Optional time-series to monitor. Can be set multiple times."
                },
                new Option
                {
                    Key         = nameof(context.ChangesSinceTime),
                    Setter      = value => context.ChangesSinceTime = InstantPattern.ExtendedIsoPattern.Parse(value).GetValueOrThrow(),
                    Getter      = () => string.Empty,
                    Description = "The starting changes-since time in ISO 8601 format. Defaults to 'right now'"
                },
                new Option
                {
                    Key         = nameof(context.PollInterval),
                    Setter      = value => context.PollInterval = value.ToUpperInvariant().ParseDuration(),
                    Getter      = () => context.PollInterval.SerializeToString(),
                    Description = "The polling interval in ISO 8601 Duration format."
                },
                new Option
                {
                    Key         = nameof(context.MaximumChangeCount),
                    Setter      = value => context.MaximumChangeCount = int.Parse(value),
                    Getter      = () => context.MaximumChangeCount.ToString(),
                    Description = "When greater than 0, exit after detecting this many changed time-series."
                },
                new Option
                {
                    Key         = nameof(context.AllowQuickPolling),
                    Setter      = value => context.AllowQuickPolling = bool.Parse(value),
                    Getter      = () => context.AllowQuickPolling.ToString(),
                    Description = "Allows very quick polling. Good for testing, bad for production."
                },
                new Option
                {
                    Key         = nameof(context.SavedChangesSinceJson),
                    Setter      = value => context.SavedChangesSinceJson = value,
                    Getter      = () => context.SavedChangesSinceJson,
                    Description = $"Loads the /{nameof(context.ChangesSinceTime)} value from this JSON file."
                },
                new Option
                {
                    Key         = nameof(context.DetectedChangesCsv),
                    Setter      = value => context.DetectedChangesCsv = value,
                    Getter      = () => context.DetectedChangesCsv,
                    Description = $"When set, save all detected changes to this CSV file and exit."
                },
            };

            var usageMessage
                = $"Monitor time-series changes in an AQTS time-series."
                  + $"\n"
                  + $"\nusage: {GetProgramName()} [-option=value] [@optionsFile] [location] [timeSeriesIdentifierOrGuid] ..."
                  + $"\n"
                  + $"\nSupported -option=value settings (/option=value works too):\n\n  -{string.Join("\n  -", options.Select(o => o.UsageText()))}"
                  + $"\n"
                  + $"\nISO 8601 timestamps use a yyyy'-'mm'-'dd'T'HH':'mm':'ss'.'fffffffzzz format."
                  + $"\n"
                  + $"\n  The 7 fractional seconds digits are optional."
                  + $"\n  The zzz timezone can be 'Z' for UTC, or +HH:MM, or -HH:MM"
                  + $"\n"
                  + $"\n  Eg: 2017-04-01T00:00:00Z represents April 1st, 2017 in UTC."
                  + $"\n"
                  + $"\nISO 8601 durations use a 'PT'[nnH][nnM][nnS] format."
                  + $"\n"
                  + $"\n  Only the required components are needed."
                  + $"\n"
                  + $"\n  Eg: PT5M represents 5 minutes."
                  + $"\n      PT90S represents 90 seconds (1.5 minutes)"
                  + $"\n"
                  + $"\nUse the @optionsFile syntax to read more options from a file."
                  + $"\n"
                  + $"\n  Each line in the file is treated as a command line option."
                  + $"\n  Blank lines and leading/trailing whitespace is ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

            foreach (var arg in resolvedArgs)
            {
                var match = ArgRegex.Match(arg);

                if (!match.Success)
                {
                    if (HelpKeywords.Contains(arg))
                    {
                        throw new ExpectedException($"Showing help page\n\n{usageMessage}");
                    }

                    // Try positional arguments: [locationIdentifier] [timeSeriesIdentifier]
                    if (Guid.TryParse(arg, out var _))
                    {
                        context.TimeSeries.Add(arg);
                        continue;
                    }

                    if (TimeSeriesIdentifier.TryParse(arg, out var _))
                    {
                        context.TimeSeries.Add(arg);
                        continue;
                    }

                    if (arg.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase))
                    {
                        context.SavedChangesSinceJson = arg;
                        continue;
                    }

                    if (arg.EndsWith(".csv", StringComparison.InvariantCultureIgnoreCase))
                    {
                        context.DetectedChangesCsv = arg;
                        continue;
                    }

                    context.LocationIdentifier = arg;
                    continue;
                }

                var key   = match.Groups["key"].Value.ToLower();
                var value = match.Groups["value"].Value;

                var option =
                    options.FirstOrDefault(o => o.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));

                if (option == null)
                {
                    throw new ExpectedException($"Unknown -option=value: {arg}\n\n{usageMessage}");
                }

                option.Setter(value);
            }

            if (string.IsNullOrWhiteSpace(context.Server))
            {
                throw new ExpectedException($"A /{nameof(context.Server)} option is required.\n\n{usageMessage}");
            }

            return(context);
        }
示例#5
0
        private void ParseArgs(string[] args)
        {
            var resolvedArgs = args
                               .SelectMany(ResolveOptionsFromFile)
                               .ToArray();

            var options = new[]
            {
                new Option {
                    Description = "AQUARIUS Time-Series connection options:"
                },
                new Option
                {
                    Key           = nameof(Context.Server),
                    Setter        = value => Context.Server = value,
                    Getter        = () => Context.Server
                    , Description = "The AQTS app server from which time-series data will be retrieved."
                },
                new Option
                {
                    Key         = nameof(Context.Username),
                    Setter      = value => Context.Username = value,
                    Getter      = () => Context.Username,
                    Description = "AQTS username."
                },
                new Option
                {
                    Key         = nameof(Context.Password),
                    Setter      = value => Context.Password = value,
                    Getter      = () => Context.Password,
                    Description = "AQTS credentials."
                },

                new Option(), new Option {
                    Description = "SharpShooter Reports options:"
                },
                new Option
                {
                    Key         = nameof(Context.TemplatePath),
                    Setter      = value => Context.TemplatePath = value,
                    Getter      = () => Context.TemplatePath,
                    Description = "Path of the SharpShooter Reports template (*.RST) or Aquarius Report template (*.ART) file."
                },
                new Option
                {
                    Key         = nameof(Context.OutputPath),
                    Setter      = value => Context.OutputPath = value,
                    Getter      = () => Context.OutputPath,
                    Description = "Path to the generated report output. Only PDF output is supported."
                },
                new Option
                {
                    Key         = nameof(Context.LaunchReportDesigner),
                    Setter      = value => Context.LaunchReportDesigner = bool.Parse(value),
                    Getter      = () => Context.LaunchReportDesigner.ToString(),
                    Description = "When true, launch the SharpShooter Report Designer."
                },

                new Option(), new Option {
                    Description = "Dataset options:"
                },
                new Option
                {
                    Key         = nameof(Context.QueryFrom),
                    Setter      = value => Context.QueryFrom = value,
                    Getter      = () => Context.QueryFrom,
                    Description = "The starting point for all time-series. Can be overriden by individual series. [default: Beginning of record]"
                },
                new Option
                {
                    Key         = nameof(Context.QueryTo),
                    Setter      = value => Context.QueryTo = value,
                    Getter      = () => Context.QueryTo,
                    Description = "The ending point for all time-series. Can be overriden by individual series. [default: End of record]"
                },
                new Option
                {
                    Key         = nameof(Context.GroupBy),
                    Setter      = value => Context.GroupBy = ParseEnum <GroupBy>(value),
                    Getter      = () => $"{Context.GroupBy}",
                    Description = $"The grouping for all time-series. One of {string.Join(", ", Enum.GetNames(typeof(GroupBy)))}. Can be overriden by individual series."
                },
                new Option
                {
                    Key         = "TimeSeries",
                    Setter      = value => Context.TimeSeries.Add(ParseTimeSeries(value)),
                    Getter      = () => string.Empty,
                    Description = "Load the specified time-series as a dataset."
                },
                new Option
                {
                    Key         = "RatingModel",
                    Setter      = value => Context.RatingModels.Add(ParseRatingModel(value)),
                    Getter      = () => string.Empty,
                    Description = "Load the specified rating-model as a dataset."
                },
                new Option
                {
                    Key         = "ExternalDataSet",
                    Setter      = value => Context.ExternalDataSets.Add(ParseExternalDataSet(value)),
                    Getter      = () => string.Empty,
                    Description = "Load the external DataSet XML file."
                },

                new Option(), new Option {
                    Description = "Report uploading options:"
                },
                new Option
                {
                    Key         = nameof(Context.UploadedReportLocation),
                    Setter      = value => Context.UploadedReportLocation = value,
                    Getter      = () => Context.UploadedReportLocation,
                    Description = "Upload the generated report to this AQTS location identifier. If empty, no report will be uploaded."
                },
                new Option
                {
                    Key         = nameof(Context.UploadedReportTitle),
                    Setter      = value => Context.UploadedReportTitle = value,
                    Getter      = () => Context.UploadedReportTitle,
                    Description = $"Upload the generated report with this title. Defaults to the -{nameof(Context.OutputPath)} base filename."
                },
            };

            var usageMessage
                = $"Run a SharpShooter Reports template with AQTS data."
                  + $"\n"
                  + $"\nusage: {GetProgramName()} [-option=value] [@optionsFile] ..."
                  + $"\n"
                  + $"\nSupported -option=value settings (/option=value works too):\n\n  {string.Join("\n  ", options.Select(o => o.UsageText()))}"
                  + $"\n"
                  + $"\nRetrieving time-series data from AQTS: (more than one -TimeSeries=value option can be specified)"
                  + $"\n"
                  + $"\n  -TimeSeries=identifierOrUniqueId[,From=date][,To=date][,Unit=outputUnit][,GroupBy=option][,DataSetName=dataSetName]"
                  + $"\n"
                  + $"\n     =identifierOrUniqueId     - Use either the uniqueId or the <parameter>.<label>@<location> syntax."
                  + $"\n     ,From=date                - Retrieve data from this date. [default: Beginning of record]"
                  + $"\n     ,To=date                  - Retrieve data until this date. [default: End of record]"
                  + $"\n     ,Unit=outputUnit          - Convert the values to the unit. [default: The default unit of the time-series]"
                  + $"\n     ,GroupBy=option           - Groups data by {string.Join("|", Enum.GetNames(typeof(GroupBy)))} [default: {Context.GroupBy}]"
                  + $"\n     ,DataSetName=datasetName  - Override the name of the dataset. [default: 'TimeSeries#' where # is the 1-based index of the time-series]"
                  + $"\n"
                  + $"\n  Use the -TimeSeries4=... or -TimeSeries[4]=... syntax to force the dataset name to a specific index."
                  + $"\n"
                  + $"\n  Dates specified as yyyy-MM-ddThh:mm:ss.fff. Only the year component is required."
                  + $"\n"
                  + $"\nRetrieving rating model info from AQTS: (more than one -RatingModel=value option can be specified)"
                  + $"\n"
                  + $"\n  -RatingModel=identifierOrUniqueId[,From=date][,To=date][,Unit=outputUnit][,GroupBy=option][,DataSetName=dataSetName]"
                  + $"\n"
                  + $"\n     =identifierOrUniqueId     - Use either the uniqueId or the <InputParameter>-<OutputParameter>.<label>@<location> syntax."
                  + $"\n     ,From=date                - Retrieve data from this date. [default: Beginning of record]"
                  + $"\n     ,To=date                  - Retrieve data until this date. [default: End of record]"
                  + $"\n     ,StepSize=increment       - Set the expanded table step size. [default: 0.1]"
                  + $"\n     ,DataSetName=datasetName  - Override the name of the dataset. [default: 'RatingCurve#' where # is the 1-based index of the rating model]"
                  + $"\n"
                  + $"\n  Use the -RatingModel4=... or -RatingModel[4]=... syntax to force the dataset name to a specific index."
                  + $"\n"
                  + $"\n  Dates specified as yyyy-MM-ddThh:mm:ss.fff. Only the year component is required."
                  + $"\n"
                  + $"\nUsing external data sets: (more than one -ExternalDataSet=value option can be specified)"
                  + $"\n"
                  + $"\n  -ExternalDataSet=pathToXml[,DataSetName=dataSetName]"
                  + $"\n"
                  + $"\n     =pathToXml                - A standard .NET DataSet, serialized to XML."
                  + $"\n     ,DataSetName=datasetName  - Override the name of the dataset. [default: The name stored within the XML]"
                  + $"\n"
                  + $"\nUnknown -name=value options will be merged with the appropriate data set and table."
                  + $"\n"
                  + $"\n  Simple -name=value options like -MySetting=MyValue will be added to the Common.CommandLineParameters table."
                  + $"\n"
                  + $"\n  Dotted -name=value options like -ReportParameters.Parameters.Description=MyValue will be merged into the named dataset.table.column."
                  + $"\n"
                  + $"\nUse the @optionsFile syntax to read more options from a file."
                  + $"\n"
                  + $"\n  Each line in the file is treated as a command line option."
                  + $"\n  Blank lines and leading/trailing whitespace is ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

            foreach (var arg in resolvedArgs)
            {
                var match = ArgRegex.Match(arg);

                if (!match.Success)
                {
                    if (HelpKeywords.Contains(arg))
                    {
                        throw new ExpectedException($"Showing help.\n\n{usageMessage}");
                    }

                    throw new ExpectedException($"Unknown argument '{arg}'.");
                }

                var key   = match.Groups["key"].Value;
                var value = match.Groups["value"].Value;

                var option = options.FirstOrDefault(o => o.Key != null && o.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));

                if (option != null)
                {
                    option.Setter(value);
                    continue;
                }

                match = IndexedDataSetRegex.Match(key);

                if (!match.Success)
                {
                    AddReportParameter(key, value);
                    continue;
                }

                var category = match.Groups["category"].Value;
                var index    = int.Parse(match.Groups["index"].Value);

                if (index <= 0)
                {
                    throw new ExpectedException($"'{arg}' has an index of {index}. A value greater than zero is required.");
                }

                if (category.StartsWith(nameof(Context.TimeSeries), StringComparison.InvariantCultureIgnoreCase))
                {
                    var timeSeries = ParseTimeSeries(value);

                    ForceDataSetName(timeSeries, $"TimeSeries{index}");

                    Context.TimeSeries.Add(timeSeries);
                }
                else
                {
                    var ratingModel = ParseRatingModel(value);

                    ForceDataSetName(ratingModel, $"RatingCurve{index}");

                    Context.RatingModels.Add(ratingModel);
                }
            }
        }
示例#6
0
        private static void ParseArgsIntoContext(Context context, string[] resolvedArgs)
        {
            var options = new[]
            {
                new Option {
                    Description = "https://waterwatch.io credentials"
                },
                new Option
                {
                    Key         = nameof(context.WaterWatchOrgId),
                    Description = "WaterWatch.io organisation Id",
                    Setter      = value => context.WaterWatchOrgId = value,
                    Getter      = () => context.WaterWatchOrgId,
                },
                new Option
                {
                    Key         = nameof(context.WaterWatchApiKey),
                    Description = "WaterWatch.io API key",
                    Setter      = value => context.WaterWatchApiKey = value,
                    Getter      = () => context.WaterWatchApiKey,
                },
                new Option
                {
                    Key         = nameof(context.WaterWatchApiToken),
                    Description = "WaterWatch.io API token",
                    Setter      = value => context.WaterWatchApiToken = value,
                    Getter      = () => context.WaterWatchApiToken,
                },

                new Option(), new Option {
                    Description = "Configuration options"
                },
                new Option
                {
                    Key         = nameof(context.OutputMode),
                    Description =
                        $"Measurement value output mode. One of {string.Join(", ", Enum.GetNames(typeof(OutputMode)))}.",
                    Setter = value => context.OutputMode = (OutputMode)Enum.Parse(typeof(OutputMode), value, true),
                    Getter = () => context.OutputMode.ToString(),
                },
                new Option
                {
                    Key         = nameof(context.OutputDivisor),
                    Description = "Divisor applied to all output values.",
                    Setter      = value => context.OutputDivisor = double.Parse(value),
                    Getter      = () => context.OutputDivisor.ToString("G"),
                },
                new Option
                {
                    Key         = nameof(context.SaveStatePath),
                    Description = "Path to persisted state file",
                    Setter      = value => context.SaveStatePath = value,
                    Getter      = () => context.SaveStatePath,
                },
                new Option
                {
                    Key         = nameof(context.SyncFromUtc),
                    Description = "Optional UTC sync time. [default: last known sensor time]",
                    Setter      = value => context.SyncFromUtc = ParseDateTime(value),
                },
                new Option
                {
                    Key         = nameof(context.NewSensorSyncDays),
                    Description = "Number of days to sync data when a new sensor is detected.",
                    Setter      = value => context.NewSensorSyncDays = int.Parse(value),
                    Getter      = () => context.NewSensorSyncDays.ToString(),
                },

                new Option(), new Option {
                    Description = "Sensor filtering options"
                },
                new Option
                {
                    Key         = "SensorName",
                    Description = "Sensor name regular expression filter. Can be specified multiple times.",
                    Setter      = value => context.SensorNameFilters.Add(ParseRegexFilter(value))
                },
                new Option
                {
                    Key         = "SensorSerial",
                    Description = "Sensor serial number regular expression filter. Can be specified multiple times.",
                    Setter      = value => context.SensorSerialFilters.Add(ParseRegexFilter(value))
                },
            };

            var usageMessage
                = $"Extract the latest sensor readings from a https://waterwatch.io account"
                  + $"\n"
                  + $"\nusage: {GetProgramName()} [-option=value] [@optionsFile] ..."
                  + $"\n"
                  + $"\nSupported -option=value settings (/option=value works too):\n\n  {string.Join("\n  ", options.Select(o => o.UsageText()))}"
                  + $"\n"
                  + $"\nSupported /{nameof(context.SyncFromUtc)} date formats:"
                  + $"\n"
                  + $"\n  {string.Join("\n  ", SupportedDateFormats)}"
                  + $"\n"
                  + $"\nUse the @optionsFile syntax to read more options from a file."
                  + $"\n"
                  + $"\n  Each line in the file is treated as a command line option."
                  + $"\n  Blank lines and leading/trailing whitespace are ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

            foreach (var arg in resolvedArgs)
            {
                var match = ArgRegex.Match(arg);

                if (!match.Success)
                {
                    if (HelpKeywords.Contains(arg))
                    {
                        throw new ExpectedException($"Showing help page\n\n{usageMessage}");
                    }

                    if (File.Exists(arg))
                    {
                        // This is the magic which allows the preprocessor to be used in AQUARIUS DAS and EnviroSCADA 2018.1-or-earlier.
                        // Those products require that a preprocessor has one and only one argument, which is a "script file".
                        // This recursive call interprets any existing file as an @options.txt argument list.
                        // This is not necessary for EnviroSCADA 2019.1+ or Connect, since both of those allow arbitrary preprocessor command line arguments.
                        ParseArgsIntoContext(context, LoadArgsFromFile(arg).ToArray());
                        continue;
                    }

                    throw new ExpectedException($"Unknown command line argument: {arg}");
                }

                var key   = match.Groups["key"].Value.ToLower();
                var value = match.Groups["value"].Value;

                var option =
                    options.FirstOrDefault(o =>
                                           o.Key != null && o.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));

                if (option == null)
                {
                    throw new ExpectedException($"Unknown -option=value: {arg}\n\n{usageMessage}");
                }

                option.Setter(value);
            }
        }