WJ import loader: metrology PCs are computers that control a synthesized tool
Routing by pctype wrongly swept ~105 metrology PCs (CMM/Genspect/Keyence/Wax) into measuringtools - but a PC that drives an instrument is still a computer; the physical CMM/gauge is the tool. Dropped the pctype override: those PCs now import as computers (measuringtools drops to the 48 real instrument rows). Classic has no separate tool row for a metrology PC, so they'd be orphaned. New metrology stage synthesizes a measuring-tool asset per metrology PC (typed by its pctype: CMM / Form Tracer / Vision System / Genspect) and a Controls relationship PC -> tool, mirroring what the runtime collector does. Result: computers 663->751, measuringtools = 48 real + 88 synthesized (each linked to its controlling PC), 88 new Controls relationships, no orphans.
This commit is contained in:
@@ -50,8 +50,10 @@ def _upsert(h, path, payload, unique_field, idfield, list_path=None):
|
||||
|
||||
|
||||
# --- classic machinetype routing (per the resolved import decisions) ---------
|
||||
# measuringtools are the physical instruments (the CMM/gauge machine types). A
|
||||
# PC that DRIVES one (pctype CMM/Genspect/Keyence/Wax) is still a computer, not a
|
||||
# measuring tool - routing by pctype wrongly swept ~105 PCs into measuringtools.
|
||||
MEASURING_MTYPES = {3, 5, 8, 23, 47, 48} # CMM, Wax, Eddy, Measuring, Inspection, Spline
|
||||
MEASURING_PCTYPES = {5, 6, 7, 8} # PC subtypes: CMM, Wax/Trace, Keyence, Genspect
|
||||
COMPUTER_MTYPES = {33, 20} # PC, Server
|
||||
NETWORK_MTYPES = {16, 17, 18, 19, 46} # Access, IDF, Camera, Switch, Firewall
|
||||
SKIP_MTYPES = {15, 44} # Printer (printers table), USB (cmmc source)
|
||||
@@ -79,9 +81,7 @@ def _route(machinetypeid, pctypeid, islocationonly=False):
|
||||
if machinetypeid in MEASURING_MTYPES:
|
||||
return 'measuringtool'
|
||||
if machinetypeid in COMPUTER_MTYPES:
|
||||
if machinetypeid == 33 and pctypeid in MEASURING_PCTYPES:
|
||||
return 'measuringtool'
|
||||
return 'computer'
|
||||
return 'computer' # a metrology PC is still a PC (it CONTROLS a tool)
|
||||
return 'machine' # includes the 134 untyped (machinetypeid=1) shop machines
|
||||
|
||||
|
||||
@@ -349,6 +349,52 @@ def stage_printers(h):
|
||||
return {'printers': made}
|
||||
|
||||
|
||||
# classic pctype -> the measuring instrument that PC drives
|
||||
METROLOGY_PCTYPE_TOOL = {5: 'CMM', 6: 'Form Tracer', 7: 'Vision System', 8: 'Genspect'}
|
||||
|
||||
|
||||
def stage_metrology(h):
|
||||
"""Metrology PCs (CMM/Genspect/Keyence/Wax) are computers that CONTROL an
|
||||
instrument. Classic has no separate tool row, so synthesize a measuring-tool
|
||||
asset per metrology PC and a Controls relationship (PC -> tool), mirroring
|
||||
what the runtime collector does. Runs after the asset hub (needs the PC's
|
||||
assetid from the crosswalk)."""
|
||||
controls_id = _upsert(h, '/api/assets/relationshiptypes',
|
||||
{'relationshiptype': 'Controls'}, 'relationshiptype',
|
||||
'relationshiptypeid', '/api/assets/relationshiptypes')
|
||||
made = linked = 0
|
||||
for m in h.source.rows('shopdb_src',
|
||||
'SELECT machineid, alias, hostname, machinenumber, businessunitid, '
|
||||
'mapleft, maptop, pctypeid FROM machines '
|
||||
'WHERE machinetypeid=33 AND pctypeid IN (5,6,7,8) '
|
||||
'AND (islocationonly IS NULL OR islocationonly=0)'):
|
||||
pc_assetid = h.ids.get('asset', m['machineid'])
|
||||
if not pc_assetid:
|
||||
continue # PC not imported (dup-skipped etc.)
|
||||
toolname = METROLOGY_PCTYPE_TOOL[m['pctypeid']]
|
||||
typeid = _upsert(h, '/api/measuringtools/types', {'name': toolname},
|
||||
'name', 'measuringtooltypeid', '/api/measuringtools/types')
|
||||
pcname = (m['alias'] or m['hostname'] or m['machinenumber'] or '').strip()
|
||||
status, data = h.post('/api/measuringtools', {
|
||||
'assetnumber': f"MT-{m['machineid']}",
|
||||
'name': (f"{pcname} {toolname}").strip() or toolname,
|
||||
'measuringtooltypeid': typeid,
|
||||
'businessunitid': h.ids.get('businessunit', m['businessunitid']),
|
||||
'mapx': m['mapleft'], 'mapy': m['maptop']})
|
||||
if status not in (200, 201):
|
||||
continue
|
||||
tool_assetid = _id_of(data, 'assetid')
|
||||
made += 1
|
||||
if tool_assetid and controls_id:
|
||||
h.ids.put('metrologytool', m['machineid'], tool_assetid)
|
||||
st, _ = h.post('/api/assets/relationships', {
|
||||
'sourceassetid': pc_assetid, 'targetassetid': tool_assetid,
|
||||
'relationshiptypeid': controls_id})
|
||||
if st in (200, 201):
|
||||
linked += 1
|
||||
return {'measuringtools_synth': made, 'controls_links': linked}
|
||||
|
||||
|
||||
def stage_communications(h):
|
||||
"""Fold each asset's primary IP from the source communications table. There
|
||||
is no bulk-communications endpoint, so this is one of the plan's documented
|
||||
@@ -722,6 +768,7 @@ STAGES = {
|
||||
'assets': stage_assets,
|
||||
'locations': stage_locations,
|
||||
'printers': stage_printers,
|
||||
'metrology': stage_metrology,
|
||||
'communications': stage_communications,
|
||||
'applications': stage_applications,
|
||||
'warranties': stage_warranties,
|
||||
@@ -739,7 +786,7 @@ def main():
|
||||
parser.add_argument(
|
||||
'--stages',
|
||||
default='reference,employees,catalog,assets,locations,printers,'
|
||||
'communications,applications,warranties,notifications,'
|
||||
'metrology,communications,applications,warranties,notifications,'
|
||||
'knowledgebase,relationships,subnets,usb,verify',
|
||||
help='comma list of stages to run')
|
||||
args = parser.parse_args()
|
||||
|
||||
Reference in New Issue
Block a user