Skip to content

Commit

Permalink
updated to django 1.11, OWFS support, fixed daemon handler, export fi…
Browse files Browse the repository at this point in the history
…le link

0.7.0b18
- added OWFS support to onewire device
- updated to django 1.11
- added download link for export files to export job
- fixed daemon handler
  • Loading branch information
trombastic committed May 26, 2017
1 parent 8aa6de5 commit 511869e
Show file tree
Hide file tree
Showing 24 changed files with 290 additions and 154 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,9 @@
- updated docs
- some hmi changes

0.7.0b18
- added OWFS support to onewire device
- updated to django 1.11
- added download link for export files to export job
- fixed daemon handler

12 changes: 9 additions & 3 deletions docs/django_settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ Open the urls configuration file and add the nesseary rewrite rule to the django
::

...
url(r'^admin/', admin.site.urls),
url(r'^', include('pyscada.hmi.urls')),
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include('pyscada.hmi.urls')),
]
...

Expand Down Expand Up @@ -60,7 +65,8 @@ Add the PyScada and the subapps to the installed apps list.
'pyscada.visa',
'pyscada.hmi',
'pyscada.systemstat',
'pyscada.export'
'pyscada.export',
'pyscada.onewire'
]

To use the MySQL Database, fill in the database, the user and password as selected in the *create Database section*.
Expand Down
6 changes: 3 additions & 3 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ Open a Shell (cmd.exe) and install the folowing packages via pip.
::

pip install gunicorn
pip install django">=1.10,<1.11"
pip install django">=1.11,<1.12"
pip install numpy
pip install python-daemon
pip install pyscada
Expand All @@ -141,11 +141,11 @@ Create a new Django Project
# Linux/OSX
cd /var/www/pyscada/
sudo -u pyscada django-admin.py startproject PyScadaServer
sudo -u pyscada django-admin startproject PyScadaServer
# Windows
cd C:/Users/_YOUR_USERNAME_/www
python django-admin.py startproject PyScadaServer
python django-admin startproject PyScadaServer


Expand Down
2 changes: 1 addition & 1 deletion pyscada/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#__import__('pkg_resources').declare_namespace('pyscada')

__version__ = '0.7.0b17'
__version__ = '0.7.0b18'
__author__ = 'Martin Schröder'

default_app_config = 'pyscada.apps.PyScadaConfig'
Expand Down
20 changes: 11 additions & 9 deletions pyscada/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from django import forms
from django.conf import settings
from django.contrib.admin import AdminSite
from django.contrib.auth.models import User, Group
from django.contrib.auth.admin import UserAdmin, GroupAdmin

import datetime

Expand Down Expand Up @@ -87,16 +89,15 @@ def __init__(self, *args, **kwargs):
for choice in wtf:
color_choices.append((choice.id,choice.color_code()))
w.choices = color_choices
# override w.render_option method
def render_option_color(self,selected_choices, option_value, option_label):
html = self._render_option(selected_choices, option_value, option_label.upper())
font_color = hex(int('ffffff',16)-int(option_label[1::],16))[2::]
return html.replace('<option','<option style="background: %s; color: #%s"'%(option_label,font_color))
def create_option_color(self, name, value, label, selected, index, subindex=None, attrs=None):
font_color = hex(int('ffffff',16)-int(label[1::],16))[2::]
#attrs = self.build_attrs(attrs,{'style':'background: %s; color: #%s'%(label,font_color)})
self.option_inherits_attrs = True
return self._create_option(name, value, label, selected, index, subindex, attrs={'style':'background: %s; color: #%s'%(label,font_color)})
import types
from django.forms.widgets import Select
w.widget._render_option = w.widget.render_option # copy old method
f = types.MethodType(render_option_color, w.widget,Select)
w.widget.render_option = f # add new method
w.widget._create_option = w.widget.create_option # copy old method
w.widget.create_option = types.MethodType(create_option_color, w.widget,Select) # replace old with new

class VarieblesAdmin(admin.ModelAdmin):
list_display = ('id','name','description','unit','device_name','value_class','active','writeable',)
Expand Down Expand Up @@ -187,7 +188,6 @@ class EventAdmin(admin.ModelAdmin):
admin_site.register(Device,DeviceAdmin)
admin_site.register(Variable,VarieblesAdmin)
admin_site.register(Scaling)
# admin.site.register(VariableConfigFileImport,VariableImportAdmin)
admin_site.register(Unit)
admin_site.register(Event,EventAdmin)
admin_site.register(RecordedEvent,RecordedEventAdmin)
Expand All @@ -196,3 +196,5 @@ class EventAdmin(admin.ModelAdmin):
admin_site.register(Log,LogAdmin)
admin_site.register(BackgroundTask,BackgroundTaskAdmin)
admin_site.register(VariableState,VariableStateAdmin)
admin_site.register(User,UserAdmin)
admin_site.register(Group,GroupAdmin)
4 changes: 2 additions & 2 deletions pyscada/export/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ExportTaskAdmin(admin.ModelAdmin):
filter_horizontal = ('variables',)
list_display = ('id','label','datetime_start','datetime_fineshed',\
'mean_value_period','file_format','done','busy','failed',)
readonly_fields = ('datetime_fineshed','done','busy','failed',)
readonly_fields = ('datetime_fineshed','done','busy','failed','backgroundtask','downloadlink')

admin_site.register(ScheduledExportTask,ScheduledExportTaskAdmin)
admin_site.register(ExportTask,ExportTaskAdmin)
45 changes: 28 additions & 17 deletions pyscada/export/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pyscada.export.hdf5_file import unix_time_stamp_to_matlab_datenum
from pyscada.export.csv_file import excel_compatible_csv
from pyscada.export.csv_file import unix_time_stamp_to_excel_datenum
from pyscada.export.models import ExportTask
# Django
from django.conf import settings

Expand All @@ -19,12 +20,12 @@



def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active_vars=None,file_extension=None,append_to_file=False,no_mean_value = False,mean_value_period = 5.0,**kwargs):
def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active_vars=None,file_extension=None,append_to_file=False,no_mean_value = False,mean_value_period = 5.0,backgroundtask_id=None,export_task_id=None,**kwargs):
'''
read all data
'''
if kwargs.has_key('backgroundtask_id'):
tp = BackgroundTask.objects.get(id= kwargs['backgroundtask_id'])
if backgroundtask_id is not None:
tp = BackgroundTask.objects.get(id= backgroundtask_id)
tp.message='init'
tp.timestamp=time()
tp.pid=str(os.getpid())
Expand Down Expand Up @@ -68,6 +69,7 @@ def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active
tp.failed = 1
tp.save()
return

#
if filename is None:
if hasattr(settings,'PYSCADA_EXPORT'):
Expand All @@ -91,7 +93,7 @@ def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active
db_time_min = RecordedData.objects.first()
if not db_time_min:
tp.timestamp = time()
tp.message = 'not data to export'
tp.message = 'no data to export'
tp.failed = 1
tp.save()
return
Expand All @@ -100,7 +102,7 @@ def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active
db_time_max = RecordedData.objects.last()
if not db_time_max:
tp.timestamp = time()
tp.message = 'not data to export'
tp.message = 'no data to export'
tp.failed = 1
tp.save()
return
Expand All @@ -113,12 +115,10 @@ def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active

if kwargs.has_key('filename_suffix'):
filename = os.path.join(backup_file_path,backup_file_name + '_' + cdstr_from + '_' + cdstr_to + '_' + kwargs['filename_suffix'])
xml_filename = os.path.join(backup_file_path,backup_file_name + '_' + cdstr_from + '_' + cdstr_to + '_' + kwargs['filename_suffix'] + '.xml')
else:
filename = os.path.join(backup_file_path,backup_file_name + '_' + cdstr_from + '_' + cdstr_to)
xml_filename = os.path.join(backup_file_path,backup_file_name + '_' + cdstr_from + '_' + cdstr_to + '.xml')
else: # generate xml file name from filename
xml_filename = filename.split('.')[0] + '.xml'



# check if file exists
if os.path.exists(filename + file_extension) and not append_to_file:
Expand All @@ -130,8 +130,13 @@ def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active

# append the extension
filename = filename + file_extension

# todo add ExportFile object
xml_filename = filename.split('.')[0] + '.xml'
# add Filename to ExportTask
if export_task_id is not None:
job = ExportTask.objects.filter(pk=export_task_id).first()
if job:
job.filename = filename
job.save()

#
if active_vars is None:
Expand Down Expand Up @@ -172,7 +177,7 @@ def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active
elif file_extension in ['.csv']:
bf = excel_compatible_csv(filename,version = '1.1',description = description ,name = name, creation_date = strftime('%d-%b-%Y %H:%M:%S'))
# export config to an separate file to avoid attr > 64k
export_xml_config_file(xml_filename) # todo make optional
#export_xml_config_file(xml_filename) # todo make optional


# less then 24
Expand Down Expand Up @@ -226,7 +231,10 @@ def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active
id = var.pk,\
description=var.description,\
value_class = validate_value_class(value_class),\
unit = udunit)
unit = udunit,\
color= var.chart_line_color_code(),\
short_name = var.short_name,\
chart_line_thickness = var.chart_line_thickness)
continue

# data[var.pk][::][time,value]
Expand Down Expand Up @@ -285,10 +293,13 @@ def export_recordeddata_to_file(time_min=None,time_max=None,filename=None,active

# write data
bf.write_data(var.name,_cast_value(out_data,validate_value_class(value_class)),\
id = var.pk,\
description=var.description,\
value_class = validate_value_class(value_class),\
unit = udunit)
id = var.pk,\
description=var.description,\
value_class = validate_value_class(value_class),\
unit = udunit,\
color= var.chart_line_color_code(),\
short_name = var.short_name,\
chart_line_thickness = var.chart_line_thickness)


bf.close_file()
Expand Down
11 changes: 7 additions & 4 deletions pyscada/export/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@ def _export_handler(job,today):
export_recordeddata_to_file(\
job.time_min(),\
job.time_max(),\
None,\
job.variables.values_list('pk',flat=True),\
file_ext,\
filename=None,\
active_vars=job.variables.values_list('pk',flat=True),\
file_extension = file_ext,\
filename_suffix=job.filename_suffix,\
backgroundtask_id=bt.pk,\
mean_value_period = job.mean_value_period)
export_task_id = job.pk,\
mean_value_period = job.mean_value_period
)
job = ExportTask.objects.get(pk=job.pk)
job.done = True
job.busy = False
job.datetime_fineshed = datetime.now(UTC)
Expand Down
20 changes: 20 additions & 0 deletions pyscada/export/migrations/0011_exporttask_filename.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2017-04-06 10:24
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('export', '0010_auto_20161128_1049'),
]

operations = [
migrations.AddField(
model_name='exporttask',
name='filename',
field=models.CharField(blank=True, max_length=1000, null=True),
),
]
24 changes: 15 additions & 9 deletions pyscada/export/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

from django.db import models
from django.contrib.auth.models import User
from django.conf import settings

import time
import time, os
from datetime import datetime
from pytz import UTC
#
Expand Down Expand Up @@ -41,15 +42,17 @@ class ExportTask(models.Model):
filename_suffix = models.CharField(max_length=400, default='',blank=True)
datetime_min = models.DateTimeField(default=None, null=True)
datetime_max = models.DateTimeField(default=None, null=True)
user = models.ForeignKey(User,null=True,blank=True, on_delete=models.SET_NULL)
user = models.ForeignKey(User,null=True,blank=True, on_delete=models.SET_NULL)
datetime_start = models.DateTimeField(default=datetime_now)
datetime_fineshed = models.DateTimeField(null=True,blank=True)
done = models.BooleanField(default=False,blank=True) # label task has been done
busy = models.BooleanField(default=False,blank=True) # label task is in operation done
failed = models.BooleanField(default=False,blank=True) # label task has failed
filename = models.CharField(blank=True,null=True,max_length=1000)


def __unicode__(self):
return unicode(self.label)

def time_min(self):
return time.mktime(self.datetime_min.timetuple())
def time_max(self):
Expand All @@ -58,10 +61,13 @@ def start(self):
return time.mktime(self.datetime_start.timetuple())
def fineshed(self):
return time.mktime(self.datetime_fineshed.timetuple())
def downloadlink(self):
if not self.done:
return '<b>self.filename<b/>'
backup_file_path = os.path.expanduser('~/measurement_data_dumps')
if hasattr(settings,'PYSCADA_EXPORT'):
if settings.PYSCADA_EXPORT.has_key('output_folder'):
backup_file_path = os.path.expanduser(settings.PYSCADA_EXPORT['output_folder'])
return '<a href="%s">%s</a>'%(self.filename.replace(backup_file_path,'/measurement'),self.filename.replace(backup_file_path,'/measurement'))
downloadlink.allow_tags = True

'''
class ExportFile(models.Model):
id = models.AutoField(primary_key=True)
exporttask = models.ForeignKey(ExportTask,null=True, on_delete=models.SET_NULL)
filename = models.FilePathField()
'''
Loading

0 comments on commit 511869e

Please sign in to comment.