455 lines
15 KiB
Python
455 lines
15 KiB
Python
# -*- 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)
|