From b5a5f8c282c2ecaab16fa1653fd87e3dcf75148d Mon Sep 17 00:00:00 2001 From: CL Date: Wed, 24 Jan 2024 16:19:05 +0100 Subject: [PATCH] Delete CalculatedVariable (moved to the pyscada.operations plugin) in order to not loose your calculated variables you should install pyscada.operations plugin before running the pyscada migration 108. --- pyscada/admin.py | 119 +--- ...alculatedvariableselector_period_fields.py | 17 + ...move_calculatedvariable_period_and_more.py | 44 ++ pyscada/models.py | 610 +----------------- pyscada/signals.py | 30 - 5 files changed, 79 insertions(+), 741 deletions(-) mode change 100644 => 100755 pyscada/admin.py create mode 100644 pyscada/migrations/0107_alter_calculatedvariableselector_period_fields.py create mode 100644 pyscada/migrations/0108_remove_calculatedvariable_period_and_more.py diff --git a/pyscada/admin.py b/pyscada/admin.py old mode 100644 new mode 100755 index 8408136d..2e63a421 --- a/pyscada/admin.py +++ b/pyscada/admin.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals from pyscada.models import Device, DeviceProtocol, DeviceHandler -from pyscada.models import PeriodicField, CalculatedVariableSelector, CalculatedVariable from pyscada.models import Variable, VariableProperty, DataSource, DataSourceModel from pyscada.models import Scaling, Color from pyscada.models import Unit, Dictionary, DictionaryItem @@ -251,43 +250,9 @@ def last_value(self, instance): return f"ValueError {e} - with timestamp {v.timestamp_old} : {v.prev_value.__str__()} {instance.unit.unit}" except Variable.DoesNotExist: pass - def get_queryset(self, request): - """Limit Pages to those that belong to the request's user.""" - qs = super(VariableStateAdmin, self).get_queryset(request) - return qs.filter(calculatedvariable__isnull=True) - - -class CalculatedVariableSelectorAdmin(admin.ModelAdmin): - list_display = ("id", "main_variable", "active") - list_display_links = ("main_variable",) - list_editable = ("active",) - raw_id_fields = ("main_variable",) - filter_horizontal = ("period_fields",) - save_as = True - save_as_continue = True - - # Disable changing variable - def get_readonly_fields(self, request, obj=None): - if obj is not None and obj.main_variable is not None: - return ["main_variable"] - return [] - - -class PeriodicFieldAdmin(admin.ModelAdmin): - list_display = ( - "id", - "__str__", - "type", - "property", - "start_from", - "period", - "period_factor", - ) - list_filter = ("calculatedvariableselector",) - # list_editable = ('type', 'property', 'start_from', 'period', 'period_factor',) - list_display_links = None # ('__str__',) - save_as = True - save_as_continue = True + except TimeoutError: + return "Timeout on value query" + return f" - : NaN {instance.unit.unit}" class DeviceForm(forms.ModelForm): @@ -524,82 +489,7 @@ class CoreVariableAdmin(VariableAdmin): ) list_display_links = ("name",) - -class ExtendedCalculatedVariable(Variable): - class Meta: - proxy = True - verbose_name = "Calculated variable" - verbose_name_plural = "Calculated variables" - - -class CalculatedVariableAdminInline(admin.StackedInline): - model = CalculatedVariable - - -class CalculatedVariableAdmin(VariableAdmin): - list_display = ( - "name", - "last_check", - "state", - "last_value", - "chart_line_color", - "short_name", - ) - list_editable = ( - "chart_line_color", - "short_name", - ) - list_filter = ( - ProtocolListFilter, - DeviceListFilter, - VariableListFilter, - "active", - "writeable", - "unit__unit", - ) - list_display_links = None - - def has_add_permission(self, request): - return False - - def get_changelist_form(self, request, **kwargs): - return VariableAdminFrom - - def last_check(self, instance): - return instance.calculatedvariable.last_check - - def state(self, instance): - return instance.calculatedvariable.state - - def store_variable(self, instance): - return instance.calculatedvariable.store_variable - - def main_device(self, instance): - return instance.calculatedvariable.store_variable.device - def formfield_for_foreignkey(self, db_field, request, **kwargs): - if db_field.name == "device": - kwargs["queryset"] = Device.objects.filter( - short_name=CalculatedVariableSelector.dname - ) - return super(CalculatedVariableAdmin, self).formfield_for_foreignkey( - db_field, request, **kwargs - ) - - def get_queryset(self, request): - """Limit Pages to those that belong to the request's user.""" - qs = super(admin.ModelAdmin, self).get_queryset(request) - return qs.filter(calculatedvariable__isnull=False) - - inlines = [CalculatedVariableAdminInline] - - @property - def media(self): - response = super(admin.ModelAdmin, self).media - return response - - save_as = True - save_as_continue = True # show only datasource with can_select as True if db_field.name == "datasource": ids = [] @@ -1158,9 +1048,6 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs): admin_site.register(DeviceHandler, DeviceHandlerAdmin) admin_site.register(Variable, CoreVariableAdmin) admin_site.register(VariableProperty, VariablePropertyAdmin) -admin_site.register(CalculatedVariableSelector, CalculatedVariableSelectorAdmin) -admin_site.register(PeriodicField, PeriodicFieldAdmin) -admin_site.register(ExtendedCalculatedVariable, CalculatedVariableAdmin) admin_site.register(Scaling, ScalingAdmin) admin_site.register(Unit) admin_site.register(ComplexEvent, ComplexEventAdmin) diff --git a/pyscada/migrations/0107_alter_calculatedvariableselector_period_fields.py b/pyscada/migrations/0107_alter_calculatedvariableselector_period_fields.py new file mode 100644 index 00000000..ac34d240 --- /dev/null +++ b/pyscada/migrations/0107_alter_calculatedvariableselector_period_fields.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.5 on 2023-12-14 11:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("pyscada", "0106_datasource_datasourcemodel_djangodatabase_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="calculatedvariableselector", + name="period_fields", + field=models.ManyToManyField(blank=True, to="pyscada.periodicfield"), + ), + ] diff --git a/pyscada/migrations/0108_remove_calculatedvariable_period_and_more.py b/pyscada/migrations/0108_remove_calculatedvariable_period_and_more.py new file mode 100644 index 00000000..e565a0f5 --- /dev/null +++ b/pyscada/migrations/0108_remove_calculatedvariable_period_and_more.py @@ -0,0 +1,44 @@ +# Generated by Django 4.2.5 on 2024-01-16 16:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("pyscada", "0107_alter_calculatedvariableselector_period_fields"), + ] + + operations = [ + migrations.RemoveField( + model_name="calculatedvariable", + name="period", + ), + migrations.RemoveField( + model_name="calculatedvariable", + name="store_variable", + ), + migrations.RemoveField( + model_name="calculatedvariable", + name="variable_calculated_fields", + ), + migrations.RemoveField( + model_name="calculatedvariableselector", + name="main_variable", + ), + migrations.RemoveField( + model_name="calculatedvariableselector", + name="period_fields", + ), + migrations.DeleteModel( + name="ExtendedCalculatedVariable", + ), + migrations.DeleteModel( + name="CalculatedVariable", + ), + migrations.DeleteModel( + name="CalculatedVariableSelector", + ), + migrations.DeleteModel( + name="PeriodicField", + ), + ] diff --git a/pyscada/models.py b/pyscada/models.py index 88bb4e30..41b6c289 100755 --- a/pyscada/models.py +++ b/pyscada/models.py @@ -22,7 +22,6 @@ import datetime import json import signal -from monthdelta import monthdelta from os import kill, waitpid import os @@ -31,7 +30,6 @@ from struct import * from os import getpid -from dateutil import relativedelta import errno import numpy as np import logging @@ -80,6 +78,21 @@ async def channels_test(): channels_driver = True +# Kept for pyscada migration 81 + +def validate_nonzero(value): + if value == 0: + raise ValidationError( + _("Quantity %(value)s is not allowed"), + params={"value": value}, + ) + +def start_from_default(): + return make_aware( + datetime.datetime.combine(datetime.date.today(), datetime.datetime.min.time()) + ) + + # # Manager # @@ -2246,17 +2259,6 @@ def _get_objects_for_html( self, list_to_append=None, obj=None, exclude_model_names=None ): list_to_append = get_objects_for_html(list_to_append, self, exclude_model_names) - if hasattr(self, "calculatedvariableselector"): - list_to_append = self.calculatedvariableselector._get_objects_for_html( - list_to_append, None, ["main_variable"] - ) - if hasattr(self, "calculatedvariable"): - list_to_append = get_objects_for_html( - list_to_append, - self.calculatedvariable, - ["variable_calculated_fields", "store_variable"], - ) - return list_to_append def get_protocol_variable(self): @@ -2279,588 +2281,6 @@ def get_protocol_variable(self): return None -def validate_nonzero(value): - if value == 0: - raise ValidationError( - _("Quantity %(value)s is not allowed"), - params={"value": value}, - ) - - -def start_from_default(): - return make_aware( - datetime.datetime.combine(datetime.date.today(), datetime.datetime.min.time()) - ) - - -class PeriodicField(models.Model): - """ - Auto calculate and store value related to a Variable for a time range. - Example: - store the min of each month. - - store difference of each day between 9am an 8:59am - """ - - type_choices = ( - (0, "min"), - (1, "max"), - (2, "total"), - (3, "difference"), - (4, "difference percent"), - (5, "delta"), - (6, "mean"), - (7, "first"), - (8, "last"), - (9, "count"), - (10, "count value"), - (11, "range"), - (12, "step"), - (13, "change count"), - (14, "distinct count"), - ) - type = models.SmallIntegerField( - choices=type_choices, - help_text="Min: Minimum value of a field
" - "Max: Maximum value of a field
" - "Total: Sum of all values in a field
" - "Difference: Difference between first and last value of a field
" - "Difference percent: Percentage change between " - "first and last value of a field
" - "Delta: Cumulative change in value, only counts increments
" - "Mean: Mean value of all values in a field
" - "First: First value in a field
" - "Last: Last value in a field
" - "Count: Number of values in a field
" - "Count value: Number of a value in a field
" - "Range: Difference between maximum and minimum values of a field
" - "Step: Minimal interval between values of a field
" - "Change count: Number of times the field’s value changes
" - "Distinct count: Number of unique values in a field", - ) - property = models.CharField( - default="", - blank=True, - null=True, - max_length=255, - help_text="Min: superior or equal this value, ex: 53.5 " - "(use >53.5 for strictly superior)
" - "Max: lower or equal this value, ex: 53.5 " - "(use <53.5 for strictly lower)
" - "Count value : enter the value to count", - ) - start_from = models.DateTimeField( - default=start_from_default, - help_text="Calculate from this DateTime and then each period_factor*period", - ) - period_choices = ( - (0, "second"), - (1, "minute"), - (2, "hour"), - (3, "day"), - (4, "week"), - (5, "month"), - (6, "year"), - ) - period = models.SmallIntegerField(choices=period_choices) - period_factor = models.PositiveSmallIntegerField( - default=1, - validators=[validate_nonzero], - help_text="Example: set to 2 and choose " "minute to have a 2 minutes period", - ) - - def __str__(self): - s = self.type_choices[self.type][1] + "-" - if self.property != "" and self.property is not None: - s += str(self.property).replace("<", "lt").replace(">", "gt") + "-" - s += str(self.period_factor) + self.period_choices[self.period][1] - if self.period_factor > 1: - s += "s" - s += "-from:" + str(self.start_from.date()) + "T" + str(self.start_from.time()) - return s - - def validate_unique(self, exclude=None): - qs = PeriodicField.objects.filter( - type=self.type, - property=self.property, - start_from=self.start_from, - period=self.period, - period_factor=self.period_factor, - ).exclude(id=self.id) - if len(qs): - raise ValidationError("This periodic field already exist.") - - -class CalculatedVariableSelector(models.Model): - main_variable = models.OneToOneField(Variable, on_delete=models.CASCADE) - period_fields = models.ManyToManyField(PeriodicField) - active = models.BooleanField(default=True) - dname = "for_calculated_variables" - - def get_new_calculated_variable(self, main_var, period): - v = Variable.objects.get(id=main_var.id) - try: - d = Device.objects.get(short_name=self.dname) - except Device.DoesNotExist: - d = Device.objects.create( - short_name=self.dname, - description="Device used to store calculated variables", - protocol_id=1, - ) - sv_name = ( - v.name[ - : Variable._meta.get_field("name").max_length - - len(str(period).replace(":", "-")) - - 1 - ] - + "-" - + str(period).replace(":", "-") - ) - sv_name = sv_name[: Variable._meta.get_field("name").max_length] - if len(Variable.objects.filter(name=sv_name)) == 0: - v.id = None - v.name = sv_name - v.description = str(period) - v.writeable = False - v.cov_increment = -1 - v.device_id = d.id - v.scaling = None - v.value_class = "FLOAT64" - v.save() - logger.debug("Create CalculatedVariable: " + sv_name) - pv = CalculatedVariable( - store_variable=v, variable_calculated_fields=self, period=period - ) - else: - pv = None - - return pv - - def create_all_calculated_variables(self): - cvs = [] - self.refresh_from_db() - for p in self.period_fields.all(): - cv = self.get_new_calculated_variable(self.main_variable, p) - if cv is not None: - cvs.append(cv) - - # logger.debug(cvs) - CalculatedVariable.objects.bulk_create(cvs) - - def __str__(self): - return self.main_variable.name - - def _get_objects_for_html( - self, list_to_append=None, obj=None, exclude_model_names=None - ): - list_to_append = get_objects_for_html(list_to_append, self, exclude_model_names) - for calculatedvariable in self.calculatedvariable_set.all(): - list_to_append = get_objects_for_html( - list_to_append, - calculatedvariable, - [ - "variable_calculated_fields", - ], - ) - - return list_to_append - - -class CalculatedVariable(models.Model): - store_variable = models.OneToOneField(Variable, on_delete=models.CASCADE) - variable_calculated_fields = models.ForeignKey( - CalculatedVariableSelector, on_delete=models.CASCADE - ) - period = models.ForeignKey(PeriodicField, on_delete=models.CASCADE) - last_check = models.DateTimeField(blank=True, null=True) - state = models.CharField(default="", max_length=100) - - def __str__(self): - return self.store_variable.name - - def check_to_now(self, force_write=False, add_partial_info=False): - if self.last_check is not None: - self.check_period(self.last_check, now(), force_write, add_partial_info) - else: - self.check_period( - self.period.start_from, now(), force_write, add_partial_info - ) - - def check_period(self, d1, d2, force_write=False, add_partial_info=False): - # logger.debug("Check period of %s [%s - %s]" % (self.store_variable, d1, d2)) - self.state = "Checking [%s to %s]" % (d1, d2) - self.state = self.state[0:100] - self.save(update_fields=["state"]) - - if is_naive(d1): - d1 = make_aware(d1) - if is_naive(d2): - d2 = make_aware(d2) - output = [] - - if self.period_diff_quantity(d1, d2) is None: - # logger.debug("No period in date interval : %s (%s %s)" % (self.period, d1, d2)) - self.state = "[%s to %s] < %s" % ( - d1, - d2, - str(self.period.period_factor) - + self.period.period_choices[self.period.period][1], - ) - self.state = self.state[0:100] - self.save(update_fields=["state"]) - return None - - td = self.add_timedelta() - - d = self.get_valid_range(d1, d2) - if d is None: - self.state = "No time range found [%s to %s] %s" % (d1, d2, self.period) - self.state = self.state[0:100] - self.save(update_fields=["state"]) - return None - [d1, d2] = d - - if self.period_diff_quantity(d1, d2) is None: - logger.debug( - "No period in new date interval : %s (%s %s)" % (self.period, d1, d2) - ) - self.state = "[%s to %s] < %s" % ( - d1, - d2, - str(self.period.period_factor) - + self.period.period_choices[self.period.period][1], - ) - self.state = self.state[0:100] - self.save(update_fields=["state"]) - return None - - # logger.debug("Valid range : %s - %s" % (d1, d2)) - - while d2 >= d1 + td and d1 + td <= now(): - # logger.debug("add for %s - %s" % (d1, d1 + td)) - td1 = d1.timestamp() - try: - v_stored = RecordedData.objects.get_values_in_time_range( - time_min=td1, - time_max=td1 + 1, - variable=self.store_variable, - add_latest_value=False, - ) - except AttributeError: - v_stored = [] - if ( - not force_write - and len(v_stored) - and len(v_stored[self.store_variable.id][0]) - ): - # logger.debug("Value already exist in RecordedData for %s - %s" % (d1, d1 + td)) - pass - else: - calc_value = self.get_value(d1, d1 + td) - if calc_value is not None and self.store_variable.update_value( - calc_value, td1 - ): - item = self.store_variable.create_recorded_data_element() - item.date_saved = d1 - if item is not None: - output.append(item) - d1 = d1 + td - - if len(output): - self.last_check = output[-1].date_saved # + td - else: - # logger.debug("Nothing to add") - self.last_check = min(d1, d2, now()) - - # Add partial last value when then is data but the period is not elapsed - # do not use this data in last check to recalculate it again till the period is elapsed - calc_value = self.get_value(d2 - td, d2) - td2 = (d2 - td).timestamp() - if ( - add_partial_info - and calc_value is not None - and self.store_variable.update_value(calc_value, td2) - ): - item = self.store_variable.create_recorded_data_element() - item.date_saved = d2 - td - if item is not None: - output.append(item) - - # Save recorded data elements to DB - if len(output): - m = "Adding : " - for c in output: - m += str(c) + " " + str(c.date_saved) + " - " - logger.debug(m) - RecordedData.objects.bulk_create( - output, batch_size=100, ignore_conflicts=True - ) - - self.state = "Checked [%s to %s]" % (d1, d2) - self.state = self.state[0:100] - self.save(update_fields=["last_check", "state"]) - - def get_value(self, d1, d2): - try: - tmp = RecordedData.objects.get_values_in_time_range( - variable=self.variable_calculated_fields.main_variable, - time_min=d1.timestamp(), - time_max=d2.timestamp(), - time_in_ms=True, - ) - except AttributeError: - tmp = [] - values = [] - if len(tmp) > 0: - for v in tmp[self.variable_calculated_fields.main_variable.id]: - values.append(v[1]) - type_str = self.period.type_choices[self.period.type][1] - if type_str == "min": - p = str(self.period.property) - if p == "" or p is None or p == "None": - res = min(values) - elif p.startswith("<"): - try: - p = float(p.split("<")[1]) - res = min_pass(values, p, "gt") - except ValueError: - logger.warning( - "Period field %s property after < is not a float : %s" - % (self.period, self.period.property) - ) - res = None - else: - try: - p = float(p) - res = min_pass(values, p, "gte") - except ValueError: - logger.warning( - "Period field %s property is not a float : %s" - % (self.period, self.period.property) - ) - res = None - elif type_str == "max": - p = str(self.period.property) - if p == "" or p is None or p == "None": - res = max(values) - elif p.startswith(">"): - try: - p = float(p.split(">")[1]) - res = max_pass(values, p, "lt") - except ValueError: - logger.warning( - "Period field %s property after > is not a float : %s" - % (self.period, self.period.property) - ) - res = None - else: - try: - p = float(p) - res = max_pass(values, p, "lte") - except ValueError: - logger.warning( - "Period field %s property is not a float : %s" - % (self.period, self.period.property) - ) - res = None - elif type_str == "total": - res = sum(values) - elif type_str == "difference": - res = values[-1] - values[0] - elif type_str == "difference percent": - if values[0] == 0: - res = None - else: - res = (values[-1] - values[0]) / (values[0] * 100) - elif type_str == "delta": - res = 0 - v = None - for i in values: - if v is not None and i - v > 0: - res += i - v - v = i - elif type_str == "mean": - res = np.mean(values) - elif type_str == "first": - res = values[0] - elif type_str == "last": - res = values[-1] - elif type_str == "count": - res = len(values) - elif type_str == "count value": - try: - p = float(self.period.property) - res = values.count(p) - except ValueError: - logger.warning( - "Period field %s property is not a float" % self.period - ) - res = None - elif type_str == "range": - res = max(values) - min(values) - elif type_str == "step": - res = 0 - j = None - if len(values) > 1: - for i in values: - if j is not None: - res = min(res, abs(i - j)) - j = i - else: - res = None - elif type_str == "change count": - res = 0 - j = None - if len(values) > 1: - for i in values: - if j is not None and j != i: - res += 1 - j = i - else: - res = None - elif type_str == "distinct count": - res = len(set(values)) - else: - logger.warning("Periodic field type unknown") - res = None - - # logger.debug(str(d1) + " " + str(self.period) + " " + str(res)) - return res - else: - # logger.debug("No values for this period") - return None - - def get_valid_range(self, d1, d2): - if is_naive(d1): - d1 = make_aware(d1) - if is_naive(d2): - d2 = make_aware(d2) - if d2 <= d1: - logger.warning("Use get_valid_range with d_start > d_end") - return None - if self.period.start_from == d1: - d_start = 0 - else: - d_start = self.period_diff_quantity(self.period.start_from, d1) - if d_start is not None: - if d_start != int(d_start): - d_start = int(d_start) + 1 - else: - d_start = int(d_start) - else: - logger.debug("d_start - start_from < period_factor*period") - return None - d_end = self.period_diff_quantity(self.period.start_from, d2) - if d_end is not None: - d_end = int(d_end) - else: - logger.debug("d_end - start_from < period_factor*period") - return None - if d_end <= d_start: - logger.debug("d_end - d_start < period_factor*period") - return None - - td = self.add_timedelta() - - d_start = d_start / self.period.period_factor - if d_start != int(d_start): - d_start = int(d_start) + 1 - else: - d_start = int(d_start) - - d_end = d_end / self.period.period_factor - if d_end != int(d_end): - d_end = int(d_end) + 1 - else: - d_end = int(d_end) - - dd_start = d_start * td + self.period.start_from - dd_end = d_end * td + self.period.start_from - - if dd_end > d2: - logger.debug("%s > %s" % (dd_end, d2)) - dd_end -= self.add_timedelta(self._period_diff_quantity(d2, dd_end)) - logger.debug("dd_end : %s" % dd_end) - - return [dd_start, dd_end] - - def add_timedelta(self, delta=None): - if delta is None: - delta = self.period.period_factor - td = None - period_str = self.period.period_choices[self.period.period][1] - if period_str == "year": - td = monthdelta(12) * delta - elif period_str == "month": - td = monthdelta(delta) - elif period_str == "week": - td = datetime.timedelta(weeks=delta) - elif period_str == "day": - td = datetime.timedelta(days=delta) - elif period_str == "hour": - td = datetime.timedelta(hours=delta) - elif period_str == "minute": - td = datetime.timedelta(minutes=delta) - elif period_str == "second": - td = datetime.timedelta(seconds=delta) - return td - - def _period_diff_quantity(self, d1, d2): - period_str = self.period.period_choices[self.period.period][1] - if period_str == "year": - res = self.years_diff_quantity(d1, d2) - elif period_str == "month": - res = self.months_diff_quantity(d1, d2) - elif period_str == "week": - res = self.weeks_diff_quantity(d1, d2) - elif period_str == "day": - res = self.days_diff_quantity(d1, d2) - elif period_str == "hour": - res = self.hours_diff_quantity(d1, d2) - elif period_str == "minute": - res = self.minutes_diff_quantity(d1, d2) - elif period_str == "second": - res = self.seconds_diff_quantity(d1, d2) - return res - - def period_diff_quantity(self, d1, d2): - res = self._period_diff_quantity(d1, d2) - if res >= self.period.period_factor: - return res - else: - return None - - def years_diff_quantity(self, d1, d2): - return relativedelta.relativedelta(d2, d1).years - - def months_diff_quantity(self, d1, d2): - return ( - relativedelta.relativedelta(d2, d1).months - + self.years_diff_quantity(d1, d2) * 12 - ) - - def weeks_diff_quantity(self, d1, d2): - return self.days_diff_quantity(d1, d2) / 7 - - def days_diff_quantity(self, d1, d2): - diff = (d2 - d1).total_seconds() / 60 / 60 / 24 - # logger.debug("Days: " + str(diff)) - return diff - - def hours_diff_quantity(self, d1, d2): - diff = (d2 - d1).total_seconds() / 60 / 60 - # logger.debug("Hours: " + str(diff)) - return diff - - def minutes_diff_quantity(self, d1, d2): - diff = (d2 - d1).total_seconds() / 60 - # logger.debug("Minutes: " + str(diff)) - return diff - - def seconds_diff_quantity(self, d1, d2): - diff = (d2 - d1).total_seconds() - # logger.debug("Seconds: " + str(diff)) - return diff - - class DeviceWriteTask(models.Model): id = models.AutoField(primary_key=True) variable = models.ForeignKey( diff --git a/pyscada/signals.py b/pyscada/signals.py index ac401bfd..766d6ad3 100644 --- a/pyscada/signals.py +++ b/pyscada/signals.py @@ -8,8 +8,6 @@ BackgroundProcess, VariableProperty, DeviceHandler, - CalculatedVariableSelector, - CalculatedVariable, ) from pyscada.admin import VariableState @@ -42,34 +40,6 @@ def _vp_value_change(sender, instance, **kwargs): instance.value_changed = True -@receiver(m2m_changed, sender=CalculatedVariableSelector.period_fields.through) -def _create_calculated_variables(sender, instance, action, **kwargs): - """ - Create calculated variables - """ - if type(instance) is CalculatedVariableSelector and action == "post_add": - try: - logger.debug( - "m2m " - + str(action) - + " " - + str(type(instance).__name__) - + "." - + str(instance) - + "-" - + str(instance.id) - ) - except Exception as e: - logger.debug("post_add pyscada " + str(e)) - instance.create_all_calculated_variables() - - -@receiver(post_delete, sender=CalculatedVariable) -def post_delete_user(sender, instance, *args, **kwargs): - if instance.store_variable: - instance.store_variable.delete() - - @receiver(post_save, sender=VariableProperty) @receiver(post_save, sender=Variable) @receiver(post_save, sender=Device)