mirror of
https://gitlab.com/sfz.aalen/infra/fabaccess.git
synced 2025-03-12 15:01:47 +01:00
Merge branch 'NewApproach' into 'main'
New approach See merge request luca_lutz/fabaccess!2
This commit is contained in:
commit
0a06c36dd1
162
.gitignore
vendored
162
.gitignore
vendored
@ -1,160 +1,2 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
/sql
|
||||
/.env
|
@ -14,8 +14,10 @@ DB_USERNAME=postgres
|
||||
DB_PASSWORD=postgres
|
||||
DB_DATABASE=fab_access
|
||||
|
||||
MQTT_PORT=1883
|
||||
MQTT_USERNAME=user
|
||||
MQTT_PASSWORD=password
|
||||
MQTT_BROKER=mqtt
|
||||
MQTT_CLIENT=FabMan
|
||||
```
|
||||
|
||||
|
@ -21,7 +21,10 @@ services:
|
||||
MQTT_PASSWORD: ${MQTT_PASSWORD:?err}
|
||||
MQTT_BROKER: ${MQTT_BROKER:?err}
|
||||
MQTT_CLIENT: ${MQTT_CLIENT:?err}
|
||||
# Various
|
||||
MACHINES: ${MACHINES:?err}
|
||||
restart: unless-stopped
|
||||
|
||||
db:
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
@ -30,8 +33,11 @@ services:
|
||||
POSTGRES_DB: ${DB_DATABASE}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
|
||||
mqtt:
|
||||
image: eclipse-mosquitto:2
|
||||
ports:
|
||||
- 1883:1883
|
||||
environment:
|
||||
MQTT_USERNAME: ${MQTT_USERNAME}
|
||||
MQTT_PASSWORD: ${MQTT_PASSWORD}
|
||||
|
@ -1,33 +1,27 @@
|
||||
import os
|
||||
|
||||
def _read_from_env(key: str, default: str = None) -> str:
|
||||
if (value := os.environ.get(key, default)) is not None:
|
||||
return value
|
||||
raise Exception(f'[Config] Error: Cannot find required value {key}')
|
||||
class Config:
|
||||
|
||||
def get_keycloak_config():
|
||||
return {
|
||||
'server_url': _read_from_env('KEYCLOAK_URL'),
|
||||
'username': _read_from_env('KEYCLOAK_USERNAME'),
|
||||
'password': _read_from_env('KEYCLOAK_PASSWORD'),
|
||||
'realm_name': _read_from_env('REALM'),
|
||||
}
|
||||
def _read_from_env(key: str, default: str = None) -> str:
|
||||
if (value := os.environ.get(key, default)) is not None:
|
||||
return value
|
||||
raise Exception(f'[Config] Error: Cannot find required value {key}')
|
||||
|
||||
def get_database_config():
|
||||
return {
|
||||
'host': _read_from_env('DB_HOSTNAME'),
|
||||
'user': _read_from_env('DB_USERNAME'),
|
||||
'port': int(_read_from_env('DB_PORT', '3306')),
|
||||
'password': _read_from_env('DB_PASSWORD'),
|
||||
'dbname': _read_from_env('DB_DATABASE'),
|
||||
}
|
||||
mqtt_broker = _read_from_env('MQTT_BROKER')
|
||||
mqtt_port = int(_read_from_env('MQTT_PORT','1883'))
|
||||
mqtt_client_id = _read_from_env('MQTT_CLIENT')
|
||||
mqtt_user_name = _read_from_env('MQTT_USERNAME')
|
||||
mqtt_password = _read_from_env('MQTT_PASSWORD')
|
||||
|
||||
def get_mqtt_config():
|
||||
print("port", int(_read_from_env('MQTT_PORT', '1883')))
|
||||
return {
|
||||
'client_name': _read_from_env('MQTT_CLIENT'),
|
||||
'username': _read_from_env('MQTT_USERNAME', ''),
|
||||
'password': _read_from_env('MQTT_PASSWORD', ''),
|
||||
'broker': _read_from_env('MQTT_BROKER'),
|
||||
'port': int(_read_from_env('MQTT_PORT', '1883')),
|
||||
}
|
||||
keycloak_url = _read_from_env('KEYCLOAK_URL')
|
||||
keycloak_user_name = _read_from_env('KEYCLOAK_USER_NAME')
|
||||
keycloak_password = _read_from_env('KEYCLOAK_USER_PW')
|
||||
keycloak_realm = _read_from_env('KEYCLOAK_REALM')
|
||||
|
||||
db_host_name = _read_from_env('DB_HOSTNAME')
|
||||
db_user_name = _read_from_env('DB_USERNAME')
|
||||
db_password = _read_from_env('DB_PASSWORD')
|
||||
db_database = _read_from_env('DB_DATABASE')
|
||||
db_port = int(_read_from_env('DB_PORT','5432'))
|
||||
|
||||
machines = _read_from_env('MACHINES')
|
45
fab_access/keycloak_handler.py
Normal file
45
fab_access/keycloak_handler.py
Normal file
@ -0,0 +1,45 @@
|
||||
from config import Config
|
||||
from keycloak import KeycloakAdmin
|
||||
|
||||
|
||||
class KeycloakHandler:
|
||||
Config.keycloak_password
|
||||
|
||||
@staticmethod
|
||||
def login():
|
||||
KeycloakHandler.admin = KeycloakAdmin(
|
||||
server_url=Config.keycloak_url,
|
||||
username=Config.keycloak_user_name,
|
||||
password=Config.keycloak_password,
|
||||
realm_name=Config.keycloak_realm,
|
||||
verify=True
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_card_id(card_id):
|
||||
users = KeycloakHandler.admin.get_users()
|
||||
# Filter not working for Attributes because of multidimensional JSON
|
||||
|
||||
user = [user for user in users if "attributes" in user and "FabCard" in user["attributes"] and user["attributes"]["FabCard"] == [card_id]]
|
||||
print(f'Found {len(user)} user(s) with card_id: {card_id}')
|
||||
|
||||
match len(user):
|
||||
case 0:
|
||||
return None
|
||||
case 1:
|
||||
print(f'FabCard matches with user {user[0]["username"]}')
|
||||
return user[0]
|
||||
case other:
|
||||
print(f'Error! too many users with card_id: {card_id}')
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def user_is_privileged(username):
|
||||
groups = KeycloakHandler.admin.get_user_groups(user_id=KeycloakHandler.admin.get_user_id(username))
|
||||
groups = [group['name'] for group in groups]
|
||||
|
||||
if 'Mentoren' in groups:
|
||||
print('Overrided becouse of "Mentor" group')
|
||||
return True
|
||||
else:
|
||||
return False
|
@ -1,154 +1,100 @@
|
||||
from keycloak import KeycloakAdmin
|
||||
import json
|
||||
import psycopg
|
||||
import os
|
||||
from paho.mqtt.client import Client as MQTTClient
|
||||
from config import Config
|
||||
from mqtt_client import MqttHandler
|
||||
from keycloak_handler import KeycloakHandler
|
||||
from sql_handler import SQLHandler
|
||||
|
||||
import config
|
||||
from mqtt_helper import MQTTHelper
|
||||
def has_permission(user_permissions, machine_id):
|
||||
parsed_permissions = [permission.split('.') for permission in user_permissions]
|
||||
parsed_machine_id = machine_id.split('.')
|
||||
|
||||
conn: psycopg.Connection
|
||||
keycloak_admin: KeycloakAdmin
|
||||
for permission in parsed_permissions:
|
||||
missmatch = False
|
||||
for i, id_sequence in enumerate(parsed_machine_id):
|
||||
if permission[i] == '*':
|
||||
return True
|
||||
if permission[i] != id_sequence:
|
||||
missmatch = True
|
||||
break
|
||||
if not missmatch:
|
||||
return True
|
||||
|
||||
def publish(client, topic,msg):
|
||||
result = client.publish(topic, msg)
|
||||
status: 0 | 1 = result[0]
|
||||
if status == 0:
|
||||
#print(f"Send `{msg}` to topic `{topic}`")
|
||||
pass
|
||||
else:
|
||||
print(f"Failed to send message to topic {topic}")
|
||||
return False
|
||||
|
||||
def changePlug(client: MQTTClient, plug_id: str, is_running: bool):
|
||||
publish(client,f"/FabLogging/{plug_id}/POWER", is_running)
|
||||
cmd = "On" if is_running else "Off"
|
||||
publish(client, f"/aktoren/{plug_id}/cmnd/POWER", cmd)
|
||||
|
||||
def changeDisplay(client: MQTTClient, reader_id: str, status: int, fab_card_id: str):
|
||||
publish(client, f"/cmnd/reader/{reader_id}", '{"Cmd": "message", "MssgID": %s , "ClrTxt":"" , "AddnTxt":"%s"}' % (status, fab_card_id))
|
||||
|
||||
def hasPermission(UserPermsJSON, PermissionPath):
|
||||
# ToDo: refactor this
|
||||
permission = False
|
||||
error = 0
|
||||
MachinePermArray = PermissionPath.split(".")
|
||||
for UserPerm in UserPermsJSON:
|
||||
#print(f"check {UserPerm}")
|
||||
UserPermArray = UserPerm.split(".")
|
||||
x = 0
|
||||
for UserPermPart in UserPermArray:
|
||||
if(error == 0):
|
||||
#print(f"Compare {UserPermPart} and {MachinePermArray[x]}")
|
||||
if not (MachinePermArray[x] == UserPermPart):
|
||||
if(UserPermPart == "*"):
|
||||
#print("* regelt")
|
||||
permission = True
|
||||
else:
|
||||
error = 1
|
||||
#print(f"MISmatch between {MachinePermArray[x]} and {UserPermPart}")
|
||||
else:
|
||||
pass
|
||||
#print(f"Match between {MachinePermArray[x]} and {UserPermPart}")
|
||||
x = x + 1
|
||||
if(error == 1):
|
||||
pass
|
||||
#print("Error")
|
||||
else:
|
||||
#print("Hurra")
|
||||
permission = True
|
||||
error = 0
|
||||
return permission
|
||||
|
||||
def handle_msg(client: MQTTClient, userdata, msg):
|
||||
global conn
|
||||
|
||||
payload = msg.payload.decode()
|
||||
print(f"Received `{payload}` from `{msg.topic}` topic")
|
||||
|
||||
fab_card_id = json.loads(msg.payload.decode())["UID"]
|
||||
reader_id = msg.topic.split("/")[-1]
|
||||
|
||||
# check user exists
|
||||
users = keycloak_admin.get_users({ 'attributes': { 'FabCard': fab_card_id } })
|
||||
if (len(users) != 1):
|
||||
print(f'Found {len(users)} users with {fab_card_id=}')
|
||||
changeDisplay(client, reader_id, 16, fab_card_id)
|
||||
return
|
||||
|
||||
# retrieve user attributes from DB
|
||||
user = users[0]
|
||||
print(f"FabCard matches with user {user['username']}")
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(f'SELECT PlugName,PermissionPath,Status,LastUser FROM ReaderPlug WHERE ReaderID=?;', (reader_id,))
|
||||
res = cursor.fetchone()
|
||||
if res is None:
|
||||
print(f'Error fetching card info from db for {reader_id=}')
|
||||
return
|
||||
plug_id, permission_path, is_running, last_user = res
|
||||
|
||||
# check permissions
|
||||
user_permissions = user['attributes']['FabPermissions'][0]
|
||||
if not hasPermission(user_permissions, permission_path):
|
||||
changeDisplay(client, reader_id, 7, "")
|
||||
return
|
||||
|
||||
# check for mentor
|
||||
if is_running and last_user != user['username']:
|
||||
def gen_display_name(user):
|
||||
# display names must be not longer than 8 chrs
|
||||
if 'firstName' in user.keys() and 'lastName' in user.keys():
|
||||
full_name = f'{user["firstName"]} {user["lastName"]}'
|
||||
if len(full_name) > 8:
|
||||
display_name = f'{user["firstName"][0]}.{user["lastName"][:6]}'
|
||||
else:
|
||||
try:
|
||||
if (keycloak_admin.get_user_groups(user_id=user['id'])[0]['name'] == 'Mentoren'):
|
||||
print('Overrided becouse of "Mentor" Group')
|
||||
else:
|
||||
print(f'Bereits benutzt von {last_user}')
|
||||
error = True
|
||||
except IndexError:
|
||||
print("No groups available")
|
||||
error = True
|
||||
if(error):
|
||||
changeDisplay(client, reader_id, 9, last_user)
|
||||
display_name = user['username'][:8]
|
||||
except KeyError:
|
||||
print('user has no username')
|
||||
return 'Error'
|
||||
return(display_name)
|
||||
|
||||
# toggle machine state
|
||||
print(f"Turn Plug {'off' if is_running else 'on'}")
|
||||
changePlug(client, plug_id, is_running)
|
||||
publish(client, f"/FabLogging/{plug_id}/USER", user['username'])
|
||||
def handle_request(msg, client):
|
||||
print('')
|
||||
print(f'Received `{msg.payload.decode()}` from `{msg.topic}` topic')
|
||||
fabcard_id = json.loads(msg.payload.decode())['UID']
|
||||
reader_id = msg.topic.split('/')[-1]
|
||||
|
||||
KeycloakHandler.login()
|
||||
user = KeycloakHandler.get_user_by_card_id(fabcard_id)
|
||||
if not user:
|
||||
MqttHandler.print_to_display(reader_id, 16, fabcard_id)
|
||||
return
|
||||
|
||||
db_data = SQLHandler.get_machine_data(reader_id)
|
||||
|
||||
machine_id = db_data["machine_id"]
|
||||
last_user = db_data["last_user"]
|
||||
machine_status = db_data["machine_status"]
|
||||
plug_id = db_data["plug_id"]
|
||||
|
||||
# ToDo: refactor display name construction
|
||||
try:
|
||||
firstCombo = user['firstName']+" "
|
||||
DisplayUser = firstCombo+user['lastName']
|
||||
if(len(firstCombo) >= 7):
|
||||
DisplayUser = user['firstName'][:7]
|
||||
user_permissions = json.loads(user['attributes']['FabPermissions'][0])
|
||||
except KeyError:
|
||||
DisplayUser = user['username']
|
||||
if(len(DisplayUser) > 7):
|
||||
DisplayUser = DisplayUser[0:7] + "." + DisplayUser[7+1: ]
|
||||
DisplayUser = DisplayUser[:8]
|
||||
print(f'user with id {fabcard_id} is missing FabPermissions attr')
|
||||
except IndexError:
|
||||
print(f'user with id {fabcard_id} is missing FabPermissions attr')
|
||||
|
||||
changeDisplay(client, reader_id, 20, f"Login\n{DisplayUser}" if is_running else "Bitte anmelden")
|
||||
if not has_permission(user_permissions, machine_id):
|
||||
print(f"user with id {fabcard_id} is missing {machine_id}")
|
||||
MqttHandler.print_to_display(reader_id, 7, '')
|
||||
return
|
||||
|
||||
username = user['username']
|
||||
display_name = gen_display_name(user)
|
||||
|
||||
if not machine_status:
|
||||
print(f'Turn Plug {plug_id} on')
|
||||
MqttHandler.switch_plug(plug_id, 1)
|
||||
MqttHandler.print_to_display(reader_id, 20, f'Login\n{display_name}')
|
||||
else:
|
||||
if not (username == last_user or KeycloakHandler.user_is_privileged(username)):
|
||||
MqttHandler.print_to_display(reader_id, 9, last_user)
|
||||
return
|
||||
print(f'Turn Plug {plug_id} off')
|
||||
MqttHandler.switch_plug(plug_id, 0)
|
||||
MqttHandler.print_to_display(reader_id, 20, f'Bitte anmelden')
|
||||
|
||||
SQLHandler.update_machine(reader_id, username, machine_status)
|
||||
MqttHandler.publish(f'/FabLogging/{plug_id}/USER', username)
|
||||
|
||||
# write new status to db
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(
|
||||
f'UPDATE ReaderPlug SET Status=?, LastUser="?" WHERE ReaderID="?";',
|
||||
(is_running, user['username'], reader_id),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
def main():
|
||||
global conn
|
||||
global keycloak_admin
|
||||
|
||||
mqtt_client = MQTTHelper(**config.get_mqtt_config())
|
||||
mqtt_client.subscribe("/rfid_reader/#", handle_msg)
|
||||
mqtt_client.loop_forever()
|
||||
|
||||
try:
|
||||
conn = psycopg.connect(**config.get_database_config())
|
||||
except mariadb.Error as e:
|
||||
print(f"Error connecting to MariaDB Platform")
|
||||
raise e
|
||||
|
||||
keycloak_admin = KeycloakAdmin(**config.get_keycloak_config(), verify=True)
|
||||
MqttHandler.setup(handle_request)
|
||||
KeycloakHandler.login()
|
||||
SQLHandler.setup()
|
||||
|
||||
MqttHandler.connect_mqtt()
|
||||
MqttHandler.subscribe("/rfid_reader/#")
|
||||
SQLHandler.init_db()
|
||||
MqttHandler.loop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
62
fab_access/mqtt_client.py
Normal file
62
fab_access/mqtt_client.py
Normal file
@ -0,0 +1,62 @@
|
||||
from paho.mqtt import client as mqtt_client
|
||||
from config import Config
|
||||
import json
|
||||
|
||||
class MqttHandler:
|
||||
@staticmethod
|
||||
def setup(msg_handler):
|
||||
MqttHandler.msg_handler = msg_handler
|
||||
MqttHandler.client = None
|
||||
|
||||
@staticmethod
|
||||
def connect_mqtt():
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
if rc == 0:
|
||||
print('Connected to MQTT Broker!')
|
||||
else:
|
||||
print('Failed to connect, return code %d\n', rc)
|
||||
|
||||
MqttHandler.client = mqtt_client.Client(Config.mqtt_client_id)
|
||||
MqttHandler.client.username_pw_set('admin', 'user')
|
||||
MqttHandler.client.on_connect = on_connect
|
||||
MqttHandler.client.username_pw_set(Config.mqtt_user_name, Config.mqtt_password)
|
||||
MqttHandler.client.connect(Config.mqtt_broker, Config.mqtt_port)
|
||||
|
||||
@staticmethod
|
||||
def publish(topic, msg):
|
||||
result = MqttHandler.client.publish(topic, msg)
|
||||
# result: [0, 1]
|
||||
status = result[0]
|
||||
if status == 0:
|
||||
#print(f'Send `{msg}` to topic `{topic}`')
|
||||
pass
|
||||
else:
|
||||
print(f'Failed to send message to topic {topic}')
|
||||
|
||||
|
||||
@staticmethod
|
||||
def subscribe(topic):
|
||||
def on_message(client, userdata, msg):
|
||||
MqttHandler.msg_handler(msg, client)
|
||||
|
||||
MqttHandler.client.subscribe(topic)
|
||||
MqttHandler.client.on_message = on_message
|
||||
|
||||
@staticmethod
|
||||
def loop():
|
||||
MqttHandler.client.loop_forever()
|
||||
|
||||
@staticmethod
|
||||
def switch_plug(PlugID, state):
|
||||
MqttHandler.publish(f'/FabLogging/{PlugID}/POWER', 1 if state else 0)
|
||||
MqttHandler.publish(f'/aktoren/{PlugID}/cmnd/POWER', 'On' if state else 'OFF')
|
||||
|
||||
@staticmethod
|
||||
def print_to_display(MachineID, status, text):
|
||||
message = {
|
||||
'Cmd': 'message',
|
||||
'MssgID': status,
|
||||
'ClrTxt': '',
|
||||
'AddnTxt': text,
|
||||
}
|
||||
MqttHandler.publish(f'/cmnd/reader/{MachineID}', json.dumps(message))
|
82
fab_access/sql_handler.py
Normal file
82
fab_access/sql_handler.py
Normal file
@ -0,0 +1,82 @@
|
||||
import psycopg2
|
||||
from psycopg2 import sql, extensions
|
||||
import json
|
||||
|
||||
from config import Config
|
||||
|
||||
class SQLHandler:
|
||||
@staticmethod
|
||||
def setup():
|
||||
SQLHandler.cursor = None
|
||||
SQLHandler.conn = None
|
||||
SQLHandler.conn = psycopg2.connect(host=Config.db_host_name, user=Config.db_user_name, port=Config.db_port, password=Config.db_password, dbname=Config.db_database)
|
||||
# get the isolation leve for autocommit
|
||||
autocommit = extensions.ISOLATION_LEVEL_AUTOCOMMIT
|
||||
print ("ISOLATION_LEVEL_AUTOCOMMIT:", extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
|
||||
# set the isolation level for the connection's cursors
|
||||
# will raise ActiveSqlTransaction exception otherwise
|
||||
SQLHandler.conn.set_isolation_level( autocommit )
|
||||
SQLHandler.cursor = SQLHandler.conn.cursor()
|
||||
|
||||
@staticmethod
|
||||
def get_machine_data(reader_id):
|
||||
# TODO Database
|
||||
# - get machine_id from database
|
||||
# - get last_user from database
|
||||
# - get machine_status from database, is 0 for off, 1 for on
|
||||
# - get plug_id from database
|
||||
SQLHandler.cursor.execute("SELECT * FROM readerplug WHERE readerplug.reader_id = %s;", (reader_id,))
|
||||
data = [row for row in SQLHandler.cursor.fetchall()]
|
||||
if(len(data) > 0):
|
||||
return {
|
||||
'machine_id': data[0][2],
|
||||
'last_user': data[0][4],
|
||||
'machine_status': data[0][3],
|
||||
'plug_id': data[0][0]
|
||||
}
|
||||
else:
|
||||
print("No maching Card Reader found in db")
|
||||
return "Error"
|
||||
|
||||
@staticmethod
|
||||
def update_machine(reader_id,last_user,machine_status):
|
||||
SQLHandler.cursor.execute("UPDATE readerplug SET machine_status = %s WHERE readerplug.reader_id = %s", (False if machine_status else True,reader_id))
|
||||
SQLHandler.cursor.execute("UPDATE readerplug SET last_user = %s WHERE readerplug.reader_id = %s", (last_user,reader_id))
|
||||
SQLHandler.conn.commit()
|
||||
|
||||
@staticmethod
|
||||
def init_db():
|
||||
SQLHandler.cursor.execute("SELECT datname FROM pg_database;")
|
||||
dbs = [row[0] for row in SQLHandler.cursor.fetchall()]
|
||||
if not (Config.db_database in dbs):
|
||||
print(f"Missing database ({Config.db_database}) -> creating new db")
|
||||
SQLHandler.cursor.execute(sql.SQL("CREATE DATABASE {};").format(sql.Identifier( Config.db_database )))
|
||||
else:
|
||||
print(f"Found DB {Config.db_database} -> Using existing one")
|
||||
|
||||
SQLHandler.cursor.execute("SELECT * FROM pg_catalog.pg_tables\
|
||||
WHERE schemaname != 'pg_catalog' AND \
|
||||
schemaname != 'information_schema';")
|
||||
tables = [row[1] for row in SQLHandler.cursor.fetchall()]
|
||||
if not ("readerplug" in tables):
|
||||
print("Missing table -> creating new table in db")
|
||||
SQLHandler.cursor.execute("\
|
||||
CREATE TABLE readerplug (\
|
||||
reader_id int NOT NULL, \
|
||||
plug_id varchar(255) NOT NULL, \
|
||||
machine_id varchar(255) NOT NULL, \
|
||||
machine_status boolean NOT NULL, \
|
||||
last_user varchar(255) NOT NULL \
|
||||
)")
|
||||
else:
|
||||
print("Found Table -> Using existing one")
|
||||
SQLHandler.conn.commit()
|
||||
|
||||
SQLHandler.cursor.execute("SELECT * FROM readerplug;")
|
||||
if(len(SQLHandler.cursor.fetchall()) < 1):
|
||||
print("Found no machines in table, adding machines from config")
|
||||
machines = json.loads(Config.machines)
|
||||
for machine in machines:
|
||||
SQLHandler.cursor.execute("INSERT INTO readerplug (reader_id, plug_id, machine_id, machine_status, last_user) VALUES (%s, %s, %s, False, 'no_user');", (machine[0],machine[1],machine[2]))
|
||||
SQLHandler.conn.commit()
|
@ -1,4 +1,4 @@
|
||||
keycloak_wrapper
|
||||
paho-mqtt
|
||||
psycopg[binary]
|
||||
psycopg2-binary
|
||||
python-keycloak
|
Loading…
x
Reference in New Issue
Block a user