Factory Automation Transport#
Estimated time to read: 31 minutes
The goal of this tutorial is introduce one of the more common scenarios of the use of the Factory Automation module, which is the coordination of Fleet Managers. Out of the box, Factory Automation already provides support for the use and implementation of this scenario.
Note
This tutorial will assume the user already has some familiarity with Factory Automation and has finished the tutorial for Factory Automation and all tutorials for Connect IoT.
Note
During this tutorial, the Automation Manager will run in console mode to highlight the most important events as they take place.
Overview#
In this example, we will model a machine that will have a dependency to one fleet manager, which will be responsible for feeding raw materials to the machine.
This example is focused on showing how Factory Automation module behaves, so details about the MES model used and custom logic in MES are purely for demonstration purposes and do not serve as examples to be replicated on productive environments.
Overview of MES Model#
In this example, the goal will be to have a material that symbolizes a batch of cookies being processed in a Resource that will be an Oven, with a Resource LoadPort to receive the container dock/undock and a feeder for the raw material coal. It will also require a Resource LoadPort that will be the AGV.
Note
Some details, for example, on lookup table names are omitted feel free to add the names that make more sense to your model.
Creating a Simple MES model#
- Create a Calendar
- Create a Facility
- Create an Area
- Create a Step -
Oven - Create a Flow with the Step -
Oven - Create a Resource -
Oven - Create a Service that will link the Resource
Ovento the StepOven - Create a Step -
Coal Feed - Create a Flow for the Step -
Coal Feed - Create a Resource Consumable Feed -
Coal Feeder - Create a Service that links the Resource
Coal Feederwith the StepCoal Feed - In the
OvenResource, Manage Consumable Feeds and add theCoal Feeder - Create a Resource LoadPort -
Coal LoadPort - Add Resource -
Coal LoadPortas SubResource of the Resource -Oven
At the end of these steps, the system will have a Resource - Oven with a Resource LoadPort - Coal LoadPort and a Resource Consumable Feed - Coal Feeder. It will also have the necessary flows, steps and the services to link them both.
Creating Materials#
- Create a Product -
Cookies - With the
Default Start Flow Paththe Flow for the Step -Oven - Create a Product -
Coal - With the
Default Start Flow Paththe Flow for the Step -Coal - Create a Material -
CookieBatch - With Product -
CookieandQuantity-100 - Create a Material -
Material Coal - With the Product -
CoalandQuantity-100 - Create a Container -
CoalContainer001 - Manage Positions of the Container and add to the
CoalContainer001the MaterialMaterial Coal - Create BOM -
BOM Cookies - BOM Item
- Product -
Coal- Quantity -
0.3andSource Step-Coal Feed
- Quantity -
- Product -
- In the Step -
Ovenadd the BOM Context with theBOM CookiesandAssembly Type-Automatic at Track In(this assembly type will consume automatically according to the BOM when the Material is tracked in a Resource)
The model now has a Material - CookieBatch ready to be dispatched to the Resource - Oven. The Oven has an empty LoadPort and an empty Feeder.
Use Case - Raw Material Replenishing#
Overview#
The use case of the replenishing will consist on checking if there is any material in the consumable feed when the material is dispatched. If there is no material in the feeder, request material from the fleet manager of the raw materials. This a very simple use case, since in real scenarios typically there needs to be more resolution on when to refill and what product will refill which consumable feed.
After the job is created, the fleet manager will assign a robot to the job created. It will then pick the Container - CoalContainer001 with the Material - Material Coal. Next step it will dock the Container in the load port and then attach the material to the Coal Feeder. The last step will be for the robot to finish the job, by returning to a default position.
Request Material#
This is the first step in the replenishing lifecycle. We will create a DEE Action, that is appended on the Post of the Dispatch Materials to create a new request material job.
Create Request Material - IoTEventDefinition#
Create an IoT Event Definition - Request Material to define the structure of the job. It will have the Scope set to Factory Automation and the properties FleetManager, Resource and ResourceFeeder all defined as string. Notice that here is where we will define all the context the job requires to be able to execute. The required context to execute will depend on the particular case that is being addressed.
Create a DEE Action - Check If Materials On Feeder On Dispatch#
DEE Actions are the mechanism in the MES where we can add customization hooks to default system functionality. These hooks can be on the beginning of the execution Pre or in the end Post. In this particular case, we will append in the Post of the service DispatchMaterials.
Note
It's a good practice to add a prefix for DEEs that are not system made to make it distinguishable (i.e Custom).
- Go to
Administration > DEE Actions - Select
New - Give as Name
CheckIfMaterialsOnFeedOnDispatch, for now the classification and action group is not important
The DEE code is split between two important sections: the condition phase and the execution phase. Only if the condition phase returns with true, will the execution phase be invoked. In the history of an action (i.e TrackIn) in the MES, it will be explicit if a DEE was evaluated and eventually if it was executed. For more information on DEEs, see DEE Actions.
The context of the DEE is present on the Inputs dictionary. This context will depend on to what service the DEE is appended. The system provides helpful information on what is in the context by expanding the left pane after adding the action group.
Let's add the Action Group to our DEE.
- Go to the
Detailstab - select
Addon the Action Group - Search for
MaterialManagement.MaterialManagementOrchestration.DispatchMaterials.Post. If the action group is not present: - Go to
Administration > DEE Actions - Select the Settings (three vertical dots) next to the
Action Groups - Select
Add new Action Group - Give as
Name-MaterialManagement.MaterialManagementOrchestration.DispatchMaterials.Post - Select
Create - Check it and select
Add
In order to find the action group where we want to append our DEE, you can consult the API documentation ⧉, or analyze the history whenever the action that you are interested is executed and it will be apparent what are multiple sub-actions that you can append your business logic. Note also that in the DEE in the code view, in the right pane it Input Parameters, it will now show all available parameters.
Starting on the code for the Test Condition Code. We want to validate that the Inputs that we are interested are correct, to validate we should process and then pass that value to our DEE context.
TestCondition Code:
/// <summary>
/// Summary text: Request Material if Feeder has no consumables
/// Actions groups:
/// * MaterialManagement.MaterialManagementOrchestration.DispatchMaterials.Post
/// Depends On:
/// Is Dependency For:
/// Exceptions:
/// </summary>
var serviceProvider = (IServiceProvider)Input["ServiceProvider"];
var utilitiesDEEContext = serviceProvider.GetService<IDeeContextUtilities>();
// Validate input
if (Input["DispatchMaterialsInput"] is not DispatchMaterialsInput dispatchMaterialsInput)
{
throw new ArgumentNullCmfException("DispatchMaterialsInput");
}
// Retrieve resource
var resource = dispatchMaterialsInput.Materials.FirstOrDefault().Value.Resource;
utilitiesDEEContext.SetContextParameter("CheckIfMaterialsOnFeedOnDispatch_Inputs", new Dictionary<string, object>()
{
{ "CheckIfMaterialsOnFeedOnDispatch_Resource", resource },
});
return true;
Execution Code:
// System
UseReference("", "System.Linq");
UseReference("", "System.Data");
// CMF
UseReference("Cmf.Navigo.BusinessOrchestration.dll", "Cmf.Navigo.BusinessOrchestration.MaterialManagement.InputObjects");
UseReference("Cmf.Navigo.BusinessOrchestration.dll", "Cmf.Navigo.BusinessOrchestration.Abstractions");
UseReference("Cmf.Foundation.BusinessObjects.dll", "Cmf.Foundation.BusinessOrchestration.DataPlatform.InputObjects");
UseReference("Cmf.Foundation.BusinessObjects.dll", "Cmf.Foundation.BusinessOrchestration.DataPlatform.OutputObjects");
UseReference("Cmf.Foundation.BusinessOrchestration.dll", "Cmf.Foundation.BusinessOrchestration.DataPlatform.Domain");
UseReference("Cmf.Foundation.BusinessOrchestration.dll", "Cmf.Foundation.BusinessOrchestration.Abstractions");
// Common
UseReference("Cmf.Common.CustomActionUtilities.dll", "Cmf.Common.CustomActionUtilities");
UseReference("Cmf.Common.CustomActionUtilities.dll", "Cmf.Common.CustomActionUtilities.Abstractions");
UseReference("Newtonsoft.Json.dll","Newtonsoft.Json");
var serviceProvider = (IServiceProvider)Input["ServiceProvider"];
var deeUtilities = serviceProvider.GetService<IDeeContextUtilities>();
var entityFactory = serviceProvider.GetService<IEntityFactory>();
var inputs = deeUtilities.GetContextParameter("CheckIfMaterialsOnFeedOnDispatch_Inputs") as Dictionary<string, object>;
var resource = inputs["CheckIfMaterialsOnFeedOnDispatch_Resource"] as IResource;
// Retrieve Consumable Feeders for the Table for Resource
INgpDataSet feedersResult = resource.GetConsumableFeeds(null, out _);
DataSet feedersList = NgpDataSet.ToDataSet(feedersResult);
IResourceCollection feeders = entityFactory.CreateCollection<IResourceCollection>();
// Validate Resource has Feeders
if (feedersList.HasData())
{
// Iterate Consumable Feeders for the Resource and Retrieve the Entity values
foreach (DataRow dataRow in feedersList.Tables[0].Rows)
{
string subResourceName = dataRow["SubResourceTargetEntityName"].ToString();
if (!string.IsNullOrWhiteSpace(subResourceName))
{
IResource feeder = entityFactory.Create<IResource>();
feeder.Name = subResourceName;
feeders.Add(feeder);
}
}
if (feeders.Any())
{
// Load Feeders of the Resource
feeders.Load();
// Load Relation between Resource Feeders and Materials
feeders.LoadRelations("MaterialResource");
// Retrieve empty feeders
feeders.Where(feeder => !feeder.RelationCollection.ContainsKey("MaterialResource")).ToList().ForEach(feeder => {
// Post a new Job if Feeder has no Consumables
// Will create the Request Material Job
var dataPlatform = serviceProvider.GetService<IDataPlatformManagementOrchestration>();
AppProperties appProperties = new AppProperties()
{
ApplicationContext = "Transport Request from MES for Request Raw Material",
ApplicationName = "FleetManager-RawMaterial",
EventDefinition = "RequestMaterial",
EventTime = DateTime.Now
};
Dictionary<string, object> dataToSend = new Dictionary<string, object>();
dataToSend.Add("Resource", resource.Name);
dataToSend.Add("ResourceFeeder", feeder.Name);
dataToSend.Add("FleetManager", "FleetManager-RawMaterial");
PostEventInput postEventInput = new PostEventInput
{
AppProperties = appProperties,
Data = Newtonsoft.Json.Linq.JObject.Parse(JsonConvert.SerializeObject(dataToSend)),
};
PostEventOutput postEventOutput = dataPlatform.PostEvent(postEventInput);
});
}
}
The goal is to check if there are feeders without materials attached, then create a new request material job for each feeder without material.
Now we can dispatch the Material - Cookie Batch and see if there is any job created. In order to check the Job Queue:
- Go to the
Automationleft pane - Select Factory Automation
- Select
Views > IoT Events Queue
A job has been created, but it has not been processed. This is expected as currently we have no Factory Automation worker running and processing jobs and we have no definition of what Factory Automation should do with the request material job.
Create a Factory Automation Worker#
The Factory Automation worker will be responsible for consuming and executing jobs from the queue. The worker will have a limit on how many concurrent jobs can be executed at any given time. It is also important to understand that jobs assigned to a worker in case of the worker process failing will be invalidated. It is then a good practice to balance the amount of controllers with workers in your Factory Automation to mitigate risk.
Create a Factory Automation - Protocol#
- Go to
Business Data > Automation Protocol - Create
New - Name
FactoryAutomation - Select
Packagefor the driver of Factory Automation - Select the version available
- In the Parameters
- Notice that the maximum concurrent jobs is of
10 - select
NextandCreate
Create a Factory Automation - Driver Definition#
- Go to
Business Data > Automation Driver Definition - Create
New - Name
FactoryAutomationWorker_DriverDefinition - Select Automation Protocol
FactoryAutomation - Entity Type Resource
- select
NextandCreate
The Factory Automation driver is a particular case where it does not need any further definitions on the driver definition.
Create a Factory Automation - Controller#
- Go to
Business Data > Automation Controller - Create
New - Name
FactoryAutomationWorker_Controller - Select Version
- Entity Type Resource
- Add Driver Definition
- Name
Handler - Driver Definition -
FactoryAutomationWorker_DriverDefinition - select
NextandCreate
Factory Automation requires access to the database, the recommended way to store passwords in the system is by using secure strings in the Configuration Entries. For the tutorial either add directly in the settings the configurations of your database or create configuration entries for each setting and populate them in the configurations. For more information, go to the Get Configurations task.
Create a Factory Automation - Controller Instance#
- Create a Resource
FactoryAutomationWorkerwith ProcessingTypeComponent - Go to
Business Data > Automation Manager - Create
New - Name
FactoryAutomation - Logical Address
FactoryAutomation -
Select Version
-
Go to
Business Data > Automation Controller > FactoryAutomationWorker_Controller - select
Connect - Select the Resource
FactoryAutomationWorkerand Automation ManagerFactoryAutomation - In the Drivers also select the Resource
FactoryAutomationWorker - Go to
Business Data > Automation Manager > FactoryAutomation - Click
Download - Run the Automation Manager
- Unzip the Automation Manager
- Go to
scripts - Run
StartConsole.bat
In the MES system we can see that the Factory Automation Worker is Communicating and that our job is now on the Job List.
In the Job List, let's stop the job for now, as we still need the workflow for the job itself. Select the Job and select Stop.
Request Material - Create Job Workflow - Main#
- Go to
Business Data > Automation Controller - Create
New - Select Scope
FactoryAutomation - Add IoTEventDefinition
RequestMaterial
The workflow for a job must have a start and an end. The workflow created by default will already place the tasks On Job Start and JobEnd to signal this requirement. The job is agnostic to any particular driver and does not communicate to a machine but serves as a lifecycle of actions. The first action that we will perform is notifying whomever is serving the fleet manager.
The Transport Requester task will provide the beginning and the end of the transport. It will notify the listener of the new job and will wait for a Transport Reply to notify it that the job has finished. We will drag and drop the Transport Requester this task will passthrough the job context and give the success or error of the job. We will also store the FleetManager of the job.
Note
In the scope of this tutorial we will have just the beginning of the transport as a Transport Requester and then receive information that comes to the fleet manager for the rest of the lifecycle, but depending on your scenario, you can have as much bi-directional communication as needed.
Note
In order for the jobs to be restartable, you can add a Checkpoint task, with the job context.
Request Material - Create Job Workflow - Transport Interactions#
There are four different actions in the job lifecycle that apply to this use case:
Robot Assigned- when the fleet manager assigns a robotPicked Container- when the robot picks a filled containerDrop Container- when the robot docks a containerDrop Material- when the material is attached to the feeder
Factory Automation provides an easy way to have touch points between the fleet manager and the job execution with the task Transport Receiver. Using the Transport Receiver with command Status Update and the flag Include Id in Command as true, we receive information and parse it as outputs, for only our job. The Status Update will be a notification, whereas an Interaction will register a callback. The interactions are dependent on the fleet manager, so we will have to activate the receiver only when we know the fleet manager, this is why we stored the fleet manager in the Main workflow. We will also want to store which robot is being used in the job execution so we can pass along that context for the other actions.
Note
The flag Include Id in Command is very important. If the flag is set as false it means it will be a broadcast to all. If the flag is true and the context is inside a controller of scope - Factory Automation, it will not be mandatory to provide an id, it will inherit it from the job context. If, like the images in this tutorial the id or the setting is not available, it means you are currently in a version that does not support this feature and the controller will have to manage this mechanism.
- Create new page
Transport Interactions - Use the
Retrieve Data - Settings
- Trigger on Store -
true
- Trigger on Store -
- Outputs
- Add
- Name -
FleetManager - Identifier -
FleetManager - Type -
String
- Name -
- Add
- Drag and Drop the
Transport Receivertask - Settings
- Auto-Activate -
False - Command -
Status Update
- Auto-Activate -
- Outputs
- Name - Robot with type
String - Name - Container with type
String - Name - Destination with type
String
- Name - Robot with type
- Link the
Retrieve DataoutputFleetManagerto the inputFleetManagerTypeand to theActivate
- Now use the
Switchtask to control the flow by type - Input
- Name
Type - Type -
String
- Name
- Outputs (the settings pattern is the same for all outputs)
- RobotAssigned
- Equals -
RobotAssigned - Type -
Boolean - Value -
true
- Equals -
- PickedContainer
- DropContainer
- DropMaterial
- RobotAssigned
Now depending on the type of interaction, the job will perform different actions.
For the RobotAssigned, the job will persist internally that information.
- Use the
Store Datatask to store the RobotAssigned - Settings
- Working Mode -
StoreOnActive
- Working Mode -
- Inputs
- Add
- Name -
RobotAssigned - Identifier -
RobotAssigned - Type -
String - Storage -
Temporary
- Name -
- Add
- Link the
Robotoutput from the taskTransport Receiverto the inputRobot Assignedof theStore Datatask - Link the
RobotAssignedoutput from the taskSwitchto the inputActivateof theStore Datatask
For the next actions we will require integration with the MES.
Create a DEE Action - RobotPickedContainer#
The DEE Action will be responsible for docking the container selected by the fleet manager in the AGV.
Note
It's a good practice to add a prefix for DEEs that are not system made to differentiate (i.e Custom).
- Go to
Administration > DEE Actions - Select
New - Give as Name
RobotPickedContainer - Classification
ConnectIoT
In this case we will not require an action group, this DEE will not be appended to a system service but will be execute directly via a business Rule.
Execution Code:
var serviceProvider = (IServiceProvider)Input["ServiceProvider"];
var entityFactory = serviceProvider.GetService<IEntityFactory>();
IContainer container = entityFactory.Create<IContainer>();
container.Name = Input["Container"] as string;
IResource robot = entityFactory.Create<IResource>();
robot.Name = Input["Robot"] as string;
container.Load();
// Load the relation between the Container and the Materials in the Container
container.LoadRelations("MaterialContainer");
robot.Load();
// Validate: Only Pick filled containers
if(!container.ContainerMaterials.Any()) {
throw new Exception("Container must have Materials");
}
// If container is docked, undock it
if(container.ResourceAssociationType == ContainerResourceAssociationType.DockedContainer){
container.Undock();
}
// Dock on Robot
container.Dock(robot);
The code is very simple, we validate the container has materials and then dock it in the robot. If the container is already docker somewhere else, we will undock it.
Create a Rule to encapsulate the DEE Action:
- Go to
Business Data > Rule - Create
New - Name -
RobotPickedContainer - Scope -
ConnectIoT - DEE Action -
RobotPickedContainer - select
Create
Create a DEE Action - RobotDroppedContainer#
The DEE Action will be responsible for undocking the container of the AGV and docking to the load port of the resource. This DEE Action is very similar to what was done previously, nevertheless it was decided to not merge both to provide clear different hooks. This means that if any further logic is needed we already have entry points available
- Go to
Administration > DEE Actions - Select
New - Give as Name
RobotDroppedContainer - Classification
ConnectIoT
In this case we will not require an action group, this DEE will not be appended to a system service but will be execute directly via a business Rule.
Execution Code:
var serviceProvider = (IServiceProvider)Input["ServiceProvider"];
var entityFactory = serviceProvider.GetService<IEntityFactory>();
IContainer container = entityFactory.Create<IContainer>();
container.Name = Input["Container"] as string;
IResource robot = entityFactory.Create<IResource>();
robot.Name = Input["Robot"] as string;
IResource destinationResource = entityFactory.Create<IResource>();
destinationResource.Name = Input["Destination"] as string;
container.Load();
// Load the relation between the Container and the Materials in the Container
container.LoadRelations("MaterialContainer");
destinationResource.Load();
// Validate: Only Drop filled containers
if(!container.ContainerMaterials.Any()) {
throw new Exception("Container must have Materials");
}
// Undock from Robot
if(container.ResourceAssociationType == ContainerResourceAssociationType.DockedContainer) {
container.Undock();
}
// Dock on Load Port
container.Dock(destinationResource);
The code is analogous to the RobotPickedContainer, but it this case the container is moved from the robot in to the Resource Load Port.
Create a Rule to encapsulate the DEE Action:
- Go to
Business Data > Rule - Create
New - Name -
RobotDroppedContainer - Scope -
ConnectIoT - DEE Action -
RobotDroppedContainer - Select
Create
Create a DEE Action - RobotDropMaterial#
The DEE Action will be responsible for attaching the Material in the Container to the Resource Feeder of the main resource.
- Go to
Administration > DEE Actions - Select
New - Give as Name
RobotDropMaterial - Classification
ConnectIoT
In this case we will not require an action group, this DEE will not be appended to a system service but will be execute directly via a business Rule.
Execution Code:
var serviceProvider = (IServiceProvider)Input["ServiceProvider"];
var entityFactory = serviceProvider.GetService<IEntityFactory>();
IContainer container = entityFactory.Create<IContainer>();
container.Name = Input["Container"] as string;
IResource robot = entityFactory.Create<IResource>();
robot.Name = Input["Robot"] as string;
IResource destinationFeeder = entityFactory.Create<IResource>();
destinationFeeder.Name = Input["Destination"] as string;
container.Load();
// Load the relation between the Container and the Materials in the Container
container.LoadRelations("MaterialContainer");
destinationFeeder.Load();
if(!container.ContainerMaterials.Any()) {
throw new Exception("Container must have Materials");
} else {
// Create Input for AttachConsumables
Dictionary<IMaterial, IAttachConsumableParameters> materialsToAttach = new Dictionary<IMaterial, IAttachConsumableParameters>();
foreach(var containerMaterial in container.ContainerMaterials) {
materialsToAttach.Add(containerMaterial.SourceEntity, new AttachConsumableParameters());
}
// Attach Container Materials to the Feeder
destinationFeeder.AttachConsumables(materialsToAttach);
}
The code is similar to what we saw in the other actions, but in this action we don't want to perform dock or undock, but we want to retrieve the Materials in the Container and attach them to the Resource Feeder.
Create a Rule to encapsulate the DEE Action:
- Go to
Business Data > Rule - Create
New - Name -
RobotDropMaterial - Scope -
ConnectIoT - DEE Action -
RobotDropMaterial - select
Create
Request Material - Create Job Workflow - Transport Interactions - Actions#
Now we can use the DEE Actions in the workflow of the Job. Going back to the workflow of the Request Material Controller in the page Transport Interactions.
- Drag and drop task
Execute Action - Description -
RobotPickedContainer - Settings
- Rule -
RobotPickedContainer
- Rule -
- Inputs
- Add
- Name -
Container - Type -
String
- Name -
- Add
- Name -
Robot - Type -
String
- Name -
- Add
- Link the output Container of the
Transport Receiverto the input Container of theExecute ActionRobotPickedContainer - Link the output
Robotof theRetrieve Datato the inputRobotof theExecute ActionRobotDroppedContainer - Link the output
PickedContainerto the inputActivateof theExecute ActionRobotPickedContainer - Drag and drop task
Execute Action - Description -
RobotDroppedContainer - Settings
- Rule -
RobotDroppedContainer
- Rule -
- Inputs
- Add
- Name -
Container - Type -
String
- Name -
- Add
- Name -
Robot - Type -
String
- Name -
- Add
- Name -
Destination - Type -
String
- Name -
- Add
- Link the output Container of the
Transport Receiverto the input Container of theExecute ActionRobotDroppedContainer - Link the output
Destinationof theTransport Receiverto the input Container of theExecute ActionRobotDroppedContainer - Link the output
Robotof theRetrieve Datato the inputRobotof theExecute ActionRobotDroppedContainer - Link the output
DroppedContainerto the inputActivateof theExecute ActionRobotDroppedContainer - Drag and drop task
Execute Action- Description -
RobotDropMaterial - Settings
- Rule -
RobotDropMaterial - Inputs
- Add
- Name -
Container - Type -
String
- Name -
- Add
- Name -
Robot - Type -
String
- Name -
- Add
- Name -
Destination - Type -
String
- Name -
- Description -
- Link the output Container of the
Transport Receiverto the input Container of theExecute ActionRobotDropMaterial - Link the output
Destinationof theTransport Receiverto the input Container of theExecute ActionRobotDropMaterial - Link the output
Robotof theRetrieve Datato the inputRobotof theExecute ActionRobotDropMaterial -
Link the output
PickedContainerto the inputActivateof theExecute ActionRobotDropMaterial -
Use the
Store Datatask to store the RobotAssigned- Settings
- Working Mode -
StoreOnActive - Inputs
- Add
- Name -
RobotAssigned - Identifier -
RobotAssigned - Type -
String - Storage -
Temporary
- Name -
- Link the
Robotoutput from the taskTransport Receiverto the inputRobot Assignedof theStore Datatask - Link the
RobotAssignedoutput from the taskSwitchto the inputActivateof theStore Datatask
Fleet-Manager - Raw Material#
At this moment we have a consumer for the jobs and we have a workflow with the lifecycle of the job. Now we require the integration to the Fleet Manager. For this example we will assume that the fleet manager runs on MQTT and has the following topics:
- Topic -
FleetManager.Robot.Assign - Payload -
{"JobId":"<jobID>","Robot":"<robot>"} - Topic -
FleetManager.Robot.PickContainer - Payload -
{"JobId":"<jobID>","Container":"<container>"} - Topic -
FleetManager.Robot.DropContainer - Payload -
{"JobId":"<jobID>","Container":"<container>","Destination":"\<destination>"} - Topic -
FleetManager.Robot.PickContainer - Payload -
{"JobId":"<jobID>","Container":"<container>","Destination":"<destination>"} - Topic -
FleetManager.Robot.ArrivedAtDestination - Payload -
{"JobId":"<jobID>","Container":"<container>"}
Fleet-Manager - Raw Material - Protocol#
- Go to
Business Data > Automation Protocol - Create
New - Name
MQTT Protocol - Select
Packagefor the driver of the mqtt driver - Select the version available
- select
NextandCreate
Fleet-Manager - Raw Material - Driver Definition#
- Go to
Business Data > Automation DriverDefinition - Create
New - Name
Fleet-Manager-RawMaterial_DriverDefinition - Select Automation Protocol
MQTT Protocol - Entity Type Resource
- select
Next - Add properties (all properties will have the same details, except the one's mentioned otherwise):
- Name -
RequestMaterial- Topic Name -
Machine1.Request.Material - Type -
String - Protocol Data Type -
String
- Topic Name -
- Name -
RobotAssigned- Topic Name -
FleetManager.Robot.Assign
- Topic Name -
- Name -
PickedContainer- Topic Name -
FleetManager.Robot.PickContainer
- Topic Name -
- Name -
DropContainer- Topic Name -
FleetManager.Robot.DropContainer
- Topic Name -
- Name -
DropMaterial- Topic Name -
FleetManager.Robot.DropMaterial
- Topic Name -
- Name -
ArrivedAtDestination- Topic Name -
FleetManager.Robot.ArrivedAtDestination
- Topic Name -
- Add events
- Name -
RobotAssigned - Name -
PickedContainer - Name -
DropContainer - Name -
DropMaterial - Name -
ArrivedAtDestination - Add event properties with matching properties
Fleet-Manager - Raw Material - Controller#
- Go to
Business Data > Automation Controller - Create
New - Entity Type Resource
- Add Driver Definition
- Name
Handler - Driver Definition -
FleetManager-RawMaterial_DriverDefinition - Select
NextandCreate
Fleet-Manager - Raw Material - Controller - Setup#
Feel free to use also the Get Configurations task to control the settings of the On Equipment Setup. This is a simple case so for this case, let's add the address as localhost and keep the default settings.
Fleet-Manager - Raw Material - Controller - Request Material#
Create a new page Request Material. The first action in the fleet manager will be to notify the fleet manager of a request material. In this workflow we will add a Transport Receiver task for the fleet manager FleetManager-RawMaterial with command Transportation. This is the task that will be notified by the Transport Requester of the Request Material job. Then we will publish to an mqtt topic Machine1.Request.Material the required payload, this is mapped by executing the Set Equipment Properties task for the Automation Property RequestMaterial.
Fleet-Manager - Raw Material - Controller - RobotAssigned#
The next actions will follow a very similar pattern. Create a page RobotAssigned When we receive an On Equipment Event task for the Event RobotAssigned we will notify our job, using the Transport Requester task with command interaction. We want to just notify the job that is managing this lifecycle so add the flag Include Id in Command as true and we will have to fill the Id input. It will also be helpful to add a simple Code task, that will transform the string received into a JSON object. We will also apply an AnyToConstant converter connecting to the type input of the Transport Requester task, this converter will apply a string with value RobotAssigned. The type will be used in the job to interpret to do with the interaction.
Note
The flag Include Id in Command is very important. If the flag is set as false it means it will be a broadcast to all. If the task is true a job id must be provided. This is different than when we were in the context of the job, if we are in the context of Connect IoT we must provide the Id.
Content of the Code task:
public async main(inputs: any, outputs: any): Promise<any> {
const data = JSON.parse(inputs.Value);
outputs.jobId.emit(data["JobId"])
outputs.data.emit(JSON.parse(inputs.Value))
}
The code task will have the string input Value and the Output jobId and data. The Success output of the Code task will link to the Activate of the Transport Requester.
Fleet-Manager - Raw Material - Controller - Others#
The next actions are defined in the same fashion, changing the event that triggers them and changing the converter AnyToConstant to feed the different types.
Fleet-Manager - Raw Material - Controller - Arrived At Destination#
The event ArrivedAtDestination will mark the end of the job. It will no longer call an interaction, but will call a Transport Replier, this task will signal the end of the job to the Transport Requester task of the main job.
Fleet-Manager - Raw Material - Controller Instance#
- Create a Resource
FleetManager-RawMaterialwith ProcessingTypeComponent - Go to
Business Data > Automation Manager - Create
New - Name
FleetManager-RawMaterial - Logical Address
FleetManager-RawMaterial -
Select Version
-
Go to
Business Data > Automation Controller > FleetManager-RawMaterial_Controller - Select
Connect - Select Resource
FleetManager-RawMaterialand Automation ManagerFleetManager-RawMaterial - In the Drivers also select the Resource
FleetManager-RawMaterial - Go to
Business Data > Automation Manager > FactoryAutomation - Click
Download - Run the Automation Manager
- Unzip the Automation Manager
- Go to scripts
- Run StartConsole.bat
Start the MQTT Broker#
For this implementation we will use an MQTT broker Mosquitto ⧉. Download and install mosquitto.
In order to start the broker with mosquitto, go to where mosquitto was installed.
Note
If the mosquitto was installed as a windows service you won't need to start a console for the mosquitto broker.
To start the broker open a powershell console:
Now the system should have the fleet manager and the Factory Automation worker communicating.
Executing a Job Lifecycle#
All the components have been created so we can perform the full job lifecycle.
Let's subscribe to the topic where we will send the request material, so we can confirm that we will receive the correct payload:
Dispatch the Material - Cookie Batch to the Oven Resource. As we have seen previously a job will be created and will be processed.
Now we can see that the worker processed the job.
In the worker it logs that the message was sent successfully so let's see the fleet manager.
Lastly in the mqtt subscriber we will see that a message arrived as expected.
We have finished the request of the material to the fleet manager. In the next step, the fleet manager will reply with the assignment of a robot. In order to simulate this let´s use mosquitto. We will want to send a message to the topic of the robot assignment event FleetManager.Robot.Assign and the payload must have the jobId and the robot. We will use AGV1 as the name of the robot to match what we created in the MES and the jobId we can see in the logs that it was 2310311623180000077, in your case the job id may be different.
cd "C:\Program Files\mosquitto"
.\mosquitto_pub.exe -h localhost -t "FleetManager.Robot.Assign" -m '{"JobId":"2310311623180000077","Robot":"AGV1"}'
Now the fleet manager will receive the event and forward it to the job.
In the job, it will store the information about the robot assigned.
The next action will be the robot picking a container, we will the container we created CoalContainer001.
.\mosquitto_pub.exe -h localhost -t "FleetManager.Robot.PickContainer" -m '{"JobId":"2310311623180000077","Container":"CoalContainer001"}'
Now the fleet manager will receive the event and forward it to the job.
The worker will receive the message and invoke the execute action for the robot picked container.
In the MES we can now see that the Resource AGV1 will now have a container docked.
In the next action the robot will drop the container in the load port of the Resource Oven.
.\mosquitto_pub.exe -h localhost -t "FleetManager.Robot.DropContainer" -m '{"JobId":"2310311623180000077","Container":"CoalContainer001", "Destination":"Coal LoadPort"}'
The fleet manager will forward the message to the job.
The worker will receive the message and invoke the execute action for the robot dropped container.
In the MES we can now see that the Resource AGV1 will now have no docked and the container will now be at the Resource Coal LoadPort.
Now the robot will drop the material into the feeder Resource Coal Feeder.
.\mosquitto_pub.exe -h localhost -t "FleetManager.Robot.DropMaterial" -m '{"JobId":"2310311623180000077","Container":"CoalContainer001", "Destination":"Coal Feeder"}'
The fleet manager will forward the message to the job.
The worker will receive the message and invoke the execute action for the robot dropped material.
In the Resource Oven if we select Manage Consumables we can see that now we have a material in our consumable feed.
.\mosquitto_pub.exe -h localhost -t "FleetManager.Robot.ArrivedAtDestination" -m '{"JobId":"2310311623180000077","Container":"CoalContainer001"}'
The fleet manager will forward the message to the job.
The job Transport Requester will be notified and emit outputs. This will signal the end of the job.
In the Factory Automation GUI we can now see that our job is Processed
Handling Failures#
In this tutorial we did not address error scenarios. If the job is canceled, you can use the Transport Replier task to signal the end of the job, but with the command CancelTransportation. If the job is in error, you can use the Transport Replier task with the command Transportation, but with the input error filled in. The error messages will then be visible in the Factory Automation GUI.
Info
More information on the Factory Automation section of the User Guide.












































