computers: declare subordinate devices instead of coding each one
A PC that drives a device which is its own asset had been implemented twice. METROLOGY_TOOL_MAP covered CMM, Keyence, Genspect and wax-trace, minting a measuring_tool. A separate path keyed on one hardcoded pc-type minted a Part Marker machine and filed it under its operation. Both create a device, link the PC with controls, and archive that link when the PC is re-imaged: one mechanism with different nouns, written out twice because the second case arrived later. That is the same trap as the site literals in ADR-015 - a pattern implemented per instance rather than declared - and it has a known next occurrence. Part markers already share operation numbers, and any site with two marking lasers or two wax-trace units on one number needs identical treatment. One SUBORDINATE_DEVICE_MAP now declares asset type, type name, naming suffix, whether the device files partof the operation, and the relationship label. The labels are unchanged per case on purpose: those values are in the production database and only rows carrying them are archived by a collector push. A site overrides or adds an entry through subordinatedevice_<pctype> settings, per ADR-015, so the next case needs no code. A malformed override falls back to the default rather than failing the push, because a bad setting must not stop a bay reporting its inventory. metrology_tool_for stays as a shim over the same map: filters.py and the older tests read it, and unifying must not change what it returns. A test pins that. Also adds flask relationships check-shared-machines, which finds the next 0615 rather than waiting for someone to notice duplicate backups. Several devices legitimately sharing a number and two PCs mis-numbered at imaging look the same from outside; the difference is whether child assets exist, so that is what it reports. Read-only.
This commit is contained in:
@@ -1046,3 +1046,59 @@ def test_a_marker_is_never_filed_under_its_own_pc(
|
||||
assert comp.assetid not in targets, 'marker filed under its own PC'
|
||||
assert any("own asset number" in w
|
||||
for w in r.get_json()['data']['warnings'])
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# The subordinate-device map: adding a device type is configuration, not code
|
||||
# =============================================================================
|
||||
|
||||
def test_a_site_can_add_a_device_type_without_a_code_change(
|
||||
client, db, collector_key, computer_assettype, operation_0615):
|
||||
"""The point of the map. A site with two marking lasers on one operation
|
||||
number needs the same treatment part markers got; before this it needed an
|
||||
edit to the plugin."""
|
||||
import json
|
||||
from shopdb.core.models import Setting, Asset
|
||||
|
||||
db.session.add(Setting(
|
||||
key='subordinatedevice_site-laser',
|
||||
value=json.dumps({'assettype': 'machine', 'typename': 'Marking Laser',
|
||||
'description': 'Site laser', 'suffix': 'LASER',
|
||||
'partof': True, 'label': 'collector:partmarker'}),
|
||||
category='pctypemapping'))
|
||||
db.session.commit()
|
||||
|
||||
client.post('/api/collector/computers',
|
||||
json={'hostname': 'LASERPC1', 'machinenumber': '0615',
|
||||
'pctype': 'site-laser'},
|
||||
headers={'X-API-Key': collector_key})
|
||||
|
||||
device = Asset.query.filter_by(assetnumber='LASERPC1-LASER').first()
|
||||
assert device is not None
|
||||
assert device.machine.machinetype.machinetype == 'Marking Laser'
|
||||
|
||||
|
||||
def test_a_malformed_override_does_not_break_the_collector(
|
||||
client, db, collector_key, computer_assettype, operation_0615):
|
||||
"""A bad setting must not stop a bay reporting its inventory."""
|
||||
from shopdb.core.models import Setting
|
||||
|
||||
db.session.add(Setting(key='subordinatedevice_gea-shopfloor-partmarker',
|
||||
value='{not json', category='pctypemapping'))
|
||||
db.session.commit()
|
||||
|
||||
r = client.post('/api/collector/computers', json=_marker('MARKERPC9'),
|
||||
headers={'X-API-Key': collector_key})
|
||||
assert r.status_code == 200, r.get_json()
|
||||
|
||||
|
||||
def test_the_metrology_map_still_answers_through_the_shim(app):
|
||||
"""filters.py and the older tests read metrology_tool_for. Unifying the two
|
||||
maps must not change what it returns."""
|
||||
with app.app_context():
|
||||
from plugins.computers.pctypemap import metrology_tool_for
|
||||
assert metrology_tool_for('gea-shopfloor-cmm') == (
|
||||
'CMM', 'Coordinate measuring machine')
|
||||
# A machine-typed device is not a measuring tool.
|
||||
assert metrology_tool_for('gea-shopfloor-partmarker') is None
|
||||
assert metrology_tool_for('gea-shopfloor-collections') is None
|
||||
|
||||
Reference in New Issue
Block a user