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

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

            var options = new[]
            {
                new Option {
                    Description = "AQSamples connection options:"
                },
                new Option {
                    Key = nameof(context.ServerUrl), Setter = value => context.ServerUrl = value, Getter = () => context.ServerUrl, Description = "AQS server URL"
                },
                new Option {
                    Key = nameof(context.ApiToken), Setter = value => context.ApiToken = value, Getter = () => context.ApiToken, Description = "AQS API Token"
                },

                new Option(), new Option {
                    Description = "Output options:"
                },
                new Option {
                    Key = nameof(context.CsvOutputPath), Setter = value => context.CsvOutputPath = value, Getter = () => context.CsvOutputPath, Description = $"Path to output file [default: ExportedObservations-yyyyMMddHHmmss.csv in the same folder as the EXE]"
                },
                new Option {
                    Key = nameof(context.Overwrite), Setter = value => context.Overwrite = ParseBoolean(value), Getter = () => $"{context.Overwrite}", Description = "Overwrite existing files?"
                },
                new Option {
                    Key = nameof(context.UtcOffset), Setter = value => context.UtcOffset = ParseOffset(value), Getter = () => string.Empty, Description = $"UTC offset for output times [default: Use system timezone, currently {context.UtcOffset:m}]"
                },

                new Option(), new Option {
                    Description = "Cumulative filter options: (ie. AND-ed together). Can be set multiple times."
                },
                new Option {
                    Key = nameof(context.StartTime), Setter = value => context.StartTime = ParseDateTimeOffset(value), Getter = () => string.Empty, Description = "Include observations after this time."
                },
                new Option {
                    Key = nameof(context.EndTime), Setter = value => context.EndTime = ParseDateTimeOffset(value), Getter = () => string.Empty, Description = "Include observations before this time."
                },
                new Option {
                    Key = nameof(context.LocationIds).Singularize(), Setter = value => context.LocationIds.Add(value), Getter = () => string.Empty, Description = "Observations matching these locations."
                },
                new Option {
                    Key = nameof(context.AnalyticalGroupIds).Singularize(), Setter = value => context.AnalyticalGroupIds.Add(value), Getter = () => string.Empty, Description = "Observations matching these analytical groups."
                },
                new Option {
                    Key = nameof(context.ObservedPropertyIds).Singularize(), Setter = value => context.ObservedPropertyIds.Add(value), Getter = () => string.Empty, Description = "Observations matching these observed properties."
                },
                new Option {
                    Key = nameof(context.ProjectIds).Singularize(), Setter = value => context.ProjectIds.Add(value), Getter = () => string.Empty, Description = "Observations matching these projects."
                },
            };

            var usageMessage
                = $"Export observations from an AQUARIUS Samples server."
                  + $"\n"
                  + $"\nusage: {ExeHelper.ExeName} [-option=value] [@optionsFile] ..."
                  + $"\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 is ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

            var helpGuidance = "See /help screen for details.";

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

                if (!match.Success)
                {
                    if (HelpKeyWords.Contains(arg))
                    {
                        throw new ExpectedException(usageMessage);
                    }

                    throw new ExpectedException($"Unknown argument: {arg}\n\n{helpGuidance}");
                }

                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{helpGuidance}");
                }

                option.Setter(value);
            }

            return(context);
        }
예제 #2
0
        private static Context ParseArgs(string[] args)
        {
            var context = new Context();

            var resolvedArgs = InjectOptionsFileByDefault(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.ConfigPath),
                    Setter      = value => context.ConfigPath = value,
                    Getter      = () => context.ConfigPath,
                    Description = $"Path to the JSON configuration file. [default: '{nameof(Config)}.json' in the same folder as the EXE]"
                },
                new Option
                {
                    Key         = nameof(context.CreateMissingTimeSeries),
                    Setter      = value => context.CreateMissingTimeSeries = bool.Parse(value),
                    Getter      = () => $"{context.CreateMissingTimeSeries}",
                    Description = "When true, any missing time-series will be created."
                },
            };

            var usageMessage
                = $"An external processor for calculating total discharge for arbitrary-length events."
                  + $"\n"
                  + $"\nusage: {ExeHelper.ExeName} [-option=value] [@optionsFile] ..."
                  + $"\n"
                  + $"\nSupported -option=value settings (/option=value works too):\n\n  {string.Join("\n  ", options.Select(o => o.UsageText()))}"
                  + $"\n"
                  + $"\nWhen no other command line options are given, the Options.txt file in"
                  + $"\nsame folder as the EXE will be used if it exists."
                  + $"\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."
                ;

            var helpGuidance = "See /help screen for details.";

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

                if (!match.Success)
                {
                    if (HelpKeyWords.Contains(arg))
                    {
                        throw new ExpectedException(usageMessage);
                    }

                    throw new ExpectedException($"Unknown argument: {arg}\n\n{helpGuidance}");
                }

                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{helpGuidance}");
                }

                option.Setter(value);
            }

            return(context);
        }
예제 #3
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 = "The AQTS app server."
                },
                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.SkipConfirmation), Setter = value => context.SkipConfirmation = bool.Parse(value), Getter = () => context.SkipConfirmation.ToString(), Description = "When true, skip the confirmation prompt. '/Y' is a shortcut for this option."
                },
                new Option {
                    Key = nameof(context.DryRun), Setter = value => context.DryRun = bool.Parse(value), Getter = () => context.DryRun.ToString(), Description = "When true, don't make any changes. '/N' is a shortcut for this option."
                },
                new Option {
                    Key = nameof(context.RecreateLocations), Setter = value => context.RecreateLocations = bool.Parse(value), Getter = () => context.RecreateLocations.ToString(), Description = "When true, recreate the location with the same properties."
                },
                new Option {
                    Key = "Location", Setter = value => context.LocationsToDelete.Add(value), Getter = () => string.Join(", ", context.LocationsToDelete), Description = "Locations to delete."
                },
                new Option {
                    Key = "TimeSeries", Setter = value => context.TimeSeriesToDelete.Add(value), Getter = () => string.Join(", ", context.TimeSeriesToDelete), Description = "Time-series to delete."
                },
                new Option {
                    Key = nameof(context.VisitsBefore), Setter = value => context.VisitsBefore = ParseDateTime(value), Getter = () => context.VisitsBefore?.ToString("O"), Description = "Delete all visits in matching locations before and including this date."
                },
                new Option {
                    Key = nameof(context.VisitsAfter), Setter = value => context.VisitsAfter = ParseDateTime(value), Getter = () => context.VisitsAfter?.ToString("O"), Description = "Delete all visits in matching locations after and including this date."
                },
            };

            var usageMessage
                = $"Deletes locations, time-series, and/or field visits from an AQTS server."
                  + $"\n"
                  + $"\nusage: {GetProgramName()} [-option=value] [@optionsFile] [location] [time-series] ..."
                  + $"\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 is ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                  + $"\n"
                  + $"\nLocation deletion:"
                  + $"\n================="
                  + $"\nLocations can be specified by either location identifier or location unique ID."
                  + $"\nLocation identifiers are matched case-insensitively."
                  + $"\nPublish API wildcard expansion of '*' is supported. '02*' will match locations beginning with '02'."
                  + $"\n"
                  + $"\nTime-series deletion:"
                  + $"\n====================="
                  + $"\nTime-series can specified by identifier or by time-series unique ID."
                  + $"\n"
                  + $"\nField-visit deletion:"
                  + $"\n====================="
                  + $"\nWhen the /{nameof(context.VisitsBefore)}= or /{nameof(context.VisitsAfter)}= options are given, all the visits falling within the time-range will be deleted."
                  + $"\nIf no locations are specified when deleting field visits, visits from all locations will be considered."
                ;

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

                if (!match.Success)
                {
                    if (arg.StartsWith("/") || arg.StartsWith("-"))
                    {
                        var keyword = arg.Substring(1);

                        if (keyword.Equals("y", StringComparison.InvariantCultureIgnoreCase))
                        {
                            context.SkipConfirmation = true;
                            continue;
                        }

                        if (keyword.Equals("n", StringComparison.InvariantCultureIgnoreCase))
                        {
                            context.DryRun = true;
                            continue;
                        }

                        throw new ExpectedException($"Unknown argument: {arg}\n\n{usageMessage}");
                    }

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

                    // Otherwise assume it is a location to delete
                    context.LocationsToDelete.Add(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("No AQTS server specified. See /help or -help for details");
            }

            if (string.IsNullOrWhiteSpace(context.Username) || string.IsNullOrWhiteSpace(context.Password))
            {
                throw new ExpectedException("Valid AQTS credentials must be supplied.");
            }

            if (!context.LocationsToDelete.Any() && !context.TimeSeriesToDelete.Any() && !context.VisitsAfter.HasValue && !context.VisitsBefore.HasValue)
            {
                throw new ExpectedException($"You must specify something to delete. See /help or -help for details.");
            }

            return(context);
        }
예제 #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.AssemblyPath), Setter = value => context.AssemblyPath = value, Getter = () => context.AssemblyPath, Description = "Path to the plugin assembly."
                },
                new Option {
                    Key = nameof(context.OutputPath), Setter = value => context.OutputPath = value, Getter = () => context.OutputPath, Description = "Path to packaged output file, usually with a '.report' extension"
                },
                new Option {
                    Key = nameof(context.DeployedFolderName), Setter = value => context.DeployedFolderName = value, Getter = () => context.DeployedFolderName, Description = "Name of the deployed folder"
                },
                new Option {
                    Key = nameof(context.Subfolders), Setter = value => context.Subfolders = bool.Parse(value), Getter = () => context.Subfolders.ToString(), Description = "Include all subfolders"
                },
                new Option {
                    Key = nameof(context.Include), Setter = value => AddToList(value, context.Include), Getter = () => string.Join(", ", context.Include), Description = "Include file or DOS wildcard pattern"
                },
                new Option {
                    Key = nameof(context.Exclude), Setter = value => AddToList(value, context.Exclude), Getter = () => string.Join(", ", context.Exclude), Description = "Exclude file or DOS wildcard pattern"
                },
                new Option {
                    Key = nameof(context.ForceInclude), Setter = value => AddToList(value, context.ForceInclude), Getter = () => string.Join(", ", context.ForceInclude), Description = "Force include file or DOS wildcard pattern"
                },
            };

            var usageMessage
                = $"Package a report plugin into a deployable .report file."
                  + $"\n"
                  + $"\nusage: {GetProgramName()} [-option=value] [@optionsFile] pluginFolderOrAssembly"
                  + $"\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 is ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

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

                if (!match.Success)
                {
                    if (File.Exists(arg))
                    {
                        context.AssemblyPath = arg;
                        continue;
                    }

                    throw new ExpectedException($"Unknown argument: {arg}\n\n{usageMessage}");
                }

                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.IsNullOrEmpty(context.AssemblyPath))
            {
                throw new ExpectedException($"You must specify the /{nameof(context.AssemblyPath)} option.");
            }

            if (string.IsNullOrEmpty(context.OutputPath))
            {
                throw new ExpectedException($"You must specify the /{nameof(context.OutputPath)} option.");
            }

            return(context);
        }
예제 #5
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);
        }
        private void ParseArgs(string[] args)
        {
            var resolvedArgs = args
                               .SelectMany(ResolveOptionsFromFile)
                               .ToArray();

            var options = new[]
            {
                new Option {
                    Key = "Plugin", Setter = value => Context.PluginPath = value, Getter = () => Context.PluginPath, Description = "Path to the plugin assembly to debug"
                },
                new Option {
                    Key = "Data", Setter = value => AddDataPath(Context, value), Getter = () => string.Empty, Description = "Path to the data file to be parsed. Can be set more than once."
                },
                new Option {
                    Key = "Location", Setter = value => Context.LocationIdentifier = value, Getter = () => Context.LocationIdentifier, Description = "Optional location identifier context"
                },
                new Option {
                    Key = "UtcOffset", Setter = value => Context.LocationUtcOffset = TimeSpan.Parse(value), Getter = () => Context.LocationUtcOffset.ToString(), Description = "UTC offset in .NET TimeSpan format."
                },
                new Option {
                    Key = "Json", Setter = value => Context.JsonPath = value, Getter = () => Context.JsonPath, Description = "Optional path to write the appended results as JSON"
                },
                new Option {
                    Key = "ExpectedError", Setter = value => Context.ExpectedError = value, Getter = () => Context.ExpectedError, Description = "Expected error message"
                },
                new Option {
                    Key = "ExpectedStatus", Setter = value => Context.ExpectedStatus = (ParseFileStatus)Enum.Parse(typeof(ParseFileStatus), value, true), Getter = () => Context.ExpectedStatus.ToString(), Description = $"Expected parse status. One of {string.Join(", ", Enum.GetNames(typeof(ParseFileStatus)))}"
                },
            };

            var usageMessage = $"Parse a file using a field data plugin, logging the results."
                               + $"\n"
                               + $"\nusage: {GetProgramName()} [-option=value] ..."
                               + $"\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 is ignored."
                               + $"\n  Comment lines begin with a # or // marker."
            ;

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

                if (!match.Success)
                {
                    throw new ExpectedException($"Unknown argument: {arg}\n\n{usageMessage}");
                }

                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.IsNullOrEmpty(Context.PluginPath))
            {
                throw new ExpectedException("No plugin assembly specified.");
            }

            if (!Context.DataPaths.Any())
            {
                throw new ExpectedException("No data file specified.");
            }
        }
예제 #7
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 = "Export duration configuration: Changes will trigger a full resync:"
                },
                new Option
                {
                    Key         = nameof(context.Config.ExportDurationAttributeName),
                    Setter      = value => context.Config.ExportDurationAttributeName = value,
                    Getter      = () => context.Config.ExportDurationAttributeName,
                    Description = "Name of time-series extended attribute storing the export duration."
                },
                new Option
                {
                    Key         = nameof(context.Config.DefaultExportDurationDays),
                    Setter      = value => context.Config.DefaultExportDurationDays = int.Parse(value),
                    Getter      = () => $"{context.Config.DefaultExportDurationDays}",
                    Description = "Default export duration when the extended attribute value cannot be parsed."
                },

                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.MaximumPointsPerSensor),
                    Setter      = value => context.MaximumPointsPerSensor = int.Parse(value),
                    Getter      = () => context.MaximumPointsPerSensor.ToString(),
                    Description = "The maximum number of points uploaded per SOS sensor"
                },
                new Option
                {
                    Key         = nameof(context.MaximumPollDuration),
                    Setter      = value => context.MaximumPollDuration = TimeSpan.Parse(value, CultureInfo.InvariantCulture),
                    Getter      = () => context.MaximumPollDuration?.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."
                },
                new Option
                {
                    Key         = nameof(context.SosLoginRoute),
                    Setter      = value => context.SosLoginRoute = value,
                    Getter      = () => context.SosLoginRoute,
                    Description = "SOS server login route"
                },
                new Option
                {
                    Key         = nameof(context.SosLogoutRoute),
                    Setter      = value => context.SosLogoutRoute = value,
                    Getter      = () => context.SosLogoutRoute,
                    Description = "SOS server logout route"
                },
            };

            var usageMessage
                = $"Export time-series changes in AQTS time-series to an OGC SOS server."
                  + $"\n"
                  + $"\nusage: {ExeHelper.ExeName} [-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);
        }
예제 #8
0
        private static Context GetContext(string[] args)
        {
            var resolvedArgs = args
                               .SelectMany(ResolveOptionsFromFile)
                               .ToArray();

            var context = new Context();

            var options = new[]
            {
                new Option
                {
                    Key         = nameof(context.ExcelPath),
                    Setter      = value => context.ExcelPath = value,
                    Getter      = () => context.ExcelPath,
                    Description = "Specifies the Excel workbook to split"
                },
                new Option
                {
                    Key         = nameof(context.Sheets),
                    Setter      = value => context.Sheets.Add(value),
                    Description = "Split out the named sheets. [default: All sheets]"
                },
                new Option
                {
                    Key         = nameof(context.OutputPath),
                    Setter      = value => context.OutputPath = value,
                    Getter      = () => context.OutputPath,
                    Description = $"Output path of CSVs (default: {{{Splitter.ExcelPathPattern}}}.{{{Splitter.SheetNamePattern}}}.csv"
                },
                new Option
                {
                    Key         = nameof(context.Overwrite),
                    Setter      = value => context.Overwrite = bool.Parse(value),
                    Getter      = () => $"{context.Overwrite}",
                    Description = "Set to true to overwrite existing files."
                },
                new Option
                {
                    Key         = nameof(context.TrimEmptyColumns),
                    Setter      = value => context.TrimEmptyColumns = bool.Parse(value),
                    Getter      = () => $"{context.TrimEmptyColumns}",
                    Description = "Set to false to retain empty columns at the end of each row."
                },
                new Option
                {
                    Key         = nameof(context.DateTimeFormat),
                    Setter      = value => context.DateTimeFormat = value,
                    Getter      = () => context.DateTimeFormat,
                    Description = "Sets the format of any timestamps, using .NET datetime format [default: ISO8601]"
                },
            };

            var usageMessage
                = $"Extracts all the sheets in an Excel workbook into multiple CSV files"
                  + $"\n"
                  + $"\nusage: {GetProgramName()} [-option=value] [@optionsFile] [location] ..."
                  + $"\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 is ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

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

                if (!match.Success)
                {
                    if (ResolvePositionalArgument(context, arg))
                    {
                        continue;
                    }

                    throw new ExpectedException($"Unknown argument: {arg}\n\n{usageMessage}");
                }

                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);
            }

            ValidateContext(context);

            return(context);
        }
예제 #9
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);
                }
            }
        }
예제 #10
0
        private static Context ParseArgs(string[] args)
        {
            var context = new Context();

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

            var options = new[]
            {
                new Option {
                    Description = "AQUARIUS Samples connection options:"
                },
                new Option
                {
                    Key         = nameof(context.SamplesServer),
                    Setter      = value => context.SamplesServer = value,
                    Getter      = () => context.SamplesServer,
                    Description = "AQS server URL"
                },
                new Option
                {
                    Key         = nameof(context.SamplesApiToken),
                    Setter      = value => context.SamplesApiToken = value,
                    Getter      = () => context.SamplesApiToken,
                    Description = "AQS API Token"
                },

                new Option(), new Option {
                    Description = "AQUARIUS Time-Series connection options:"
                },
                new Option
                {
                    Key         = nameof(context.TimeSeriesServer),
                    Setter      = value => context.TimeSeriesServer = value,
                    Getter      = () => context.TimeSeriesServer,
                    Description = "AQTS server"
                },
                new Option
                {
                    Key         = nameof(context.TimeSeriesUsername),
                    Setter      = value => context.TimeSeriesUsername = value,
                    Getter      = () => context.TimeSeriesUsername,
                    Description = "AQTS username"
                },
                new Option
                {
                    Key         = nameof(context.TimeSeriesPassword),
                    Setter      = value => context.TimeSeriesPassword = value,
                    Getter      = () => context.TimeSeriesPassword,
                    Description = "AQTS password"
                },

                new Option(), new Option {
                    Description = "Export options:"
                },
                new Option
                {
                    Key         = nameof(context.DryRun),
                    Setter      = value => context.DryRun = ParseBoolean(value),
                    Getter      = () => $"{context.DryRun}",
                    Description = "When true, don't export and upload reports, just validate what would be done."
                },
                new Option
                {
                    Key         = nameof(context.ExportTemplateName),
                    Setter      = value => context.ExportTemplateName = value,
                    Getter      = () => context.ExportTemplateName,
                    Description = "The Observation Export Spreadsheet Template to use for all exports."
                },
                new Option
                {
                    Key         = nameof(context.AttachmentFilename),
                    Setter      = value => context.AttachmentFilename = value,
                    Getter      = () => context.AttachmentFilename,
                    Description = $"Filename of the exported attachment."
                },
                new Option
                {
                    Key         = nameof(context.AttachmentTags),
                    Setter      = value => ParseAttachmentTagValue(context, value),
                    Getter      = () => string.Empty,
                    Description = "Uploaded attachments will have these tag values applies, in key:value format."
                },
                new Option
                {
                    Key         = nameof(context.DeleteExistingAttachments),
                    Setter      = value => context.DeleteExistingAttachments = ParseBoolean(value),
                    Getter      = () => $"{context.DeleteExistingAttachments}",
                    Description = "Delete any existing location attachments with the same name."
                },
                new Option
                {
                    Key         = nameof(context.ExportTime),
                    Setter      = value => context.ExportTime = ParseDateTimeOffset(value),
                    Getter      = () => string.Empty,
                    Description = $"The timestamp used for all {{{FilenameGenerator.TimePattern}}} pattern substitutions. [default: The current time]"
                },


                new Option(),
                new Option
                {
                    Description = "Cumulative filter options: (ie. AND-ed together). Can be set multiple times."
                },
                new Option
                {
                    Key         = nameof(context.StartTime),
                    Setter      = value => context.StartTime = ParseDateTimeOffset(value),
                    Getter      = () => string.Empty,
                    Description = "Include observations after this time. [default: Start of record]"
                },
                new Option
                {
                    Key         = nameof(context.EndTime),
                    Setter      = value => context.EndTime = ParseDateTimeOffset(value),
                    Getter      = () => string.Empty,
                    Description = "Include observations before this time. [default: End of record]"
                },
                new Option
                {
                    Key         = nameof(context.LocationIds).Singularize(),
                    Setter      = value => ParseListItem(context.LocationIds, value),
                    Getter      = () => string.Empty,
                    Description = "Observations matching these sampling locations."
                },
                new Option
                {
                    Key         = nameof(context.LocationGroupIds).Singularize(),
                    Setter      = value => ParseListItem(context.LocationGroupIds, value),
                    Getter      = () => string.Empty,
                    Description = "Observations matching these sampling location groups."
                },
                new Option
                {
                    Key         = nameof(context.AnalyticalGroupIds).Singularize(),
                    Setter      = value => ParseListItem(context.AnalyticalGroupIds, value),
                    Getter      = () => string.Empty,
                    Description = "Observations matching these analytical groups."
                },
                new Option
                {
                    Key         = nameof(context.ObservedPropertyIds).Singularize(),
                    Setter      = value => ParseListItem(context.ObservedPropertyIds, value),
                    Getter      = () => string.Empty,
                    Description = "Observations matching these observed properties."
                },
            };

            var usageMessage
                = $"Export observations from AQUARIUS Samples using a spreadsheet template into AQUARIUS Time-Series locations as attachments."
                  + $"\n"
                  + $"\nusage: {ExeHelper.ExeName} [-option=value] [@optionsFile] ..."
                  + $"\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 is ignored."
                  + $"\n  Comment lines begin with a # or // marker."
                ;

            var helpGuidance = "See /help screen for details.";

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

                if (!match.Success)
                {
                    if (HelpKeyWords.Contains(arg))
                    {
                        throw new ExpectedException(usageMessage);
                    }

                    throw new ExpectedException($"Unknown argument: {arg}\n\n{helpGuidance}");
                }

                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{helpGuidance}");
                }

                option.Setter(value);
            }

            return(context);
        }
예제 #11
0
파일: Program.cs 프로젝트: yanxuYX/examples
        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);
            }
        }