Sweep.py: add getters and setters for private fields (#659)

* style, Sweep.py: remove a double negation

* style, NanoVNASaver.py: simplify sweepSource computation

* Sweep.py: add getters and setters for private fields

Beware that this commit removes a lock from
SweepSettings.update_tex_power, and adds one to
DeviceSettings.updatecustomPoint.
Both changse may be incorrect, depending on the role of the lock
(issue #657).

Follows: 6eb24f23 d09b55e1 dbea311a

Since d09b55e1, the Properties.name class attribute is overriden by
each assignment to the properties.name instance attribute.
This is most probably unwanted.

This commit

 * removes @dataclass, which is confusing as some attributes are
   managed because of the lock.
   Because of this, it has to restore __repr__ and __eq__.
 * provides getters and setters for private attributes, and
   protects each update by a thread lock
 * adds a regression test for the bug fixed by d09b55e1 (immutable
   properties).
pull/669/head
Name 2023-07-30 09:03:06 +02:00 zatwierdzone przez GitHub
rodzic 5bed1bc6cc
commit abb80a5160
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
7 zmienionych plików z 113 dodań i 42 usunięć

Wyświetl plik

@ -216,9 +216,9 @@ class SweepControl(Control):
self.update_sweep() self.update_sweep()
def update_sweep(self): def update_sweep(self):
sweep = self.app.sweep self.app.sweep.update(
with sweep.lock: start = self.get_start(),
sweep.start = self.get_start() end = self.get_end(),
sweep.end = self.get_end() segments = self.get_segments(),
sweep.segments = self.get_segments() points = self.app.vna.datapoints,
sweep.points = self.app.vna.datapoints )

Wyświetl plik

@ -511,10 +511,12 @@ class NanoVNASaver(QWidget):
if source is not None: if source is not None:
self.sweepSource = source self.sweepSource = source
else: else:
self.sweepSource = ( time = strftime('%Y-%m-%d %H:%M:%S', localtime())
f"{self.sweep.properties.name}" name = self.sweep.properties.name
f" {strftime('%Y-%m-%d %H:%M:%S', localtime())}" if name:
).lstrip() self.sweepSource = name + ' ' + time
else:
self.sweepSource = time
def markerUpdated(self, marker: Marker): def markerUpdated(self, marker: Marker):
with self.dataLock: with self.dataLock:

Wyświetl plik

@ -17,11 +17,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
import logging import logging
from dataclasses import dataclass, replace
from enum import Enum from enum import Enum
from math import log from math import log
from threading import Lock from threading import Lock
from typing import Iterator from typing import Iterator, NamedTuple
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -32,28 +31,69 @@ class SweepMode(Enum):
AVERAGE = 2 AVERAGE = 2
class Properties: class Properties(NamedTuple):
name: str = "" name: str = ""
mode: "SweepMode" = SweepMode.SINGLE mode: "SweepMode" = SweepMode.SINGLE
averages: tuple[int, int] = (3, 0) averages: tuple[int, int] = (3, 0)
logarithmic: bool = False logarithmic: bool = False
@dataclass
class Sweep: class Sweep:
start: int = 3600000 def __init__(self,
end: int = 30000000 start: int = 3600000,
points: int = 101 end: int = 30000000,
segments: int = 1 points: int = 101,
properties: "Properties" = Properties() segments: int = 1,
properties: "Properties" = Properties(),
def __post_init__(self): ):
self.lock = Lock() self._start = start
self._end = end
self._points = points
self._segments = segments
self._properties = properties
self._lock = Lock()
self.check() self.check()
logger.debug("%s", self) logger.debug("%s", self)
def __repr__(self) -> str:
return 'Sweep(' + ', '.join(map(str, (
self.start, self.end, self.points, self.segments, self.properties
))) + ')'
def __eq__(self, other) -> bool:
return (self.start == other.start
and self.end == other.end
and self.points == other.points
and self.segments == other.segments
and self.properties == other.properties)
def copy(self) -> "Sweep": def copy(self) -> "Sweep":
return replace(self) with self._lock:
return Sweep(self.start, self.end, self.points, self.segments,
self._properties)
# Getters for attributes, either private or computed.
@property
def start(self) -> int:
return self._start
@property
def end(self) -> int:
return self._end
@property
def points(self) -> int:
return self._points
@property
def segments(self) -> int:
return self._segments
# Properties are immutable, this does not circumvent the accessors.
@property
def properties(self) -> "Properties":
return self._properties
@property @property
def span(self) -> int: def span(self) -> int:
@ -63,6 +103,37 @@ class Sweep:
def stepsize(self) -> int: def stepsize(self) -> int:
return round(self.span / (self.points * self.segments - 1)) return round(self.span / (self.points * self.segments - 1))
# Setters
def set_points(self, points: int) -> None:
with self._lock:
self._points = points
self.check()
def update(self, start: int, end: int, segments: int, points: int) -> None:
with self._lock:
self._start = start
self._end = end
self._segments = segments
self._points = points
self.check()
def set_name(self, name: str) -> None:
with self._lock:
self._properties = self.properties._replace(name=name)
def set_mode(self, mode: "SweepMode") -> None:
with self._lock:
self._properties = self.properties._replace(mode=mode)
def set_averages(self, amount: int, truncates: int) -> None:
with self._lock:
self._properties = self.properties._replace(averages=(amount, truncates))
def set_logarithmic(self, logarithmic: bool) -> None:
with self._lock:
self._properties = self.properties._replace(logarithmic=logarithmic)
def check(self): def check(self):
if ( if (
self.segments <= 0 self.segments <= 0
@ -77,12 +148,12 @@ class Sweep:
return 1 - log(self.segments + 1 - index) / log(self.segments + 1) return 1 - log(self.segments + 1 - index) / log(self.segments + 1)
def get_index_range(self, index: int) -> tuple[int, int]: def get_index_range(self, index: int) -> tuple[int, int]:
if not self.properties.logarithmic: if self.properties.logarithmic:
start = self.start + index * self.points * self.stepsize
end = start + (self.points - 1) * self.stepsize
else:
start = round(self.start + self.span * self._exp_factor(index)) start = round(self.start + self.span * self._exp_factor(index))
end = round(self.start + self.span * self._exp_factor(index + 1)) end = round(self.start + self.span * self._exp_factor(index + 1))
else:
start = self.start + index * self.points * self.stepsize
end = start + (self.points - 1) * self.stepsize
logger.debug("get_index_range(%s) -> (%s, %s)", index, start, end) logger.debug("get_index_range(%s) -> (%s, %s)", index, start, end)
return start, end return start, end

Wyświetl plik

@ -93,8 +93,7 @@ class SweepWorker(QtCore.QRunnable):
self.running = True self.running = True
self.percentage = 0 self.percentage = 0
with self.app.sweep.lock: sweep = self.app.sweep.copy()
sweep = self.app.sweep.copy()
if sweep != self.sweep: # parameters changed if sweep != self.sweep: # parameters changed
self.sweep = sweep self.sweep = sweep

Wyświetl plik

@ -189,8 +189,7 @@ class DeviceSettingsWindow(QtWidgets.QWidget):
return return
logger.debug("DP: %s", self.datapoints.itemText(i)) logger.debug("DP: %s", self.datapoints.itemText(i))
self.app.vna.datapoints = int(self.datapoints.itemText(i)) self.app.vna.datapoints = int(self.datapoints.itemText(i))
with self.app.sweep.lock: self.app.sweep.set_points(self.app.vna.datapoints)
self.app.sweep.points = self.app.vna.datapoints
self.app.sweep_control.update_step_size() self.app.sweep_control.update_step_size()
def updateBandwidth(self, i): def updateBandwidth(self, i):
@ -217,6 +216,6 @@ class DeviceSettingsWindow(QtWidgets.QWidget):
if points != self.app.vna.datapoints: if points != self.app.vna.datapoints:
logger.debug("DP: %s", points) logger.debug("DP: %s", points)
self.app.vna.datapoints = points self.app.vna.datapoints = points
self.app.sweep.points = self.app.vna.datapoints self.app.sweep.set_points(self.app.vna.datapoints)
self.app.sweep_control.update_step_size() self.app.sweep_control.update_step_size()
self.custom_points_Eidt.setText(str(points)) self.custom_points_Eidt.setText(str(points))

Wyświetl plik

@ -290,18 +290,15 @@ class SweepSettingsWindow(QtWidgets.QWidget):
logger.debug("update_averaging(%s, %s)", amount, truncates) logger.debug("update_averaging(%s, %s)", amount, truncates)
averages.setText(str(amount)) averages.setText(str(amount))
truncs.setText(str(truncates)) truncs.setText(str(truncates))
with self.app.sweep.lock: self.app.sweep.set_averages(amount, truncates)
self.app.sweep.properties.averages = (amount, truncates)
def update_logarithmic(self, logarithmic: bool): def update_logarithmic(self, logarithmic: bool):
logger.debug("update_logarithmic(%s)", logarithmic) logger.debug("update_logarithmic(%s)", logarithmic)
with self.app.sweep.lock: self.app.sweep.set_logarithmic(logarithmic)
self.app.sweep.properties.logarithmic = logarithmic
def update_mode(self, mode: "SweepMode"): def update_mode(self, mode: "SweepMode"):
logger.debug("update_mode(%s)", mode) logger.debug("update_mode(%s)", mode)
with self.app.sweep.lock: self.app.sweep.set_mode(mode)
self.app.sweep.properties.mode = mode
def update_padding(self, padding: int): def update_padding(self, padding: int):
logger.debug("update_padding(%s)", padding) logger.debug("update_padding(%s)", padding)
@ -310,11 +307,9 @@ class SweepSettingsWindow(QtWidgets.QWidget):
def update_title(self, title: str = ""): def update_title(self, title: str = ""):
logger.debug("update_title(%s)", title) logger.debug("update_title(%s)", title)
with self.app.sweep.lock: self.app.sweep.set_name(title)
self.app.sweep.properties.name = title
self.app.update_sweep_title() self.app.update_sweep_title()
def update_tx_power(self, freq_range, power_desc): def update_tx_power(self, freq_range, power_desc):
logger.debug("update_tx_power(%r)", power_desc) logger.debug("update_tx_power(%r)", power_desc)
with self.app.sweep.lock: self.app.vna.setTXPower(freq_range, power_desc)
self.app.vna.setTXPower(freq_range, power_desc)

Wyświetl plik

@ -53,3 +53,8 @@ class TestCases(unittest.TestCase):
sweep2 = sweep.copy() sweep2 = sweep.copy()
self.assertEqual(sweep, sweep2) self.assertEqual(sweep, sweep2)
sweep.set_points(14)
self.assertEqual(sweep.points, 14)
sweep.set_name('bla')
self.assertEqual(sweep.properties.name, 'bla')