MQTT rework started

master
Glueck Martin 2024-05-23 15:09:01 +02:00
parent 4e5ee2cf59
commit c9d4796936
4 changed files with 318 additions and 0 deletions

46
Attr_Dict.py Normal file
View File

@ -0,0 +1,46 @@
class Attr_Dict (dict) :
def _convert_dict (self, o) :
return self.__class__ (o)
# end def _convert_dict
def _convert_sequence (self, o) :
result = []
for v in o :
if isinstance (v, (list, tuple)) :
result.append (self._convert_sequence (v))
elif isinstance (v, dict) :
result.append (self._convert_dict (v))
else :
result.append (v)
if isinstance (o, tuple) :
return tuple (result)
return result
# end def _convert_sequence
def __getitem__ (self, key) :
value = super ().__getitem__ (key)
if isinstance (value, dict) :
value = self [key] = self._convert_dict (value)
if isinstance (value, (tuple, list)) :
value = self [key] = self._convert_sequence (value)
return value
# end def __getitem__
def __getattr__ (self, name) :
try :
return self [name]
except KeyError :
raise AttributeError (name)
# end def __getattr__
def __setattr__ (self, name, value) :
self [name] = value
# end def __setattr__
# end class Attr_Dict
if __name__ == "__main__" :
a = Attr_Dict (a = {"b" : 1}, b = [{"a": 1}], c = ({"a" : 1}, ))

145
Order_Watch.py Normal file
View File

@ -0,0 +1,145 @@
from pathlib import Path
from lxml import etree
from App_State import App_State, Attr_Dict
from filesystem_watcher import \
_Logger_, WindowsApiObserverExcpetionHandler, \
WindowsApiEmitterExceptionHandling, Directory_Handler
import time
import json
import datetime
class Order_Handler (Directory_Handler) :
def __init__ (self, path, client, config) :
super ().__init__ (path)
self.client = client
mqr = config.mqtt ["topic-root"]
baskets = f"{mqr}/baskets"
self.mqtt = Attr_Dict \
( basket_topic = f"{baskets}/{{basket_no}}"
, basket_state = f"{baskets}/{{basket_no}}/state"
, basket_user = f"{baskets}/{{basket_no}}/user"
, basket_changed = f"{baskets}/{{basket_no}}/changed"
, baskets = baskets
)
# end def __init__
def on_any_event (self, event) :
if event.event_type == 'created' :
file = Path (event.src_path)
if file.suffix == ".txt" :
xml = file.with_suffix (".xml")
if xml.exists () :
self.new_order (xml)
if file.suffix == ".xml" :
txt = file.with_suffix (".txt")
if txt.exists () :
self.new_order (file)
elif event.event_type == 'deleted' :
file = Path (event.src_path)
if file.suffix == ".xml" :
basket_no = file.stem
for t in self.mqtt.values () :
self.client.publish \
( t.format (basket_no = basket_no)
, payload = None, retain = False
)
self.L.info ("Order removed %s", basket_no)
super ().on_any_event (event)
# end def on_any_event
def new_order (self, xml_file : Path) :
try :
self.L.info ("New basket %s", xml_file.name)
xml = etree.parse (xml_file).getroot ()
com = xml.xpath ("//TEXT_SHORT") [0].text
user, line = xml.xpath ("//INFO8") [0].text.split ("/")
basket_no = xml_file.stem
self.client.publish \
( self.mqtt.basket_state.format (basket_no = basket_no)
, payload = "new", retain = True
)
self.client.publish \
( self.mqtt.basket_changed.format (basket_no = basket_no)
, payload = App_State.snow ("import"), retain = True
)
basket = dict \
(user = user, line = line, commission = com)
basket ["positions"] = self._scan_order_pos (xml)
self.client.publish \
( self.mqtt.basket_topic.format (basket_no = basket_no)
, payload = json.dumps (basket), retain = True
)
self.L.info ("Created new basket %s", basket_no)
except Exception as e :
self.L.error (e, exc_info = True)
# end def new_order
def _scan_order_pos (self, xml) :
result = []
bl = xml.xpath ("//BuilderList") [0]
for a in bl.getchildren () :
art_name = a.xpath ("Pname") [0].text.strip ()
count = int (a.xpath ("Count") [0].text)
pos = a.get ("LineNo")
text = a.xpath ("ARTICLE_TEXT_INFO1" ) [0].text.strip ()
price = float (a.xpath ("ARTICLE_PRICE_INFO1") [0].text)
result.append \
( dict ( art_name = art_name
, count = count
, pos = pos
, text = text
, price = price
)
)
return result
# end def _scan_order_pos
# end class Order_Handler
class Order_Watch (App_State) :
order_incomming_dir = r"n:\glueck\watch-me\inbox"
def __init__ (self, config_file) :
self.load_config (config_file)
self.client = self.connect_to_mqtt ()
# end def __init__
def _start_watchdog (self) :
handler = Order_Handler \
(self.order_incomming_dir, self.client, self.config)
return WindowsApiObserverExcpetionHandler.Start_Directory_Watcher \
(handler)
# end def _start_watchdog
def run (self) :
observer = None
try:
observer = self._start_watchdog ()
while True:
time.sleep (1)
if WindowsApiEmitterExceptionHandling.Exception :
App_State.L.exception \
(str (WindowsApiEmitterExceptionHandling.Exception))
WindowsApiEmitterExceptionHandling.Exception = None
if observer :
observer.stop ()
observer = self._start_watchdog ()
finally :
if observer :
observer.stop ()
observer.join ()
# end def run
# end class Order_Watch
if __name__ == "__main__" :
parser = Order_Watch.Add_Logging_Attributes ()
parser.add_argument ("config_file", type = str)
cmd = parser.parse_args ()
_Logger_.L = Order_Watch.Setup_Logging (cmd)
ow = Order_Watch (cmd.config_file)
ow.run ()

121
filesystem_watcher.py Normal file
View File

@ -0,0 +1,121 @@
import time
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
from watchdog.observers.read_directory_changes import \
WindowsApiEmitter, DEFAULT_OBSERVER_TIMEOUT, BaseObserver
class _Logger_ : pass
class WindowsApiEmitterExceptionHandling (WindowsApiEmitter, _Logger_) :
Exception = None
def run (self, * args, ** kw) :
try :
super ().run (* args, ** kw)
except Exception as err :
WindowsApiEmitterExceptionHandling.Exception = err
# end def run
# end class WindowsApiEmitterExceptionHandling
class WindowsApiObserverExcpetionHandler (Observer, _Logger_) :
def __init__ (self, timeout = DEFAULT_OBSERVER_TIMEOUT) :
BaseObserver.__init__ \
( self
, emitter_class = WindowsApiEmitterExceptionHandling
, timeout = timeout
)
# end def __init__
@classmethod
def Start_Directory_Watcher (cls, handler, ** kw) :
L = cls.L
retry_wait = kw.pop ("RETRY_SLEEP", 1)
while True :
try :
observer = WindowsApiObserverExcpetionHandler ()
observer.schedule (handler, handler.directory, ** kw)
observer.start ()
L.info ("Directory watching for %s started", handler.directory)
return observer
except Exception as e :
if getattr (e, "errno", None) == 2 :
L.error ("%s does not exists, retry", path)
observer.stop ()
time.sleep (retry_wait)
else :
raise
# end def Start_Directory_Watcher
# end class WindowsApiObserverExcpetionHandler
class Directory_Handler (FileSystemEventHandler, _Logger_) :
def __init__ (self, directory) :
super ().__init__ ()
self.directory = directory
# end def __init__
def on_any_event (self, event):
self.L.debug ("%s %s", event.event_type, event.src_path)
if event.event_type == 'deleted' and event.src_path == self.directory :
WindowsApiEmitterExceptionHandling.Exception = \
f"Directory has been deleted {self.directory}"
#if event.event_type == 'created' :
# try:
# fileBase = os.path.basename(event.src_path).split('.')[0]
# filePath = os.path.dirname(event.src_path) (fileBase,filePath))
# except Exception as e:
# pass
# if event.is_directory:
# return None
#
# elif event.event_type == 'created':
# # Take any action here when a file is first created.
# print(datetime.datetime.now())
# print("Received created event - %s." % event.src_path)
#
# elif event.event_type == 'modified':
# # Taken any action here when a file is modified.
# print(datetime.datetime.now())
# print("Received modified event - %s." % event.src_path)
#
# else:
# print(event.event_type,event.src_path)
# end def on_any_event
# end class Directory_Handler
if __name__ == "__main__" :
from App_State import App_State
import time
import os
cmd = App_State.Add_Logging_Attributes ().parse_args ()
L = _Logger_.L = App_State.Setup_Logging (cmd)
L.info ("PID_ %d", os.getpid ())
def start_watching (path) :
return WindowsApiObserverExcpetionHandler.Start_Directory_Watcher \
(Directory_Handler, path)
# end def start_watching
path = r"k:\glueck\watch-me"
observer = None
try:
observer = start_watching (path)
while True:
time.sleep (1)
if WindowsApiEmitterExceptionHandling.Exception :
App_State.L.exception \
(str (WindowsApiEmitterExceptionHandling.Exception))
WindowsApiEmitterExceptionHandling.Exception = None
if observer :
observer.stop ()
observer = start_watching (path)
finally :
if observer :
observer.stop ()
observer.join ()

6
order_watch.yaml Normal file
View File

@ -0,0 +1,6 @@
mqtt:
broker: odoo-test
port: 1883
user: imos
password: gAAAAABmTy4fipVAw10oKCE2aLWq79BF9Id5H8-lEEm8tOgcoaGgIJRWwBdR0nj-lrN-70hcLMHVqNudk7FTJjOvE0KOCkDr0A==
topic-root: imos-order