Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions cmf-cli/Commands/new/iot/GenerateDriverCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Collections.Generic;
using System.CommandLine;
using System.IO.Abstractions;
using System.Linq;
using System.Threading.Tasks;

namespace Cmf.CLI.Commands.New.IoT
Expand Down Expand Up @@ -84,7 +85,9 @@ public void Execute(IDirectoryInfo workingDir)
driver.PackageFullName,
driver.PackageVersion,
driver.HasCommands,
driver.HasTemplates);
driver.HasTemplates,
driver.DriverBaseClass,
driver.DriverBasePackage);
this.CommandName = "iot-driver";
base.RunCommand(args);
}
Expand All @@ -103,6 +106,20 @@ private DriverValues HandleDriver(DriverValues driver)
driver.HasCommands = AnsiConsole.Prompt(new ConfirmationPrompt("Does the protocol support commands?") { DefaultValue = driver.HasCommands });
driver.HasTemplates = AnsiConsole.Prompt(new ConfirmationPrompt("Do you wish to use templates? (By saying no, you will only have events and commands from the driver definition)") { DefaultValue = driver.HasTemplates });

if (ExecutionContext.Instance.ProjectConfig.MESVersion.Major >= 12)
{
driver.DriverBaseClass = AnsiConsole.Prompt(
Comment thread
Cafernandes marked this conversation as resolved.
new SelectionPrompt<string>()
.Title("Which driver base class does this driver extend?")
.UseConverter(name => DriverBases.All.TryGetValue(name, out var metadata)
? $"{name} - {metadata.Description}"
: name)
.AddChoices(DriverBases.All.Keys)
.DefaultValue("DeviceDriverBase"));

driver.DriverBasePackage = DriverBases.GetPackageName(driver.DriverBaseClass);
}

return driver;
}

Expand All @@ -116,7 +133,9 @@ private List<string> GenerateArgs(
string packageName,
string packageVersion,
bool hasCommands,
bool hasTemplates)
bool hasTemplates,
string driverBaseClass,
string driverBasePackage)
{
var mesVersion = ExecutionContext.Instance.ProjectConfig.MESVersion;
Log.Debug($"Creating IoT Driver at {packageLocation}");
Expand All @@ -132,7 +151,9 @@ private List<string> GenerateArgs(
"--packageVersion", packageVersion,
"--npmRegistry", ExecutionContext.Instance.ProjectConfig.NPMRegistry.ToString(),
"--hasCommands", hasCommands.ToString(),
"--hasTemplates", hasTemplates.ToString()
"--hasTemplates", hasTemplates.ToString(),
"--driverBaseClass", driverBaseClass,
"--driverBasePackage", driverBasePackage
});

return args;
Expand Down
41 changes: 41 additions & 0 deletions cmf-cli/Utilities/IoTStructures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ public class DriverValues

[JsonIgnore]
public bool HasTemplates { get; set; }

[JsonIgnore]
public string DriverBaseClass { get; set; } = "DeviceDriverBase";

[JsonIgnore]
public string DriverBasePackage { get; set; } = "@criticalmanufacturing/connect-iot-driver";
}

[JsonObject]
Expand Down Expand Up @@ -483,4 +489,39 @@ public static class TaskBases
{ "SystemRequestReplyTaskBase", "Exclusive to Data Flow. Sends replies to a SystemRequestListenerTaskBase (for example, \"Send System Action Reply\" task)." },
};
}

public class DriverBaseMetadata
{
public string Description { get; set; }
public string PackageName { get; set; }
}

public static class DriverBases
{
public static readonly Dictionary<string, DriverBaseMetadata> All = new()
{
{
"DeviceDriverBase", new DriverBaseMetadata
{
Description = "(Default): Base class for standard driver operations.",
PackageName = "@criticalmanufacturing/connect-iot-driver"
}
},
{
"DeviceFileDriverBase", new DriverBaseMetadata
{
Description = "Extends standard driver behavior with file-based operations.",
PackageName = "@criticalmanufacturing/connect-iot-base-file-driver"
}
},
};

public static string GetPackageName(string driverBaseClass)
{
return !string.IsNullOrWhiteSpace(driverBaseClass)
&& All.TryGetValue(driverBaseClass, out var metadata)
? metadata.PackageName
: "@criticalmanufacturing/connect-iot-driver";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,32 @@
"displayName": "Driver Has Templates",
"defaultValue": "false"
},
"driverBaseClass": {
"type": "parameter",
"datatype": "string",
"description": "Driver base class",
"displayName": "Driver Base Class",
"defaultValue": "DeviceDriverBase",
"replaces": "<%= $CLI_PARAM_DriverBaseClass %>"
},
"driverBasePackage": {
"type": "parameter",
"datatype": "string",
"description": "Package that contains the selected driver base class",
"displayName": "Driver Base Package",
"defaultValue": "@criticalmanufacturing/connect-iot-driver",
"replaces": "<%= $CLI_PARAM_DriverBasePackage %>"
},
"isDeviceFileDriverBase": {
"type": "computed",
"datatype": "bool",
"value": "driverBaseClass == \"DeviceFileDriverBase\""
},
"isDeviceDriverBase": {
"type": "computed",
"datatype": "bool",
"value": "driverBaseClass == \"DeviceDriverBase\""
},
"v1120OrGreater": {
"type": "generated",
"generator": "regexMatch",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
},
"dependencies": {
"@criticalmanufacturing/connect-iot-common": "<%= $CLI_PARAM_TargetSystemVersionProcessed %>",
//#if (isDeviceFileDriverBase)
"@criticalmanufacturing/connect-iot-base-file-driver": "<%= $CLI_PARAM_TargetSystemVersionProcessed %>",
//#endif
"@criticalmanufacturing/connect-iot-driver": "<%= $CLI_PARAM_TargetSystemVersionProcessed %>",
"inversify": "6.0.2",
"moment": "2.29.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { injectable, inject, Container } from "inversify";
import { CommunicationState, PropertyValuePair } from "@criticalmanufacturing/connect-iot-driver";
import { Property, EventOccurrence, PropertyValue, Command, Event as EquipmentEvent, DeviceDriverBase, CommandParameter } from "@criticalmanufacturing/connect-iot-driver";
import { Property, EventOccurrence, PropertyValue, Command, Event as EquipmentEvent, CommandParameter } from "@criticalmanufacturing/connect-iot-driver";
import { <%= $CLI_PARAM_DriverBaseClass %> } from "<%= $CLI_PARAM_DriverBasePackage %>";
import { <%= $CLI_PARAM_Identifier %>CommunicationSettings, <%= $CLI_PARAM_IdentifierCamel %>DefaultCommunicationSettings, validateCommunicationParameters } from "./communicationSettings";
import { validateProperties, validateEvents, validateEventProperties, validateCommands, validateCommandParameters } from "./extendedData/index";
import { Utils } from "@criticalmanufacturing/connect-iot-common";
Expand All @@ -10,7 +11,7 @@ import { ExtensionHandler } from "./extensions";
//#endif

@injectable()
export class <%= $CLI_PARAM_Identifier %>DeviceDriver extends DeviceDriverBase {
export class <%= $CLI_PARAM_Identifier %>DeviceDriver extends <%= $CLI_PARAM_DriverBaseClass %> {
private _communicationSettings: <%= $CLI_PARAM_Identifier %>CommunicationSettings;
private _container: Container;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import "reflect-metadata";
import { Container } from "inversify";

import { DeviceDriver, TYPES as COMMUNICATION_TYPES, container as driverContainer } from "@criticalmanufacturing/connect-iot-driver";
//#if (isDeviceFileDriverBase)
import { FileHandler, MountPointHandler } from "<%= $CLI_PARAM_DriverBasePackage %>";
//#endif
import { TYPES } from "./types";
import { <%= $CLI_PARAM_Identifier %>DeviceDriver } from "./driverImplementation";
//#if hasTemplates
Expand All @@ -11,6 +14,10 @@ import { ExtensionHandler } from "./extensions";
const container = new Container();
container.parent = driverContainer;
container.parent?.bind<Container>(TYPES.Injector).toConstantValue(container);
//#if (isDeviceFileDriverBase)
container.parent?.bind<FileHandler>(COMMUNICATION_TYPES.FileHandler).to(FileHandler).inSingletonScope();
container.parent?.bind<MountPointHandler>(COMMUNICATION_TYPES.MountPointHandler).to(MountPointHandler).inSingletonScope();
//#endif
//#if hasTemplates
container.bind<ExtensionHandler>(TYPES.ExtensionHandler).to(ExtensionHandler).inSingletonScope();
//#endif
Expand Down
85 changes: 85 additions & 0 deletions tests/Specs/New.cs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,10 @@ public void IoTDriverDefaultValues(string mesVersion)
console.Input.PushTextWithEnter(""); // Identifier
console.Input.PushTextWithEnter(""); // HasCommands
console.Input.PushTextWithEnter(""); // HasTemplates
if (Version.Parse(mesVersion).Major >= 12)
{
console.Input.PushTextWithEnter(""); // DriverBaseClass: DeviceDriverBase (default)
}

AnsiConsole.Console = console; // so that the prompts asked by the command use this console instance

Expand Down Expand Up @@ -468,6 +472,87 @@ public void IoTDriverDefaultValues(string mesVersion)
}
}

[Theory, Trait("TestCategory", "Integration")]
[InlineData("12.0.0", "DeviceDriverBase")]
[InlineData("12.0.0", "DeviceFileDriverBase")]
public void IoTDriverBase(string mesVersion, string driverBaseClass)
{
string dir = TestUtilities.GetTmpDirectory();
string packageId = "Cmf.Custom.IoT";
string packageFolderPackages = "Cmf.Custom.IoT.Packages";

var cur = Directory.GetCurrentDirectory();

try
{
CopyNewFixture(dir, mesVersion: mesVersion);
RunNew(new IoTCommand(), packageId, dir);

Directory.SetCurrentDirectory($"{dir}/{packageId}/{packageFolderPackages}");

TestingConsole.TestConsole console = new();
console.Profile.Capabilities.Interactive = true;
console.Input.PushTextWithEnter(""); // directory name: driver-sample (default)
console.Input.PushTextWithEnter(""); // package scope (default)
console.Input.PushTextWithEnter(""); // package name (default)
console.Input.PushTextWithEnter(""); // package version (default)
console.Input.PushTextWithEnter(""); // identifier: SampleDriver (default)
console.Input.PushTextWithEnter(""); // HasCommands: No (default false)
console.Input.PushTextWithEnter(""); // HasTemplates: No (default false)

var driverBaseChoices = new[] { "DeviceDriverBase", "DeviceFileDriverBase" };
int driverBaseIndex = Array.IndexOf(driverBaseChoices, driverBaseClass);
for (int i = 0; i < driverBaseIndex; i++)
{
console.Input.PushKey(ConsoleKey.DownArrow);
}
console.Input.PushTextWithEnter(""); // confirm driver base selection

AnsiConsole.Console = console;

var cmd = new Command("x");
var newCommand = new GenerateDriverCommand();
newCommand.Configure(cmd);
TestUtilities.GetParser(cmd).Invoke("");

Directory.Exists(Path.GetFullPath("src/driver-sample")).Should().BeTrue("Driver folder should be generated");

var driverImpl = File.ReadAllText(Path.GetFullPath("src/driver-sample/src/driverImplementation.ts"));
var inversifyConfig = File.ReadAllText(Path.GetFullPath("src/driver-sample/src/inversify.config.ts"));
var packageJson = File.ReadAllText(Path.GetFullPath("src/driver-sample/package.json"));

if (driverBaseClass == "DeviceFileDriverBase")
{
driverImpl.Should().Contain("DeviceFileDriverBase",
"Driver should import DeviceFileDriverBase when needsFileAccess is true");
driverImpl.Should().NotContain("DeviceDriverBase",
"Driver should not import DeviceDriverBase when needsFileAccess is true");
driverImpl.Should().Contain("extends DeviceFileDriverBase",
"Driver class should extend DeviceFileDriverBase when needsFileAccess is true");
inversifyConfig.Should().Contain("FileHandler",
"inversify.config should bind FileHandler when needsFileAccess is true");
packageJson.Should().Contain("connect-iot-base-file-driver",
"package.json should include connect-iot-base-file-driver when needsFileAccess is true");
}
else
{
driverImpl.Should().NotContain("DeviceFileDriverBase",
"Driver should not reference DeviceFileDriverBase when needsFileAccess is false");
driverImpl.Should().Contain("extends DeviceDriverBase",
"Driver class should extend DeviceDriverBase when needsFileAccess is false");
inversifyConfig.Should().NotContain("FileHandler",
"inversify.config should not bind FileHandler when needsFileAccess is false");
packageJson.Should().NotContain("connect-iot-base-file-driver",
"package.json should not include connect-iot-base-file-driver when needsFileAccess is false");
}
}
finally
{
Directory.SetCurrentDirectory(cur);
Directory.Delete(dir, true);
}
}

[Theory, Trait("TestCategory", "Integration")]
[InlineData("11.2.0", "testName", false)]
[InlineData("11.3.0", "", false)]
Expand Down
Loading