|
|
@@ -22,52 +22,17 @@ __all__ = (
|
|
|
# Cooling
|
|
|
#
|
|
|
|
|
|
-class CoolingSource(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
|
|
+class CoolingTemperatureMixin(models.Model):
|
|
|
"""
|
|
|
- A facility-level source of cooling; e.g. a chiller, cooling tower, or dry cooler.
|
|
|
+ Adds design supply/return temperatures sharing a single unit, with normalized (°C) fields for ordering.
|
|
|
"""
|
|
|
- site = models.ForeignKey(
|
|
|
- to='Site',
|
|
|
- on_delete=models.PROTECT
|
|
|
- )
|
|
|
- location = models.ForeignKey(
|
|
|
- to='dcim.Location',
|
|
|
- on_delete=models.PROTECT,
|
|
|
- blank=True,
|
|
|
- null=True
|
|
|
- )
|
|
|
- name = models.CharField(
|
|
|
- verbose_name=_('name'),
|
|
|
- max_length=100,
|
|
|
- db_collation="natural_sort"
|
|
|
- )
|
|
|
- type = models.CharField(
|
|
|
- verbose_name=_('type'),
|
|
|
- max_length=50,
|
|
|
- choices=CoolingSourceTypeChoices
|
|
|
- )
|
|
|
- status = models.CharField(
|
|
|
- verbose_name=_('status'),
|
|
|
- max_length=50,
|
|
|
- choices=CoolingSourceStatusChoices,
|
|
|
- default=CoolingSourceStatusChoices.STATUS_ACTIVE
|
|
|
- )
|
|
|
- cooling_capacity = models.DecimalField(
|
|
|
- verbose_name=_('cooling capacity'),
|
|
|
- max_digits=10,
|
|
|
- decimal_places=2,
|
|
|
- blank=True,
|
|
|
- null=True,
|
|
|
- validators=[MinValueValidator(0)],
|
|
|
- help_text=_('Total cooling capacity (kW)')
|
|
|
- )
|
|
|
supply_temperature = models.DecimalField(
|
|
|
verbose_name=_('supply temperature'),
|
|
|
max_digits=6,
|
|
|
decimal_places=2,
|
|
|
blank=True,
|
|
|
null=True,
|
|
|
- help_text=_('Design supply temperature')
|
|
|
+ help_text=_('Supply (cold) temperature')
|
|
|
)
|
|
|
return_temperature = models.DecimalField(
|
|
|
verbose_name=_('return temperature'),
|
|
|
@@ -75,7 +40,7 @@ class CoolingSource(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
|
|
decimal_places=2,
|
|
|
blank=True,
|
|
|
null=True,
|
|
|
- help_text=_('Design return temperature')
|
|
|
+ help_text=_('Return (warm) temperature')
|
|
|
)
|
|
|
temperature_unit = models.CharField(
|
|
|
verbose_name=_('temperature unit'),
|
|
|
@@ -98,27 +63,8 @@ class CoolingSource(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
|
|
null=True
|
|
|
)
|
|
|
|
|
|
- clone_fields = (
|
|
|
- 'site', 'location', 'type', 'status', 'cooling_capacity', 'supply_temperature', 'return_temperature',
|
|
|
- 'temperature_unit',
|
|
|
- )
|
|
|
- prerequisite_models = (
|
|
|
- 'dcim.Site',
|
|
|
- )
|
|
|
-
|
|
|
class Meta:
|
|
|
- ordering = ['site', 'name']
|
|
|
- constraints = (
|
|
|
- models.UniqueConstraint(
|
|
|
- fields=('site', 'name'),
|
|
|
- name='%(app_label)s_%(class)s_unique_site_name'
|
|
|
- ),
|
|
|
- )
|
|
|
- verbose_name = _('cooling source')
|
|
|
- verbose_name_plural = _('cooling sources')
|
|
|
-
|
|
|
- def __str__(self):
|
|
|
- return self.name
|
|
|
+ abstract = True
|
|
|
|
|
|
@property
|
|
|
def abs_supply_temperature(self):
|
|
|
@@ -130,9 +76,6 @@ class CoolingSource(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
|
|
# Public alias for _abs_return_temperature; Django templates cannot access underscore-prefixed attributes.
|
|
|
return self._abs_return_temperature
|
|
|
|
|
|
- def get_status_color(self):
|
|
|
- return CoolingSourceStatusChoices.colors.get(self.status)
|
|
|
-
|
|
|
def save(self, *args, **kwargs):
|
|
|
# Store the given temperatures (if any) in degrees Celsius for use in database ordering
|
|
|
if self.temperature_unit:
|
|
|
@@ -154,6 +97,80 @@ class CoolingSource(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
|
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
+ def clean(self):
|
|
|
+ super().clean()
|
|
|
+
|
|
|
+ # A temperature unit is required when a temperature is set
|
|
|
+ if (self.supply_temperature is not None or self.return_temperature is not None) and not self.temperature_unit:
|
|
|
+ raise ValidationError(_("Must specify a unit when setting a temperature"))
|
|
|
+
|
|
|
+
|
|
|
+class CoolingSource(CoolingTemperatureMixin, ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
|
|
+ """
|
|
|
+ A facility-level source of cooling; e.g. a chiller, cooling tower, or dry cooler.
|
|
|
+ """
|
|
|
+ site = models.ForeignKey(
|
|
|
+ to='Site',
|
|
|
+ on_delete=models.PROTECT
|
|
|
+ )
|
|
|
+ location = models.ForeignKey(
|
|
|
+ to='dcim.Location',
|
|
|
+ on_delete=models.PROTECT,
|
|
|
+ blank=True,
|
|
|
+ null=True
|
|
|
+ )
|
|
|
+ name = models.CharField(
|
|
|
+ verbose_name=_('name'),
|
|
|
+ max_length=100,
|
|
|
+ db_collation="natural_sort"
|
|
|
+ )
|
|
|
+ type = models.CharField(
|
|
|
+ verbose_name=_('type'),
|
|
|
+ max_length=50,
|
|
|
+ choices=CoolingSourceTypeChoices
|
|
|
+ )
|
|
|
+ status = models.CharField(
|
|
|
+ verbose_name=_('status'),
|
|
|
+ max_length=50,
|
|
|
+ choices=CoolingSourceStatusChoices,
|
|
|
+ default=CoolingSourceStatusChoices.STATUS_ACTIVE
|
|
|
+ )
|
|
|
+ cooling_capacity = models.DecimalField(
|
|
|
+ verbose_name=_('cooling capacity'),
|
|
|
+ max_digits=10,
|
|
|
+ decimal_places=2,
|
|
|
+ blank=True,
|
|
|
+ null=True,
|
|
|
+ validators=[MinValueValidator(0)],
|
|
|
+ help_text=_('Total cooling capacity (kW)')
|
|
|
+ )
|
|
|
+ # supply_temperature, return_temperature, temperature_unit, _abs_* provided by CoolingTemperatureMixin
|
|
|
+
|
|
|
+ clone_fields = (
|
|
|
+ 'site', 'location', 'type', 'status', 'cooling_capacity', 'supply_temperature', 'return_temperature',
|
|
|
+ 'temperature_unit',
|
|
|
+ )
|
|
|
+ prerequisite_models = (
|
|
|
+ 'dcim.Site',
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ ordering = ['site', 'name']
|
|
|
+ constraints = (
|
|
|
+ models.UniqueConstraint(
|
|
|
+ fields=('site', 'name'),
|
|
|
+ name='%(app_label)s_%(class)s_unique_site_name'
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ verbose_name = _('cooling source')
|
|
|
+ verbose_name_plural = _('cooling sources')
|
|
|
+
|
|
|
+ def __str__(self):
|
|
|
+ return self.name
|
|
|
+
|
|
|
+ def get_status_color(self):
|
|
|
+ return CoolingSourceStatusChoices.colors.get(self.status)
|
|
|
+
|
|
|
def clean(self):
|
|
|
super().clean()
|
|
|
|
|
|
@@ -164,12 +181,8 @@ class CoolingSource(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
|
|
|
location=self.location, location_site=self.location.site, site=self.site)
|
|
|
)
|
|
|
|
|
|
- # A temperature unit is required when a temperature is set
|
|
|
- if (self.supply_temperature is not None or self.return_temperature is not None) and not self.temperature_unit:
|
|
|
- raise ValidationError(_("Must specify a unit when setting a temperature"))
|
|
|
-
|
|
|
|
|
|
-class CoolingFeed(FlowRateMixin, PressureMixin, PrimaryModel, PathEndpoint, CabledObjectModel):
|
|
|
+class CoolingFeed(CoolingTemperatureMixin, FlowRateMixin, PressureMixin, PrimaryModel, PathEndpoint, CabledObjectModel):
|
|
|
"""
|
|
|
A coolant loop delivered from a CoolingSource to a rack or CDU. Supply and return loops are
|
|
|
represented as separate feeds so each can be traced independently.
|
|
|
@@ -231,7 +244,8 @@ class CoolingFeed(FlowRateMixin, PressureMixin, PrimaryModel, PathEndpoint, Cabl
|
|
|
|
|
|
clone_fields = (
|
|
|
'cooling_source', 'rack', 'status', 'type', 'mark_connected', 'fluid_type', 'cooling_capacity', 'flow_rate',
|
|
|
- 'flow_rate_unit', 'pressure', 'pressure_unit', 'tenant',
|
|
|
+ 'flow_rate_unit', 'pressure', 'pressure_unit', 'supply_temperature', 'return_temperature', 'temperature_unit',
|
|
|
+ 'tenant',
|
|
|
)
|
|
|
prerequisite_models = (
|
|
|
'dcim.CoolingSource',
|