fix caricamenti
This commit is contained in:
@@ -10,6 +10,7 @@ class Config:
|
||||
c.read(["env/ftp.ini", "env/db.ini"])
|
||||
|
||||
# FTP setting
|
||||
self.service_port = c.getint("ftpserver", "service_port")
|
||||
self.firstport = c.getint("ftpserver", "firstPort")
|
||||
self.proxyaddr = c.get("ftpserver", "proxyAddr")
|
||||
self.portrangewidth = c.getint("ftpserver", "portRangeWidth")
|
||||
|
||||
31
utils/config/loader_send_data.py
Normal file
31
utils/config/loader_send_data.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""set configurations
|
||||
|
||||
"""
|
||||
from configparser import ConfigParser
|
||||
|
||||
class Config:
|
||||
def __init__(self):
|
||||
|
||||
c = ConfigParser()
|
||||
c.read(["env/send.ini", "env/db.ini"])
|
||||
|
||||
# LOG setting
|
||||
self.logfilename = c.get("logging", "logFilename")
|
||||
|
||||
# Worker setting
|
||||
self.max_threads = c.getint("threads", "max_num")
|
||||
|
||||
# DB setting
|
||||
self.dbhost = c.get("db", "hostname")
|
||||
self.dbport = c.getint("db", "port")
|
||||
self.dbuser = c.get("db", "user")
|
||||
self.dbpass = c.get("db", "password")
|
||||
self.dbname = c.get("db", "dbName")
|
||||
self.max_retries = c.getint("db", "maxRetries")
|
||||
|
||||
# Tables
|
||||
self.dbusertable = c.get("tables", "userTableName")
|
||||
self.dbrectable = c.get("tables", "recTableName")
|
||||
self.dbrawdata = c.get("tables", "rawTableName")
|
||||
self.dbrawdata = c.get("tables", "rawTableName")
|
||||
self.dbnodes = c.get("tables", "nodesTableName")
|
||||
@@ -40,7 +40,14 @@ async def make_pipe_sep_matrix(cfg: object, id: int, pool: object) -> list:
|
||||
UnitName, ToolNameID, ToolData = await get_data(cfg, id, pool)
|
||||
righe = ToolData.splitlines()
|
||||
matrice_valori = []
|
||||
for riga in [riga for riga in righe if ';|;' in riga]:
|
||||
"""
|
||||
Ciclo su tutte le righe del file CSV, escludendo quelle che:
|
||||
non hanno il pattern ';|;' perché non sono dati ma è la header
|
||||
che hanno il pattern 'No RX' perché sono letture non pervenute o in errore
|
||||
che hanno il pattern '.-' perché sono letture con un numero errato - negativo dopo la virgola
|
||||
che hanno il pattern 'File Creation' perché vuol dire che c'è stato un errore della centralina
|
||||
"""
|
||||
for riga in [riga for riga in righe if ';|;' in riga and 'No RX' not in riga and '.-' not in riga and 'File Creation' not in riga]:
|
||||
timestamp, batlevel, temperature, rilevazioni = riga.split(';',3)
|
||||
EventDate, EventTime = timestamp.split(' ')
|
||||
if batlevel == '|':
|
||||
@@ -70,7 +77,7 @@ async def make_ain_din_matrix(cfg: object, id: int, pool: object) -> list:
|
||||
list: A list of lists, where each inner list represents a row in the matrix.
|
||||
"""
|
||||
UnitName, ToolNameID, ToolData = await get_data(cfg, id, pool)
|
||||
node_channels, node_types, node_ains, node_dins = get_nodes_type(cfg, ToolNameID, UnitName)
|
||||
node_channels, node_types, node_ains, node_dins = await get_nodes_type(cfg, ToolNameID, UnitName, pool)
|
||||
righe = ToolData.splitlines()
|
||||
matrice_valori = []
|
||||
pattern = r'^(?:\d{4}\/\d{2}\/\d{2}|\d{2}\/\d{2}\/\d{4}) \d{2}:\d{2}:\d{2}(?:;\d+\.\d+){2}(?:;\d+){4}$'
|
||||
@@ -104,10 +111,10 @@ async def make_channels_matrix(cfg: object, id: int, pool: object) -> list:
|
||||
list: A list of lists, where each inner list represents a row in the matrix.
|
||||
"""
|
||||
UnitName, ToolNameID, ToolData = await get_data(cfg, id, pool)
|
||||
node_channels, node_types, node_ains, node_dins = get_nodes_type(cfg, ToolNameID, UnitName)
|
||||
node_channels, node_types, node_ains, node_dins = await get_nodes_type(cfg, ToolNameID, UnitName, pool)
|
||||
righe = ToolData.splitlines()
|
||||
matrice_valori = []
|
||||
for riga in [riga for riga in righe if ';|;' in riga]:
|
||||
for riga in [riga for riga in righe if ';|;' in riga and 'No RX' not in riga and '.-' not in riga and 'File Creation' not in riga]:
|
||||
timestamp, batlevel, temperature, rilevazioni = riga.replace(';|;',';').split(';',3)
|
||||
EventDate, EventTime = timestamp.split(' ')
|
||||
valori_splitted = [valore for valore in rilevazioni.split(';') if valore != '|']
|
||||
@@ -132,10 +139,10 @@ async def make_musa_matrix(cfg: object, id: int, pool: object) -> list:
|
||||
list: A list of lists, where each inner list represents a row in the matrix.
|
||||
"""
|
||||
UnitName, ToolNameID, ToolData = await get_data(cfg, id, pool)
|
||||
node_channels, node_types, node_ains, node_dins = get_nodes_type(cfg, ToolNameID, UnitName)
|
||||
node_channels, node_types, node_ains, node_dins = await get_nodes_type(cfg, ToolNameID, UnitName, pool)
|
||||
righe = ToolData.splitlines()
|
||||
matrice_valori = []
|
||||
for riga in [riga for riga in righe if ';|;' in riga]:
|
||||
for riga in [riga for riga in righe if ';|;' in riga and 'No RX' not in riga and '.-' not in riga and 'File Creation' not in riga]:
|
||||
timestamp, batlevel, rilevazioni = riga.replace(';|;',';').split(';',2)
|
||||
if timestamp == '':
|
||||
continue
|
||||
@@ -194,17 +201,21 @@ async def make_gd_matrix(cfg: object, id: int, pool: object) -> list:
|
||||
UnitName, ToolNameID, ToolData = await get_data(cfg, id, pool)
|
||||
righe = ToolData.splitlines()
|
||||
matrice_valori = []
|
||||
pattern = r'^-\d*dB$'
|
||||
for riga in [riga for riga in righe if ';|;' in riga]:
|
||||
timestamp, batlevel, temperature, rilevazioni = riga.split(';',3)
|
||||
pattern = r';-?\d+dB$'
|
||||
for riga in [riga for riga in righe if ';|;' in riga and 'No RX' not in riga and '.-' not in riga and 'File Creation' not in riga]:
|
||||
timestamp, rilevazioni = riga.split(';|;',1)
|
||||
EventDate, EventTime = timestamp.split(' ')
|
||||
if batlevel == '|':
|
||||
batlevel = temperature
|
||||
temperature, rilevazioni = rilevazioni.split(';',1)
|
||||
if re.match(pattern, rilevazioni):
|
||||
valori_nodi = rilevazioni.lstrip('|;').rstrip(';').split(';|;') # Toglie '|;' iniziali, toglie eventuali ';' finali, dividi per ';|;'
|
||||
for num_nodo, valori_nodo in enumerate(valori_nodi, start=1):
|
||||
valori = valori_nodo.split(';')
|
||||
matrice_valori.append([UnitName, ToolNameID, num_nodo, date_check.conforma_data(EventDate), EventTime, batlevel, temperature] + valori + ([None] * (19 - len(valori))))
|
||||
logger.info(f"GD id {id}: {pattern} {rilevazioni}")
|
||||
if re.search(pattern, rilevazioni):
|
||||
batlevel, temperature, rssi = rilevazioni.split(';')
|
||||
logger.info(f"GD id {id}: {EventDate}, {EventTime}, {batlevel}, {temperature}, {rssi}")
|
||||
elif all(char == ';' for char in rilevazioni):
|
||||
pass
|
||||
elif ';|;' in rilevazioni:
|
||||
unit_metrics, data = rilevazioni.split(';|;')
|
||||
batlevel, temperature = unit_metrics.split(';')
|
||||
logger.info(f"GD id {id}: {EventDate}, {EventTime}, {batlevel}, {temperature}, {data}")
|
||||
else:
|
||||
logger.warning(f"GD id {id}: dati non trattati - {rilevazioni}")
|
||||
|
||||
return matrice_valori
|
||||
@@ -1,5 +1,5 @@
|
||||
from utils.database.loader_action import load_data, update_status, unlock
|
||||
from utils.database import DATA_LOADED
|
||||
from utils.database import WorkflowFlags
|
||||
from utils.csv.data_preparation import make_pipe_sep_matrix, make_ain_din_matrix, make_channels_matrix, make_tlp_matrix, make_gd_matrix, make_musa_matrix
|
||||
|
||||
import logging
|
||||
@@ -32,13 +32,13 @@ async def main_loader(cfg: object, id: int, pool: object, action: str) -> None:
|
||||
logger.info("matrice valori creata")
|
||||
# Load the data into the database
|
||||
if await load_data(cfg, matrice_valori, pool):
|
||||
await update_status(cfg, id, DATA_LOADED, pool)
|
||||
await update_status(cfg, id, WorkflowFlags.DATA_LOADED, pool)
|
||||
await unlock(cfg, id, pool)
|
||||
else:
|
||||
logger.warning(f"Action '{action}' non riconosciuta.")
|
||||
|
||||
|
||||
async def get_next_csv_atomic(pool, table_name, status):
|
||||
async def get_next_csv_atomic(pool, table_name, status, next_status):
|
||||
"""Preleva atomicamente il prossimo CSV da elaborare"""
|
||||
async with pool.acquire() as conn:
|
||||
# IMPORTANTE: Disabilita autocommit per questa transazione
|
||||
@@ -47,14 +47,17 @@ async def get_next_csv_atomic(pool, table_name, status):
|
||||
try:
|
||||
async with conn.cursor() as cur:
|
||||
# Usa SELECT FOR UPDATE per lock atomico
|
||||
|
||||
await cur.execute(f"""
|
||||
SELECT id, unit_type, tool_type, unit_name, tool_name
|
||||
FROM {table_name}
|
||||
WHERE locked = 0 AND status = %s
|
||||
WHERE locked = 0
|
||||
AND ((status & %s) > 0 OR %s = 0)
|
||||
AND (status & %s) = 0
|
||||
ORDER BY id
|
||||
LIMIT 1
|
||||
FOR UPDATE SKIP LOCKED
|
||||
""", (status,))
|
||||
""", (status, status, next_status))
|
||||
|
||||
result = await cur.fetchone()
|
||||
if result:
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
CSV_RECEIVED = 0
|
||||
DATA_LOADED = 1
|
||||
DATA_ELABORATED = 2
|
||||
DATA_SENT = 3
|
||||
class WorkflowFlags:
|
||||
CSV_RECEIVED = 0 # 0000
|
||||
DATA_LOADED = 1 # 0001
|
||||
DATA_ELABORATED = 2 # 0010
|
||||
SENT_RAW_DATA = 4 # 0100
|
||||
SENT_ELAB_DATA = 8 # 1000
|
||||
|
||||
|
||||
# Mappatura flag -> colonna timestamp
|
||||
FLAG_TO_TIMESTAMP = {
|
||||
WorkflowFlags.CSV_RECEIVED: "inserted_at",
|
||||
WorkflowFlags.DATA_LOADED: "loaded_at",
|
||||
WorkflowFlags.DATA_ELABORATED: "elaborated_at",
|
||||
WorkflowFlags.SENT_RAW_DATA: "sent_raw_at",
|
||||
WorkflowFlags.SENT_ELAB_DATA: "sent_elab_at"
|
||||
}
|
||||
|
||||
BATCH_SIZE = 1000
|
||||
@@ -2,11 +2,10 @@
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from utils.database import FLAG_TO_TIMESTAMP, BATCH_SIZE
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
timestamp_cols = ["inserted_at", "loaded_at", "elaborated_at", "sent_at"]
|
||||
|
||||
|
||||
async def load_data(cfg: object, matrice_valori: list, pool: object) -> bool:
|
||||
"""Carica una lista di record di dati grezzi nel database.
|
||||
|
||||
@@ -62,15 +61,23 @@ async def load_data(cfg: object, matrice_valori: list, pool: object) -> bool:
|
||||
`RssiModule` = IF({cfg.dbrawdata}.`RssiModule` != new_data.RssiModule, new_data.RssiModule, {cfg.dbrawdata}.`RssiModule`),
|
||||
`Created_at` = NOW()
|
||||
"""
|
||||
|
||||
#logger.info(f"Query insert: {sql_insert_RAWDATA}.")
|
||||
#logger.info(f"Matrice valori da inserire: {matrice_valori}.")
|
||||
rc = False
|
||||
async with pool.acquire() as conn:
|
||||
async with conn.cursor() as cur:
|
||||
for attempt in range(cfg.max_retries):
|
||||
try:
|
||||
logging.info(f"Loading data attempt {attempt + 1}.")
|
||||
await cur.executemany(sql_insert_RAWDATA, matrice_valori)
|
||||
await conn.commit()
|
||||
|
||||
for i in range(0, len(matrice_valori), BATCH_SIZE):
|
||||
batch = matrice_valori[i:i + BATCH_SIZE]
|
||||
|
||||
await cur.executemany(sql_insert_RAWDATA, batch)
|
||||
await conn.commit()
|
||||
|
||||
logging.info(f"Completed batch {i//BATCH_SIZE + 1}/{(len(matrice_valori)-1)//BATCH_SIZE + 1}")
|
||||
|
||||
logging.info("Data loaded.")
|
||||
rc = True
|
||||
break
|
||||
@@ -93,7 +100,7 @@ async def load_data(cfg: object, matrice_valori: list, pool: object) -> bool:
|
||||
return rc
|
||||
|
||||
|
||||
async def update_status(cfg: object, id: int, status: int, pool: object) -> None:
|
||||
async def update_status(cfg: object, id: int, status: str, pool: object) -> None:
|
||||
"""Aggiorna lo stato di un record nella tabella dei record CSV.
|
||||
|
||||
Args:
|
||||
@@ -106,7 +113,11 @@ async def update_status(cfg: object, id: int, status: int, pool: object) -> None
|
||||
async with conn.cursor() as cur:
|
||||
try:
|
||||
await cur.execute(
|
||||
f"update {cfg.dbrectable} set status = {status}, {timestamp_cols[status]} = now() where id = {id}"
|
||||
f"""update {cfg.dbrectable} set
|
||||
status = status | {status},
|
||||
{FLAG_TO_TIMESTAMP[status]} = now()
|
||||
where id = {id}
|
||||
"""
|
||||
)
|
||||
await conn.commit()
|
||||
logging.info(f"Status updated id {id}.")
|
||||
|
||||
@@ -4,6 +4,7 @@ import logging
|
||||
import aiomysql
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class FTPConnection:
|
||||
"""
|
||||
Manages an FTP or FTP_TLS connection, providing a context manager for automatic disconnection.
|
||||
@@ -38,7 +39,11 @@ class FTPConnection:
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.ftp.quit()
|
||||
|
||||
async def send_csv_to_customer(cfg: dict, id: int, unit: str, tool: str, csv_data: str, pool: object) -> bool:
|
||||
async def send_raw_csv_to_customer(cfg: dict, id: int, unit: str, tool: str, csv_data: str, pool: object) -> bool:
|
||||
None
|
||||
return True
|
||||
|
||||
async def send_elab_csv_to_customer(cfg: dict, id: int, unit: str, tool: str, csv_data: str, pool: object) -> bool:
|
||||
"""
|
||||
Sends elaborated CSV data to a customer via FTP.
|
||||
|
||||
@@ -79,8 +84,6 @@ async def send_csv_to_customer(cfg: dict, id: int, unit: str, tool: str, csv_dat
|
||||
passive = ftp_parms.get('passive', True)
|
||||
port = ftp_parms.get('port', 21)
|
||||
|
||||
|
||||
|
||||
# Connessione FTP
|
||||
with FTPConnection(host=send_ftp_info["ftp_addrs"], port=port, use_tls=use_tls, user=send_ftp_info["ftp_user"], passwd=send_ftp_info["ftp_passwd"], passive=passive) as ftp:
|
||||
|
||||
Reference in New Issue
Block a user