Skip to content

Commit

Permalink
Merge branch 'ax'
Browse files Browse the repository at this point in the history
  • Loading branch information
MAKOMO committed Jan 15, 2025
2 parents bec520a + 0a61a4e commit 3c2395a
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 69 deletions.
42 changes: 21 additions & 21 deletions src/artisanlib/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -3290,29 +3290,29 @@ def event_popup_action(self, action:QAction) -> None:
pass

def updateWebLCDs(self, bt:Optional[str] = None, et:Optional[str] = None, time:Optional[str] = None, alertTitle:Optional[str] = None, alertText:Optional[str] = None, alertTimeout:Optional[int] = None) -> None:
try:
payload:Dict[str,Dict[str,Union[str,int]]] = {'data': {}}
if not (bt is None and et is None) and self.flagon and not self.flagstart:
# in monitoring only mode, timer might be set by PID RS
time = None
if bt is not None:
payload['data']['bt'] = bt
if et is not None:
payload['data']['et'] = et
if time is not None:
payload['data']['time'] = time
if alertText is not None:
payload['alert'] = {}
payload['alert']['text'] = alertText
if alertTitle:
payload['alert']['title'] = alertTitle
if alertTimeout:
payload['alert']['timeout'] = alertTimeout
if self.aw.weblcds_server is not None:
if self.aw.weblcds_server is not None:
try:
payload:Dict[str,Dict[str,Union[str,int]]] = {'data': {}}
if not (bt is None and et is None) and self.flagon and not self.flagstart:
# in monitoring only mode, timer might be set by PID RS
time = None
if bt is not None:
payload['data']['bt'] = bt
if et is not None:
payload['data']['et'] = et
if time is not None:
payload['data']['time'] = time
if alertText is not None:
payload['alert'] = {}
payload['alert']['text'] = alertText
if alertTitle:
payload['alert']['title'] = alertTitle
if alertTimeout:
payload['alert']['timeout'] = alertTimeout
from json import dumps as json_dumps
self.aw.weblcds_server.send_msg(json_dumps(payload, indent=None, separators=(',', ':')))
except Exception as e: # pylint: disable=broad-except
_log.exception(e)
except Exception as e: # pylint: disable=broad-except
_log.exception(e)

# note that partial values might be given here (time might update, but not the values)
@pyqtSlot(str,str,str)
Expand Down
13 changes: 8 additions & 5 deletions src/artisanlib/curves.py
Original file line number Diff line number Diff line change
Expand Up @@ -1565,11 +1565,14 @@ def setWebLCDsURL(self) -> None:

def getWebLCDsURL(self) -> str:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
localIP = s.getsockname()[0]
s.close()
return f'http://{str(localIP)}:{str(self.aw.WebLCDsPort)}/artisan'
# use Artisan's host IP address
# s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# s.connect(('8.8.8.8', 80))
# localIP = s.getsockname()[0]
# s.close()
# return f'http://{str(localIP)}:{str(self.aw.WebLCDsPort)}/artisan'
# use Artisan's host name (more stable over DHCP updates)
return f'http://{socket.gethostname().casefold()}:{str(self.aw.WebLCDsPort)}/artisan'

@pyqtSlot(bool)
def toggleWebLCDs(self, b:bool = False) -> None:
Expand Down
28 changes: 26 additions & 2 deletions src/artisanlib/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)

import zlib
import logging.config
from yaml import safe_load as yaml_load
from typing import Final, Optional, List, Dict, Tuple, Union, cast, Any, Callable, TYPE_CHECKING #for Python >= 3.9: can remove 'List' since type hints can now use the generic 'list'
from typing import Final, Optional, Mapping, List, Dict, Tuple, Union, cast, Any, Callable, TYPE_CHECKING #for Python >= 3.9: can remove 'List' since type hints can now use the generic 'list'

from functools import reduce as freduce

Expand Down Expand Up @@ -653,6 +654,7 @@ def permissionUpdated(permission:'QPermission') -> None:
pass

# configure logging

try:
with open(os.path.join(getResourcePath(),'logging.yaml'), encoding='utf-8') as logging_conf:
conf = yaml_load(logging_conf)
Expand All @@ -669,6 +671,29 @@ def permissionUpdated(permission:'QPermission') -> None:
logging.config.dictConfig(conf)
except Exception: # pylint: disable=broad-except
pass
class FilteredLogger(logging.Logger):

def __init__(self, name:str, level:Any=logging.NOTSET) -> None:
super().__init__(name, level)
self._message_lockup: Dict[int,int] = {}

def _log(self, level:int, msg:Any, args:Any, exc_info:Any=None, extra:Optional[Mapping[str, object]]=None,
stack_info:bool=False, stacklevel: int = 1) -> None:
# don't change signature for typing, but fix to log_interval=10
# log_interval:Optional[int]=None) -> None:
# if log_interval is None or log_interval == 1:
# super()._log(level, msg, args, exc_info, extra, stack_info, stacklevel)
# else:
log_interval = 10
message_Id = zlib.crc32(msg.encode('utf-8'))
if message_Id not in self._message_lockup:
self._message_lockup[message_Id] = 0
super()._log(level, msg, args, exc_info, extra, stack_info, stacklevel)
elif self._message_lockup[message_Id] % log_interval == 0:
msg += f' -- Suppressed {log_interval} equal messages'
super()._log(level, msg, args, exc_info, extra, stack_info, stacklevel)
self._message_lockup[message_Id] += 1
logging.setLoggerClass(FilteredLogger)

_log: Final[logging.Logger] = logging.getLogger(__name__)

Expand Down Expand Up @@ -4187,7 +4212,6 @@ def blockTicks(self) -> int:
def setSamplingRate(self, rate:int) -> None:
self.qmc.delay = max(self.qmc.min_delay, rate)
self.sampling_ticks_to_block_quantifiction = self.blockTicks() # we update the quantification block ticks
_log.info('setSamplingRate(%s)', self.qmc.delay)

@pyqtSlot()
def updateMessageLog(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion src/artisanlib/modbusport.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def formatMS(start:float, end:float) -> str:
def connect(self) -> None:
if self._asyncLoopThread is None:
self._asyncLoopThread = AsyncLoopThread()
asyncio.run_coroutine_threadsafe(self.connect_async(), self._asyncLoopThread.loop).result()
asyncio.run_coroutine_threadsafe(self.connect_async(), self._asyncLoopThread.loop).result()


async def connect_async(self) -> None:
Expand Down
100 changes: 61 additions & 39 deletions src/artisanlib/weblcds.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,11 @@

_log: Final[logging.Logger] = logging.getLogger(__name__)

class WebLCDs:
class WebView:

__slots__ = [ '_loop', '_thread', '_app', '_port', '_last_send', '_min_send_interval', '_resource_path', '_nonesymbol', '_timecolor',
'_timebackground', '_btcolor', '_btbackground', '_etcolor', '_etbackground',
'_showetflag', '_showbtflag' ]
__slots__ = [ '_loop', '_thread', '_app', '_port', '_last_send', '_min_send_interval', '_resource_path', '_index_path', '_websocket_path' ]

def __init__(self, port:int, resource_path:str, nonesymbol:str, timecolor:str, timebackground:str, btcolor:str,
btbackground:str, etcolor:str, etbackground:str, showetflag:bool, showbtflag:bool) -> None:
def __init__(self, port:int, resource_path:str, index_path:str, websocket_path:str) -> None:

self._loop: Optional[asyncio.AbstractEventLoop] = None # the asyncio loop
self._thread: Optional[Thread] = None # the thread running the asyncio loop
Expand All @@ -49,45 +46,25 @@ def __init__(self, port:int, resource_path:str, nonesymbol:str, timecolor:str, t
self._last_send:float = time.time() # timestamp of the last message send to the clients
self._min_send_interval:float = 0.03

self._port: int = port
self._resource_path:str = resource_path
self._index_path:str = index_path
self._websocket_path:str = websocket_path

aiohttp_jinja2.setup(self._app,
loader=jinja2.FileSystemLoader(resource_path))

self._app.add_routes([
web.get('/artisan', self.index),
web.get('/websocket', self.websocket_handler),
web.get(f'/{self._index_path}', self.index),
web.get(f'/{self._websocket_path}', self.websocket_handler),
web.static('/', resource_path, append_version=True)
])

self._port: int = port
self._resource_path:str = resource_path
self._nonesymbol:str = nonesymbol
self._timecolor:str = timecolor
self._timebackground:str = timebackground
self._btcolor:str = btcolor
self._btbackground:str = btbackground
self._etcolor:str = etcolor
self._etbackground:str = etbackground
self._showetflag:bool = showetflag
self._showbtflag:bool = showbtflag

@aiohttp_jinja2.template('artisan.tpl')
async def index(self, _request: 'Request') -> Dict[str,str]:
showspace_str = 'inline' if not (self._showbtflag and self._showetflag) else 'none'
showbt_str = 'inline' if self._showbtflag else 'none'
showet_str = 'inline' if self._showetflag else 'none'
return {
'port': str(self._port),
'nonesymbol': self._nonesymbol,
'timecolor': self._timecolor,
'timebackground': self._timebackground,
'btcolor': self._btcolor,
'btbackground': self._btbackground,
'etcolor': self._etcolor,
'etbackground': self._etbackground,
'showbt': showbt_str,
'showet': showet_str,
'showspace': showspace_str
}
# needs to be defined in subclass
@aiohttp_jinja2.template('empty.tpl')
async def index(self, _request: 'Request') -> Dict[str,str]: # pylint:disable=no-self-use
return {}

async def send_msg_to_all(self, message:str) -> None:
if 'websockets' in self._app and self._app['websockets'] is not None:
Expand Down Expand Up @@ -160,7 +137,6 @@ def start_background_loop(loop: asyncio.AbstractEventLoop) -> None:
loop.close()

def startWeb(self) -> bool:
_log.info('start WebLCDs on port %s', self._port)
try:
self._loop = asyncio.new_event_loop()
self._thread = Thread(target=self.start_background_loop, args=(self._loop,), daemon=True)
Expand All @@ -173,7 +149,6 @@ def startWeb(self) -> bool:
return False

def stopWeb(self) -> None:
_log.info('stop WebLCDs')
# _loop.stop() needs to be called as follows as the event loop class is not thread safe
if self._loop is not None:
self._loop.call_soon_threadsafe(self._loop.stop)
Expand All @@ -182,3 +157,50 @@ def stopWeb(self) -> None:
if self._thread is not None:
self._thread.join()
self._thread = None


class WebLCDs(WebView):

__slots__ = [ '_nonesymbol', '_timecolor', '_timebackground', '_btcolor', '_btbackground', '_etcolor', '_etbackground',
'_showetflag', '_showbtflag' ]

def __init__(self, port:int, resource_path:str, nonesymbol:str, timecolor:str, timebackground:str, btcolor:str,
btbackground:str, etcolor:str, etbackground:str, showetflag:bool, showbtflag:bool) -> None:
super().__init__(port, resource_path, 'artisan', 'websocket')

self._nonesymbol:str = nonesymbol
self._timecolor:str = timecolor
self._timebackground:str = timebackground
self._btcolor:str = btcolor
self._btbackground:str = btbackground
self._etcolor:str = etcolor
self._etbackground:str = etbackground
self._showetflag:bool = showetflag
self._showbtflag:bool = showbtflag

@aiohttp_jinja2.template('artisan.tpl')
async def index(self, _request: 'Request') -> Dict[str,str]:
showspace_str = 'inline' if not (self._showbtflag and self._showetflag) else 'none'
showbt_str = 'inline' if self._showbtflag else 'none'
showet_str = 'inline' if self._showetflag else 'none'
return {
'port': str(self._port),
'nonesymbol': self._nonesymbol,
'timecolor': self._timecolor,
'timebackground': self._timebackground,
'btcolor': self._btcolor,
'btbackground': self._btbackground,
'etcolor': self._etcolor,
'etbackground': self._etbackground,
'showbt': showbt_str,
'showet': showet_str,
'showspace': showspace_str
}

def startWeb(self) -> bool:
_log.info('start WebLCDs on port %s', self._port)
return super().startWeb()

def stopWeb(self) -> None:
_log.info('stop WebLCDs')
super().stopWeb()
2 changes: 2 additions & 0 deletions src/includes/Machines/Loring/Smart_Roast.aset
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ type=3
wordorderLittle=true
optimizer=true
fetch_max_blocks=true
IP_retries=1
IP_timeout=0.3

[Quantifiers]
clusterEventsFlag=false
Expand Down
2 changes: 2 additions & 0 deletions src/includes/Machines/Loring/Smart_Roast_Auto.aset
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ type=3
wordorderLittle=true
optimizer=true
fetch_max_blocks=true
IP_timeout=0.3
IP_retries=1

[Quantifiers]
clusterEventsFlag=false
Expand Down
2 changes: 1 addition & 1 deletion src/requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pytest-cov==6.0.0
#pytest-bdd==6.1.1
#pytest-benchmark==4.0.0
#pytest-mock==3.11.1
hypothesis>=6.123.13
hypothesis>=6.123.17
coverage>=7.6.10
coverage-badge==1.1.2
codespell==2.3.0
Expand Down

0 comments on commit 3c2395a

Please sign in to comment.