Acuit Pinpoint Workstation Plug-ins Overview
Acuit Pinpoint Workstation plug-ins are .NET class libraries that are dynamically loaded by Acuit Pinpoint Workstation into its process. Plug-ins can then integrate with the Acuit Pinpoint Workstation workflow by subscribing to various .NET events that are published by Acuit Pinpoint Workstation and by registering user interface components to appear within the Acuit Pinpoint Workstation user interface in various well-defined regions.
Warning
The Acuit Pinpoint Workstation plug-in architecture has changed with version 8, with many breaking changes. Plug-ins previously created for version 7 or earlier will need to be updated to work with version 8.
See Acuit Pinpoint Plug-ins for general details about Acuit Pinpoint plug-ins.
NuGet Dependencies
The packages necessary for developing Acuit Pinpoint Workstation plug-ins are published via the NuGet package manager. Various packages related to Acuit Pinpoint are available, but the Acuit.Pinpoint.Workstation.PlugIns meta package is the only Acuit Pinpoint package reference needed for developing plug-ins for Acuit Pinpoint Workstation.
Simply add a reference to this package within your Visual Studio project to obtain everything needed to integrate with Acuit Pinpoint Workstation.
Note
Remember that the version of the Acuit.Pinpoint.Workstation.PlugIns package that you reference in your project determines the minimum version of Acuit Pinpoint Workstation that will be required to host your plug-in.
Plug-in Modules
An Acuit Pinpoint Workstation plug-in is composed of one ore more Acuit Pinpoint plug-in modules that are loaded and initialized by Acuit Pinpoint Workstation when it starts. During the plug-in module initialization, it integrates with Acuit Pinpoint Workstation as necessary to perform its custom behavior.
Windows Presentation Foundation (WPF)
Acuit Pinpoint Workstation uses WPF for its user interface. Plug-ins that provide any user interface components must use WPF to build these components.
Services
Plug-ins integrate with Acuit Pinpoint Workstation via services obtained via dependency injection. For example, a plug-in module could monitor when units are scanned or released at the station via messages provided by the IMessenger service, obtained and used like this:
public sealed class PlugInModule(IMessenger messenger) : IPlugInModule, IRecipient<UnitScannedMessage>, IRecipient<UnitReleasedMessage>, IDisposable
{
public void Dispose()
{
messenger.UnregisterAll(this);
}
public void ConfigureServices(IServiceCollection services, PlugInHostContext hostContext)
{
}
public void Initialize(IServiceProvider serviceProvider, PlugInHostContext hostContext)
{
messenger.RegisterAll(this);
}
public void Receive(UnitScannedMessage message)
{
// Process unit scan...
}
public void Receive(UnitReleasedMessage message)
{
// Process unit release...
}
}
Acuit Pinpoint Workstation makes the following services available to plug-in modules:
- IAlarmsManager
- IApplicationDispatcher
- IBarCodeScannersService
- IDeviceRegistry - The following device types are registered by Acuit Pinpoint Workstation:
- IDistributedCache
- IErrorHandler
- IHealthCheckPublisherRegistry
- IHealthCheckRegistry
- IHostEnvironment
- IItemScanningService
- ILogger<TCategoryName>
- ILoggerFactory
- IMemoryCache - This is a memory cache that is shared across Acuit Pinpoint Workstation and all plug-ins. NOTE: Entries in this cache are never removed to relieve memory pressure. This means:
- The
Sizeproperty for entries is ignored. - Entries should always include time-based expirations to ensure they will eventually be removed.
- The
- IMenuRegistry
- IMessenger
- IOptionsRegistry
- IPinpointClientFactory
- IReadingsRegistry
- IRegionManager
- IServerFileRetriever
- IServiceProvider - This will be specific to this context.
- ITableProvider
- ITestingCoordinator
- ITestManager
- ITestParameterValueProviderRegistry
- TimeProvider
- IUnitProductionQueue
- IUserInterfaceService
- IWorkflowEventManager
- IWorkstationState
- IWorkstationStatusManager
- Any services added by this or other plug-in modules in assemblies in the same directory - These services will be available via the IServiceProvider argument to the plug-in module's Initialize method, not the plug-in module type constructor.
Workstation State
One of the primary ways that a plug-in integrates with Acuit Pinpoint Workstation is via the IWorkstationState service. This service provides access to the basic state of the application via this hierarchy of properties:
- RunningStation - Represents the running station, with members providing station information and stations settings and actions that can be performed at the station independent of worker or unit status.
- WorkerAtStation - Represents a worker logged on at the station, with members providing worker information and actions that can be performed when a worker is logged on, independent of unit status.
- UnitAtStation - Represents a unit at the station, with members providing unit information and actions that can be performed on the unit.
- WorkerAtStation - Represents a worker logged on at the station, with members providing worker information and actions that can be performed when a worker is logged on, independent of unit status.
Messenger
The IMessenger service is the other primary way that a plug-in integrates with Acuit Pinpoint Workstation. This service facilitates exchanging messages between different objects in Acuit Pinpoint Workstation and plug-in modules, without requiring the objects to have direct knowledge of each other (for example, without needing to carefully track workstation state via the IWorkstationState object hierarchy).
While the messenger service allows any message objects to be exchanged for any reason, Acuit Pinpoint Workstation uses two types of messages: request messages and event messages.
Request Messages
Request messages generally represent various requests that can be initiated by the Acuit Pinpoint Workstation user interface, such as initiating a component scan, recording a scanned component, or releasing a unit. These message types are always classes derived from Request<T> (note that request messages sent by Acuit Pinpoint Workstation are derived from WorkstationRequestMessage<T>). If there is a particular response type appropriate for the request, it is used; otherwise, a bool is used where the handler can respond with true to indicate that it was handled. When Acuit Pinpoint Workstation sends a request message, a response is always expected.
Plug-ins can send request messages to programmatically trigger actions in Acuit Pinpoint Workstation. Note that the message might or might not have any effect or receive a response, depending on the current state of Acuit Pinpoint Workstation.
Plug-ins can override the default behavior in Acuit Pinpoint Workstation by registering recipients for request messages. If a recipient registered by a plug-in provides a response to a request message, then the default Acuit Pinpoint Workstation behavior for that message will not occur. If multiple recipients are registered for the same request message, the order in which they receive the message is not guaranteed, except that all plug-in recipients will receive the request message first, and if none of them provide a response, then Acuit Pinpoint Workstation will handle it via its default behavior and provide the response (if applicable given the current Acuit Pinpoint Workstation state). Registered recipients should always check the HasReceivedResponse property before processing the message, as only one response is allowed.
Event Messages
Event messages provide notifications of various application events or state changes. These are never derived from Request<T>, as responses are not required. However, these message types sometimes have settable or mutable properties that recipients can change to affect behavior (for example, the UnitReleasingMessage message's Cancel property can be set to prevent a unit from being released from the station). Plug-ins can register recipients for event messages to update their state based on changes occurring at the workstation. Plug-ins should not send standard Acuit Pinpoint Workstation event messages.
Note
Every message sent via the messenger will always be sent to all registered recipients for that message.
The various messages used by Acuit Pinpoint Workstation can be found in the Acuit.Pinpoint.Workstation.Messaging namespace. Request message types are named like commands (i.e., imperative verbs), like AddUnitDefectMessage, while event message types are named like events (i.e., passive voice), like UnitDefectAddedMessage.
Plug-ins are free to use this service for their own custom message types.
User Interface Regions
Plug-ins can seamlessly integrate with the Acuit Pinpoint Workstation user interface by creating views and registering them to appear in certain user interface regions.
For example, the following plug-in module registers a WPF user control of type TestView to appear in the "manned unit plug-in region" of the Acuit Pinpoint Workstation user interface:
Warning
This example has not yet been updated for version 8.
[ModuleExport("ExamplePlugIn", typeof(PlugInModule))]
public class PlugInModule : IModule
{
readonly IRegionViewRegistry _RegionViewRegistry;
[ImportingConstructor]
public PlugInModule(IRegionViewRegistry regionViewRegistry)
{
_RegionViewRegistry = regionViewRegistry;
}
public void Initialize()
{
_RegionViewRegistry.RegisterViewWithRegion(RegionNames.MannedUnitPlugInRegion, typeof(TestView));
}
}
See RegionNames for a description of the various regions that are available for use by plug-ins.
Custom Workflow Activities
Plug-ins or their included dependencies can provide custom workflow activities or other types that can be used within Acuit Pinpoint Workstation workflows, with the following special considerations.
When workflows are loaded from XAML, a reference to a custom type (e.g., a custom workflow activity) provided by a plug-in or one of its dependencies will be resolved by the XAML loader as long as:
- The assembly containing the custom type is loaded. A plug-in module should exist in the plug-in assembly that ensures that any assemblies containing types that can be referenced by workflows (i.e., that are specified in the XAML) are loaded. If the types are in the same assembly as the plug-in module, no extra steps are necessary. Types in separate packages that are referenced by plug-in assemblies should typically include a service collection extension method that adds the services and initializers used by that package; calling this method during the plug-in module initialization will be sufficient to load the assembly.
- The plug-in type's XAML namespace and name resolves to a single loaded assembly across all plug-in assembly load contexts. This means that this type can only exist in one plug-in package reference in use at the workstation. If there are multiple types in different plug-in assembly load contexts with the same name and XAML namespace, then the XAML loader will select the first one found, which may have unexpected results.
If a custom workflow activity provided by a plug-in or one of its dependencies depends on services added by the plug-in module, then the module must be sure to add its isolated service provider to the workflow event manager, via AddServiceProvider.