Skip to content

Commit

Permalink
fixed hmi, new register handling in modbus device
Browse files Browse the repository at this point in the history
0.7.0b5
- added color chooser to VariableAdmin
- fixed display bool values in charts
- added servertime to footer of hmi
- new register handling structure in modbus device
  • Loading branch information
trombastic committed Jul 6, 2016
1 parent 760981b commit 75e6371
Show file tree
Hide file tree
Showing 13 changed files with 305 additions and 208 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
- updated docs
- removed unused code

0.7.0.b4
0.7.0b4
- fixed reinit loop in daq daemon
- added restart of daq when Scaling model has changed
- fixed hmi.widget save without a title
Expand All @@ -27,4 +27,10 @@
- added unixtime value in web hmi (experimental)
- fixed custom html panel variable not uptading in hmi
- added restart_daemon field in background task model
- added check of write permissions for pid and log file in DaemonHandler
- added check of write permissions for pid and log file in DaemonHandler

0.7.0b5
- added color chooser to VariableAdmin
- fixed display bool values in charts
- added servertime to footer of hmi
- new register handling structure in modbus device
51 changes: 51 additions & 0 deletions extras/service/SysV-init/pyscada_daq_daemon
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash

### BEGIN INIT INFO
# Provides: pyscada_daq_daemon
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the pyscada daq daemon
# Description: starts pyscada daq daemon
### END INIT INFO

RUN_AS='pyscada'

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

start () {
echo "Spawning pyscada daq daemon"
start-stop-daemon --start -c $RUN_AS -d /var/www/pyscada/PyScadaServer --exec manage.py -- daqdaemon start
return
}


stop () {
start-stop-daemon --start -c $RUN_AS -d /var/www/pyscada/PyScadaServer --exec manage.py -- daqdaemon stop
return
}

case "$1" in
start)
echo "Starting"
start
;;
stop)
echo "Stopping"
stop
;;
restart)
echo "Restarting"
stop
sleep 1
start
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart} " >&2
exit 1
;;
esac

exit 0
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.0b4'
__version__ = '0.7.0b5'
__author__ = 'Martin Schröder'

default_app_config = 'pyscada.apps.PyScadaConfig'
Expand Down
28 changes: 24 additions & 4 deletions pyscada/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from pyscada.models import Device
from pyscada.models import Variable
from pyscada.models import Scaling
from pyscada.models import Scaling, Color
from pyscada.models import Unit
from pyscada.models import DeviceWriteTask
from pyscada.models import Log
Expand All @@ -18,7 +18,7 @@

import datetime

class VariableAdminForm(forms.ModelForm):
class VariableImportAdminForm(forms.ModelForm):
json_configuration = forms.CharField(widget=forms.Textarea)

class Meta:
Expand All @@ -27,7 +27,7 @@ class Meta:

class VariableImportAdmin(admin.ModelAdmin):
actions = None
form = VariableAdminForm
form = VariableImportAdminForm
fields = ('json_configuration',)
list_display = ('name','active')

Expand Down Expand Up @@ -65,7 +65,6 @@ def last_value(self, instance):
class DeviceAdminFrom(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DeviceAdminFrom, self).__init__(*args, **kwargs)
wtf = Variable.objects.all();
w = self.fields['device_type'].widget
# return a list of installed drivers for device classes
device_type_choises = (('generic','no Device'),)
Expand Down Expand Up @@ -94,12 +93,33 @@ class DeviceAdmin(admin.ModelAdmin):
list_display_links = ('short_name', 'description')
form = DeviceAdminFrom

class VarieblesAdminFrom(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(VarieblesAdminFrom, self).__init__(*args, **kwargs)
wtf = Color.objects.all();
w = self.fields['chart_line_color'].widget
color_choices = []
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))
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

class VarieblesAdmin(admin.ModelAdmin):
list_display = ('id','name','description','device_name','value_class','active','writeable',)
list_editable = ('active','writeable',)
list_display_links = ('name',)
list_filter = ('device__short_name', 'active','writeable')
search_fields = ['name',]
form = VarieblesAdminFrom
def device_name(self, instance):
return instance.device.short_name

Expand Down
8 changes: 4 additions & 4 deletions pyscada/export/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ class ExportTask(models.Model):
file_format_choises = (('hdf5','Hierarchical Data Format Version 5'),('mat','Matlab® mat v7.3 compatible file'),('CSV_EXCEL','Microsoft® Excel® compatible csv file'))
file_format = models.CharField(max_length=400, default='hdf5',choices=file_format_choises)
filename_suffix = models.CharField(max_length=400, default='',blank=True)
time_min = models.FloatField(default=None, null=True)
time_max = models.FloatField(default=None, null=True)
time_min = models.FloatField(default=None, null=True) #TODO DateTimeField
time_max = models.FloatField(default=None, null=True) #TODO DateTimeField
user = models.ForeignKey(User,null=True,blank=True, on_delete=models.SET_NULL)
start = models.FloatField(default=0,blank=True) # time wenn task should be started
fineshed = models.FloatField(default=0,blank=True) # time wenn task has been finished
start = models.FloatField(default=0,blank=True) #TODO DateTimeField # time wenn task should be started
fineshed = models.FloatField(default=0,blank=True) #TODO DateTimeField # time wenn task has been finished
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
Expand Down
66 changes: 33 additions & 33 deletions pyscada/hmi/static/pyscada/js/pyscada/pyscada.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* Javascript library for the PyScada web client based on jquery and flot,
version 0.7.0b4
version 0.7.0b5
Copyright (c) 2013-2016 Martin Schröder
Licensed under the GPL.
*/
var version = "0.7.0b4"
var version = "0.7.0b5"
var NotificationCount = 0
var UpdateStatusCount = 0;
var InitStatusCount = 0;
Expand Down Expand Up @@ -56,6 +56,7 @@ function hideInitStatus(){
if (InitStatusCount <= 0){
$("#loadingAnimation").hide();
}
init_chart_data_fetch_pending_count--;
}
function raiseDataOutOfDateError(){
if (!DataOutOfDate){
Expand Down Expand Up @@ -83,18 +84,27 @@ function fetchData(variable_keys,first_timestamp,init,plot_instance) {
$.ajax({
url: RootUrl+'json/cache_data/',
dataType: "json",
timeout: ((first_timestamp == 0) ? fetch_data_timeout*10 : fetch_data_timeout),
timeout: ((init == 1) ? fetch_data_timeout*10 : fetch_data_timeout),
type: "POST",
data:{ timestamp: init ? data_first_timestamp:data_last_timestamp, variables: variable_keys, first_timestamp:first_timestamp, init: init},
dataType:"json"
}).done(function(data) {
update_charts = true;
if (init){
if (typeof(data['timestamp'])==="number"){
timestamp = data['timestamp'];
delete data['timestamp'];
}else{
timestamp = 0;
}
if (typeof(data['server_time'])==="number"){
server_time = data['server_time'];
delete data['server_time'];

var date = new Date(server_time);
$(".server_time").html(date.toLocaleString());
}else{
server_time = 0;
}
if (init){
$.each(data, function(key, val) {
//append data to data array
if (typeof(val)==="object" && typeof plot_instance !== 'undefined'){
Expand All @@ -116,11 +126,6 @@ function fetchData(variable_keys,first_timestamp,init,plot_instance) {
});
init_chart_data_fetch_pending_count--;
}else{
timestamp = data['timestamp'];
delete data['timestamp'];
server_time = data['server_time'];
delete data['server_time'];

if (data_last_timestamp < timestamp){
data_last_timestamp = timestamp;
}
Expand Down Expand Up @@ -176,8 +181,6 @@ function fetchData(variable_keys,first_timestamp,init,plot_instance) {
setTimeout(function() {fetchData();}, refresh_rate);
}



$("#AutoUpdateButton").removeClass("btn-warning");
$("#AutoUpdateButton").addClass("btn-success");
if (JsonErrorCount > 0) {
Expand All @@ -204,14 +207,10 @@ function fetchData(variable_keys,first_timestamp,init,plot_instance) {
});
});
}
if (init){
init_chart_data_fetch_pending_count--;
hideInitStatus();
}
if (init){ hideInitStatus(); }
hideUpdateStatus();
$("#AutoUpdateButton").removeClass("btn-success");
$("#AutoUpdateButton").addClass("btn-warning");
$("#AutoUpdateStatus").hide();
}
);
updateLog();
Expand Down Expand Up @@ -359,6 +358,7 @@ function updateDataValues(key,val){
// unixtime
var date = new Date(val*1000);
$(".type-numeric.unixtime_local_date_time.var-" + key).html(date.toLocaleString());
$(".type-numeric.unixtime_utc_date_time.var-" + key).html(date.toUTCString());
$(".type-numeric.hex_str_full.var-" + key).html(val.toString(16).toUpperCase());
}

Expand Down Expand Up @@ -553,41 +553,41 @@ function PyScadaPlot(id){
}

}


function CheckBuffer(key){
if (data[key].length > BufferSize){
// if buffer is full drop the first element
data[key].splice(-data[key].length,data[key].length-BufferSize);
}
}

function addData(key,time,val){
if (typeof(data[key])==="object"){
data[key].push([time, val]);
if (data[key].length > BufferSize){
// if buffer is full drop the oldest elements
data[key].splice(-data[key].length,data[key].length-BufferSize);
}
CheckBuffer(key);
}
}

function AppendData(key,value){
if (typeof(data[key])==="object"){
data[key] = data[key].concat(value);
if (data[key].length > BufferSize){
// if buffer is full drop the first element
data[key].splice(-data[key].length,data[key].length-BufferSize);
}
CheckBuffer(key);
}
}

function PreppendData(key,value){
if (typeof(data[key])==="object"){
data[key] = value.concat(data[key]);
CheckBuffer(key);
}
}

function UpdateData(key,timestamp){
if (typeof(data[key])==="object"){
if (data[key].length >= 1){
if (typeof(data[key][data[key].length-1][1])==="number"){
data[key].push([timestamp,data[key][data[key].length-1][1]]);
if (data[key].length > BufferSize){
// if buffer is full drop the first element
data[key].splice(-data[key].length,data[key].length-BufferSize);
}
if (typeof(data[key][data[key].length-1][1])==="number" | typeof(data[key][data[key].length-1][1])==="boolean"){
data[key][data[key].length-1][0] = timestamp;
CheckBuffer(key);
}
}
}
Expand Down Expand Up @@ -832,6 +832,6 @@ $( document ).ready(function() {
InitVariableKeys.push(val)
}
});
setTimeout(function() {fetchData()}, 10000);
setTimeout(function() {fetchData()}, 5000);
setTimeout(function() {fetchData(InitVariableKeys,0,1);}, 15000);
});
3 changes: 2 additions & 1 deletion pyscada/hmi/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
</div> <!-- /container -->
</div>
<footer class="footer">
PyScada {% if version_string %}Version: {{version_string}}{% endif %}
<span class="pull-left">PyScada {% if version_string %}Version: {{version_string}}{% endif %}</span>
<span class="pull-right server_time"></span>
</footer>
<!-- Bootstrap core JavaScript
================================================== -->
Expand Down
5 changes: 3 additions & 2 deletions pyscada/hmi/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,10 @@ def get_cache_data(request):
query_first_value = init,\
time_in_ms = True,\
key_is_variable_name = True,\
add_timetamp_field = True,\
variable_id__in = active_variables)
data["timestamp"] = time.time()*1000 # TODO max_time from data

#data["timestamp"] = time.time()*1000 # TODO max_time from data
data["server_time"] = time.time()*1000
jdata = json.dumps(data)
return HttpResponse(jdata, content_type='application/json')
Expand Down
19 changes: 19 additions & 0 deletions pyscada/migrations/0028_auto_20160630_0831.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('pyscada', '0027_auto_20160530_1436'),
]

operations = [
migrations.AlterField(
model_name='variable',
name='short_name',
field=models.CharField(default=b'', max_length=80, verbose_name=b'variable short name', blank=True),
),
]
20 changes: 20 additions & 0 deletions pyscada/migrations/0029_scaling_limit_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('pyscada', '0028_auto_20160630_0831'),
]

operations = [
migrations.AddField(
model_name='scaling',
name='limit_input',
field=models.BooleanField(default=0),
preserve_default=False,
),
]
Loading

0 comments on commit 75e6371

Please sign in to comment.