component
Sunon MF40200V1-1000U-G99 · 5 V tachometer fan
Public Made by Adomby adom
Tachometer fan with factory flying leads, an explicit Adom connector harness and gentle GLB state demonstration.
main
"""Check the published motion asset and provenance without a graphics runtime."""
from pathlib import Path
import hashlib,json,struct
root=Path(__file__).resolve().parents[1]
manifest=json.loads((root/'states.json').read_text()); motion=manifest['motion']
blob=(root/motion['model']).read_bytes()
assert hashlib.sha256(blob).hexdigest()==motion['sha256']
magic,version,length=struct.unpack_from('<4sII',blob)
assert (magic,version,length)==(b'glTF',2,len(blob))
n,kind=struct.unpack_from('<II',blob,12);assert kind==0x4e4f534a
model=json.loads(blob[20:20+n]);nodes=model['nodes']
def unique(name):
found=[i for i,node in enumerate(nodes) if node.get('name')==name]
assert len(found)==1,(name,found)
return found[0]
rotor=unique(motion['nodes']['rotor']);housing=unique(motion['nodes']['stationary'])
rootnode=unique(motion['nodes']['assembly'])
assert rotor in nodes[rootnode]['children'] and housing in nodes[rootnode]['children']
assert len(nodes[rotor]['children'])==8
for binding in motion['bindings']:
animations=[a for a in model['animations'] if a.get('name')==binding['animation']]
assert len(animations)==1
assert all(c['target']['node']==rotor and c['target']['path']=='rotation' for c in animations[0]['channels'])
for asset in json.loads((root/'provenance/assets.json').read_text())['assets']:
assert hashlib.sha256((root/asset['file']).read_bytes()).hexdigest()==asset['sha256'],asset['file']
print('PASS: GLB structure, unique bindings, rotor-only animation, asset hashes')