diff --git a/ConformU/AlpacaProtocolTestManager.cs b/ConformU/AlpacaProtocolTestManager.cs index ebdaa26..eeb3364 100644 --- a/ConformU/AlpacaProtocolTestManager.cs +++ b/ConformU/AlpacaProtocolTestManager.cs @@ -136,7 +136,8 @@ public async Task TestAlpacaProtocol() try { - TL.LogMessage("TestAlpacaProtocol", $"Task started"); + // Create a blank line at the start of the console log + Console.WriteLine(""); string clientHostAddress = $"{settings.AlpacaDevice.ServiceType.ToString().ToLowerInvariant()}://{settings.AlpacaDevice.IpAddress}:{settings.AlpacaDevice.IpPort}"; @@ -339,6 +340,9 @@ public async Task TestAlpacaProtocol() } } + // Create a blank line in the console log + Console.WriteLine(""); + // Set the return code to the number of errors + issues returnCode = errorMessages.Count + issueMessages.Count; } diff --git a/ConformU/ConformLogger.cs b/ConformU/ConformLogger.cs index c62934b..5c37add 100644 --- a/ConformU/ConformLogger.cs +++ b/ConformU/ConformLogger.cs @@ -167,7 +167,7 @@ public void SetStatusMessage(string status) public new void LogMessage(string method, string message) { // Write the message to the console - Console.WriteLine($"{method} {message}"); + Console.WriteLine($"{method}{(string.IsNullOrEmpty(method)?"":" ")}{message}"); // Write the message to the log file base.LogMessage(method,message); diff --git a/ConformU/Pages/CheckAlpacaProtocol.razor b/ConformU/Pages/CheckAlpacaProtocol.razor index 3fb580f..0e9971a 100644 --- a/ConformU/Pages/CheckAlpacaProtocol.razor +++ b/ConformU/Pages/CheckAlpacaProtocol.razor @@ -148,6 +148,9 @@ // Clear the screen log screenLog = ""; + // Add a blank line to the console log + Console.WriteLine(""); + // Create a task to run the conformance test conformanceTestTask = new Task(() => { diff --git a/ConformU/Program.cs b/ConformU/Program.cs index 2cda9b2..0433c6c 100644 --- a/ConformU/Program.cs +++ b/ConformU/Program.cs @@ -66,13 +66,15 @@ public static async Task Main(string[] args) // ROOT command var rootCommand = new RootCommand($"Conform Universal {Update.ConformuVersionDisplayString}\r\nCopyright (c) 2021-{DateTime.Now.Year} Peter Simpson\r\n\r\n" + - $"Use conformu [command] -h for information on options available in each command."); + $"Enter conformu [command] -h for information on options available in each command.\r\n\r\n" + + $"If no command or options are provided Conform Universal will start as a GUI application using default parameters." + ); // CONFORMANCE command - var conformanceCommand = new Command("conformance", "Check the specified device for ASCOM device interface conformance"); + var conformanceCommand = new Command("conformance", "Check the specified device for ASCOM device interface conformance with all tests enabled."); // ALPACA PROTOCOL command - var alpacaProtocolCommand = new Command("alpacaprotocol", "Check the specified device for Alpaca protocol conformance"); + var alpacaProtocolCommand = new Command("alpacaprotocol", "Check the specified Alpaca device for Alpaca protocol conformance"); // CONFORMANCE USING SETTINGS command var conformanceUsingSettingsCommand = new Command("conformance-settings", "Check the device configured in the settings file for ASCOM device interface conformance"); @@ -81,7 +83,7 @@ public static async Task Main(string[] args) var alpacaUsingSettingsCommand = new Command("alpacaprotocol-settings", "Check the device configured in the settings file for Alpaca protocol conformance"); // START AS GUI command - var startAsGuiCommand = new Command("gui", "Start Conform Universal as an interactive GUI application with options to change the log file, results file and settings file locations"); + var startAsGuiCommand = new Command("gui", "Start Conform Universal as a GUI application with options to change the log file, results file and settings file locations"); #endregion @@ -176,10 +178,10 @@ public static async Task Main(string[] args) }; // LOG FILE PATH option - Option logFilePathOption = new( + Option logFilePathOption = new( aliases: new string[] { "-p", "--logfilepath" }, - description: "Fully qualified path to the log file folder.\r\n" + - "Overrides the default log file path used by the GUI application, but is ignored when the --logfilename option is used.") + description: "Relative or fully qualified path to the log file folder.\r\n" + + "Overrides the default GUI log file path, but is ignored when the --logfilename option is present.") { ArgumentHelpName = "PATH" }; @@ -191,16 +193,16 @@ public static async Task Main(string[] args) if (OperatingSystem.IsWindows()) { // Get the log file path - string logFilePath = (string)result.GetValueOrDefault(); + DirectoryInfo logFilePath = (DirectoryInfo)result.GetValueOrDefault(); // Check each character to see if it matches an invalid character foreach (char invalidCharacter in Path.GetInvalidPathChars()) { - if (logFilePath.Contains(invalidCharacter)) // Found an invalid character detected + if (logFilePath.FullName.Contains(invalidCharacter)) // Found an invalid character detected { // Set the error message Console.WriteLine($"\r\n{RED_TEXT}Found invalid log file path character: '{invalidCharacter}' ({(int)invalidCharacter:X2}){WHITE_TEXT}"); - result.ErrorMessage = $"\r\nLog file path contains invalid characters: {logFilePath}"; + result.ErrorMessage = $"\r\nLog file path contains invalid characters: {logFilePath.FullName}"; } } } @@ -209,8 +211,8 @@ public static async Task Main(string[] args) // LOG FILE NAME option Option logFileNameOption = new( aliases: new string[] { "-n", "--logfilename" }, - description: "Filename of the log file (fully qualified or relative to the current directory).\r\n" + - "The default GUI log filename and location will be used if this option is omitted.") + description: "Relative or fully qualified name of the log file.\r\n" + + "The default GUI log file name will be used if this option is omitted.") { ArgumentHelpName = "FILENAME" }; @@ -245,21 +247,87 @@ public static async Task Main(string[] args) // RESULTS FILE option Option resultsFileOption = new( aliases: new string[] { "-r", "--resultsfile" }, - description: "Filename of the machine readable results file (fully qualified or relative to the current directory).\r\n" + + description: "Relative or fully qualified name of the machine readable results file.\r\n" + "The default GUI filename and location will be used if this option is omitted.") { ArgumentHelpName = "FILENAME" }; + // Add a validator for the results file name + resultsFileOption.AddValidator(result => + { + // Validate Windows file names + if (OperatingSystem.IsWindows()) + { + // Get the log file name as a FileInfo + FileInfo resultsFileInfo = (FileInfo)result.GetValueOrDefault(); + + // Check each character to see if it matches an invalid character + foreach (char invalidCharacter in Path.GetInvalidFileNameChars()) + { + //Console.WriteLine($"Invalid log file name character: '{invalidCharacter}' ({(int)invalidCharacter:X2})"); + // Ignore colon and backslash, which are marked as invalid in a file name + if ((invalidCharacter != '\\') & (invalidCharacter != ':')) + { + if (resultsFileInfo.FullName.Contains(invalidCharacter)) // Found an invalid character detected + { + // Set the error message + Console.WriteLine($"\r\n{RED_TEXT}Found invalid results file name character: '{invalidCharacter}' ({(int)invalidCharacter:X2}){WHITE_TEXT}"); + result.ErrorMessage = $"\r\nResults file name contains invalid characters: {resultsFileInfo.FullName}"; + } + } + } + } + }); + // SETTINGS FILE option Option settingsFileOption = new( aliases: new string[] { "-s", "--settingsfile" }, - description: "Filename of the settings file to use (fully qualified or relative to the current directory).\r\n" + - "The GUI application settings file will be used if this option is omitted.") + description: "Relative or fully qualified name of the settings file.\r\n" + + "The default GUI application settings file will be used if this option is omitted.") { ArgumentHelpName = "FILENAME" }; + // Add a validator for the settings file name + settingsFileOption.AddValidator(result => + { + // Get the log file name as a FileInfo + FileInfo settingsFileInfo = (FileInfo)result.GetValueOrDefault(); + + // Validate Windows file names + if (OperatingSystem.IsWindows()) + { + bool fileNameOk = true; + + // Check each character to see if it matches an invalid character + foreach (char invalidCharacter in Path.GetInvalidFileNameChars()) + { + //Console.WriteLine($"Invalid log file name character: '{invalidCharacter}' ({(int)invalidCharacter:X2})"); + // Ignore colon and backslash, which are marked as invalid in a file name + if ((invalidCharacter != '\\') & (invalidCharacter != ':')) + { + if (settingsFileInfo.FullName.Contains(invalidCharacter)) // Found an invalid character detected + { + // Set the error message + Console.WriteLine($"\r\n{RED_TEXT}Found invalid settings file name character: '{invalidCharacter}' ({(int)invalidCharacter:X2}){WHITE_TEXT}"); + result.ErrorMessage = $"\r\nSettings file name contains invalid characters: {settingsFileInfo.FullName}"; + fileNameOk = false; + } + } + } + if (!fileNameOk) + return; + } + + // Validate that the file exists + if (!File.Exists(settingsFileInfo.FullName)) + { + //Console.WriteLine($"\r\n{RED_TEXT}Settings file '{settingsFileInfo.FullName}' does not exist.{WHITE_TEXT}"); + result.ErrorMessage = $"\r\nSettings file '{settingsFileInfo.FullName}' does not exist."; + } + }); + #endregion #region Associate arguments and options with commands @@ -274,45 +342,45 @@ public static async Task Main(string[] args) // CONFORMANCE COMMAND - add arguments and options conformanceCommand.AddArgument(deviceArgument); - conformanceCommand.AddOption(settingsFileOption); conformanceCommand.AddOption(logFileNameOption); conformanceCommand.AddOption(logFilePathOption); conformanceCommand.AddOption(resultsFileOption); + conformanceCommand.AddOption(settingsFileOption); conformanceCommand.AddOption(debugDiscoveryOption); conformanceCommand.AddOption(debugStartUpOption); // ALPCA COMMAND - add arguments and options alpacaProtocolCommand.AddArgument(alpacaDeviceArgument); - alpacaProtocolCommand.AddOption(settingsFileOption); alpacaProtocolCommand.AddOption(logFileNameOption); alpacaProtocolCommand.AddOption(logFilePathOption); alpacaProtocolCommand.AddOption(resultsFileOption); + alpacaProtocolCommand.AddOption(settingsFileOption); alpacaProtocolCommand.AddOption(debugDiscoveryOption); alpacaProtocolCommand.AddOption(debugStartUpOption); // ALPCA USING SETTINGS COMMAND - add options - alpacaUsingSettingsCommand.AddOption(settingsFileOption); alpacaUsingSettingsCommand.AddOption(logFileNameOption); alpacaUsingSettingsCommand.AddOption(logFilePathOption); alpacaUsingSettingsCommand.AddOption(resultsFileOption); + alpacaUsingSettingsCommand.AddOption(settingsFileOption); alpacaUsingSettingsCommand.AddOption(debugDiscoveryOption); alpacaUsingSettingsCommand.AddOption(debugStartUpOption); // CONFORMANCE USING SETTINGS COMMAND - add options - conformanceUsingSettingsCommand.AddOption(settingsFileOption); conformanceUsingSettingsCommand.AddOption(logFileNameOption); conformanceUsingSettingsCommand.AddOption(logFilePathOption); conformanceUsingSettingsCommand.AddOption(resultsFileOption); + conformanceUsingSettingsCommand.AddOption(settingsFileOption); conformanceUsingSettingsCommand.AddOption(debugDiscoveryOption); conformanceUsingSettingsCommand.AddOption(debugStartUpOption); // START AS GUI COMMAND - add options startAsGuiCommand.AddOption(logFileNameOption); startAsGuiCommand.AddOption(logFilePathOption); - startAsGuiCommand.AddOption(debugDiscoveryOption); - startAsGuiCommand.AddOption(debugStartUpOption); startAsGuiCommand.AddOption(resultsFileOption); startAsGuiCommand.AddOption(settingsFileOption); + startAsGuiCommand.AddOption(debugDiscoveryOption); + startAsGuiCommand.AddOption(debugStartUpOption); #endregion @@ -559,7 +627,7 @@ static int RootCommandHandler(bool version) /// /// /// - static int StartGuiHandler(FileInfo file, string path, bool debugStartUp, bool debugDiscovery, FileInfo resultsFile, FileInfo settingsFile) + static int StartGuiHandler(FileInfo file, DirectoryInfo path, bool debugStartUp, bool debugDiscovery, FileInfo resultsFile, FileInfo settingsFile) { // Initialise required variables required by several commands InitialiseVariables(file, path, debugStartUp, debugDiscovery, resultsFile, settingsFile); @@ -745,7 +813,7 @@ static int RunAlpacaProtocolTest() /// /// - private static void InitialiseVariables(FileInfo logFileInfo, string logFilePath, bool debugStartUp, bool debugDiscovery, FileInfo resultsFileInfo, FileInfo settingsFileInfo) + private static void InitialiseVariables(FileInfo logFileInfo, DirectoryInfo logPathInfo, bool debugStartUp, bool debugDiscovery, FileInfo resultsFileInfo, FileInfo settingsFileInfo) { argList = new(); @@ -763,8 +831,7 @@ private static void InitialiseVariables(FileInfo logFileInfo, string logFilePath } string logFileName = logFileInfo?.FullName ?? ""; - if (string.IsNullOrEmpty(logFilePath)) - logFilePath = ""; + string logFilePath = logPathInfo?.FullName ?? ""; // Use fully qualified file name if present, otherwise use log file path and relative file name if (Path.IsPathFullyQualified(logFileName)) // Full file name and path provided so split into path and filename and ignore any supplied log file path diff --git a/publish.cmd b/publish.cmd index f648f1c..94c97a8 100644 --- a/publish.cmd +++ b/publish.cmd @@ -8,9 +8,9 @@ cd cd J:\ConformU echo *** Build application -MSBuild "J:\ConformU\ConformU.sln" /p:Configuration=Debug /p:Platform="Any CPU" /t:Restore +MSBuild ConformU.sln /p:Configuration=Debug /p:Platform="Any CPU" /t:Restore cd -MSBuild "J:\ConformU\ConformU.sln" /p:Configuration=Debug /p:Platform="Any CPU" /t:Rebuild +MSBuild ConformU.sln /p:Configuration=Debug /p:Platform="Any CPU" /t:Rebuild echo *** Completed Build echo *** Publishing MacOS Intel silicon @@ -38,12 +38,6 @@ dotnet publish -c Debug /p:Platform="Any CPU" -r linux-x64 --framework net7.0 -- bsdtar -cJf publish/conformu.linux-x64.needsexec.tar.xz -C ConformU\bin\Debug\net7.0\linux-x64\publish\ * echo *** Completed Linux X64 -echo *** Build application -MSBuild "ConformU.sln" /p:Configuration=Debug /p:Platform="Any CPU" /t:Restore -cd -MSBuild "ConformU.sln" /p:Configuration=Debug /p:Platform="Any CPU" /t:Rebuild -echo *** Completed Build - echo *** Publishing Windows 64bit dotnet publish ConformU/ConformU.csproj -c Debug /p:Platform="Any CPU" -r win-x64 --framework net7.0-windows --self-contained true /p:PublishTrimmed=false /p:PublishSingleFile=true -o ./publish/ConformU64/ echo *** Completed 64bit publish