Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updates #63

Merged
merged 20 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions install_venv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ function install_dependences(){
python3-mysqldb
python3-pip
zlib1g-dev
pkg-config
"
apt_proxy install -y $DEB_TO_INSTALL

Expand Down Expand Up @@ -332,15 +333,15 @@ function pyscada_init(){
(
cd $SERVER_ROOT
# Migration and static files
sudo -u pyscada python3 manage.py migrate
sudo -u pyscada python3 manage.py collectstatic --noinput
sudo -u pyscada -E env PATH=${PATH} python3 manage.py migrate
sudo -u pyscada -E env PATH=${PATH} python3 manage.py collectstatic --noinput

# Load fixtures with default configuration for chart lin colors and units
sudo -u pyscada python3 manage.py loaddata color
sudo -u pyscada python3 manage.py loaddata units
sudo -u pyscada -E env PATH=${PATH} python3 manage.py loaddata color
sudo -u pyscada -E env PATH=${PATH} python3 manage.py loaddata units

# Initialize the background service system of pyscada
sudo -u pyscada python3 manage.py pyscada_daemon init
sudo -u pyscada -E env PATH=${PATH} python3 manage.py pyscada_daemon init
)

if [[ "$answer_update" == "n" ]]; then
Expand Down Expand Up @@ -465,9 +466,7 @@ function user_setup(){
# stop pyscada and show some python3 packages installed
function stop_pyscada(){
debug "stop_pyscada"

pip3 list | grep -i -E 'pyscada|channels|asgiref'


echo "Stopping PyScada"
systemctl stop pyscada gunicorn gunicorn.socket
sleep 1 # Give systemd time to shutdown
Expand Down
6 changes: 3 additions & 3 deletions pyscada/core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

for app in apps.app_configs.values():
if app.name.startswith('pyscada.') and app.name != "pyscada.core":

try:
m = __import__(f"{app.name}.urls", fromlist=[str('a')])
urlpatterns += m.urlpatterns

except ModuleNotFoundError:
pass
except Exception as e:
logger.warning(e, exc_info=True)

3 changes: 1 addition & 2 deletions pyscada/export/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@

try:
import h5py
#logger.debug('%d, h5py import successful' % getpid())
except:
#logger.error('%d, unhandled exception\n%s' % (getpid(), traceback.format_exc()))
logger.error('Cannot import h5py', exc_info=True)
pass

class ExportProcess(BaseProcess):
Expand Down
16 changes: 13 additions & 3 deletions pyscada/hmi/static/pyscada/js/pyscada/pyscada_v0-7-0rc14.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,11 @@ var store_temp_ajax_data = null;
* @returns {string} Return a date
*/
function timestamp_conversion(id,val){
if (isNaN(val)) {
return val;
}else {
val = parseFloat(val);
}
if (id == 1){
// convert millisecond timestamp to local date
val = new Date(val).toDateString();
Expand Down Expand Up @@ -553,7 +558,7 @@ var store_temp_ajax_data = null;

function colorToRgb(color_id) {
if (color_id == -1) {return [92, 200, 92];}
else if (color_id == 0) {return [250, 250, 250];}
else if (color_id == 0) {return [210, 210, 210];}
var r = get_config_from_hidden_config("color", 'id', color_id, 'r');
var g = get_config_from_hidden_config("color", 'id', color_id, 'g');
var b = get_config_from_hidden_config("color", 'id', color_id, 'b');
Expand Down Expand Up @@ -776,7 +781,8 @@ var store_temp_ajax_data = null;
if (typeof(val)==="number") {
if (timestamp_conversion_value != null && timestamp_conversion_value != 0 && typeof(timestamp_conversion_value) != "undefined"){
// Transform timestamps
r_val=timestamp_conversion(timestamp_conversion_value,val);
r_val=dictionary(var_id, val, type.replace('-', ''));
r_val=timestamp_conversion(timestamp_conversion_value,r_val);
}else {
// Transform value in dictionaries
r_val=dictionary(var_id, val, type.replace('-', ''));
Expand All @@ -790,7 +796,11 @@ var store_temp_ajax_data = null;
}
if (display_value_option_id == 'None' || ci_type == 0){
// Change background color
e.style.backgroundColor = update_data_colors(control_item_id,val);
if (e.classList.contains("process-flow-diagram-item")) {
e.style.fill = update_data_colors(control_item_id,val);
}else {
e.style.backgroundColor = update_data_colors(control_item_id,val);
}
}
})

Expand Down
5 changes: 5 additions & 0 deletions pyscada/hmi/templates/403.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% if exception %}
<h1>{{ exception }}</h1>
{% else %}
<h1>403 Forbidden</h1>
{% endif %}
5 changes: 4 additions & 1 deletion pyscada/hmi/templates/process_flow_diagram.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<div class="widget-body process-flow-diagram-body {{ widget_extra_css_class }}" >
{% for item in process_flow_diagram.process_flow_diagram_items.all %}
<div class="hidden variable-config" data-name="{{item.control_item.name}}" data-device-id={{item.control_item.device.id}} data-key="{{item.control_item.key}}" data-init-type="1" data-type="variable" data-device-polling_interval="{{ item.control_item.device.polling_interval }}"></div>
{% endfor %}
<!--<div class="panel panel-primary" style="height: {{ process_flow_diagram.background_image.height|add:"68" }}px; width: {{ process_flow_diagram.background_image.width|add:"30" }}px; margin-left:15px;">-->
<div class="panel panel-primary">
{% if process_flow_diagram.title %}
Expand Down Expand Up @@ -41,7 +44,7 @@ <h3 class="panel-title">{{ process_flow_diagram.title }}</h3>
</text>
-->
<text x="{{ item.left }}" y="{{ item.top }}" fill="black" font-size="{{ item.font_size }}"
class="control-item type-numeric {{ item.control_item.web_class_str }}"
class="control-item type-numeric process-flow-diagram-item {{ item.control_item.web_class_str }}"
id="{% if form %}{{ form.web_id }}-{% endif %}{{ item.control_item.web_id }}-{{ uuid }}">
Loading...
</text>
Expand Down
2 changes: 1 addition & 1 deletion pyscada/hmi/templates/widget_row.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<div class="row">
{% endif %}
{% for content in main_content %}
<div id="widget_{{content.widget.pk}}" class="widget {{ content.widget.css_class}} {% if content.widget.extra_css_class %}{{ content.widget.extra_css_class}}{% endif %}"> <!-- {{ content.widget.title }} -->
<div id="widget_{{content.widget.pk}}" class="widget {{ content.widget.css_class }} {% if content.widget.extra_css_class %}{{ content.widget.extra_css_class }}{% endif %}"> <!-- {{ content.widget.title }} -->
{% if topbar and content.topbar is not None %}{{ content.topbar|safe }}{% endif %}
{{ content.html|safe }}
</div>
Expand Down
10 changes: 8 additions & 2 deletions pyscada/hmi/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from django.contrib.auth import logout
from django.views.decorators.csrf import requires_csrf_token
from django.conf import settings
from django.core.exceptions import PermissionDenied

import time
import json
Expand Down Expand Up @@ -76,8 +77,13 @@ def view(request, link_title):
if v is None:
raise View.DoesNotExist
#v = View.objects.get(link_title=link_title)
except (View.DoesNotExist, View.MultipleObjectsReturned):
return HttpResponse(status=404)
except View.DoesNotExist as e:
logger.warning(f"{request.user} has no permission for view {link_title}")
raise PermissionDenied("You don't have access to this view.")
except View.MultipleObjectsReturned as e:
logger.error(f"{e} for view link_title", exc_info=True)
raise PermissionDenied(f"Multiples views with this link : {link_title}")
#return HttpResponse(status=404)

if v.theme is not None:
base_template = str(v.theme.base_filename) + ".html"
Expand Down
21 changes: 12 additions & 9 deletions pyscada/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,14 +700,15 @@ def get_label(self, value):
return None
return label_found or value

def append(self, label, value, silent=False):
try:
DictionaryItem.objects.get(label=label, value=value, dictionary=self)
if not silent:
def append(self, label, value, silent=False, update=None):
if update is None:
_, created = DictionaryItem.objects.get_or_create(label=label, value=value, dictionary=self)
if not created and not silent:
logger.warning('Item ({}:{}) for dictionary {} already exist'.format(label, value, self))
except DictionaryItem.DoesNotExist:
di = DictionaryItem(label=label, value=value, dictionary=self)
di.save()
elif update == "label":
DictionaryItem.objects.update_or_create(value=value, dictionary=self, defaults={"label": label,})
elif update == "value":
DictionaryItem.objects.update_or_create(label=label, dictionary=self, defaults={"value": value,})

def remove(self, label=None, value=None):
if label is not None and value is not None:
Expand Down Expand Up @@ -1504,7 +1505,7 @@ def check_period(self, d1, d2, force_write=False, add_partial_info=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))
#logger.debug("Value already exist in RecordedData for %s - %s" % (d1, d1 + td))
pass
else:
calc_value = self.get_value(d1, d1 + td)
Expand Down Expand Up @@ -1860,7 +1861,7 @@ def __str__(self):
elif self.device:
return self.device.short_name
else:
return self.id
return str(self.id)

@property
def get_device_id(self):
Expand Down Expand Up @@ -2285,6 +2286,7 @@ def do_event_check(self):
var_list_final = {}
vp_list_final = {}

# check all level by inceasing order, keep the last level
for item in self.complexeventlevel_set.all().order_by('order'):
(is_valid, var_list, vp_list) = item.is_valid()
if item_found is None and not active and self.last_level != item.level and is_valid:
Expand All @@ -2309,6 +2311,7 @@ def do_event_check(self):
prev_event.save()
active = active or item.active

# for the highest level, compose mail and change output values
if item_found is not None:
if item_found.send_mail: # Send Mail
(subject, message, html_message,) = self.compose_mail(item_found, var_list_final, vp_list_final)
Expand Down
2 changes: 1 addition & 1 deletion pyscada/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def _get_objects_for_html(list_to_append=None, obj=None, exclude_model_names=Non
# ManyToMany
for fields in obj._meta.local_many_to_many:
for field in getattr(obj, fields.name).all():
if field not in exclude_model_names:
if fields.name not in exclude_model_names:
if hasattr(field, '_get_objects_for_html'):
list_to_append.update(field._get_objects_for_html(list_to_append))
else:
Expand Down
8 changes: 4 additions & 4 deletions pyscada/utils/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ def stop(self, sig=signal.SIGTERM):
self.pid = sp.pid
if self.pid is None or self.pid == 0:
# todo : raise exception
logger.error("Can't determine process id exiting.")
logger.error("Can't determine process id exiting.", exc_info=True)
return False
if self.pid != getpid():
# calling from outside the daemon instance
Expand Down Expand Up @@ -746,7 +746,7 @@ def run(self):
try:
RecordedData.objects.bulk_create(item, batch_size=1000)
except IntegrityError:
logger.debug('RecordedData objects already exists, retry ignoring conflicts')
logger.debug(f'RecordedData objects already exists, retrying ignoring conflicts for : {", ".join(str(i.id) + " " + str(i.variable.id) for i in item)}')
RecordedData.objects.bulk_create(item, batch_size=1000, ignore_conflicts=True)
if status == 1: # Process OK
pass
Expand Down Expand Up @@ -1007,7 +1007,7 @@ def loop(self):
process['id'] = bp.id
elif process['failed'] == 3:
# todo : raise exception
logger.error(f"process {self.bp_label % process['key']} failed 3 times")
logger.error(f"process {self.bp_label % process['key']} failed 3 times", exc_info=True)
else:
logger.warning(f"process {self.bp_label % process['key']} failed more than 3 times")
process['failed'] += 1
Expand Down Expand Up @@ -1132,7 +1132,7 @@ def loop(self):
process['id'] = bp.id
elif process['failed'] == 3:
# todo : raise exception
logger.error(f"process {self.bp_label % process['key']} failed 3 times")
logger.error(f"process {self.bp_label % process['key']} failed 3 times", exc_info=True)
else:
logger.warning(f"process {self.bp_label % process['key']} failed more than 3 times")
process['failed'] += 1
Expand Down
1 change: 1 addition & 0 deletions tests/project_template/project_name/settings.py-tpl
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ EMAIL_PREFIX = 'A Message From PyScada'
# for admins and managers
SERVER_EMAIL=DEFAULT_FROM_EMAIL
EMAIL_SUBJECT_PREFIX=EMAIL_PREFIX
EMAIL_TIMEOUT=5

# PyScada settings
# https://github.com/pyscada/PyScada
Expand Down