|
|
@@ -3,7 +3,7 @@ package apiclient |
|
|
import ( |
|
|
import ( |
|
|
"context" |
|
|
"context" |
|
|
"crypto/tls" |
|
|
"crypto/tls" |
|
|
"fmt" |
|
|
|
|
|
|
|
|
"errors" |
|
|
"log/slog" |
|
|
"log/slog" |
|
|
"net/http" |
|
|
"net/http" |
|
|
"reflect" |
|
|
"reflect" |
|
|
@@ -18,6 +18,8 @@ import ( |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
func UpdateDB(db *gorm.DB, ctx context.Context, cfg *config.Config, writer *kafka.Writer, appState *appcontext.AppState) error { |
|
|
func UpdateDB(db *gorm.DB, ctx context.Context, cfg *config.Config, writer *kafka.Writer, appState *appcontext.AppState) error { |
|
|
|
|
|
slog.Info("UpdateDB: Avvio procedura di sincronizzazione database...") |
|
|
|
|
|
|
|
|
tr := &http.Transport{ |
|
|
tr := &http.Transport{ |
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
|
|
} |
|
|
} |
|
|
@@ -25,15 +27,25 @@ func UpdateDB(db *gorm.DB, ctx context.Context, cfg *config.Config, writer *kafk |
|
|
|
|
|
|
|
|
token, err := GetToken(ctx, cfg, client) |
|
|
token, err := GetToken(ctx, cfg, client) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
|
|
|
slog.Error("UpdateDB: Fallito recupero del Token di autenticazione", "error", err) |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
|
|
|
slog.Info("UpdateDB: Token di autenticazione recuperato con successo") |
|
|
|
|
|
|
|
|
if trackers, err := GetTrackers(token, client, cfg); err == nil { |
|
|
|
|
|
|
|
|
// 1. Sincronizzazione TRACKERS |
|
|
|
|
|
trackers, err := GetTrackers(token, client, cfg) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
slog.Error("UpdateDB: Errore durante la chiamata GetTrackers", "error", err) |
|
|
|
|
|
} else { |
|
|
|
|
|
slog.Info("UpdateDB: Tracker scaricati dall'API", "count", len(trackers)) |
|
|
syncTable(db, trackers) |
|
|
syncTable(db, trackers) |
|
|
|
|
|
slog.Info("UpdateDB: Tabella 'trackers' allineata su DB") |
|
|
|
|
|
|
|
|
if err := controller.SendKafkaMessage(writer, &model.ApiUpdate{Method: "DELETE", MAC: "all"}, ctx); err != nil { |
|
|
if err := controller.SendKafkaMessage(writer, &model.ApiUpdate{Method: "DELETE", MAC: "all"}, ctx); err != nil { |
|
|
slog.Error("Error in sending delete all from lookup message", "error", err) |
|
|
|
|
|
|
|
|
slog.Error("UpdateDB: Errore nell'invio del messaggio Kafka 'DELETE all' al lookup", "error", err) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
kafkaMsgCount := 0 |
|
|
for _, v := range trackers { |
|
|
for _, v := range trackers { |
|
|
apiUpdate := model.ApiUpdate{ |
|
|
apiUpdate := model.ApiUpdate{ |
|
|
Method: "POST", |
|
|
Method: "POST", |
|
|
@@ -42,59 +54,124 @@ func UpdateDB(db *gorm.DB, ctx context.Context, cfg *config.Config, writer *kafk |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if err := controller.SendKafkaMessage(writer, &apiUpdate, ctx); err != nil { |
|
|
if err := controller.SendKafkaMessage(writer, &apiUpdate, ctx); err != nil { |
|
|
msg := fmt.Sprintf("Error in sending POST kafka message: %v", err) |
|
|
|
|
|
slog.Error(msg) |
|
|
|
|
|
|
|
|
slog.Error("UpdateDB: Errore nell'invio del messaggio Kafka 'POST tracker'", "tracker_id", v.ID, "error", err) |
|
|
|
|
|
} else { |
|
|
|
|
|
kafkaMsgCount++ |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
slog.Info("UpdateDB: Sincronizzazione messaggi Kafka per i tracker completata", "inviati", kafkaMsgCount) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if gateways, err := GetGateways(token, client, cfg); err == nil { |
|
|
|
|
|
|
|
|
// 2. Sincronizzazione GATEWAYS |
|
|
|
|
|
gateways, err := GetGateways(token, client, cfg) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
slog.Error("UpdateDB: Errore durante la chiamata GetGateways", "error", err) |
|
|
|
|
|
} else { |
|
|
|
|
|
slog.Info("UpdateDB: Gateway scaricati dall'API", "count", len(gateways)) |
|
|
syncTable(db, gateways) |
|
|
syncTable(db, gateways) |
|
|
|
|
|
slog.Info("UpdateDB: Tabella 'gateways' allineata su DB") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if floors, err := GetFloors(token, client, cfg); err == nil { |
|
|
|
|
|
|
|
|
// 3. Sincronizzazione FLOORS |
|
|
|
|
|
floors, err := GetFloors(token, client, cfg) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
slog.Error("UpdateDB: Errore durante la chiamata GetFloors", "error", err) |
|
|
|
|
|
} else { |
|
|
|
|
|
slog.Info("UpdateDB: Piani (floors) scaricati dall'API", "count", len(floors)) |
|
|
syncTable(db, floors) |
|
|
syncTable(db, floors) |
|
|
|
|
|
slog.Info("UpdateDB: Tabella 'floors' allineata su DB") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if zones, err := GetZones(token, client, cfg); err == nil { |
|
|
|
|
|
|
|
|
// 4. Sincronizzazione ZONES |
|
|
|
|
|
zones, err := GetZones(token, client, cfg) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
slog.Error("UpdateDB: Errore durante la chiamata GetZones", "error", err) |
|
|
|
|
|
} else { |
|
|
|
|
|
slog.Info("UpdateDB: Zone scaricate dall'API", "count", len(zones)) |
|
|
syncTable(db, zones) |
|
|
syncTable(db, zones) |
|
|
|
|
|
slog.Info("UpdateDB: Tabella 'zones' allineata su DB") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if trackerZones, err := GetTrackerZones(token, client, cfg); err == nil { |
|
|
|
|
|
|
|
|
// 5. Sincronizzazione TRACKER ZONES |
|
|
|
|
|
trackerZones, err := GetTrackerZones(token, client, cfg) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
slog.Error("UpdateDB: Errore durante la chiamata GetTrackerZones", "error", err) |
|
|
|
|
|
} else { |
|
|
|
|
|
slog.Info("UpdateDB: Tracker-Zones scaricate dall'API", "count", len(trackerZones)) |
|
|
syncTable(db, trackerZones) |
|
|
syncTable(db, trackerZones) |
|
|
|
|
|
slog.Info("UpdateDB: Tabella 'tracker_zones' allineata su DB") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if inferredPosition, err := InferPosition(token, client, cfg); err == nil { |
|
|
|
|
|
|
|
|
// 6. Inferenza delle posizioni (Stime AI passate) |
|
|
|
|
|
inferredPosition, err := InferPosition(token, client, cfg) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
slog.Error("UpdateDB: Errore durante la chiamata InferPosition", "error", err) |
|
|
|
|
|
} else { |
|
|
|
|
|
updateCount := 0 |
|
|
for _, v := range inferredPosition.Items { |
|
|
for _, v := range inferredPosition.Items { |
|
|
mac := convertMac(v.Mac) |
|
|
mac := convertMac(v.Mac) |
|
|
db.Model(&model.Tracker{}).Where("mac = ?", mac).Update("x", v.X).Update("y", v.Y) |
|
|
|
|
|
|
|
|
res := db.Model(&model.Tracker{}).Where("mac = ?", mac).Updates(map[string]interface{}{"x": v.X, "y": v.Y}) |
|
|
|
|
|
if res.Error != nil { |
|
|
|
|
|
slog.Error("UpdateDB: Errore aggiornamento coordinate stimate per tracker", "mac", mac, "error", res.Error) |
|
|
|
|
|
} else if res.RowsAffected > 0 { |
|
|
|
|
|
updateCount++ |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
slog.Info("UpdateDB: Aggiornamento posizioni storiche completato", "tracker_aggiornati", updateCount) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 7. Controllo e Inizializzazione SETTINGS |
|
|
var settings appcontext.Settings |
|
|
var settings appcontext.Settings |
|
|
db.First(&settings) |
|
|
|
|
|
|
|
|
if err := db.First(&settings).Error; err != nil { |
|
|
|
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) { |
|
|
|
|
|
slog.Error("UpdateDB: Errore durante la lettura delle impostazioni", "error", err) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if settings.ID == 0 { |
|
|
if settings.ID == 0 { |
|
|
msg := "settings are empty" |
|
|
|
|
|
slog.Info(msg) |
|
|
|
|
|
db.Create(appState.GetSettings()) |
|
|
|
|
|
|
|
|
slog.Info("UpdateDB: Tabella settings vuota. Inizializzazione con i valori di default di AppState...") |
|
|
|
|
|
if err := db.Create(appState.GetSettings()).Error; err != nil { |
|
|
|
|
|
slog.Error("UpdateDB: Errore critico durante la creazione dei settings di default", "error", err) |
|
|
|
|
|
} else { |
|
|
|
|
|
slog.Info("UpdateDB: Settings di default salvati con successo") |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
slog.Info("UpdateDB: Settings già presenti nel database", "id", settings.ID) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
slog.Info("UpdateDB: Procedura di sincronizzazione completata.") |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func syncTable[T any](db *gorm.DB, data []T) { |
|
|
func syncTable[T any](db *gorm.DB, data []T) { |
|
|
if len(data) == 0 { |
|
|
if len(data) == 0 { |
|
|
|
|
|
slog.Warn("syncTable: Nessun dato ricevuto dall'API, salto la sincronizzazione per questa tabella.") |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
var ids []string |
|
|
var ids []string |
|
|
for _, item := range data { |
|
|
for _, item := range data { |
|
|
v := reflect.ValueOf(item).FieldByName("ID").String() |
|
|
v := reflect.ValueOf(item).FieldByName("ID").String() |
|
|
ids = append(ids, v) |
|
|
|
|
|
|
|
|
if v != "" { |
|
|
|
|
|
ids = append(ids, v) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if len(ids) == 0 { |
|
|
|
|
|
slog.Error("syncTable: Impossibile procedere. Trovati 0 ID validi tramite reflection nella struttura dei dati.") |
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
db.Transaction(func(tx *gorm.DB) error { |
|
|
|
|
|
tx.Where("id NOT IN ?", ids).Delete(new(T)) |
|
|
|
|
|
|
|
|
err := db.Transaction(func(tx *gorm.DB) error { |
|
|
|
|
|
// Elimina i record locali che non esistono più nel backend centrale |
|
|
|
|
|
if err := tx.Where("id NOT IN ?", ids).Delete(new(T)).Error; err != nil { |
|
|
|
|
|
return err |
|
|
|
|
|
} |
|
|
|
|
|
// Upsert dei dati aggiornati o nuovi |
|
|
return tx.Clauses(clause.OnConflict{UpdateAll: true}).Create(&data).Error |
|
|
return tx.Clauses(clause.OnConflict{UpdateAll: true}).Create(&data).Error |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
slog.Error("syncTable: Errore critico durante la transazione di sincronizzazione", "error", err) |
|
|
|
|
|
} |
|
|
} |
|
|
} |