Custom Modules
A MachineApp is a customized user interface that you can use to operate a machine that is controlled by an Automation1 controller. Custom Modules for MachineApps is a feature that lets you develop new modules that can be integrated and deployed with standard modules in a MachineApp.
Custom Modules interact with the Automation1 controller and other parts of the MachineApps Application with providers. A provider gives your Custom Module access to an instance of the .NET Controller object, which represents the connected controller in MachineApps. This lets you use the full Automation1 .NET API in your Custom Module. Other providers expose more features that your module can access, such as showing dialogs or notifications.
Custom Modules can also contain other functionality. A Custom Module can access the local hardware and file system of the computer where the Automation1 MachineApps application is running. Your Custom Module can reference other .NET assemblies, native DLLs, or configuration files. You can manage these files and your compiled Custom Modules through the MachineApps workspace in Automation1 Studio. Custom Modules can also rely on drivers or other installed applications or components when these dependencies can be found and/or loaded dynamically by your code that is running under .NET.
Additional Technical Details
Custom Modules are written in C#, use Windows Presentation Foundation (WPF) UI technology, and target the .NET 8.0 Long Term Support (LTS) runtime. Custom Modules reference several types that are part of the Automation1 MDK and are compiled for use with a specific version of Automation1.
Custom Modules inherently support the Model-View-ViewModel (MVVM) design pattern, which lets you specify one type for your DataContext (typically your ViewModel) and a different type for your UserControl. But you also have the flexibility to define all of your logic in the code-behind of your UserControl.
Automation1 MachineApps uses Dependency Injection and Reflection to create instances of your Custom Module types at runtime and give your Custom Module types instances of the Providers that they require. It is not necessary for you to call the constructor on your Custom Module or find the Providers that you need. The MachineApps Application does this for you when your Custom Modules are loaded with the standard modules that you add to a MachineApp.
Get Started with the Example Project
The CustomModulesExample
is a Visual Studio Solution that shows you how to create a custom module. This project has two modules in it that show an MVVM and a non-MVVM method for creating a custom module.
Project Location
You can find the custom modules example project in the installation directory of the Automation1 MDK. The path to the example is: …\Aerotech\Automation1-MDK\MachineApps\Examples\CustomModulesExample.
Project Structure
The CustomModulesExample
solution has two folders. One folder is for the Analog Inputs Module and the other is for the Analog Outputs Module.
Analog Inputs Module
This custom module uses the MVVM pattern by registering a view (AnalogInputsView) and a view model (AnalogInputsViewModel). When an instance of the module is created, the AnalogInputsViewModel is created and set as the DataContext
of the AnalogInputsView. The view model implements INotifyPropertyChanged
to let data binding occur. The view model also takes an IControllerProvider
and an INotificationProvider
as dependencies that are injected by the application when the module is created.
Analog Outputs Module
This custom module does not use the MVVM pattern. It has an AnalogOutputsView that defines the user interface in the AnalogOutputsView.xaml and has additional logic in the code-behind (AnalogOutputsView.xaml.cs). The AnalogOutputsRegistration sets the DataContext
property to null to indicate that there is no view model in this condition. The AnalogOutputsView
constructor can take providers as arguments that are injected by the application when the module is created.
Project Output
When you build the example project in Visual Studio, this process makes a dynamic link library (DLL) with the name CustomModulesExample.dll. This file contains all the things that are necessary for you to use the example custom modules in a MachineApp. But before you do this, you must use Automation1 Studio to upload the file to a controller. For information about how to do this, see the To Upload Your Custom Modules Configuration section of the MachineApps Workspace page.
API Overview
When you create a custom module, there are two specific types you will need. They are CustomModuleRegistration
and CustomModuleMetadata
. The CustomModuleRegistration
type is abstract. It will be inherited from in order to register your module. It is not necessary for you to create an instance of this type. Studio and MachineApps will create instances through reflection when necessary.
CustomModuleRegistration
The CustomModuleRegistration
type is an abstract class that is necessary to register a custom module with Studio so you can use it in MachineApps. When you use the Manage Custom Modules dialog in the MachineApps workspace to edit the custom modules configuration, Studio uses reflection to identify classes that inherit from CustomModuleRegistration
and read the Metadata
property for that module. This metadata is used by the MachineApp builder when it designs a MachineApp and provides information about the module name, type, and how you can use it.
The CustomModuleRegistration
has two other properties. They are the UserControlType
and the DataContextType
. These properties are not instances of the module user control or data context. But these properties are their Type
. When it is necessary to use an instance of a custom module in a MachineApp, the application creates an instance of the applicable type.
Tip: If you know a lot about the MVVM pattern, you can define the UserControlType
and the DataContextType
properties.
When the module is created, an instance of the view and the view model are also created. The view model is set as the DataContext
for the view. If you do not use the MVVM pattern, then it is only necessary to define a UserControlType
for your module. Refer to the example that follows.
public class MyModuleRegistration : CustomModuleRegistration
{
public override CustomModuleMetadata Metadata => new CustomModuleMetadata("My Custom Module");
public override Type UserControlType => typeof(MyView);
public override Type DataContextType => typeof(MyViewModel);
}
CustomModuleMetadata
The CustomModuleMetadata
type specifies information about your custom module.
The Name
property specifies the unique name that will identify your module. The application shows it while you are designing a MachineApp.
The MachineAppModuleType
property lets you put the module into Primary or Secondary regions when you design a MachineApp. The default value is MachineAppModuleType.Primary
.
The ModuleOrientation
property lets you limit secondary modules to specific regions. This property applies only if the MachineAppModuleType
is MachineAppModuleType.Secondary
. The default value is ModuleOrientation.Any
.
The AllowMultipleInstances
property lets you specify whether or not multiple instances of your custom module can exist in the same MachineApp. The default value is false.
The HasModuleToolbar
property lets you specify if a module toolbar is added to your module. The default value is false.
To set more property values on the CustomModuleMetadata
type, you must use the object initializers syntax when you construct the object. Refer to the example that follows.
public override CustomModuleMetadata Metadata => new CustomModuleMetadata("My Custom Module")
{
MachineAppModuleType = MachineAppModuleType.Primary,
AllowMultipleInstances = true,
HasModuleToolbar = true,
};
Providers
Providers are types that are available for you to use in your custom modules. They can help you interact with the controller or the application. They also provide more features that your module can access. To use a provider, you must request it through dependency injection. To do this, include an argument with the type of the desired provided in your custom module user control or data context constructor. Make sure the type is one that is registered by the CustomModuleRegistration
. When an instances of the module is created, MachineApps will provide an instance of the provider to the constructor. Refer to the example that follows.
// A Custom Module view model that takes a controller provider.
// Do not call this constructor. It will be called by
// MachineApps when your module is created.
public MyViewModel(IControllerProvider controllerProvider)
{
this.controllerProvider = controllerProvider;
}
For more examples, see the CustomModulesExample
project. Refer to the Get Started with the Example Project section of this page for more information.
The providers that follow are available:
Use IControllerProvider
to get an instance of the Aerotech.Automation1.DotNet.Controller
object. This object is the entry point for the .NET API to access all of the features of the connected controller. If nested views require access to the controller, your module can pass a reference to the IControllerProvider
.
IMPORTANT: In a custom module, you must use an IControllerProvider
. Do not call Aerotech.Automation1.DotNet.Controller.Connect()
to access the controller object. This makes sure that you are accessing the controller that the MachineApp is connected to.
Use INotificationProvider
to make toast notifications that appear in the top-right corner of the MachineApps application. This lets your module get the attention of the user with different severity options. You can also see these notifications in the Notifications tab of the application sidebar.
Use the IDialogProvider
to show dialogs to the user. This includes simple acknowledgment dialogs that you can customize with specific messages, captions, and button sets. It also includes dialogs that interact with files on the client PC or the controller file system.
Module Toolbar
The module toolbar shows the name of the module and lets you expand and collapse the module within the MachineApps application window. The CustomModuleMetadata
class contains the HasModuleToolbar
property that controls if the module toolbar is added to your module. The default value of this property is false. To add a module toolbar to your custom module, set the HasModuleToolbar
property to true. You can only add the module toolbar to a primary module. The code example that follows shows how to add a module toolbar.
public class ExampleModuleRegistration : CustomModuleRegistration
{
public override CustomModuleMetadata Metadata => new("ExampleModule") { HasModuleToolbar = true };
...
}
Add Controls to the Module Toolbar
By default, the module toolbar shows the name of your module and a button to expand and collapse the module. To add more buttons or text to the module toolbar, use the Aerotech.Automation1.Applications.Core
library that has a ModuleToolbarHelper.ToolbarContent
attached property that you can set in the XAML code of your custom module view. The value of the property must be a single FrameworkElement
object. To add more controls, use a Panel element such as a Grid or a StackPanel.
To use the ModuleToolbarHelper.ToolbarContent
attached property, you must add a mapping to the Aerotech.Automation1.Applications.Core
namespace in the XAML of your custom module view. Then, add a ModuleToolbarHelper.ToolbarContent
node inside the top-level UserControl
node. Refer to the example that follows to see how to add controls to the Module Toolbar.
<UserControl x:Class="YourApplication.YourModuleView"
...
xmlns:core="clr-namespace:Aerotech.Automation1.Applications.Core;assembly=Aerotech.Automation1.Applications.Core">
<core:ModuleToolbarHelper.ToolbarContent>
<aero:AeroButton Content="Click Me!" HorizontalAlignment="Left"/>
</core:ModuleToolbarHelper.ToolbarContent>
...
</UserControl>
The data context for the controls that you add to the module toolbar is the same data context as your custom module view. Thus, controls on the module toolbar can bind to properties in the data context of your custom module.
Controls Library
The Aerotech.Automation1.Applications.Controls library supplies a set of common WPF controls that are styled like the controls in Aerotech modules. You can build a custom module with the controls in this library to make your custom module look like Aerotech modules.
How to Use the Controls Library
To use the Controls library, you must add the Aerotech.Automation1.Applications.Controls.dll
library as a reference in your Visual Studio solution. Then, in your custom module view XAML, you must add a mapping to the Aerotech.Automation1.Applications.Controls
namespace. You can use the controls in this namespace to create your custom module view. Refer to the List of Controls section to see the controls that are available. See the example that follows that shows how to map the Controls library namespace.
<UserControl x:Class="YourApplication.YourModuleView"
...
xmlns:aero="clr-namespace:Aerotech.Automation1.Applications.Controls;assembly=Aerotech.Automation1.Applications.Controls">
...
</UserControl>
Appearance Property of Controls
The Controls library sets the Style
property for each control automatically, so it is not necessary to set the Style
property of a control. Some controls have more than one style that you can change with the Appearance
property on the XAML element of the control. To see which controls support the Appearance
property and the list of Appearance
values for each control, refer to the List of Controls section. See the example that follows that shows how to change the appearance of a control.
<aero:AeroButton Content="Hello, world!" Appearance="Secondary"/>
The size and color of the controls use the Primary Color, Primary Hover Color, and Use Touch Sizing options in your MachineApp settings.
List of Controls
This section lists the controls that the Controls library supplies to you. The Control Name column lists the names of the controls that are found in the Aerotech.Automation1.Applications.Controls
namespace. The Type Reference column lists the type of the control and links to the API reference for that type. The Appearance column lists the different styles of each control. The Control Style column shows you what each control style looks like.
Figure: Controls in the Control Library
Control Name | Type Reference | Appearance | Control Style |
---|---|---|---|
AeroButton | Button | Primary (default) |
|
Secondary |
|
||
Link |
|
||
AeroCheckBox | CheckBox |
|
|
AeroComboBox | ComboBox |
|
|
AeroRadioButton | RadioButton |
|
|
AeroTextBlock | TextBlock | Paragraph (default) |
This is the default. |
Subheading |
|
||
Heading |
|
||
Title |
|
||
AeroTextBox | TextBox |
|
|
AeroToggle | CheckBox |
|
Debugging Custom Modules
You can use Visual Studio or other IDE applications with debugging capabilities to debug your custom module. To do this, refer to the steps that follow.
-
Build your custom module project with the Debug configuration.
-
Set your Debug configuration build output folder as your Custom Modules working directory.
-
Upload your working directory to the controller.
-
Add your custom module to a MachineApp.
-
Launch Automation1 MachineApps.
-
Connect to your controller.
-
Launch the MachineApp that includes your custom module.
-
Open your IDE and attach its debugger to the Automation1 MachineApps application process.
Frequently Asked Questions
No. You can have as many modules as you want in a single dynamic link library. Modules must have unique names defined in their CustomModuleMetadata
.
Yes. You can have as many dynamic link libraries uploaded to the controller as you want. Each one will be scanned for custom modules. Modules must still have unique names across all of the dynamic link libraries.
Yes. You can include other dynamic link libraries that do not include any custom modules but are dependencies for your custom modules. These libraries must be uploaded from your Custom Modules working directory to the controller in Automation1 Studio.
After you make changes to your custom modules in Visual Studio, you must rebuild the project and make sure that your updated files are in your Custom Modules working directory. Then you can upload your working directory in Automation1 Studio. See the To Upload Your Custom Modules Configuration section of the MachineApps Workspace page. You must upload your working directory again and restart MachineApps in order for your changes to have an effect.
Yes. You can use the Download MCD tool in the Administration module of the Configure workspace in Automation1 Studio to download your MachineApps configuration, which will contain all of your MachineApps and your custom modules configuration. Then you can upload this MCD file by using the Upload MCD tool from the same module on the target controller.
IMPORTANT: When you upload MachineApps, this process will overwrite any existing custom modules configuration on the target controller.
Yes. Your custom module can reference NuGet packages. The NuGet package libraries that your custom module references must be uploaded from your Custom Modules working directory to the controller in Automation1 Studio. By default, some NuGet package libraries cannot be copied to the output directory when you build your project, which means they will not be included when you upload files from your working directory. To make sure that NuGet packages are copied to your project’s output directory, add the CopyLocalLockFileAssemblies
property to your custom modules project (.csproj
) file and set its value to true
.