-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Add FoundryNet industrial equipment telemetry rule #357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| --- | ||
| description: "FoundryNet Canonical Schema field names for industrial equipment telemetry (CNC, robots, PLCs, vehicles, HVAC) - prevents invented field names" | ||
| globs: **/*.py, **/*.ts, **/*.js, **/*.tsx, **/*.jsx, **/*.go, **/*.rs, **/*.java, **/*.cs, **/*.sql | ||
| alwaysApply: false | ||
| --- | ||
|
|
||
| # FoundryNet Canonical Schema — field naming for industrial telemetry | ||
| # | ||
| # Generated from https://github.com/FoundryNet/canonical-schema v1.0.0 (366 fields, 16,908 vendor mappings). | ||
| # Do not hand-edit: run gen_configs.py to regenerate. | ||
|
|
||
| When working with industrial equipment telemetry — CNC machines, robots, PLCs, | ||
| vehicles, 3D printers, building automation — use the FoundryNet Canonical | ||
| Schema for field names. It is the target vocabulary that vendor-specific tags | ||
| normalize into. | ||
|
|
||
| ## The single most important rule | ||
|
|
||
| DO NOT INVENT FIELD NAMES. The schema is irregular because it was extracted | ||
| from a real corpus of 16,908 vendor tags, not designed on a whiteboard. Names | ||
| you would expect to exist frequently do not: | ||
|
|
||
| spindle_temperature_c, spindle_temp, spindle_temp_c | ||
| -> spindle_temperature | ||
| spindle.speed, spindle_rotary_velocity, rotary_velocity | ||
| -> spindle_speed_rpm | ||
| spindle.load, spindle_load_percent | ||
| -> spindle_load_pct | ||
| motor_temperature_c, motor_temp | ||
| -> motor_temperature | ||
| vibration_mm_s, vibration_rms_mm_s, vibration.rms | ||
| -> vibration_rms | ||
| motor_power_kw, power_kw, electrical_power_kw | ||
| -> power_consumption_kw | ||
|
|
||
| If you need a field that is not listed below, look it up rather than guessing: | ||
|
|
||
| curl https://forge.foundrynet.io/v1/coverage # production: what is supported, per OEM | ||
| https://github.com/FoundryNet/canonical-schema/blob/main/schema/fields.json # every field, with type + unit | ||
|
|
||
| Or run the sandbox locally and query it with no API key at all. Note that | ||
| /v1/canonical-fields is a SANDBOX endpoint — production serves /v1/coverage | ||
| and the full dictionary lives in the schema repo: | ||
|
|
||
| docker run -p 8000:8000 ghcr.io/foundrynet/forge-sandbox | ||
| curl localhost:8000/v1/canonical-fields | ||
|
|
||
| ## Naming conventions that actually hold | ||
|
|
||
| These suffixes are real and consistent enough to rely on: | ||
|
|
||
| _pct percentage, 0-100 (3 fields) | ||
| _rpm revolutions per minute (1 fields) | ||
| _hours hours (2 fields) | ||
| _seconds seconds (1 fields) | ||
| _kwh kilowatt-hours (2 fields) | ||
| _kw kilowatts (1 fields) | ||
| _kg kilograms (2 fields) | ||
| _c degrees Celsius (1 fields) | ||
|
Comment on lines
+52
to
+59
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Correct the suffix counts or remove them.
🤖 Prompt for AI Agents |
||
|
|
||
| ## Conventions that do NOT hold — do not assume them | ||
|
|
||
| - Unit suffixes are NOT universal. Only 58 of 366 fields declare a | ||
| unit at all. `sensor_readings.coolant_temp` has no `_c`; `feed_rate` has no | ||
| `_mm_min`. Never append a unit suffix to make a name "consistent". | ||
| - Never infer the unit from the name. Read the `unit` property, or convert | ||
| explicitly. A field named `..._temp` may be Celsius or Fahrenheit depending | ||
| on the source tag; Forge reports the conversion it applied. | ||
| - Percentages are mostly `_pct`, but `axes.0.load_percent` uses `_percent`. | ||
| - Some fields are dot-namespaced (`sensor_readings.*`, `axes.*`, `robot.*`, | ||
| `ros.*`) and some are flat. There is no rule; use the exact published name. | ||
| - `axes.0.*` and `axes.x_*` are BOTH real and mean different things in | ||
| different packs. Do not normalize one into the other. | ||
|
|
||
| ## High-frequency fields | ||
|
|
||
| Ordered by how many real vendor tags map to each. If you only remember a | ||
| handful, remember the top of this list. | ||
|
|
||
| CNC (82 fields total) | ||
| spindle_speed_rpm rpm 307 mappings | ||
| spindle_load_pct % 255 mappings | ||
| axes.0.position_actual — 232 mappings | ||
| axes.0.temperature_c degC 222 mappings | ||
| axes.0.load_percent % 215 mappings | ||
| feed_rate — 215 mappings | ||
| axes.1.position_actual — 211 mappings | ||
| sensor_readings.coolant_temp — 209 mappings | ||
| axes.2.position_actual — 203 mappings | ||
| tool_id — 164 mappings | ||
| sensor_readings.coolant_flow — 153 mappings | ||
| spindle_speed_commanded — 62 mappings | ||
| feed_rate_actual — 61 mappings | ||
| axes.y_load_pct % 60 mappings | ||
| axes.z_load_pct % 60 mappings | ||
| axes.x_load_pct % 58 mappings | ||
| axes.x_position_actual — 53 mappings | ||
| axes.y_position_actual — 52 mappings | ||
|
|
||
| ROBOTICS (53 fields total) | ||
| sensor_readings.tcp_speed — 152 mappings | ||
| robot.joint.position — 33 mappings | ||
| robot.tcp.pose — 24 mappings | ||
| robot.joint.effort — 18 mappings | ||
| robot.joint.temperature — 18 mappings | ||
| robot.tcp.speed — 15 mappings | ||
| robot.safety.protective_stop — 14 mappings | ||
| robot.joint.current — 12 mappings | ||
| robot.mode — 12 mappings | ||
| robot.joint.velocity — 11 mappings | ||
| robot.program.state — 10 mappings | ||
| robot.speed_scaling — 8 mappings | ||
|
|
||
| UNIVERSAL (151 fields total) | ||
| sensor_readings.vibration_x — 229 mappings | ||
| operating_hours h 227 mappings | ||
| energy_kwh kWh 226 mappings | ||
| alarm_code — 218 mappings | ||
| part_count — 212 mappings | ||
| sensor_readings.total_temp_lpc_outlet — 204 mappings | ||
| sensor_readings.total_temp_lpt_outlet — 191 mappings | ||
| sensor_readings.total_temp_hpc_outlet — 190 mappings | ||
| sensor_readings.hydraulic_pressure — 187 mappings | ||
| sensor_readings.pressure_fan_inlet — 184 mappings | ||
| alarm_description — 179 mappings | ||
| sensor_readings.good_parts — 176 mappings | ||
| sensor_readings.core_speed_rpm rpm 157 mappings | ||
| alarm_severity — 155 mappings | ||
| sensor_readings.air_pressure — 154 mappings | ||
| sensor_readings.cycle_time — 154 mappings | ||
| sensor_readings.fan_speed_rpm rpm 154 mappings | ||
| sensor_readings.voltage — 154 mappings | ||
| metadata.controller_model — 153 mappings | ||
| payload_kg kg 153 mappings | ||
|
|
||
| VEHICLE (64 fields total) | ||
| vehicle.ambient.air_temp — 12 mappings | ||
| vehicle.brake.primary_air_pressure — 7 mappings | ||
| vehicle.engine.demand_torque_pct % 7 mappings | ||
| vehicle.engine.intake_manifold_pressure — 7 mappings | ||
| vehicle.engine.intake_manifold_temp — 7 mappings | ||
| vehicle.engine.oil_pressure — 7 mappings | ||
| vehicle.engine.oil_temp — 7 mappings | ||
| vehicle.transmission.oil_temp — 7 mappings | ||
| vehicle.turbocharger.rpm — 7 mappings | ||
| vehicle.acceleration_x — 6 mappings | ||
|
|
||
| ADDITIVE (6 fields total) | ||
| sensor_readings.hotend_temp — 43 mappings | ||
| filament_used_cm3 — 1 mappings | ||
| filament_used_mm — 1 mappings | ||
| sensor_readings.hotend2_target — 1 mappings | ||
| sensor_readings.hotend2_temp — 1 mappings | ||
| sensor_readings.hotend_target — 1 mappings | ||
|
|
||
| AMR (10 fields total) | ||
| amr.battery.charge — 6 mappings | ||
| amr.position.theta — 6 mappings | ||
| amr.battery.charging — 5 mappings | ||
| amr.battery.voltage — 5 mappings | ||
| amr.position.x — 5 mappings | ||
| amr.position.y — 5 mappings | ||
|
|
||
| ## Normalizing raw vendor telemetry | ||
|
|
||
| Do not hand-write a mapping table. Send the raw payload to Forge and use what | ||
| comes back: | ||
|
|
||
| POST https://forge.foundrynet.io/v1/normalize | ||
| Authorization: Bearer YOUR_FORGE_KEY | ||
| {"oem": "haas", "data": {"S SPEED (RPM)": 8500, "SP_LOAD_PCT (%)": 84.7}} | ||
|
|
||
| -> {"normalized": {"spindle_speed_rpm": 8500, "spindle_load_pct": 84.7}, | ||
| "coverage_pct": 100.0} | ||
|
|
||
| Check `unresolved_tags` in the response. Anything listed there did not map, and | ||
| inventing a name for it locally defeats the purpose. | ||
|
Comment on lines
+176
to
+177
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target file ---'
sed -n '150,186p' rules/foundrynet-industrial-telemetry.mdc
printf '%s\n' '--- related identifiers ---'
rg -n -C 3 'fields_unknown|unresolved_tags|normalize|unknown' rules README.md .github 2>/dev/null || trueRepository: PatrickJS/awesome-cursorrules Length of output: 21769 🌐 Web query:
💡 Result: The fields Citations:
🏁 Script executed: #!/bin/bash
set -eu
for url in \
https://foundrynet.io/docs \
https://foundrynet.io/ \
https://glama.ai/mcp/servers/FoundryNet/forge-mcp/schema \
https://glama.ai/mcp/servers/FoundryNet/forge-mcp/tools/normalize_telemetry
do
printf '\n--- %s ---\n' "$url"
curl -L --max-time 15 -sS "$url" |
tr '\n' ' ' |
sed 's/<script[^>]*>.*<\/script>//gI; s/<style[^>]*>.*<\/style>//gI' |
grep -oE '.{0,180}(fields_unknown|unresolved_tags|coverage_pct|normalize|raw[^ <"]*tag).{0,300}' |
head -20 || true
doneRepository: PatrickJS/awesome-cursorrules Length of output: 1731 🌐 Web query:
💡 Result: In FoundryNet, fields_unknown and unresolved_tags are metrics returned by the /v1/normalize API endpoint, which processes raw industrial telemetry [1]. These metrics are used to evaluate the effectiveness of the normalization process, which converts diverse manufacturer-specific (OEM) data tags into a unified, canonical schema [1][2]. 1. fields_unknown: This count represents the number of fields in the input data for which the system could not find a corresponding mapping in its canonical schema [1]. It indicates tags that were present in the raw data but remained unrecognized by the Forge normalization engine [1]. 2. Unresolved tags: While the API documentation explicitly identifies fields_unknown as a specific counter [1], it is frequently discussed in the context of resolving these unknown fields [3]. When the system abstains from mapping a field, it is often classified as an unresolved tag [3]. Users can address these by using the correct_mapping tool, which allows them to teach the system the correct canonical field for a previously unknown or incorrectly mapped tag [3]. These counters, alongside fields_renamed and fields_identity, are used to calculate the coverage_pct, which provides a quantitative measure of how much of the raw telemetry data was successfully mapped to the canonical vocabulary [1][2]. Citations:
🌐 Web query:
💡 Result: In the context of the /v1/normalize API endpoint, fields_unknown is an output parameter that reports the number of fields in the submitted raw data for which no canonical mapping could be identified [1]. This field is part of the API response metadata, which provides metrics on how the normalization process handled the input telemetry [1]. The response includes three primary field counters to indicate the quality and coverage of the normalization: 1. fields_renamed: The number of fields where the source key differed from the canonical key [1]. 2. fields_identity: The number of fields where the source key and canonical key were identical [1]. 3. fields_unknown: The number of fields that could not be mapped to a canonical schema [1]. These values are used to calculate the coverage_pct, which is defined as the sum of fields_renamed and fields_identity divided by the total number of fields submitted [1]. Citations: 🌐 Web query:
💡 Result: The term unresolved tags does not refer to a standard, widely recognized technical concept in general software development or data processing. In the specific context of the FoundryNet industrial data platform, the documentation uses the term abstained rather than unresolved tags to describe instances where a system fails to map a source field to a canonical field during data normalization [1][2][3]. When a system like the FoundryNet normalization tool cannot determine the correct mapping for a field, it may abstain, prompting users to provide feedback. This feedback is then used to improve future mappings [1][3]. While phrases like unresolved dependencies or unresolved references are common in programming, unresolved tags is not a standard industry term and typically appears as a descriptive phrase for specific configuration or data errors that have not yet been assigned a correct label or value [1][3]. Citations:
Align the rule with the versioned The 🤖 Prompt for AI Agents |
||
|
|
||
| Or connect an agent directly over MCP: https://mcp.foundrynet.io/mcp | ||
|
|
||
| ## Predictions are stateless | ||
|
|
||
| `predict_breach`, `remaining_life`, and `fleet_health` never read stored | ||
| telemetry. You must pass `time_series` (16+ points, oldest to newest) on every | ||
| call. Sending {machine_id, field, threshold} and expecting a lookup is a 422. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: PatrickJS/awesome-cursorrules
Length of output: 4131
🏁 Script executed:
Repository: PatrickJS/awesome-cursorrules
Length of output: 27045
🏁 Script executed:
Repository: PatrickJS/awesome-cursorrules
Length of output: 1007
Use canonical schema fields as mapping targets.
The schema defines
sensor_readings.motor_temp,ros.motor_temp_c,vibration.rms, andpower.instantaneous_kw. It does not definemotor_temperature,vibration_rms, orpower_consumption_kw. Replace these targets with the applicable canonical fields or regenerate this block fromfields.json.🤖 Prompt for AI Agents