Skip to content

Commit bde0400

Browse files
committed
- fix some pyrefly ignore annotations
- adds support for Toper roasting machines with PLC and touch screen produced 2025 and later, supporting burner, airflow, and drum speed control as well as the operation of the charge/discharge doors, the stirrer and the cooling fan. - limits MODBUS fetch_max_blocks to segements of length 100
1 parent 9a4c5af commit bde0400

10 files changed

Lines changed: 46 additions & 20 deletions

File tree

src/artisanlib/comm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class nonedevDlg(QDialog): # pylint: disable=too-few-public-methods # pyright: i
176176
__slots__ = ['etEdit','btEdit','ETbox','okButton','cancelButton'] # save some memory by using slots
177177

178178
def __init__(self, parent:QWidget, aw:'ApplicationWindow') -> None:
179-
super().__init__(parent) # pyrefly: ignore[bad-argument-type]
179+
super().__init__(parent) # pyrefly: ignore[bad-argument-count]
180180

181181
self.aw = aw
182182

src/artisanlib/dialogs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class ArtisanDialog(QDialog): # pyright: ignore [reportGeneralTypeIssues] # Argu
4848
__slots__ = ['aw', 'dialogbuttons']
4949

5050
def __init__(self, parent:Optional[QWidget], aw:'ApplicationWindow') -> None:
51-
super().__init__(parent) # pyrefly: ignore[bad-argument-type]
51+
super().__init__(parent) # pyrefly: ignore[bad-argument-count]
5252
self.aw = aw # the Artisan application window
5353

5454
# IMPORTANT NOTE: if dialog items have to be access after it has been closed, this Qt.WidgetAttribute.WA_DeleteOnClose attribute
@@ -141,7 +141,7 @@ class ArtisanMessageBox(QMessageBox): # pyright: ignore [reportGeneralTypeIssues
141141
__slots__ = ['timeout', 'currentTime']
142142

143143
def __init__(self, parent:Optional[QWidget] = None, title:Optional[str] = None, text:Optional[str] = None, timeout:int = 0, modal:bool = True) -> None:
144-
super().__init__(parent) # pyrefly: ignore[bad-argument-type]
144+
super().__init__(parent) # pyrefly: ignore[bad-argument-count]
145145
self.setWindowTitle(title)
146146
self.setText(text)
147147
self.setModal(modal)

src/artisanlib/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,7 +1419,7 @@ def run(self) -> None:
14191419
class MyQDoubleValidator(QDoubleValidator): # pylint: disable=too-few-public-methods # pyright: ignore [reportGeneralTypeIssues] # Argument to class must be a base class
14201420

14211421
def __init__(self, bottom:float, top:float, decimals:int, lineedit:QLineEdit, empty_default:str = '0') -> None:
1422-
super().__init__(bottom, top, decimals, lineedit) # pyrefly: ignore[bad-argument-type]
1422+
super().__init__(bottom, top, decimals, lineedit) # pyrefly: ignore[bad-argument-count]
14231423
self.lineedit = lineedit
14241424
self.empty_default = empty_default
14251425

@@ -1632,7 +1632,7 @@ def __init__(self, parent:Optional[QWidget] = None, *, locale:str, WebEngineSupp
16321632
self.recentThemeActs = []
16331633
self.applicationDirectory = QDir().current().absolutePath()
16341634

1635-
super().__init__(parent) # pyrefly: ignore[bad-argument-type]
1635+
super().__init__(parent) # pyrefly: ignore[bad-argument-count]
16361636
self.helpdialog:Optional[HelpDlg] = None
16371637

16381638
self.setAcceptDrops(True) # enable drag-and-drop

src/artisanlib/modbusport.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ class modbusport:
104104
'readingsCache', 'SVmultiplier', 'PIDmultiplier', 'SVwriteLong', 'SVwriteFloat',
105105
'wordorderLittle', '_asyncLoopThread', '_client', 'COMsemaphore', 'default_host', 'host', 'port', 'type', 'lastReadResult', 'commError' ]
106106

107+
MAX_REGISTER_SEGMENT:int = 100 # maximal length of registers fetched at once if fetch_max_blocks is set
108+
107109
def __init__(self, aw:'ApplicationWindow') -> None:
108110
self.aw = aw
109111

@@ -478,6 +480,28 @@ def readActiveRegisters(self) -> None:
478480
if self.COMsemaphore.available() < 1:
479481
self.COMsemaphore.release(1)
480482

483+
# splits sorted list of registers into list of segments of (first,last) register tuples with maximal length of MAX_REGISTER_SEGMENT
484+
# with last-first < MAX_REGISTER_SEGMENT
485+
# ex with MAX_REGISTER_SEGMENT = 100:
486+
# client.max_blocks([0, 2, 20, 1040, 1105, 1215]) ==> [(0,20), (1040, 1105), (1215, 1215)]
487+
@staticmethod
488+
def max_blocks(registers:List[int]) -> List[Tuple[int,int]]:
489+
res:List[Tuple[int,int]] = []
490+
start_register:Optional[int] = None
491+
last_register:Optional[int] = None
492+
for register in registers:
493+
if start_register is None:
494+
start_register = register
495+
elif last_register is not None and register > start_register + modbusport.MAX_REGISTER_SEGMENT - 1:
496+
res.append((start_register, last_register))
497+
start_register = register
498+
last_register = register
499+
500+
# add the last remaining, not yet appended segment
501+
if start_register is not None and last_register is not None:
502+
res.append((start_register, last_register))
503+
return res
504+
481505
async def read_active_registers_async(self) -> None:
482506
error_disconnect = False # set to True if a serious error requiring a disconnect was detected
483507
try:
@@ -488,7 +512,9 @@ async def read_active_registers_async(self) -> None:
488512
registers_sorted = sorted(registers)
489513
sequences:List[Tuple[int,int]]
490514
if self.fetch_max_blocks:
491-
sequences = [(registers_sorted[0],registers_sorted[-1])]
515+
# sequences = [(registers_sorted[0],registers_sorted[-1])]
516+
# we split into sequences with maximal 100 registers
517+
sequences = self.max_blocks(registers_sorted)
492518
else:
493519
# split in successive sequences
494520
gaps = [[s, er] for s, er in zip(registers_sorted, registers_sorted[1:]) if s+1 < er]

src/artisanlib/qcheckcombobox.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def _getMenuStyleOption(self, option:QStyleOptionViewItem, index:'QModelIndex')
190190

191191
def __init__(self, parent:Optional[QWidget] = None, placeholderText:str = '', separator:str = ', ',
192192
**kwargs:Dict[str,Any]) -> None:
193-
super().__init__(parent, **kwargs) # pyrefly: ignore[bad-argument-type]
193+
super().__init__(parent, **kwargs) # pyrefly: ignore[bad-argument-count]
194194
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
195195

196196
self.__popupIsShown:bool = False

src/artisanlib/roast_properties.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ def close(self) -> bool:
492492

493493
class RoastsComboBox(QComboBox): # pyright: ignore [reportGeneralTypeIssues] # Argument to class must be a base class
494494
def __init__(self, parent:QWidget, aw:'ApplicationWindow', selection:Optional[str] = None) -> None:
495-
super().__init__(parent) # pyrefly: ignore[bad-argument-type]
495+
super().__init__(parent) # pyrefly: ignore[bad-argument-count]
496496
self.aw:ApplicationWindow = aw
497497
self.installEventFilter(self)
498498
self.selection:Optional[str] = selection # just the roast title

src/includes/Machines/Toper/PLC.aset

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[General]
22
Delay=1000
3-
Oversampling=true
43
roastertype_setup=Toper TKM-SX
54

65
[Device]

src/requirements-dev.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
types-openpyxl>=3.1.5.20250602
22
types-Pillow>=10.2.0.20240822
3-
types-protobuf>=6.30.2.20250516
3+
types-protobuf>=6.30.2.20250703
44
types-psutil>=7.0.0.20250601
55
types-pyserial>=3.5.0.20250326
6-
types-python-dateutil==2.9.0.20250516
6+
types-python-dateutil==2.9.0.20250708
77
types-pytz>=2025.2.0.20250516
88
types-pyyaml>=6.0.12.20250516
99
types-requests>=2.32.4.20250611
1010
types-setuptools>=80.9.0.20250529
1111
types-urllib3>=1.26.25.14
12-
types-docutils>=0.21.0.20250604
12+
types-docutils>=0.21.0.20250715
1313
lxml-stubs>=0.5.1
14-
mypy==1.16.1
14+
mypy==1.17.0
1515
pyright==1.1.403
1616
ruff>=0.12.3
1717
pylint==3.3.7
@@ -25,8 +25,8 @@ pytest-cov==6.2.1
2525
#pytest-bdd==6.1.1
2626
#pytest-benchmark==4.0.0
2727
#pytest-mock==3.11.1
28-
hypothesis>=6.135.20
29-
coverage>=7.9.1
28+
hypothesis>=6.135.31
29+
coverage>=7.9.2
3030
coverage-badge==1.1.2
3131
codespell==2.4.1
3232
# the following 2 packages are not installed along aiohttp on Python3.12 and make mypy complain

src/requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pymodbus==3.6.9; python_version < '3.9' # last Python 3.8 release
3333
pymodbus==3.9.2; python_version >= '3.9'
3434
python-snap7==1.3; python_version < '3.10' # last Python 3.9 release
3535
python-snap7==2.0.2; python_version >= '3.10'
36-
Phidget22==1.22.20250422
36+
Phidget22==1.22.20250714
3737
Unidecode==1.4.0
3838
qrcode==7.4.2; python_version < '3.9' # last Python 3.8 release
3939
qrcode==8.2; python_version >= '3.9'
@@ -67,7 +67,7 @@ matplotlib==3.7.3; python_version < '3.9' # last Python 3.8 release
6767
matplotlib==3.10.3; python_version >= '3.9'
6868
jinja2==3.1.6
6969
aiohttp==3.10.11; python_version < '3.9' # last Python 3.8 release
70-
aiohttp==3.12.12; python_version >= '3.9'
70+
aiohttp==3.12.14; python_version >= '3.9'
7171
aiohttp_jinja2==1.6
7272
python-bidi==0.4.2; python_version < '3.9' # last Python 3.8 release
7373
python-bidi==0.6.6; python_version >= '3.9'

wiki/ReleaseHistory.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ v3.2.1
99
- adds tooltip to phases widget in Comparator displaying 2nd and 3rd phase bean temperatures (or RoR if ALT/Option key is pressed) limits ([Issue #1906](../../../issues/1906))
1010
- adds support for [Kraffe](https://artisan-scope.org/machines/kraffe/) shop roasters
1111
- adds support for [Berto Essential and Autonics](https://artisan-scope.org/machines/berto/) models
12-
- adds support for [Nordic](https://artisan-scope.org/machines/nordic/) PLC models including control
13-
- adds support for [Prisma](https://artisan-scope.org/machines/prisma/) USB and PLC models including control
14-
- adds support [Cogen roasting machines](https://artisan-scope.org/machines/cogen/) with Siemens PLC (v2)
12+
- adds support for [Nordic](https://artisan-scope.org/machines/nordic/) PLC models supporting burner, airflow, and drum speed control
13+
- adds support for [Prisma](https://artisan-scope.org/machines/prisma/) USB and PLC models supporting burner, airflow, and drum speed control
14+
- adds support for [Cogen roasting machines](https://artisan-scope.org/machines/cogen/) with Siemens PLC (v2)
1515
- adds support for [Easyster Smart](https://artisan-scope.org/machines/easyster/), legacy [Proaster](https://artisan-scope.org/machines/proaster/) THCR-01A as well as Easyster/Proaster machines with air pressure sensor
16+
- adds support for [Toper roasting machines](https://artisan-scope.org/machines/toper/) with PLC and touch screen produced after 2025 supporting burner, airflow, and drum speed control
1617
- adds drag-drop import of a Cropster XLS profile
1718

1819
* CHANGES

0 commit comments

Comments
 (0)