Skip to main content

Create Items with Scripting

We'll now use scripts to build a more complete item model for CoreForge.

The items go into the item classes CoreForge\RM, CoreForge\WIP\Coatings, CoreForge\WIP\Machined, CoreForge\WIP\Coated, and CoreForge\FG, which you created in Base Configuration; the script creates any that are missing. SC-2024 and SC-2024-STD, and their production rates on Machine 1 and Spray Line 1, also already exist from Base Configuration, so the script leaves them out.

Create items and rates from script
  1. Copy the following script to the Script Console in the Designer and run it. It creates or updates the items and their production rates on Machine 1, Spray Line 1, and Manual Pack 1. It's safe to run again: anything already set up as listed is left unchanged.
View generateItemsAndRates script
generateItemsAndRates.py
"""
"""
Create or update the CoreForge item model and its production rates.

Run in the Ignition Designer Script Console. It works in four steps:
1. Check that the assets, rate period and item source the rates need exist,
so the script stops with a clear message before changing anything. Then
create any of the units the rates use (lbs, units, boxes) that are missing.
2. Item classes: for each item class path used below (e.g. CoreForge\\WIP\\Coated),
check whether it exists and create it if it doesn't. checkItemClassPath
creates any missing parent classes too.
3. Items: look each item up by name (item names are unique across classes).
Update it if its description, class, colour or enabled flag differ from the
definition; create it if it doesn't exist.
4. Item-asset rates: a rate says an item can be made on an asset and how fast
(standard and scheduled rate per minute), in which units, and how many input
units make one output unit (packageCount). Update the rate if it differs
from the definition; create it if the item has no rate on that asset yet.

Unit profiles describe each asset's units once, instead of on every rate:
machining (Machine 1): lbs in, units out, lbs of waste
coating (Spray Line 1): units in, units out, units of waste
packing (Manual Pack 1): units in, boxes out, units of waste

SC-2024 and SC-2024-STD, and their rates on Machine 1 and Spray Line 1, were
created in Base Configuration, so they're not listed.

Safe to run again: a second run finds everything in place and changes nothing.
Nothing is deleted.
"""
import sys

userId = system.kanoa.security.getIDPUserId({'userName': 'SYSTEM'})

# The items: name, description, full item class path and colour
itemDefinitions = [
# Raw Materials
{'itemName': 'AL6061', 'itemDescription': 'Aluminum 6061 Billet', 'itemClassPath': 'CoreForge\\RM', 'itemColor': '#A7A9AC'},
{'itemName': 'AL7075', 'itemDescription': 'Aluminum 7075 Billet', 'itemClassPath': 'CoreForge\\RM', 'itemColor': '#8A8D91'},
{'itemName': 'AL2024', 'itemDescription': 'Aluminum 2024 Billet', 'itemClassPath': 'CoreForge\\RM', 'itemColor': '#C0C2C5'},

# Shaft Cores
{'itemName': 'SC-6061', 'itemDescription': '6061 Shaft Core', 'itemClassPath': 'CoreForge\\WIP\\Machined', 'itemColor': '#9FA1A4'},
{'itemName': 'SC-7075', 'itemDescription': '7075 Shaft Core', 'itemClassPath': 'CoreForge\\WIP\\Machined', 'itemColor': '#7C7F83'},
# SC-2024 already exists (created in Base Configuration)

# Coupling Pins
{'itemName': 'CP-6061', 'itemDescription': '6061 Coupling Pin', 'itemClassPath': 'CoreForge\\WIP\\Machined', 'itemColor': '#8F99A3'},
{'itemName': 'CP-7075', 'itemDescription': '7075 Coupling Pin', 'itemClassPath': 'CoreForge\\WIP\\Machined', 'itemColor': '#6F7982'},
{'itemName': 'CP-2024', 'itemDescription': '2024 Coupling Pin', 'itemClassPath': 'CoreForge\\WIP\\Machined', 'itemColor': '#A9B2BB'},

# Precision Rods
{'itemName': 'PR-6061', 'itemDescription': '6061 Precision Rod', 'itemClassPath': 'CoreForge\\WIP\\Machined', 'itemColor': '#A39F9C'},
{'itemName': 'PR-7075', 'itemDescription': '7075 Precision Rod', 'itemClassPath': 'CoreForge\\WIP\\Machined', 'itemColor': '#7F7B78'},
{'itemName': 'PR-2024', 'itemDescription': '2024 Precision Rod', 'itemClassPath': 'CoreForge\\WIP\\Machined', 'itemColor': '#BBB7B4'},

# Coating Blends
{'itemName': 'BL-STD', 'itemDescription': 'StandardShield Coating Blend', 'itemClassPath': 'CoreForge\\WIP\\Coatings', 'itemColor': '#4F6D7A'},
{'itemName': 'BL-HRD', 'itemDescription': 'HardShield Coating Blend', 'itemClassPath': 'CoreForge\\WIP\\Coatings', 'itemColor': '#2F3E46'},
{'itemName': 'BL-PRM', 'itemDescription': 'ChemGuard Premium Coating Blend', 'itemClassPath': 'CoreForge\\WIP\\Coatings', 'itemColor': '#1B263B'},

# Coated WIP - Shaft Cores
{'itemName': 'SC-6061-STD', 'itemDescription': '6061 Shaft Core - Standard Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#5FA8D3'},
{'itemName': 'SC-6061-HRD', 'itemDescription': '6061 Shaft Core - Hardened Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#3E92CC'},
{'itemName': 'SC-6061-PRM', 'itemDescription': '6061 Shaft Core - Premium Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#1C6EA4'},

{'itemName': 'SC-7075-STD', 'itemDescription': '7075 Shaft Core - Standard Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#4EA5D9'},
{'itemName': 'SC-7075-HRD', 'itemDescription': '7075 Shaft Core - Hardened Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#2E86AB'},
{'itemName': 'SC-7075-PRM', 'itemDescription': '7075 Shaft Core - Premium Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#1B4F72'},

# SC-2024-STD already exists (created in Base Configuration)
{'itemName': 'SC-2024-HRD', 'itemDescription': '2024 Shaft Core - Hardened Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#4A90E2'},
{'itemName': 'SC-2024-PRM', 'itemDescription': '2024 Shaft Core - Premium Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#2C6FB7'},

# Coated WIP - Coupling Pins
{'itemName': 'CP-6061-STD', 'itemDescription': '6061 Coupling Pin - Standard Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#7FB069'},
{'itemName': 'CP-6061-HRD', 'itemDescription': '6061 Coupling Pin - Hardened Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#5C8A3D'},
{'itemName': 'CP-6061-PRM', 'itemDescription': '6061 Coupling Pin - Premium Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#3F6B2A'},

{'itemName': 'CP-7075-STD', 'itemDescription': '7075 Coupling Pin - Standard Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#6EA85F'},
{'itemName': 'CP-7075-HRD', 'itemDescription': '7075 Coupling Pin - Hardened Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#4E7F3A'},
{'itemName': 'CP-7075-PRM', 'itemDescription': '7075 Coupling Pin - Premium Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#345A28'},

{'itemName': 'CP-2024-STD', 'itemDescription': '2024 Coupling Pin - Standard Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#8BCF75'},
{'itemName': 'CP-2024-HRD', 'itemDescription': '2024 Coupling Pin - Hardened Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#6DAE55'},
{'itemName': 'CP-2024-PRM', 'itemDescription': '2024 Coupling Pin - Premium Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#4F8C3B'},

# Coated WIP - Precision Rods
{'itemName': 'PR-6061-STD', 'itemDescription': '6061 Precision Rod - Standard Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#D9A441'},
{'itemName': 'PR-6061-HRD', 'itemDescription': '6061 Precision Rod - Hardened Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#C68B2C'},
{'itemName': 'PR-6061-PRM', 'itemDescription': '6061 Precision Rod - Premium Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#A66E1F'},

{'itemName': 'PR-7075-STD', 'itemDescription': '7075 Precision Rod - Standard Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#C99A3D'},
{'itemName': 'PR-7075-HRD', 'itemDescription': '7075 Precision Rod - Hardened Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#A87C2B'},
{'itemName': 'PR-7075-PRM', 'itemDescription': '7075 Precision Rod - Premium Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#855F1F'},

{'itemName': 'PR-2024-STD', 'itemDescription': '2024 Precision Rod - Standard Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#E0B35A'},
{'itemName': 'PR-2024-HRD', 'itemDescription': '2024 Precision Rod - Hardened Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#C9983C'},
{'itemName': 'PR-2024-PRM', 'itemDescription': '2024 Precision Rod - Premium Coating', 'itemClassPath': 'CoreForge\\WIP\\Coated', 'itemColor': '#A77A29'},

# ========================
# Finished Goods
# ========================

{'itemName': 'FG-SC-6061-STD-100', 'itemDescription': '100ct 6061 Standard Coated Shaft Cores', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#2E86AB'},
{'itemName': 'FG-SC-6061-HRD-100', 'itemDescription': '100ct 6061 Hardened Coated Shaft Cores', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#1B4F72'},
{'itemName': 'FG-SC-6061-PRM-50', 'itemDescription': '50ct 6061 Premium Coated Shaft Cores', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#154360'},

{'itemName': 'FG-SC-7075-STD-100', 'itemDescription': '100ct 7075 Standard Coated Shaft Cores', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#2874A6'},
{'itemName': 'FG-SC-7075-HRD-100', 'itemDescription': '100ct 7075 Hardened Coated Shaft Cores', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#21618C'},
{'itemName': 'FG-SC-7075-PRM-50', 'itemDescription': '50ct 7075 Premium Coated Shaft Cores', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#1A5276'},

{'itemName': 'FG-SC-2024-STD-100', 'itemDescription': '100ct 2024 Standard Coated Shaft Cores', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#3498DB'},
{'itemName': 'FG-SC-2024-HRD-100', 'itemDescription': '100ct 2024 Hardened Coated Shaft Cores', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#2E86C1'},
{'itemName': 'FG-SC-2024-PRM-50', 'itemDescription': '50ct 2024 Premium Coated Shaft Cores', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#2471A3'},

{'itemName': 'FG-CP-6061-STD-250', 'itemDescription': '250ct 6061 Standard Coated Coupling Pins', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#5C8A3D'},
{'itemName': 'FG-CP-6061-HRD-250', 'itemDescription': '250ct 6061 Hardened Coated Coupling Pins', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#3F6B2A'},
{'itemName': 'FG-CP-6061-PRM-100', 'itemDescription': '100ct 6061 Premium Coated Coupling Pins', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#2F4F1F'},

{'itemName': 'FG-CP-7075-STD-250', 'itemDescription': '250ct 7075 Standard Coated Coupling Pins', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#4F7D36'},
{'itemName': 'FG-CP-7075-HRD-250', 'itemDescription': '250ct 7075 Hardened Coated Coupling Pins', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#386124'},
{'itemName': 'FG-CP-7075-PRM-100', 'itemDescription': '100ct 7075 Premium Coated Coupling Pins', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#29481B'},

{'itemName': 'FG-CP-2024-STD-250', 'itemDescription': '250ct 2024 Standard Coated Coupling Pins', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#6AA84F'},
{'itemName': 'FG-CP-2024-HRD-250', 'itemDescription': '250ct 2024 Hardened Coated Coupling Pins', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#558B2F'},
{'itemName': 'FG-CP-2024-PRM-100', 'itemDescription': '100ct 2024 Premium Coated Coupling Pins', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#33691E'},

{'itemName': 'FG-PR-6061-STD-50', 'itemDescription': '50ct 6061 Standard Coated Precision Rods', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#C68B2C'},
{'itemName': 'FG-PR-6061-HRD-50', 'itemDescription': '50ct 6061 Hardened Coated Precision Rods', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#A66E1F'},
{'itemName': 'FG-PR-6061-PRM-25', 'itemDescription': '25ct 6061 Premium Coated Precision Rods', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#855F1F'},

{'itemName': 'FG-PR-7075-STD-50', 'itemDescription': '50ct 7075 Standard Coated Precision Rods', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#B9770E'},
{'itemName': 'FG-PR-7075-HRD-50', 'itemDescription': '50ct 7075 Hardened Coated Precision Rods', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#9C640C'},
{'itemName': 'FG-PR-7075-PRM-25', 'itemDescription': '25ct 7075 Premium Coated Precision Rods', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#7E5109'},

{'itemName': 'FG-PR-2024-STD-50', 'itemDescription': '50ct 2024 Standard Coated Precision Rods', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#D68910'},
{'itemName': 'FG-PR-2024-HRD-50', 'itemDescription': '50ct 2024 Hardened Coated Precision Rods', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#B7950B'},
{'itemName': 'FG-PR-2024-PRM-25', 'itemDescription': '25ct 2024 Premium Coated Precision Rods', 'itemClassPath': 'CoreForge\\FG', 'itemColor': '#9A7D0A'}
]

# The assets the rates are for
ASSETS = {
'Machine 1': 'CoreForge\\Philadelphia\\Machining\\Machine 1',
'Spray Line 1': 'CoreForge\\Philadelphia\\Coating\\Spray Line 1',
'Manual Pack 1': 'CoreForge\\Philadelphia\\Finishing\\Manual Pack 1',
}

# Units for each kind of rate: infeed, outfeed and waste
unitProfiles = {
'machining': {'infeed': 'lbs', 'outfeed': 'units', 'waste': 'lbs'},
'coating': {'infeed': 'units', 'outfeed': 'units', 'waste': 'units'},
'packing': {'infeed': 'units', 'outfeed': 'boxes', 'waste': 'units'},
}
RATE_PERIOD = 'Minute' # standardRate and scheduleRate are per minute
RATE_SOURCE = 'output' # rates are measured on the outfeed count

# The rates: item, asset, unit profile, standard and scheduled rate, and packageCount
# (Machine 1: lbs per output unit; Spray Line 1: input units per output unit;
# Manual Pack 1: input units per output box)
rateDefinitions = [
# =========================
# Machine 1 - Machined WIP
# lbs -> units
# packageCount = lbs per output unit
# =========================

{'itemName': 'SC-6061', 'assetName': 'Machine 1', 'unitProfileName': 'machining', 'standardRate': 4.0, 'scheduleRate': 3.5, 'packageCount': 0.0625},
{'itemName': 'SC-7075', 'assetName': 'Machine 1', 'unitProfileName': 'machining', 'standardRate': 3.5, 'scheduleRate': 3.0, 'packageCount': 0.0625},
# SC-2024 on Machine 1 already has a rate (created in Base Configuration)

{'itemName': 'CP-6061', 'assetName': 'Machine 1', 'unitProfileName': 'machining', 'standardRate': 8.0, 'scheduleRate': 7.0, 'packageCount': 0.025},
{'itemName': 'CP-7075', 'assetName': 'Machine 1', 'unitProfileName': 'machining', 'standardRate': 7.0, 'scheduleRate': 6.25, 'packageCount': 0.025},
{'itemName': 'CP-2024', 'assetName': 'Machine 1', 'unitProfileName': 'machining', 'standardRate': 7.5, 'scheduleRate': 6.75, 'packageCount': 0.025},

{'itemName': 'PR-6061', 'assetName': 'Machine 1', 'unitProfileName': 'machining', 'standardRate': 1.5, 'scheduleRate': 1.25, 'packageCount': 0.1667},
{'itemName': 'PR-7075', 'assetName': 'Machine 1', 'unitProfileName': 'machining', 'standardRate': 1.25, 'scheduleRate': 1.0, 'packageCount': 0.1667},
{'itemName': 'PR-2024', 'assetName': 'Machine 1', 'unitProfileName': 'machining', 'standardRate': 1.35, 'scheduleRate': 1.15, 'packageCount': 0.1667},

# =========================
# Spray Line 1 - Coated WIP
# units -> units
# packageCount = input units per output unit
# =========================

{'itemName': 'SC-6061-STD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 50.0, 'scheduleRate': 45.0, 'packageCount': 1.0},
{'itemName': 'SC-6061-HRD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 45.0, 'scheduleRate': 40.0, 'packageCount': 1.0},
{'itemName': 'SC-6061-PRM', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 40.0, 'scheduleRate': 36.0, 'packageCount': 1.0},

{'itemName': 'SC-7075-STD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 50.0, 'scheduleRate': 45.0, 'packageCount': 1.0},
{'itemName': 'SC-7075-HRD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 45.0, 'scheduleRate': 40.0, 'packageCount': 1.0},
{'itemName': 'SC-7075-PRM', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 40.0, 'scheduleRate': 36.0, 'packageCount': 1.0},

# SC-2024-STD on Spray Line 1 already has a rate (created in Base Configuration)
{'itemName': 'SC-2024-HRD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 45.0, 'scheduleRate': 40.0, 'packageCount': 1.0},
{'itemName': 'SC-2024-PRM', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 40.0, 'scheduleRate': 36.0, 'packageCount': 1.0},

{'itemName': 'CP-6061-STD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 120.0, 'scheduleRate': 108.0, 'packageCount': 1.0},
{'itemName': 'CP-6061-HRD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 105.0, 'scheduleRate': 95.0, 'packageCount': 1.0},
{'itemName': 'CP-6061-PRM', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 90.0, 'scheduleRate': 81.0, 'packageCount': 1.0},

{'itemName': 'CP-7075-STD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 120.0, 'scheduleRate': 108.0, 'packageCount': 1.0},
{'itemName': 'CP-7075-HRD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 105.0, 'scheduleRate': 95.0, 'packageCount': 1.0},
{'itemName': 'CP-7075-PRM', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 90.0, 'scheduleRate': 81.0, 'packageCount': 1.0},

{'itemName': 'CP-2024-STD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 120.0, 'scheduleRate': 108.0, 'packageCount': 1.0},
{'itemName': 'CP-2024-HRD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 105.0, 'scheduleRate': 95.0, 'packageCount': 1.0},
{'itemName': 'CP-2024-PRM', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 90.0, 'scheduleRate': 81.0, 'packageCount': 1.0},

{'itemName': 'PR-6061-STD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 25.0, 'scheduleRate': 22.0, 'packageCount': 1.0},
{'itemName': 'PR-6061-HRD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 22.0, 'scheduleRate': 20.0, 'packageCount': 1.0},
{'itemName': 'PR-6061-PRM', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 20.0, 'scheduleRate': 18.0, 'packageCount': 1.0},

{'itemName': 'PR-7075-STD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 25.0, 'scheduleRate': 22.0, 'packageCount': 1.0},
{'itemName': 'PR-7075-HRD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 22.0, 'scheduleRate': 20.0, 'packageCount': 1.0},
{'itemName': 'PR-7075-PRM', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 20.0, 'scheduleRate': 18.0, 'packageCount': 1.0},

{'itemName': 'PR-2024-STD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 25.0, 'scheduleRate': 22.0, 'packageCount': 1.0},
{'itemName': 'PR-2024-HRD', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 22.0, 'scheduleRate': 20.0, 'packageCount': 1.0},
{'itemName': 'PR-2024-PRM', 'assetName': 'Spray Line 1', 'unitProfileName': 'coating', 'standardRate': 20.0, 'scheduleRate': 18.0, 'packageCount': 1.0},

# =========================
# Manual Pack 1 - Finished Goods
# units -> boxes
# packageCount = input units per output box
# =========================

{'itemName': 'FG-SC-6061-STD-100', 'assetName': 'Manual Pack 1', 'unitProfileName': 'packing', 'standardRate': 0.75, 'scheduleRate': 0.65, 'packageCount': 100.0},
{'itemName': 'FG-SC-6061-HRD-100', 'assetName': 'Manual Pack 1', 'unitProfileName': 'packing', 'standardRate': 0.65, 'scheduleRate': 0.55, 'packageCount': 100.0},
{'itemName': 'FG-SC-6061-PRM-50', 'assetName': 'Manual Pack 1', 'unitProfileName': 'packing', 'standardRate': 0.75, 'scheduleRate': 0.65, 'packageCount': 50.0},

{'itemName': 'FG-CP-6061-STD-250', 'assetName': 'Manual Pack 1', 'unitProfileName': 'packing', 'standardRate': 0.5, 'scheduleRate': 0.45, 'packageCount': 250.0},
{'itemName': 'FG-CP-6061-HRD-250', 'assetName': 'Manual Pack 1', 'unitProfileName': 'packing', 'standardRate': 0.45, 'scheduleRate': 0.4, 'packageCount': 250.0},
{'itemName': 'FG-CP-6061-PRM-100', 'assetName': 'Manual Pack 1', 'unitProfileName': 'packing', 'standardRate': 0.6, 'scheduleRate': 0.5, 'packageCount': 100.0},

{'itemName': 'FG-PR-6061-STD-50', 'assetName': 'Manual Pack 1', 'unitProfileName': 'packing', 'standardRate': 0.4, 'scheduleRate': 0.35, 'packageCount': 50.0},
{'itemName': 'FG-PR-6061-HRD-50', 'assetName': 'Manual Pack 1', 'unitProfileName': 'packing', 'standardRate': 0.35, 'scheduleRate': 0.3, 'packageCount': 50.0},
{'itemName': 'FG-PR-6061-PRM-25', 'assetName': 'Manual Pack 1', 'unitProfileName': 'packing', 'standardRate': 0.4, 'scheduleRate': 0.35, 'packageCount': 25.0}
]


def field(row, *names):
"""First of the named columns that has a value, since column names vary between functions."""
for n in names:
if row.get(n) is not None:
return row[n]
return None

def same(value):
"""Normalize a value for comparison: 1/True, '#ABC'/'#abc' and 4/4.0 count as the same."""
if value in (True, False) or str(value) in ('1', '0', 'True', 'False'):
return str(value) in ('True', '1')
try:
return round(float(value), 6)
except:
return '' if value is None else unicode(value).strip().lower()

def changed(current, wanted, keys):
"""The keys whose values differ between an existing record and the definition."""
return [k for k in keys if k in current and same(current[k]) != same(wanted[k])]

def error():
return str(sys.exc_info()[1]).split('\n')[0]

print '=' * 70
print 'Creating or updating %d items and %d rates' % (len(itemDefinitions), len(rateDefinitions))
print '=' * 70

# Check the lists before changing anything
names = [d['itemName'] for d in itemDefinitions]
duplicates = sorted(set(n for n in names if names.count(n) > 1))
if duplicates:
raise Exception('These items are listed more than once: %s' % ', '.join(duplicates))
pairs = [(d['itemName'], d['assetName']) for d in rateDefinitions]
duplicates = sorted(set('%s on %s' % p for p in pairs if pairs.count(p) > 1))
if duplicates:
raise Exception('These rates are listed more than once: %s' % ', '.join(duplicates))
badRefs = sorted(set(d['assetName'] for d in rateDefinitions if d['assetName'] not in ASSETS) |
set(d['unitProfileName'] for d in rateDefinitions if d['unitProfileName'] not in unitProfiles))
if badRefs:
raise Exception('Rates refer to unknown assets or unit profiles: %s' % ', '.join(badRefs))

# =========================
# Step 1: Check the assets, period and source the rates need; create missing units
# =========================
print '\n-- Step 1: Assets and units for the rates'
assetIds = {}
for assetName, assetPath in sorted(ASSETS.items()):
assetIds[assetName] = system.kanoa.utilities.getFieldValue('assetId', system.kanoa.asset.getAssets({'assetPath': assetPath}))
print ' %-14s assetId %s' % (assetName, assetIds[assetName])
units = system.kanoa.utilities.convertDatasetToDict(system.kanoa.item.getItemUnits({}), 'itemUnitName', 'itemUnitId')
periods = system.kanoa.utilities.convertDatasetToDict(system.kanoa.item.getItemPeriod({}), 'itemPeriodName', 'itemPeriodId')
sources = system.kanoa.utilities.convertDatasetToDict(system.kanoa.item.getItemSource({}), 'itemSourceName', 'itemSourceId')

problems = ['asset %s (%s): it comes from Base Configuration; check its name and area' % (n, ASSETS[n])
for n in sorted(assetIds) if assetIds[n] is None]
if RATE_PERIOD not in periods: problems.append('rate period %s' % RATE_PERIOD)
if RATE_SOURCE not in sources: problems.append('item source %s' % RATE_SOURCE)
if problems: raise Exception('The rates need these, which do not exist:\n - %s' % '\n - '.join(problems))

# Units are simple to add, so create any that are missing
neededUnits = sorted(set(u for p in unitProfiles.values() for u in p.values()))
for unitName in neededUnits:
if unitName in units: continue
unitId, msg = system.kanoa.item.addItemUnit({'itemUnitName': unitName, 'enabled': True}, userId)
if msg or unitId is None: raise Exception('Could not create unit %s: %s' % (unitName, msg))
units[unitName] = unitId
print ' Created unit %s (itemUnitId %s)' % (unitName, unitId)
print ' Units: %s' % ', '.join('%s (%s)' % (u, units[u]) for u in neededUnits)

# =========================
# Step 2: Item classes - check each one exists, create it if it doesn't
# =========================
print '\n-- Step 2: Item classes'
existingClasses = {}
for row in system.kanoa.utilities.convertDatasetToJSON(system.kanoa.item.getItemClasses({})):
existingClasses[field(row, 'itemClassPath', 'path')] = row['itemClassId']

classIds = {}
for classPath in sorted(set(d['itemClassPath'] for d in itemDefinitions)):
if classPath in existingClasses:
classIds[classPath] = existingClasses[classPath]
print ' Exists: %-26s (itemClassId %s)' % (classPath, classIds[classPath])
continue
classId = system.kanoa.item.checkItemClassPath(classPath, userId) # creates it, and any missing parents
if classId is None: raise Exception('Could not create item class %s' % classPath)
classIds[classPath] = classId
print ' Created: %-26s (itemClassId %s)' % (classPath, classId)

# =========================
# Step 3: Items - update the ones that exist, create the ones that don't
# =========================
print '\n-- Step 3: Items'
itemIds = {}
existingItems = {}
for row in system.kanoa.utilities.convertDatasetToJSON(system.kanoa.item.getItems({})):
existingItems[row['itemName']] = row
itemIds[row['itemName']] = row['itemId']

itemCounts = {'created': 0, 'updated': 0, 'unchanged': 0, 'failed': 0}
for d in itemDefinitions:
name = d['itemName']
itemInfo = {
'itemName': name,
'itemDescription': d['itemDescription'],
'itemClassId': classIds[d['itemClassPath']],
'itemColor': d['itemColor'],
'enabled': True
}
try:
if name in existingItems:
changes = changed(existingItems[name], itemInfo, ('itemDescription', 'itemClassId', 'itemColor', 'enabled'))
if not changes:
print ' Unchanged: %s' % name
itemCounts['unchanged'] += 1
continue
itemInfo['itemId'] = existingItems[name]['itemId']
system.kanoa.item.updateItem(itemInfo, userId)
print ' Updated: %-20s (%s)' % (name, ', '.join(changes))
itemCounts['updated'] += 1
else:
itemId = system.kanoa.item.addItem(itemInfo, userId)
if itemId is None:
raise Exception('addItem returned no itemId')
itemIds[name] = itemId
print ' Created: %-20s %s (itemId %s)' % (name, d['itemClassPath'], itemId)
itemCounts['created'] += 1
except:
print ' FAILED: %s: %s' % (name, error())
itemCounts['failed'] += 1

# =========================
# Step 4: Item-asset rates - update the ones that exist, create the ones that don't
# =========================
print '\n-- Step 4: Item-asset rates'
existingRates = {}
for assetName, assetId in assetIds.items():
for row in system.kanoa.utilities.convertDatasetToJSON(system.kanoa.item.getAssetItems({'assetId': assetId})):
existingRates[(row['itemId'], assetId)] = row

RATE_FIELDS = ('standardRate', 'scheduleRate', 'itemPeriodId', 'infeedUnitId', 'outfeedUnitId',
'wasteUnitId', 'packageCount', 'itemSourceId', 'enabled')
rateCounts = {'created': 0, 'updated': 0, 'unchanged': 0, 'failed': 0}
for d in rateDefinitions:
label = '%s on %s' % (d['itemName'], d['assetName'])
if d['itemName'] not in itemIds:
print ' FAILED: %s: item %s does not exist' % (label, d['itemName'])
rateCounts['failed'] += 1
continue
profile = unitProfiles[d['unitProfileName']]
rateInfo = {
'itemId': itemIds[d['itemName']],
'assetId': assetIds[d['assetName']],
'standardRate': d['standardRate'],
'scheduleRate': d['scheduleRate'],
'itemPeriodId': periods[RATE_PERIOD],
'infeedUnitId': units[profile['infeed']],
'outfeedUnitId': units[profile['outfeed']],
'wasteUnitId': units[profile['waste']],
'packageCount': d['packageCount'],
'itemSourceId': sources[RATE_SOURCE],
'enabled': True
}
try:
current = existingRates.get((rateInfo['itemId'], rateInfo['assetId']))
if current is None:
rateId = system.kanoa.item.addItemAssetRate(rateInfo, userId)
print ' Created: %-34s %s/min (itemAssetRateId %s)' % (label, d['standardRate'], rateId)
rateCounts['created'] += 1
continue
changes = changed(current, rateInfo, RATE_FIELDS)
if not changes:
print ' Unchanged: %s' % label
rateCounts['unchanged'] += 1
continue
rateInfo['itemAssetRateId'] = current['itemAssetRateId']
try:
system.kanoa.item.updateItemAssetRate(rateInfo, userId) # the docs' example passes userId...
except TypeError:
system.kanoa.item.updateItemAssetRate(rateInfo) # ...but its parameter list doesn't
print ' Updated: %-34s (%s)' % (label, ', '.join(changes))
rateCounts['updated'] += 1
except:
print ' FAILED: %s: %s' % (label, error())
rateCounts['failed'] += 1

print '\n' + '=' * 70
for title, counts in (('Items', itemCounts), ('Rates', rateCounts)):
summary = (title, counts['created'], counts['updated'], counts['unchanged'], counts['failed'])
print '%s: created %d, updated %d, unchanged %d, failed %d' % summary
print '=' * 70

After running the script, you should see a lot more items in the Configuration → Ops → Assets screen.

Check your work

Copy this script into the Ignition Designer Script Console (Tools → Script Console) and run it after the script above. It counts the items in each CoreForge item class and the item rates on Machine 1, Spray Line 1, and Manual Pack 1. It doesn't change anything.

Show the check script
checkLesson-scriptItems.py
# Kanoa Ops Training - check "Create Items with Scripting" (Scaling across the enterprise, lesson 3)
#
# Run in the Ignition Designer Script Console. It counts the items in each CoreForge
# item class and the item rates on each asset. The counts include SC-2024 and
# SC-2024-STD and their rates, from Base Configuration.

ITEM_CLASSES = [
# item class path, items expected
('CoreForge\\RM', 3),
('CoreForge\\WIP\\Machined', 9),
('CoreForge\\WIP\\Coatings', 3),
('CoreForge\\WIP\\Coated', 27),
('CoreForge\\FG', 27),
]
ASSET_RATES = [
# asset path, item rates expected
('CoreForge\\Philadelphia\\Machining\\Machine 1', 9),
('CoreForge\\Philadelphia\\Coating\\Spray Line 1', 27),
('CoreForge\\Philadelphia\\Finishing\\Manual Pack 1', 9),
]

results = []

def report(ok, label, count, expected):
results.append(ok)
if ok:
print 'PASS %-28s %3d (expected at least %d)' % (label, count, expected)
else:
print 'FAIL %-28s %3d, expected at least %d' % (label, count, expected)

print '=' * 70
print 'Kanoa Ops Training - Create Items with Scripting check'
print '=' * 70

print '\n-- Items per class'
classIds = {}
for row in system.kanoa.utilities.convertDatasetToJSON(system.kanoa.item.getItemClasses({})):
classIds[row.get('itemClassPath') or row.get('path')] = row['itemClassId']
for classPath, expected in ITEM_CLASSES:
classId = classIds.get(classPath)
count = len(system.kanoa.utilities.convertDatasetToJSON(system.kanoa.item.getItems({'itemClassId': classId}))) if classId else 0
report(count >= expected, classPath, count, expected)

print '\n-- Item rates per asset'
for assetPath, expected in ASSET_RATES:
assetId = system.kanoa.utilities.getFieldValue('assetId', system.kanoa.asset.getAssets({'assetPath': assetPath}))
count = len(system.kanoa.utilities.convertDatasetToJSON(system.kanoa.item.getAssetItems({'assetId': assetId}))) if assetId else 0
report(count >= expected, assetPath.split('\\')[-1], count, expected)

print '\n' + '=' * 70
print 'Result: %d of %d checks passed' % (results.count(True), len(results))
if results.count(True) < len(results):
print 'Run the generateItemsAndRates script again: it only creates or updates what is missing or different.'
print '=' * 70