Skip to content

Commit

Permalink
chore([no ci]/CLI.Installer): Sketch out install process
Browse files Browse the repository at this point in the history
  • Loading branch information
densogiaichned committed Feb 18, 2024
1 parent 3d9d0d1 commit e2270c6
Show file tree
Hide file tree
Showing 18 changed files with 250 additions and 19 deletions.
12 changes: 12 additions & 0 deletions src/TcHaxx.Snappy.CLI.Installer/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace TcHaxx.Snappy.CLI.Installer;
public static class Constants
{
public const string TC31_PROFILES_DIRECTORY = @"Components\Plc\Profiles";
public const string TC31_REPTOOL_EXE = @"Components\Plc\Common\RepTool.exe";
public const string TC31_GLOB_PROFILE = "TwinCAT PLC Control_Build_*.profile";

public const string DEFAULT_OPTION_TCPROFILE = "latest";
public const string DEFAULT_OPTION_TOOLSPATH = @"%USERPROFILE%\.dotnet\tools\.store\tchaxx.snappy.cli";

public static readonly int REPTOOL_EXE_TIMEOUT_MS = 180_000;
}
9 changes: 9 additions & 0 deletions src/TcHaxx.Snappy.CLI.Installer/IInstallerService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Serilog;
using TcHaxx.Snappy.CLI.Installer.Options;
using TcHaxx.Snappy.Common;

namespace TcHaxx.Snappy.CLI.Installer;
public interface IInstallerService
{
public Task<ExitCodes> Install(IInstallerOptions options, ILogger? logger);
}
24 changes: 24 additions & 0 deletions src/TcHaxx.Snappy.CLI.Installer/InstallerService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Serilog;
using TcHaxx.Snappy.CLI.Installer.Options;
using TcHaxx.Snappy.Common;

namespace TcHaxx.Snappy.CLI.Installer;
public class InstallerService : IInstallerService
{
public async Task<ExitCodes> Install(IInstallerOptions options, ILogger? logger)
{

var tcProfile = TcProfile.GetTwinCatProfile(options, logger);
if (tcProfile is null)
{
return ExitCodes.E_ERROR;
}


var expandedPath = Environment.ExpandEnvironmentVariables(options.ToolsPath);
var sourceDirectoryInfo = new DirectoryInfo(expandedPath ?? options.ToolsPath);

var exitCode = await RepToolProcess.RunRepToolAsync(tcProfile, sourceDirectoryInfo, logger);
return exitCode;
}
}
9 changes: 9 additions & 0 deletions src/TcHaxx.Snappy.CLI.Installer/Options/DefaultOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace TcHaxx.Snappy.CLI.Installer.Options;
internal class DefaultOptions : IInstallerOptions
{
///<inheritdoc/>
public string TcProfile { get; init; } = Constants.DEFAULT_OPTION_TCPROFILE;

///<inheritdoc/>
public string ToolsPath { get; init; } = Constants.DEFAULT_OPTION_TOOLSPATH;
}
15 changes: 15 additions & 0 deletions src/TcHaxx.Snappy.CLI.Installer/Options/IInstallerOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace TcHaxx.Snappy.CLI.Installer.Options;

public interface IInstallerOptions
{
/// <summary>
/// TwinCAT profile to use, i.e "latest" or specific version "TwinCAT PLC Control_Build_4024.54"
/// </summary>
public string TcProfile { get; init; }

/// <summary>
/// Directory, where Dotnet global tools are installed, e.g. %USERPROFILE%\.dotnet\tools.
/// </summary>
public string ToolsPath { get; init; }

}
11 changes: 11 additions & 0 deletions src/TcHaxx.Snappy.CLI.Installer/RegistryHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Microsoft.Win32;

namespace TcHaxx.Snappy.CLI.Installer;
internal static class RegistryHelper
{
internal static string GetTwincatInstallDirectory()
{
var regKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Beckhoff\\TwinCAT3\\3.1");
return (regKey?.GetValue("InstallDir") as string) ?? string.Empty;
}
}
69 changes: 69 additions & 0 deletions src/TcHaxx.Snappy.CLI.Installer/RepToolProcess.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Diagnostics;
using Serilog;
using TcHaxx.Snappy.Common;

namespace TcHaxx.Snappy.CLI.Installer;
internal static class RepToolProcess
{
internal static async Task<ExitCodes> RunRepToolAsync(TwincatProfile tcProfile, DirectoryInfo sourceDirectory, ILogger? logger)
{
var tcDir = RegistryHelper.GetTwincatInstallDirectory();
if (string.IsNullOrWhiteSpace(tcDir))
{
logger?.Error("Couldn't read TwinCAT installation directory from Registry.");
return ExitCodes.E_ERROR;
}

var repToolExe = Path.Join(tcDir, Constants.TC31_REPTOOL_EXE);
if (!File.Exists(repToolExe))
{
throw new FileNotFoundException("RepTool.exe doesn't exist.", repToolExe);
}

try
{
using var process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = repToolExe,
Arguments = $"--profile='{tcProfile.Profile}' --installLibsRecursNoOverwrite \"{sourceDirectory.FullName}\"",
UseShellExecute = false,
RedirectStandardInput = false,
RedirectStandardError = true,
RedirectStandardOutput = true
};

process.OutputDataReceived += (sender, args) =>
{
if (string.IsNullOrEmpty(args.Data))
{
return;
}
logger?.Information("RepTool.exe: {StandardOutput}", args.Data ?? string.Empty);
};
process.ErrorDataReceived += (sender, args) =>
{
if (string.IsNullOrEmpty(args.Data))
{
return;
}
logger?.Error("RepTool.exe: {StandardError}", args.Data ?? string.Empty);
};

logger?.Information("Installing TwinCAT libraries ...");

process.Start();
var stdout = await process.StandardOutput.ReadToEndAsync();
logger?.Information("RepTool.exe: {StandardOutput}", stdout ?? string.Empty);

process.WaitForExit(Constants.REPTOOL_EXE_TIMEOUT_MS);
return (ExitCodes)process.ExitCode;
}
catch (Exception ex)
{
logger?.Fatal(ex, "Couldn't install TwinCAT libraries.");
return ExitCodes.E_EXCEPTION;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup/>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net6.0|AnyCPU'">
<NoWarn>$(NoWarn);CS1591;CS1573;CA1416</NoWarn>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
<NoWarn>$(NoWarn);CS1591;CS1573;CA1416</NoWarn>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0|AnyCPU'">
<NoWarn>$(NoWarn);CS1591;CS1573;CA1416</NoWarn>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0|AnyCPU'">
<NoWarn>$(NoWarn);CS1591;CS1573;CA1416</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog" Version="3.1.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TcHaxx.Snappy.Common\TcHaxx.Snappy.Common.csproj" />
</ItemGroup>

</Project>
41 changes: 41 additions & 0 deletions src/TcHaxx.Snappy.CLI.Installer/TcProfile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Serilog;
using TcHaxx.Snappy.CLI.Installer.Options;

namespace TcHaxx.Snappy.CLI.Installer;
internal static class TcProfile
{
internal static TwincatProfile? GetTwinCatProfile(IInstallerOptions options, ILogger? logger)
{
if (!options.TcProfile.Equals(Constants.DEFAULT_OPTION_TCPROFILE, StringComparison.OrdinalIgnoreCase))
{
logger?.Information("Using provided TwinCAT profile \"{TwinCatProfile}\"", options.TcProfile);
return new TwincatProfile(options.TcProfile);
}

var tcDir = RegistryHelper.GetTwincatInstallDirectory();
if (string.IsNullOrWhiteSpace(tcDir))
{
logger?.Error("Couldn't read TwinCAT installation directory from Registry.");
return default;
}

var profilesDir = Path.Join(tcDir, Constants.TC31_PROFILES_DIRECTORY);
if (!Directory.Exists(profilesDir))
{
logger?.Error("Directory \"{TwinCatProfileDirectory}\" doesn't exist.", profilesDir);
return default;
}

var profiles = Directory.GetFiles(profilesDir, Constants.TC31_GLOB_PROFILE);
if (!profiles.Any())
{
logger?.Error("Couldn't find any profiles \"{TwinCatProfileGlob}\" in \"{TwinCatProfileDirectory}\".",
Constants.TC31_GLOB_PROFILE, profilesDir);
}

var profileToUse = profiles.OrderByDescending(x => x).First();
logger?.Information("Using TwinCAT profile \"{TwinCatProfile}\"", profileToUse);
return new TwincatProfile(Path.GetFileNameWithoutExtension(profileToUse));
}
}

2 changes: 2 additions & 0 deletions src/TcHaxx.Snappy.CLI.Installer/TwincatProfile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
namespace TcHaxx.Snappy.CLI.Installer;
internal record TwincatProfile(string Profile);
12 changes: 10 additions & 2 deletions src/TcHaxx.Snappy.CLI/CLI/InstallOptions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
using CommandLine;
using TcHaxx.Snappy.CLI.Installer.Options;

namespace TcHaxx.Snappy.CLI.CLI;

[Verb("install", false, HelpText = "Install TcHaxx.Snappy.library.")]
internal class InstallOptions : BaseOptions
[Verb("install", false, HelpText = "Install snappy.library and dependencies.")]
public class InstallOptions : BaseOptions, IInstallerOptions
{
///<inheritdoc />
[Option("tc-profile", Default = Installer.Constants.DEFAULT_OPTION_TCPROFILE, Required = false, HelpText = "TwinCAT profile to use, i.e \"latest\" or specific version \"TwinCAT PLC Control_Build_4024.54\".")]
public string TcProfile { get; init; } = string.Empty;

///<inheritdoc />
[Option("tool-path", Default = Installer.Constants.DEFAULT_OPTION_TOOLSPATH, Required = false, HelpText = "Directory, where dotnet global tools are installed.")]
public string ToolsPath { get; init; } = string.Empty;
}
10 changes: 5 additions & 5 deletions src/TcHaxx.Snappy.CLI/Commands/CommandInstall.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
using Serilog;
using TcHaxx.Snappy.CLI.CLI;
using TcHaxx.Snappy.CLI.Installer;
using TcHaxx.Snappy.Common;

namespace TcHaxx.Snappy.CLI.Commands;

internal class CommandInstall(ILogger? logger) : ICommandInstall
{
private readonly ILogger? _logger = logger?.ForContext<CommandInstall>();
private readonly IInstallerService _installerService = new InstallerService();

public async Task<int> RunAndReturnExitCode(InstallOptions options)
public async Task<ExitCodes> RunAndReturnExitCode(InstallOptions options)
{
_logger?.Information("Installing ...");
await Task.Delay(1000);
return 0;
return await _installerService.Install(options, _logger);
}

}
5 changes: 3 additions & 2 deletions src/TcHaxx.Snappy.CLI/Commands/CommandVerify.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Serilog;
using TcHaxx.Snappy.CLI.CLI;
using TcHaxx.Snappy.Common;
using TcHaxx.Snappy.TcADS;
using TcHaxx.Snappy.Verifier;

Expand All @@ -11,7 +12,7 @@ internal class CommandVerify(IEnumerable<IVerifyService> verifyServices, ISymbol
private readonly ISymbolicServerFactory _symbolicServerFactory = symbolicServerFactory ?? throw new ArgumentNullException(nameof(symbolicServerFactory));
private readonly ILogger? _logger = logger;

public async Task<int> RunAndReturnExitCode(VerifyOptions options)
public async Task<ExitCodes> RunAndReturnExitCode(VerifyOptions options)
{
foreach (var service in _verifyServices)
{
Expand All @@ -23,6 +24,6 @@ public async Task<int> RunAndReturnExitCode(VerifyOptions options)
_logger?.Information("Starting AdsSymbolicServer...");
var adsRetVal = await symbolicServer.ConnectServerAndWaitAsync(new CancellationToken());

return (int)adsRetVal;
return (ExitCodes)adsRetVal;
}
}
3 changes: 2 additions & 1 deletion src/TcHaxx.Snappy.CLI/Commands/ICommandInstall.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using TcHaxx.Snappy.CLI.CLI;
using TcHaxx.Snappy.Common;

namespace TcHaxx.Snappy.CLI.Commands;

internal interface ICommandInstall
{
Task<int> RunAndReturnExitCode(InstallOptions options);
Task<ExitCodes> RunAndReturnExitCode(InstallOptions options);
}
3 changes: 2 additions & 1 deletion src/TcHaxx.Snappy.CLI/Commands/ICommandVerify.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using TcHaxx.Snappy.CLI.CLI;
using TcHaxx.Snappy.Common;

namespace TcHaxx.Snappy.CLI.Commands;

internal interface ICommandVerify
{
Task<int> RunAndReturnExitCode(VerifyOptions options);
Task<ExitCodes> RunAndReturnExitCode(VerifyOptions options);
}
8 changes: 5 additions & 3 deletions src/TcHaxx.Snappy.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using TcHaxx.Snappy.CLI.CLI;
using TcHaxx.Snappy.CLI.Commands;
using TcHaxx.Snappy.CLI.Logging;
using TcHaxx.Snappy.Common;
using TcHaxx.Snappy.Common.RPC;
using TcHaxx.Snappy.TcADS;
using TcHaxx.Snappy.Verifier;
Expand All @@ -17,12 +18,13 @@

using var host = BuildHost(args);

return await Parser.Default.ParseArguments<InstallOptions, VerifyOptions>(args)
var exitCode = await Parser.Default.ParseArguments<InstallOptions, VerifyOptions>(args)
.MapResult(
async (InstallOptions options) => await host.Services.GetService<ICommandInstall>()!.RunAndReturnExitCode(options),
async (VerifyOptions options) => await host.Services.GetService<ICommandVerify>()!.RunAndReturnExitCode(options),
errs => Task.FromResult((int)ExitCodes.E_CLIOPTIONS)
);
errs => Task.FromResult(ExitCodes.E_CLIOPTIONS));

return (int)exitCode;
}
catch (Exception ex)
{
Expand Down
2 changes: 1 addition & 1 deletion src/TcHaxx.Snappy.CLI/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"profiles": {
"TcHaxx.Snappy.CLI": {
"commandName": "Project",
"commandLineArgs": "verify -d D:\\densogiaichned\\snappy\\examples\\examples\\.snappy-verified"
"commandLineArgs": "install"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace TcHaxx.Snappy.CLI.CLI;
namespace TcHaxx.Snappy.Common;

/// <summary>
/// Exit codes for this application.
/// </summary>
internal enum ExitCodes
public enum ExitCodes
{
/// <summary>
/// Exit code: No error.
Expand All @@ -18,6 +18,10 @@ internal enum ExitCodes
/// <summary>
/// Exit code: Parsing arguments and/or arguments missing/wrong.
/// </summary>
E_CLIOPTIONS = 2
E_CLIOPTIONS = 2,

/// <summary>
/// Exit code: General error occured, see logs.
/// </summary>
E_ERROR = 3
}

0 comments on commit e2270c6

Please sign in to comment.