# -*- 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)