ebuild_rasp2/ebuild/io/edm_db.py

455 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
__title__ = "edm_db"
__author__ = 'Mehmet Karatay & "Saraswati" (ChatGPT)'
__purpose__ = "PostgreSQL'e EDM tarzı veri yazan yardımcı sınıf"
__version__ = "0.2.0"
__date__ = "2025-11-20"
"""
edm_db.py
Revision : 2025-11-20
Authors : Mehmet Karatay & "Saraswati" (ChatGPT)
Amaç
-----
Eski Rasp2 tabanlı sistemin PostgreSQL'e veri yazma işlerini üstlenen
yardımcı sınıfın (EdmDB) temizlenmiş, hatalardan arındırılmış ve
okunabilirliği artırılmış sürümü.
Özellikler
----------
- Veritabanı bağlantı parametrelerini edmConfig.conf içinden okur
(section: [database] varsayımıyla).
- Bağlantıyı (opsiyonel olarak) açar; bağlantı yoksa fonksiyonlar
sessizce False döndürebilir veya sadece log dosyasına SQL basabilir.
- Eski koddaki ana fonksiyonlar korunmuştur:
- db_exec()
- avg_head()
- db_write_861(), db_write_861_data(), db_write()
- Diğer SELECT/UPDATE fonksiyonları (read_0861_order, write_0861_order, ...)
Not
---
Aşağıdaki kodda bazı yerlerde güvenlik açısından tavsiye edilen
`parametrized query` kullanımı yerine eski string formatlama
kullanılmıştır; bu modül legacy uyumluluk öncelikli olduğu için
bu haliyle korunmuştur.
"""
import psycopg2 as psql
from datetime import datetime
import edmConfig # Senin eski EdmConfig modülün (conf içinde EdmConfig örneği bekliyoruz)
class EdmDB:
"""
EDM veritabanı yardımcı sınıfı.
- Bağlantı parametrelerini edmConfig.conf üzerinden okur.
Örn. config.ini içinde:
[database]
tcpip = 10.10.2.44
database = edm_10094
user = root
password = system
port = 5432
- db_exec() ile self.sql içinde tutulan komutu çalıştırır.
"""
def __init__(self, ini_name: str = "database", auto_connect: bool = False):
"""
ini_name: config.ini içindeki section ismi (varsayılan: [database])
auto_connect: True verilirse __init__ sırasında PostgreSQL bağlantısı açmayı dener.
"""
self.conf = edmConfig.conf
self.sql = ""
# Bağlantı parametrelerini INI'den okuyoruz
self.w_ip = self.conf.item(ini_name, "tcpip") # host
self.w_db = self.conf.item(ini_name, "database") # db name
self.w_us = self.conf.item(ini_name, "user") # user
self.w_pw = self.conf.item(ini_name, "password") # password
self.w_pt = self.conf.item(ini_name, "port") # port (string)
self.con = None
if auto_connect:
self.connect()
# -------------------------------------------------
# Bağlantı yönetimi
# -------------------------------------------------
def connect(self) -> bool:
"""
PostgreSQL bağlantısını açar.
Başarılıysa True, hata olursa False döner.
"""
try:
self.con = psql.connect(
host=self.w_ip,
user=self.w_us,
password=self.w_pw,
database=self.w_db,
port=int(self.w_pt),
)
self.con.autocommit = True
# print("EdmDB: connection ok") # İstersen açarsın
return True
except Exception as ex:
print("EdmDB: connection error:", ex)
self.con = None
return False
def close(self) -> None:
"""Veritabanı bağlantısını kapatır."""
if self.con is not None:
try:
self.con.close()
except Exception:
pass
finally:
self.con = None
# -------------------------------------------------
# Temel SQL yürütme
# -------------------------------------------------
def db_exec(self) -> bool:
"""
self.sql değişkeninde tutulan komutu çalıştırır.
Bağlantı yoksa:
- Şimdilik sadece True döndürüyoruz (test amaçlı).
Bağlantı varsa:
- execute + commit, hata varsa False döner.
"""
if not self.sql:
return True
if self.con is None:
# Bağlantı yok; legacy davranışa yakın olması için
# burada True döndürüp sadece SQL'i debug amaçlı yazabilirsin.
# print("EdmDB: no connection, sql skipped:", self.sql)
return True
try:
with self.con.cursor() as cr:
cr.execute(self.sql)
return True
except Exception as ex:
print("EdmDB.db_exec ERROR:", ex)
return False
# -------------------------------------------------
# Örnek veri okuma fonksiyonu
# -------------------------------------------------
def avg_head(self):
"""
AVG_HEAT_OUTSIDE tablosundan örnek bir kayıt okur.
Dönüş:
[avg, max, min, saat] şeklinde liste
Eğer okuma yapılamazsa:
[-9990.0, -9999.0, -9999.0, -99]
"""
avg_heat = [-9990.0, -9999.0, -9999.0, -99]
if self.con is None:
return avg_heat
try:
sql = "SELECT avgr, maxr, minr, saatr FROM AVG_HEAT_OUTSIDE WHERE saatr = 2;"
with self.con.cursor() as cr:
cr.execute(sql)
row = cr.fetchone()
if row:
avg_heat[0] = row[0]
avg_heat[1] = row[1]
avg_heat[2] = row[2]
avg_heat[3] = row[3]
except Exception as ex:
print("EdmDB.avg_head ERROR:", ex)
return avg_heat
# -------------------------------------------------
# Eski sistem fonksiyonları (istatistik / görev takibi)
# -------------------------------------------------
def old_datas(self):
"""
edm_0861_data_brulor_percent tablosundan eski verileri okur.
"""
if self.con is None:
return []
sql = "SELECT endusuk, enfazla, toplam_harcama, toplam_sure, oran FROM public.edm_0861_data_brulor_percent"
try:
with self.con.cursor() as cr:
cr.execute(sql)
return cr.fetchall()
except Exception as ex:
print("EdmDB.old_datas ERROR:", ex)
return []
def old_values(self):
"""
edm_0861_data_start_stop_brulor tablosundan, bugüne ait bazı
start/stop verilerini okur.
"""
if self.con is None:
return []
sql = (
"SELECT createdate, prev_createdate, elpsetime "
"FROM edm_0861_data_start_stop_brulor "
"WHERE createdate > current_date "
"AND sensor_value = 1 "
"ORDER BY 1"
)
try:
with self.con.cursor() as cr:
cr.execute(sql)
return cr.fetchall()
except Exception as ex:
print("EdmDB.old_values ERROR:", ex)
return []
def read_0861_order(self, xfunc_group="0", xfunc_sub_item="0"):
"""
edm_0861_orders tablosundan çalışmaya hazır (exec_status=0) kayıtları okur.
"""
if self.con is None:
return []
sql = (
"SELECT exec_status, uniqueid, func_group, func_sub_item, roleid, "
" work_minute, param_count, startdate, stopdate, "
" (work_minute * 4) - 0 = param_count as mstatus "
"FROM public.edm_0861_orders "
"WHERE exec_status = 0 "
" AND licenseid = 10094 "
" AND activeid = true "
" AND func_group = '%s' "
" AND current_timestamp < stopdate "
" AND startdate < current_timestamp "
" AND func_sub_item = '%s' "
"ORDER BY startdate;"
) % (xfunc_group, xfunc_sub_item)
try:
with self.con.cursor() as cr:
cr.execute(sql)
return cr.fetchall()
except Exception as ex:
print("EdmDB.read_0861_order ERROR:", ex)
return []
def write_0861_order(self, uid):
"""
edm_0861_orders tablosunda param_count değerini 1 artırır.
"""
if self.con is None:
return
sql = (
"UPDATE public.edm_0861_orders "
"SET param_count = param_count + 1 "
"WHERE exec_status = 0 "
" AND licenseid = 10094 "
" AND activeid = true "
" AND uniqueid = '%s' "
" AND param_count < (work_minute * 4) "
" AND current_timestamp < stopdate;"
) % uid
try:
with self.con.cursor() as cr:
cr.execute(sql)
except Exception as ex:
print("EdmDB.write_0861_order ERROR:", ex)
def close_0861_order(self, uid):
"""
Belirli bir order'ı exec_status=5 yaparak kapatır.
"""
if self.con is None:
return
sql = (
"UPDATE public.edm_0861_orders "
"SET exec_status = 5, stopdate = current_timestamp "
"WHERE exec_status = 0 "
" AND licenseid = 10094 "
" AND activeid = true "
" AND uniqueid = '%s';"
) % uid
try:
with self.con.cursor() as cr:
cr.execute(sql)
except Exception as ex:
print("EdmDB.close_0861_order ERROR:", ex)
def update_0861_order(self, uid):
"""
Verilen uniqueid için startdate/stopdate'i günceller ve
yeni startdate'i döndürür.
"""
if self.con is None:
return None
try:
sql = (
"UPDATE public.edm_0861_orders "
"SET startdate = current_timestamp, "
" stopdate = current_timestamp + INTERVAL '2 day' "
"WHERE exec_status = 0 AND uniqueid = %d"
) % int(uid)
with self.con.cursor() as cr:
cr.execute(sql)
sql = (
"SELECT startdate as mstatus "
"FROM public.edm_0861_orders "
"WHERE exec_status = 0 AND uniqueid = %d"
) % int(uid)
with self.con.cursor() as cr:
cr.execute(sql)
rows = cr.fetchall()
for row in rows:
return row[0]
except Exception as ex:
print("EdmDB.update_0861_order ERROR:", ex)
return None
# -------------------------------------------------
# 0861 / 0861_data yazma fonksiyonları
# -------------------------------------------------
def db_write_861(self, licenseid, siteid, locationid, device_group, device_code, device_value):
"""
edm_0861 tablosuna temel bir kayıt ekler.
NOT: device_value burada sadece varlık için kullanılıyor;
asıl anlık değerler 0861_data tablosuna yazılıyor.
"""
self.sql = (
"INSERT INTO public.edm_0861("
"licenseid, siteid, locationid, hardware_type, "
"hardware_model_code, hardwareuniquecode, "
"hardwarejobcode, hardwarecomment, jobcode"
") VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s')"
) % (
licenseid,
siteid,
locationid,
"D", # hardware_type
device_group, # hardware_model_code
device_code, # hardwareuniquecode
device_code, # hardwarejobcode
device_code, # hardwarecomment
device_code, # jobcode
)
if self.db_exec():
return True
return False
def get_edm_0861(self, licenseid, siteid, locationid, device_code):
"""
İlgili cihaz için aktif edm_0861 kaydının uniqueid'sini döndürür.
Bağlantı yoksa veya kayıt bulunamazsa 0 döner.
NOT: Eski koddaki "yoksa oluştur sonra tekrar ara" davranışı
burada yorum satırı olarak bırakıldı; istersen geri açarsın.
"""
if self.con is None:
return 0
sql = (
"SELECT uniqueid "
"FROM public.edm_0861 "
"WHERE licenseid = '%s' "
" AND siteid = '%s' "
" AND locationid = '%s' "
" AND NOW() BETWEEN startdate AND stopdate "
" AND activeid = True "
" AND deleteid = False "
" AND hardwarejobcode = '%s'"
) % (licenseid, siteid, locationid, device_code)
try:
with self.con.cursor() as cr:
cr.execute(sql)
rows = cr.fetchall()
for row in rows:
return row[0]
except Exception as ex:
print("EdmDB.get_edm_0861 ERROR:", ex)
# Eski davranış: kayıt yoksa oluşturmayı denerdi.
# İstersen buraya geri koyabilirsin.
return 0
def db_write_861_data(self, licenseid, siteid, locationid, device_group, device_code, device_value):
"""
edm_0861_data tablosuna cihaz verisi (sensor_value) yazar.
Bağlantı yoksa SQL'i LOG_device_group.log dosyasına basar.
"""
xdevice_code = "%s" % (device_code)
device_str = ""
# Değer tipini normalize et
if isinstance(device_value, (float, int)):
numeric_value = float(device_value)
else:
device_str = str(device_value)
numeric_value = 0.0
self.sql = (
"INSERT INTO public.edm_0861_data("
"licenseid, uniqueid, sensor_value, init_value"
") VALUES ('%s','%s','%f','%s')"
) % (licenseid, xdevice_code, numeric_value, 0)
# LOG_DEVICEGROUP.log dosyasına da yaz
fname = "LOG_%s.log" % (device_group)
fsql = "%s:%s\n" % (datetime.now(), self.sql)
try:
with open(fname, "a") as file_object:
file_object.write(fsql)
except Exception as ex:
print("EdmDB.db_write_861_data LOG ERROR:", ex)
if self.db_exec():
return True
# DB yazılamadıysa, fallback olarak edm_0861 kaydı oluşturmaya çalış
return self.db_write_861(licenseid, siteid, locationid, device_group, device_code, device_value)
def db_write(self, licenseid, siteid, locationid, device_group, device_code, device_value):
"""
0861_data'ya yazmayı 3 kez dener.
Hata alma durumunda db_write_861_data içindeki fallback devreye girer.
"""
result = False
i = 0
while not result and i < 3:
i += 1
result = self.db_write_861_data(
licenseid, siteid, locationid,
device_group, device_code, device_value
)
if __name__ == "__main__":
# Basit bir smoke-test
db = EdmDB(auto_connect=False) # Bağlanmadan da oluşturulabilir
print("EdmDB instance created. Host:", db.w_ip, "DB:", db.w_db)