Mapping Equipment Locations Across Multi-Building Campuses
On this page
- Problem statement
- Prerequisites
- Policy and compliance context
- Step-by-step implementation
- Step 1 — Model the payload and the authorized registry
- Step 2 — Resolve primary telemetry, with confidence gating
- Step 3 — Degrade deterministically to the cached registry
- Step 4 — Fingerprint each decision and run the resolver idempotently
- Schema and field reference
- Verification
- Troubleshooting
- Frequently asked questions
- Related
Problem statement
You need a Python resolver that assigns every grant-funded instrument to a validated campus → building → floor → room coordinate even when primary geolocation fails — Wi-Fi triangulation dies inside an RF-shielded lab, a portal API times out, or a coordinate payload arrives malformed — so that a network partition never produces a misplaced asset that becomes a 2 CFR §200.313 property finding or an OSHA hazard-zone violation.
This task sits under Lab Location & Asset Mapping, part of the broader Equipment Calibration & Lab Inventory Tracking practice. It is intentionally narrow: it resolves a location, degrades gracefully when telemetry is unreliable, and fingerprints every decision. It does not own the authorized facility registry — that canonical hierarchy and its quarantine path belong to the parent guide — and it does not adjudicate hazard policy; it produces the tamper-evident evidence a compliance officer relies on.
Prerequisites
Before deploying the resolver, confirm the following environment and policy configuration:
- Python 3.10+ for modern type hints, the union (
X | None) syntax, anddatetime.timezone. The resolution and hashing path uses only the standard library (hashlib,json,logging), which keeps the daemon runnable during a network partition with no outbound dependency. - Pydantic 2.x for payload validation. Inbound telemetry from RFID readers, the procurement ERP, and the laboratory information management system (LIMS) is validated identically to every other entry point through the shared Schema Validation Pipelines, so a malformed coordinate is rejected the same way regardless of source.
- A locally cached facility registry. The authorized
campus → building → floor → roomhierarchy must be replicated to disk so fallback resolution never reaches across the network. Treat it as a version-controlled artifact synced through your CI/CD pipeline, not a value hard-coded in a telemetry collector. - A confidence threshold from policy. Fallback resolution must never override primary telemetry when its confidence exceeds the institutional threshold (90% in the examples below). That number is owned by compliance, governed by the University Policy Mapping Frameworks, and read at runtime — not edited in code.
Policy and compliance context
Spatial asset tracking is a regulatory requirement, not an IT convenience. The resolver is architected so the regulatory boundary is satisfied by design:
- 2 CFR §200.313 (Uniform Guidance). NIH and NSF require a documented location for federally funded equipment and periodic physical-inventory reconciliation. A partition that loses an asset’s room of record is a control gap; deterministic fallback keeps every instrument anchored to an authorized zone.
- OSHA Laboratory Standard (29 CFR 1910.1450). Safety apparatus — fume hoods, emergency showers, radiation monitors — must remain inside designated hazard zones. Fallback resolution prevents the false negative that a network outage would otherwise produce when a responder pulls a hazard map.
- EPA EPCRA / RCRA. Sensor and reagent placement must align with permitted containment boundaries. Spatial drift in telemetry can invalidate an environmental compliance report, so each decision is hashed and timestamped for the record.
Every routing decision must be timestamped, hashed, and appended to an append-only ledger, satisfying the audit-trail requirement without continuous cloud connectivity. A record that cannot be resolved to an authorized zone is escalated, never silently assigned — the same guarantee the platform inherits from the Grant Lifecycle Architecture Design.
Step-by-step implementation
The flow the resolver enforces: validate the inbound payload, attempt primary geolocation, and on failure or low confidence degrade deterministically to the cached building registry rather than failing the resolution — then fingerprint every outcome into an append-only ledger. Identical inputs always produce identical outputs, so a re-processed payload writes no duplicate entry.
Figure: missing, malformed, or low-confidence coordinates degrade to a deterministic local-registry lookup; an unknown building code is quarantined for PI sign-off rather than mis-assigned.
Step 1 — Model the payload and the authorized registry
Validate the inbound telemetry with Pydantic so a malformed coordinate is rejected at construction, and replicate the authorized building hierarchy to a local cache the resolver can read with no network. The registry is the determinism anchor: the same building code always resolves to the same default room.
import hashlib
import json
import logging
from datetime import datetime, timezone
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field, ValidationError
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s",
handlers=[logging.FileHandler("location_audit.log", mode="a")],
)
logger = logging.getLogger("campus_location_resolver")
# Confidence below this (read from policy at runtime) forces a fallback.
PRIMARY_CONFIDENCE_FLOOR = 0.90
class Coordinates(BaseModel):
"""WGS84 point; bounds are enforced at construction so a malformed read is rejected."""
lat: float = Field(..., ge=-90.0, le=90.0)
lon: float = Field(..., ge=-180.0, le=180.0)
accuracy_m: float = Field(..., gt=0.0, description="Reported horizontal accuracy in metres")
class EquipmentPayload(BaseModel):
asset_id: str = Field(..., min_length=8, description="Institutional asset tag, e.g. SCI-04-8821")
serial_number: str
raw_coordinates: Optional[Coordinates] = None # None simulates a partition / API failure
timestamp: str
# Deterministic local cache; synced from the authorized facility master via CI/CD.
BUILDING_REGISTRY: dict[str, dict[str, object]] = {
"ENG-01": {"campus": "MAIN", "floors": ["B1", "1", "2", "3"], "default_room": "AA-101"},
"SCI-04": {"campus": "MAIN", "floors": ["1", "2", "3", "4"], "default_room": "BC-205"},
"BIO-02": {"campus": "WEST", "floors": ["1", "2"], "default_room": "DA-110"},
}Step 2 — Resolve primary telemetry, with confidence gating
Attempt the primary geolocation first. A point that validates and clears the policy confidence floor is accepted; anything weaker is treated as unreliable and handed to the fallback. Wide accuracy radii — common after a Wi-Fi handoff in a steel-framed building — collapse confidence so a near-miss never masquerades as a confirmed room.
class RoutingMethod(str, Enum):
PRIMARY = "primary_geolocation"
FALLBACK = "local_registry_fallback"
QUARANTINED = "quarantined"
class LocationResolution(BaseModel):
asset_id: str
campus_id: str
building_code: str
floor_identifier: str
room_code: str
routing_method: RoutingMethod
confidence_score: float
audit_hash: str = ""
def _coordinate_confidence(coords: Coordinates) -> float:
"""Map reported accuracy to a confidence score; a 50 m radius is effectively useless indoors."""
if coords.accuracy_m <= 5.0:
return 0.98
if coords.accuracy_m <= 15.0:
return 0.92
return 0.60 # too coarse to pin a room — forces fallback
def _resolve_primary(payload: EquipmentPayload) -> Optional[LocationResolution]:
"""Return a resolution only if telemetry is present and clears the policy floor."""
if payload.raw_coordinates is None:
return None
confidence = _coordinate_confidence(payload.raw_coordinates)
if confidence < PRIMARY_CONFIDENCE_FLOOR:
logger.info("Primary telemetry below floor (%.2f) for %s; deferring to fallback.",
confidence, payload.asset_id)
return None
# A real deployment reverse-geocodes the point against floor polygons here.
return LocationResolution(
asset_id=payload.asset_id, campus_id="MAIN", building_code="GEO",
floor_identifier="AUTO", room_code="AUTO-001",
routing_method=RoutingMethod.PRIMARY, confidence_score=confidence,
)Step 3 — Degrade deterministically to the cached registry
When primary telemetry is absent or too coarse, parse the building code from the asset tag and resolve against the local registry. An unrecognised code is quarantined, not defaulted to an arbitrary building — silently assigning an unknown asset to ENG-01 would corrupt the property record and any hazard map drawn from it.
def _resolve_fallback(payload: EquipmentPayload) -> LocationResolution:
"""Deterministic resolution against the offline registry; unknown codes are quarantined."""
building_code = payload.asset_id.split("-")[0] if "-" in payload.asset_id else ""
if building_code not in BUILDING_REGISTRY:
logger.warning("Unregistered building code '%s' for %s; routing to quarantine.",
building_code, payload.asset_id)
return LocationResolution(
asset_id=payload.asset_id, campus_id="UNKNOWN", building_code=building_code or "UNKNOWN",
floor_identifier="UNKNOWN", room_code="QUARANTINE",
routing_method=RoutingMethod.QUARANTINED, confidence_score=0.0,
)
entry = BUILDING_REGISTRY[building_code]
return LocationResolution(
asset_id=payload.asset_id, campus_id=str(entry["campus"]), building_code=building_code,
floor_identifier=str(entry["floors"][0]), room_code=str(entry["default_room"]),
routing_method=RoutingMethod.FALLBACK, confidence_score=0.85,
)Step 4 — Fingerprint each decision and run the resolver idempotently
Bind the asset identity, resolved location, and timestamp into a single SHA-256 digest computed from canonical (sort_keys=True) JSON, then cache by asset_id so a replayed payload returns the recorded decision and appends nothing. This is what makes a cron overlap or a re-uploaded export safe.
class CampusLocationResolver:
"""Idempotent, offline-tolerant resolver with a cryptographic audit trail."""
def __init__(self, registry: dict[str, dict[str, object]]):
self.registry = registry
self._ledger: dict[str, LocationResolution] = {}
def _audit_hash(self, payload: EquipmentPayload, res: LocationResolution) -> str:
canonical = json.dumps(
{
"asset_id": payload.asset_id,
"timestamp": payload.timestamp,
"campus": res.campus_id,
"building": res.building_code,
"floor": res.floor_identifier,
"room": res.room_code,
},
sort_keys=True,
)
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
def resolve(self, payload: EquipmentPayload) -> LocationResolution:
if payload.asset_id in self._ledger:
logger.info("Idempotent replay: %s already resolved.", payload.asset_id)
return self._ledger[payload.asset_id]
resolution = _resolve_primary(payload) or _resolve_fallback(payload)
resolution.audit_hash = self._audit_hash(payload, resolution)
self._ledger[payload.asset_id] = resolution
logger.info("Resolved %s via %s | hash=%s",
payload.asset_id, resolution.routing_method.value, resolution.audit_hash)
return resolution
if __name__ == "__main__":
resolver = CampusLocationResolver(BUILDING_REGISTRY)
try:
payload = EquipmentPayload(
asset_id="SCI-04-8821",
serial_number="SN-99421",
raw_coordinates=None, # simulate an RF-shielded lab / API timeout
timestamp=datetime.now(timezone.utc).isoformat(),
)
except ValidationError as exc:
logger.error("Rejected malformed payload: %s", exc.errors())
raise
print(resolver.resolve(payload).model_dump_json(indent=2))Schema and field reference
The fields the resolver reads and stamps. Widen the registry in your version-controlled facility config, not in code.
| Field | Type | Constraint | Source / rule |
|---|---|---|---|
asset_id |
str |
required, min_length=8, encodes building code prefix |
2 CFR §200.313 equipment tracking |
raw_coordinates.lat |
float |
-90 ≤ lat ≤ 90 |
WGS84 bounds |
raw_coordinates.lon |
float |
-180 ≤ lon ≤ 180 |
WGS84 bounds |
raw_coordinates.accuracy_m |
float |
> 0; drives confidence |
Indoor positioning quality gate |
confidence_score |
float |
fallback below PRIMARY_CONFIDENCE_FLOOR |
Institutional policy threshold |
routing_method |
enum |
primary | fallback | quarantined |
Resolution provenance for audit |
room_code |
str |
resolved against registry | Authorized facility master |
audit_hash |
str |
64-char SHA-256, canonical JSON | Non-repudiation (NIH/OSHA/EPA) |
Verification
Confirm a run behaved correctly before trusting it in production:
- Deterministic replay. Resolve the same payload twice. The second call must log
Idempotent replayand return the identicalaudit_hash— proof that re-processing writes no duplicate ledger entry. - Reproduce the hash. Recompute
sha256over the canonical{asset_id, timestamp, campus, building, floor, room}dict and confirm it equals the storedaudit_hash. An inspector can repeat this independently to tie a physical location to a tamper-evident digest. - Force the fallback path. Pass
raw_coordinates=None(or anaccuracy_mabove 15) and confirmrouting_methodislocal_registry_fallbackwith a confidence of0.85, not a failed resolution. - Confirm the quarantine branch. Resolve an asset whose prefix is absent from the registry and assert
routing_method == quarantinedandroom_code == "QUARANTINE"— never an arbitrary default building.
Troubleshooting
Three gotchas specific to this resolver:
- Coordinate drift during Wi-Fi handoffs. A single roaming event can emit one wild outlier point with a tight reported accuracy, briefly clearing the confidence floor. Apply a sliding-window filter upstream — require three consecutive payloads within a small radius before accepting a primary fix — so one bad read cannot relocate an asset.
- Registry schema mismatch after a new building is commissioned. A freshly opened building absent from
BUILDING_REGISTRYwill quarantine every asset tagged with its code. Sync the registry through your CI/CD pipeline before the assets arrive, and during a sustained sync outage divert through your Fallback Routing Protocols rather than letting quarantine saturate. - Audit-hash verification failure. A mismatch almost always means the JSON was serialized without
sort_keys=True, or atimestampwas rewritten after the fact. Always hash the canonical form, and treat a non-reproducing digest as a potential integrity event — restricting who may regenerate a hash is governed by the Security Boundary Configuration.
Frequently asked questions
Why quarantine an unknown building code instead of defaulting to a known building?
How does the resolver keep working during a full network partition?
Should fallback resolution ever override a primary coordinate fix?
Related
- Up to the parent topic: Lab Location & Asset Mapping
- Automating Calibration Reminder Emails for Lab Equipment
- Tracking High-Frequency Instrument Usage with IoT Sensors
- Setting Dynamic Reorder Thresholds for Lab Consumables
- Fallback Routing Protocols — platform-wide degradation strategy