Skip to content

Commit

Permalink
Merge pull request #248 from ChristianTremblay/release_21.01.28
Browse files Browse the repository at this point in the history
Release 21.01.28
  • Loading branch information
ChristianTremblay committed Jan 28, 2021
2 parents 3d35093 + 47c9681 commit 776b1e4
Show file tree
Hide file tree
Showing 19 changed files with 325 additions and 82 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Upload Python Package

on:
release:
types: [created]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ name: Python package

on:
push:
branches: [ master ]
branches: [ master, develop ]
pull_request:
branches: [ master ]
branches: [ master, develop ]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.5, 3.6, 3.7, 3.8]
python-version: [3.6, 3.7, 3.8, 3.9]

steps:
- uses: actions/checkout@v2
Expand All @@ -26,8 +25,19 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install flake8 pytest
pip install coveralls
pip install colorama
pip install pytest
pip install pytest-cov
pip install pandas
pip install bokeh
pip install flask
pip install flask_bootstrap
pip install colorama
pip install netifaces
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install .
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand All @@ -36,4 +46,5 @@ jobs:
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest
coverage run --source BAC0 -m pytest -v
coverage report
33 changes: 0 additions & 33 deletions .travis.yml

This file was deleted.

13 changes: 10 additions & 3 deletions BAC0/core/devices/Device.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ def __init__(
auto_save=False,
save_resampling="1s",
clear_history_on_save=False,
history_size=None
history_size=None,
reconnect_on_failure=True,
):

self.properties = DeviceProperties()
Expand All @@ -155,6 +156,7 @@ def __init__(
self.properties.save_resampling = save_resampling
self.properties.clear_history_on_save = clear_history_on_save
self.properties.default_history_size = history_size
self._reconnect_on_failure = reconnect_on_failure

self.segmentation_supported = segmentation_supported
self.custom_object_list = object_list
Expand Down Expand Up @@ -540,8 +542,11 @@ def _buildPointList(self):
self.segmentation_supported = False
self.new_state(DeviceDisconnected)
except IndexError as error:
self._log.error("Device creation failed... disconnecting")
self.new_state(DeviceDisconnected)
if self._reconnect_on_failure:
self._log.error("Device creation failed... re-connecting")
self.new_state(DeviceDisconnected)
else:
self._log.error("Device creation failed... disconnecting")

def __getitem__(self, point_name):
"""
Expand Down Expand Up @@ -751,6 +756,8 @@ def update_bacnet_properties(self):
prop_id_required=True,
)
for each in res:
if not each:
continue
v, prop = each
self.properties.bacnet_properties[prop] = v

Expand Down
8 changes: 6 additions & 2 deletions BAC0/core/devices/Points.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ def update_bacnet_properties(self):
prop_id_required=True,
)
for each in res:
if not each:
continue
v, prop = each
self.properties.bacnet_properties[prop] = v

Expand Down Expand Up @@ -542,15 +544,17 @@ def match(self, point, *, delay=5):
else:
raise RuntimeError("Stop task before redefining it")

def match_value(self, value, *, delay=5):
def match_value(self, value, *, delay=5, use_last_value=False):
"""
This allow functions like :
device['point'].match('value')
A sensor will follow a calculation...
"""
if self._match_task.task is None:
self._match_task.task = Match_Value(value=value, point=self, delay=delay)
self._match_task.task = Match_Value(
value=value, point=self, delay=delay, use_last_value=use_last_value
)
self._match_task.task.start()
self._match_task.running = True

Expand Down
164 changes: 153 additions & 11 deletions BAC0/core/devices/Virtuals.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,11 @@ def __init__(self):
self.description = ""
self.units_state = ""
self.type = "virtual"
self.history_size = None
self._df = None

def __repr__(self):
return "{} | Descr : {}".format(self.name, self.description)
return "VIRTUAL POINT-- {} | Descr : {}".format(self.name, self.description)


@note_and_log
Expand All @@ -94,7 +95,14 @@ class VirtualPoint(VirtualPointProperties):
A function is passed at creation time and this function must return a pandas Serie
"""

def __init__(self, name, fn, description=None, units="No Units"):
def __init__(
self,
name,
initial_value=None,
history_fn=None,
description=None,
units="No Units",
):
if description is None:
raise ValueError("Please provide description")
if not _PANDAS:
Expand All @@ -104,21 +112,155 @@ def __init__(self, name, fn, description=None, units="No Units"):
self.properties.name = name
self.properties.description = description
self.properties.units_state = units
self._history = fn
self._history_fn = history_fn

self._history = namedtuple("_history", ["timestamp", "value"])
self._history.timestamp = []
self._history.value = []

self._match_task = namedtuple("_match_task", ["task", "running"])
self._match_task.task = None
self._match_task.running = False

self.fake_pv = None
if initial_value:
self._set(initial_value)

def chart(self, remove=False):
"""
Add point to the bacnet trending list
"""
self._log.warning("Use bacnet.add_trend(point) instead")

def _set(self, value):
if self._history_fn is None:
self.fake_pv = value
self._trend(value)
else:
self._log.warning(
"Point is configured as a function of other points. You can't set a new value"
)

def _trend(self, res):
self._history.timestamp.append(datetime.now())
self._history.value.append(res)
if self.properties.history_size is None:
return
else:
if self.properties.history_size < 1:
self.properties.history_size = 1
if len(self._history.timestamp) >= self.properties.history_size:
try:
self._history.timestamp = self._history.timestamp[
-self.properties.history_size :
]
self._history.value = self._history.value[
-self.properties.history_size :
]
assert len(self._history.timestamp) == len(self._history.value)
except Exception as e:
self._log.exception("Can't append to history")

@property
def value(self):
"""
Retrieve value of the point
"""
return self.lastValue

@property
def lastValue(self):
"""
returns: last value read
"""
if _PANDAS:
return self.history.dropna().iloc[-1]
else:
return self._history.value[-1]

@property
def history(self):
_result = self._history()
if not isinstance(_result, pd.Series):
raise ValueError("Function of virtual point must return a Series")
"""
returns : (pd.Series) containing timestamp and value of all readings
"""
if self._history_fn is not None:
return self._history_fn()
else:
return _result
if not _PANDAS:
return dict(zip(self._history.timestamp, self._history.value))
idx = self._history.timestamp.copy()
his_table = pd.Series(index=idx, data=self._history.value[: len(idx)])
del idx
his_table.name = ("{}/{}").format(
self.properties.device.properties.name, self.properties.name
)
his_table.units = self.properties.units_state
his_table.states = "virtual"

def chart(self, remove=False):
his_table.description = self.properties.description

his_table.datatype = self.properties.type
return his_table

def match_value(self, value, *, delay=5):
"""
Add point to the bacnet trending list
This allow functions like :
device['point'].match('value')
A sensor will follow a calculation...
"""
self._log.warning("Use bacnet.add_trend() instead")
if self._match_task.task is None:
self._match_task.task = Match_Value(value=value, point=self, delay=delay)
self._match_task.task.start()
self._match_task.running = True

elif self._match_task.running and delay > 0:
self._match_task.task.stop()
self._match_task.running = False
time.sleep(1)

self._match_task.task = Match_Value(value=value, point=self, delay=delay)
self._match_task.task.start()
self._match_task.running = True

elif self._match_task.running and delay == 0:
self._match_task.task.stop()
self._match_task.running = False

else:
raise RuntimeError("Stop task before redefining it")

def __repr__(self):
return self.properties.__repr__()
return "{}/{} : {:.2f} {}".format(
self.properties.device.properties.name,
self.properties.name,
self.value,
self.properties.units_state,
)

def __add__(self, other):
return self.value + other

def __sub__(self, other):
return self.value - other

def __mul__(self, other):
return self.value * other

def __truediv__(self, other):
return self.value / other

def __lt__(self, other):
return self.value < other

def __le__(self, other):
return self.value <= other

def __eq__(self, other):
return self.value == other

def __gt__(self, other):
return self.value > other

def __ge__(self, other):
return self.value >= other
Loading

0 comments on commit 776b1e4

Please sign in to comment.