chore(plating): de-dash fusion_plating_iot too
Same em-dash -> hyphen sweep applied to fusion_plating_iot (lives under fusion_iot/ so the main commit missed it). Comments/strings only; no functional dashes in this module. Keeps git in sync with the in-place de-dash already applied to entech. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
"""Sensor dashboard — a named group of related sensors.
|
||||
"""Sensor dashboard - a named group of related sensors.
|
||||
|
||||
Use case: a shop wants one "ENP Line — Tanks 1-4" view that aggregates
|
||||
Use case: a shop wants one "ENP Line - Tanks 1-4" view that aggregates
|
||||
temperature, pH, and level probes from four bath tanks into a single
|
||||
trending chart + alert count. A dashboard is just a logical grouping;
|
||||
actual rendering happens in the UI.
|
||||
@@ -14,7 +14,7 @@ from odoo import api, fields, models
|
||||
|
||||
class FpSensorDashboard(models.Model):
|
||||
_name = 'fp.sensor.dashboard'
|
||||
_description = 'Fusion Plating — Sensor Dashboard'
|
||||
_description = 'Fusion Plating - Sensor Dashboard'
|
||||
_inherit = ['mail.thread']
|
||||
_order = 'name'
|
||||
|
||||
@@ -52,7 +52,7 @@ class FpSensorDashboard(models.Model):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': f'Sensors — {self.name}',
|
||||
'name': f'Sensors - {self.name}',
|
||||
'res_model': 'fp.tank.sensor',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('id', 'in', self.sensor_ids.ids)],
|
||||
@@ -63,7 +63,7 @@ class FpSensorDashboard(models.Model):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': f'Readings — {self.name}',
|
||||
'name': f'Readings - {self.name}',
|
||||
'res_model': 'fp.tank.reading',
|
||||
'view_mode': 'graph,list,form',
|
||||
'domain': [('sensor_id', 'in', self.sensor_ids.ids)],
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
A richer taxonomy than the `device_kind` Selection on fp.tank.sensor.
|
||||
Types describe WHAT the sensor measures (temperature, pH, conductivity,
|
||||
level, etc.) plus the data type of its readings. Same type can back
|
||||
multiple hardware models — e.g. "Temperature" covers DS18B20, PT100,
|
||||
multiple hardware models - e.g. "Temperature" covers DS18B20, PT100,
|
||||
PT1000, thermocouple.
|
||||
|
||||
Seeded defaults ship with the module (see data/fp_sensor_type_data.xml)
|
||||
@@ -19,7 +19,7 @@ from odoo import fields, models
|
||||
|
||||
class FpSensorType(models.Model):
|
||||
_name = 'fp.sensor.type'
|
||||
_description = 'Fusion Plating — Sensor Type'
|
||||
_description = 'Fusion Plating - Sensor Type'
|
||||
_order = 'sequence, name'
|
||||
|
||||
name = fields.Char(string='Name', required=True, translate=True)
|
||||
@@ -74,7 +74,7 @@ class FpSensorType(models.Model):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': f'Sensors — {self.name}',
|
||||
'name': f'Sensors - {self.name}',
|
||||
'res_model': 'fp.tank.sensor',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('sensor_type_id', '=', self.id)],
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"""Time-series of sensor readings.
|
||||
|
||||
Every POST to /fp/iot/ingest (or every broadcast from the iot proxy)
|
||||
lands as a new row here. Kept intentionally append-only — we never
|
||||
lands as a new row here. Kept intentionally append-only - we never
|
||||
update or delete readings, which makes this the compliance log for
|
||||
bath-temperature history.
|
||||
|
||||
@@ -13,7 +13,7 @@ Auto-creates a fusion.plating.quality.hold when a reading falls
|
||||
outside the sensor's alert range AND the sensor has
|
||||
`alert_on_out_of_spec` enabled. The hold is created once per
|
||||
excursion (we don't spam a new hold for every reading during a
|
||||
sustained excursion) — tracked via the sensor's most-recent
|
||||
sustained excursion) - tracked via the sensor's most-recent
|
||||
`last_reading_in_spec` flag.
|
||||
"""
|
||||
|
||||
@@ -26,7 +26,7 @@ _logger = logging.getLogger(__name__)
|
||||
|
||||
class FpTankReading(models.Model):
|
||||
_name = 'fp.tank.reading'
|
||||
_description = 'Fusion Plating — Tank Sensor Reading'
|
||||
_description = 'Fusion Plating - Tank Sensor Reading'
|
||||
_order = 'reading_at desc, id desc'
|
||||
_rec_name = 'display_name'
|
||||
|
||||
@@ -34,7 +34,7 @@ class FpTankReading(models.Model):
|
||||
'fp.tank.sensor', string='Sensor', required=True,
|
||||
ondelete='cascade', index=True,
|
||||
)
|
||||
# Denormalised for fast list views + kpi queries — auto-filled at
|
||||
# Denormalised for fast list views + kpi queries - auto-filled at
|
||||
# create time from sensor_id. Indexed for historical trending.
|
||||
tank_id = fields.Many2one(
|
||||
'fusion.plating.tank', string='Tank',
|
||||
@@ -56,7 +56,7 @@ class FpTankReading(models.Model):
|
||||
value = fields.Float(
|
||||
string='Value (raw)', required=True, digits=(12, 4),
|
||||
help='Stored reading in the sensor\'s canonical unit (for '
|
||||
'temperature sensors this is always °C — the DS18B20 and '
|
||||
'temperature sensors this is always °C - the DS18B20 and '
|
||||
'every other temperature chip reports in Celsius natively; '
|
||||
'keeping storage canonical lets us switch display units '
|
||||
'per-company without re-migrating history).',
|
||||
@@ -66,7 +66,7 @@ class FpTankReading(models.Model):
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Display-aware fields — converted to the company's preferred unit
|
||||
# Display-aware fields - converted to the company's preferred unit
|
||||
# (res.company.x_fc_default_temp_uom = 'F' or 'C'). Only the list
|
||||
# and form views should show these; internal spec comparisons use
|
||||
# `value` so thresholds stay consistent across regions.
|
||||
@@ -84,7 +84,7 @@ class FpTankReading(models.Model):
|
||||
@api.depends('value', 'parameter_id', 'parameter_id.parameter_type',
|
||||
'parameter_id.uom')
|
||||
def _compute_display(self):
|
||||
# Read once per compute call — env.company rarely changes mid-batch.
|
||||
# Read once per compute call - env.company rarely changes mid-batch.
|
||||
pref = self.env.company.x_fc_default_temp_uom or 'C'
|
||||
for r in self:
|
||||
ptype = (r.parameter_id.parameter_type or '').lower()
|
||||
@@ -96,7 +96,7 @@ class FpTankReading(models.Model):
|
||||
r.display_unit = r.parameter_id.uom_display or ''
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Deviation from setpoint — signed Δ from the sensor's effective target
|
||||
# Deviation from setpoint - signed Δ from the sensor's effective target
|
||||
# in the company-preferred unit. Zero if no setpoint defined.
|
||||
# ------------------------------------------------------------------
|
||||
deviation_from_target = fields.Float(
|
||||
@@ -184,10 +184,10 @@ class FpTankReading(models.Model):
|
||||
for r in self:
|
||||
sensor = r.sensor_id.name or 'sensor'
|
||||
at = fields.Datetime.to_string(r.reading_at) if r.reading_at else ''
|
||||
r.display_name = f'{sensor} — {r.display_value:.2f} {r.display_unit} @ {at}'
|
||||
r.display_name = f'{sensor} - {r.display_value:.2f} {r.display_unit} @ {at}'
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Create hook — evaluate against spec + raise a quality hold if we
|
||||
# Create hook - evaluate against spec + raise a quality hold if we
|
||||
# just crossed INTO an out-of-spec state.
|
||||
# ------------------------------------------------------------------
|
||||
@api.model_create_multi
|
||||
@@ -197,7 +197,7 @@ class FpTankReading(models.Model):
|
||||
try:
|
||||
rec._evaluate_spec()
|
||||
except Exception:
|
||||
# Never let alert-logic break the ingest path — the
|
||||
# Never let alert-logic break the ingest path - the
|
||||
# reading itself is what matters for compliance. Log
|
||||
# and carry on.
|
||||
_logger.exception(
|
||||
@@ -256,7 +256,7 @@ class FpTankReading(models.Model):
|
||||
'hold_reason': 'out_of_spec',
|
||||
'description': description,
|
||||
'qty_on_hold': 1,
|
||||
# state defaults to 'on_hold' — leave it
|
||||
# state defaults to 'on_hold' - leave it
|
||||
}
|
||||
# Attach facility + work-centre context if the tank has them,
|
||||
# so the hold is actionable from the shop floor (operator can
|
||||
|
||||
@@ -9,7 +9,7 @@ device registered via the iot_drivers proxy) is mapped to exactly one
|
||||
tank or bath and measures ONE bath parameter (temperature, pH,
|
||||
conductivity, etc.).
|
||||
|
||||
The same tank can carry multiple sensors — e.g. a temp probe and a pH
|
||||
The same tank can carry multiple sensors - e.g. a temp probe and a pH
|
||||
probe. Each is its own fp.tank.sensor row.
|
||||
"""
|
||||
|
||||
@@ -18,17 +18,17 @@ from odoo import api, fields, models
|
||||
|
||||
class FpTankSensor(models.Model):
|
||||
_name = 'fp.tank.sensor'
|
||||
_description = 'Fusion Plating — Tank Sensor'
|
||||
_description = 'Fusion Plating - Tank Sensor'
|
||||
_order = 'tank_id, parameter_id'
|
||||
|
||||
name = fields.Char(
|
||||
string='Sensor Name', required=True,
|
||||
help='Human label (e.g. "Tank 3 — ENP temp").',
|
||||
help='Human label (e.g. "Tank 3 - ENP temp").',
|
||||
)
|
||||
uuid = fields.Char(
|
||||
string='UUID',
|
||||
copy=False, readonly=True, index=True,
|
||||
help='Stable logical identifier — survives hardware swaps. If a '
|
||||
help='Stable logical identifier - survives hardware swaps. If a '
|
||||
'probe dies and gets replaced, keep the UUID, change the '
|
||||
'device_serial. Every measurement tied to this UUID remains '
|
||||
'part of the same logical history.',
|
||||
@@ -36,7 +36,7 @@ class FpTankSensor(models.Model):
|
||||
sensor_type_id = fields.Many2one(
|
||||
'fp.sensor.type',
|
||||
string='Sensor Type',
|
||||
help='Taxonomy — temperature, pH, conductivity, etc. '
|
||||
help='Taxonomy - temperature, pH, conductivity, etc. '
|
||||
'Independent from hardware (a "temperature" sensor could be '
|
||||
'a DS18B20, PT100, or thermocouple; device_kind captures '
|
||||
'the hardware, sensor_type_id captures the role).',
|
||||
@@ -44,7 +44,7 @@ class FpTankSensor(models.Model):
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Physical device — either an Odoo iot.device (proxied through the Pi)
|
||||
# Physical device - either an Odoo iot.device (proxied through the Pi)
|
||||
# OR a direct-ingest sensor (skipping the proxy, posting straight to
|
||||
# /fp/iot/ingest with the shared secret + device_serial).
|
||||
# ------------------------------------------------------------------
|
||||
@@ -73,7 +73,7 @@ class FpTankSensor(models.Model):
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Where this sensor lives — can be ANY of tank / work_center /
|
||||
# Where this sensor lives - can be ANY of tank / work_center /
|
||||
# facility / named-location. Keep tank_id as the primary (most
|
||||
# sensors are bath-mounted) but allow the other three as
|
||||
# alternatives so we can mount probes on ovens, ambient air,
|
||||
@@ -84,22 +84,22 @@ class FpTankSensor(models.Model):
|
||||
)
|
||||
bath_id = fields.Many2one(
|
||||
'fusion.plating.bath', string='Bath',
|
||||
help='Optional — if the sensor is bound to a specific bath '
|
||||
help='Optional - if the sensor is bound to a specific bath '
|
||||
'chemistry rather than a physical tank.',
|
||||
)
|
||||
work_center_id = fields.Many2one(
|
||||
'fusion.plating.work.center', string='Work Centre',
|
||||
help='Alternative to tank_id — use when the sensor is attached '
|
||||
help='Alternative to tank_id - use when the sensor is attached '
|
||||
'to a station, oven, or line rather than a bath tank.',
|
||||
)
|
||||
facility_id = fields.Many2one(
|
||||
'fusion.plating.facility', string='Facility',
|
||||
help='Alternative to tank_id / work_center_id — use for '
|
||||
help='Alternative to tank_id / work_center_id - use for '
|
||||
'facility-wide sensors (ambient, HVAC, perimeter).',
|
||||
)
|
||||
location_name = fields.Char(
|
||||
string='Location (free-text)',
|
||||
help='Free-text override when none of the above fit — e.g. '
|
||||
help='Free-text override when none of the above fit - e.g. '
|
||||
'"North bay wall", "Effluent pipe exit", "Rinse tank #2 '
|
||||
'roof". Shown alongside the structured location in views.',
|
||||
)
|
||||
@@ -113,7 +113,7 @@ class FpTankSensor(models.Model):
|
||||
effective_location = fields.Char(
|
||||
string='Location',
|
||||
compute='_compute_effective_location', store=True,
|
||||
help='Display string for the sensor location — prefers tank, '
|
||||
help='Display string for the sensor location - prefers tank, '
|
||||
'then work_center, then facility, then free-text.',
|
||||
)
|
||||
|
||||
@@ -133,7 +133,7 @@ class FpTankSensor(models.Model):
|
||||
rec.effective_location = ''
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Target / alerting behaviour — three concepts:
|
||||
# Target / alerting behaviour - three concepts:
|
||||
# setpoint → what we CONTROL TOWARD (dashboards, PID, trend baseline)
|
||||
# alert min → lower alarm boundary (quality hold fires below)
|
||||
# alert max → upper alarm boundary (quality hold fires above)
|
||||
@@ -164,7 +164,7 @@ class FpTankSensor(models.Model):
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Sub 7 — per-sensor polling interval
|
||||
# Sub 7 - per-sensor polling interval
|
||||
#
|
||||
# Blank on the sensor = inherit res.company default. The effective
|
||||
# value gates the ingest endpoint so too-frequent readings are
|
||||
@@ -175,7 +175,7 @@ class FpTankSensor(models.Model):
|
||||
help='How often a reading from this sensor should be stored. '
|
||||
'Leave blank to inherit the company-wide default. Readings '
|
||||
'that arrive inside this interval are dropped by the '
|
||||
'ingest endpoint — the database stays clean even if the '
|
||||
'ingest endpoint - the database stays clean even if the '
|
||||
'Pi agent polls more often.',
|
||||
)
|
||||
poll_interval_display = fields.Char(
|
||||
@@ -211,7 +211,7 @@ class FpTankSensor(models.Model):
|
||||
return self.env.company.x_fc_default_poll_interval_minutes or 30
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Effective target — resolves override → parameter default → 0
|
||||
# Effective target - resolves override → parameter default → 0
|
||||
# ------------------------------------------------------------------
|
||||
effective_target = fields.Float(
|
||||
string='Effective Setpoint',
|
||||
@@ -253,7 +253,7 @@ class FpTankSensor(models.Model):
|
||||
string='In Spec?', readonly=True,
|
||||
help='Computed from the last reading vs alert_min/alert_max.',
|
||||
)
|
||||
# Display-aware version of last_reading_value — converted to company
|
||||
# Display-aware version of last_reading_value - converted to company
|
||||
# preferred unit (res.company.x_fc_default_temp_uom).
|
||||
last_reading_display = fields.Float(
|
||||
string='Latest Value',
|
||||
@@ -308,7 +308,7 @@ class FpTankSensor(models.Model):
|
||||
rec.reading_count = len(rec.reading_ids)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Resolve effective alert range — override wins, else bath.parameter
|
||||
# Resolve effective alert range - override wins, else bath.parameter
|
||||
# ------------------------------------------------------------------
|
||||
def _get_alert_range(self):
|
||||
"""Return (min, max) floats. Zero means 'no bound'."""
|
||||
@@ -324,7 +324,7 @@ class FpTankSensor(models.Model):
|
||||
def _get_setpoint(self):
|
||||
"""Canonical (raw) setpoint used for deviation calcs.
|
||||
|
||||
Returns 0.0 if no setpoint is set at either level — callers should
|
||||
Returns 0.0 if no setpoint is set at either level - callers should
|
||||
treat 0 as "no target defined, skip deviation display".
|
||||
"""
|
||||
self.ensure_one()
|
||||
@@ -336,7 +336,7 @@ class FpTankSensor(models.Model):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': f'Readings — {self.name}',
|
||||
'name': f'Readings - {self.name}',
|
||||
'res_model': 'fp.tank.reading',
|
||||
'view_mode': 'list,form,graph',
|
||||
'domain': [('sensor_id', '=', self.id)],
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
#
|
||||
# Sub 7 — company-wide default for the IoT sensor polling interval.
|
||||
# Sub 7 - company-wide default for the IoT sensor polling interval.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
#
|
||||
# Sub 7 — Expose the IoT polling default on the Fusion Plating
|
||||
# Sub 7 - Expose the IoT polling default on the Fusion Plating
|
||||
# Settings page so admins manage it alongside other plating settings.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
Reference in New Issue
Block a user