Due difetti che i registri di tre giorni mostravano chiaramente. Il riscatto dei buoni giudicava la risposta del sito, e la giudicava male: ogni singolo tentativo risultava "sessione scaduta: risposta la pagina di accesso" — 6 trovati, 0 riscossi, 6 non riusciti, giro dopo giro per tre giorni — mentre le puntate arrivavano lo stesso. Il riconoscimento della pagina di accesso scattava su pagine che di accesso non erano, e il servizio dichiarava di non funzionare mentre funzionava. Adesso aprire il collegamento e' il riscatto: si apre, si scrive che e' stato aperto, e il numero vero lo da' la differenza di saldo, che e' l'unica misura che non mente. Resta un solo fallimento possibile, l'errore di trasporto, dove il collegamento non e' stato aperto affatto. Un premio si riscuote una volta sola: fra i due errori, insistere su un premio gia' preso non costa niente, mentre dichiarare falliti dei riscatti riusciti riempie il registro di allarmi inutili. Il giudizio vecchio non e' stato cancellato, e' in GiudicaRisposta. Il conto alla rovescia in griglia restava fermo fra un poll e l'altro. Il valore era gia' giusto — si ricava dalla scadenza ancorata all'orologio del server, non dall'ultimo poll — ma nessuno lo richiedeva finche' non arrivava una risposta, e nelle prove reali fra un poll e l'altro passava anche un secondo e mezzo: sembrava un'applicazione bloccata. Un battito da cinque volte al secondo notifica ora le tre sole proprieta' che dipendono dal tempo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
217 lines
8.1 KiB
C#
217 lines
8.1 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Windows;
|
|
using System.Windows.Threading;
|
|
using AutoBidder.Models;
|
|
using AutoBidder.Utilities;
|
|
using AutoBidder.ViewModels;
|
|
|
|
namespace AutoBidder
|
|
{
|
|
/// <summary>
|
|
/// Barra strumenti del Monitor: contatori delle aste e stato del motore.
|
|
///
|
|
/// Sono informazioni che cambiano di continuo ma non a ogni singolo poll: un
|
|
/// battito fisso di un secondo le tiene aggiornate senza far ridisegnare la
|
|
/// barra decine di volte al secondo.
|
|
/// </summary>
|
|
public partial class MainWindow
|
|
{
|
|
private DispatcherTimer? _headerTimer;
|
|
private DispatcherTimer? _timerTicker;
|
|
|
|
private void StartMonitorHeaderTimer()
|
|
{
|
|
_headerTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
|
|
_headerTimer.Tick += (_, _) => RefreshMonitorHeader();
|
|
_headerTimer.Start();
|
|
|
|
// ── Il conto alla rovescia scorre da solo ────────────────────────
|
|
//
|
|
// Il tempo mancante non ha bisogno di una risposta dal server per essere
|
|
// ricalcolato: la scadenza è ancorata all'orologio di Bidoo e il resto è
|
|
// sottrazione. Prima però veniva notificato solo all'arrivo di un poll, e
|
|
// nelle prove reali fra un poll e l'altro passava anche un secondo e mezzo:
|
|
// il numero restava fermo e sembrava tutto bloccato.
|
|
//
|
|
// Cinque battiti al secondo: la colonna mostra i decimi sotto il minuto,
|
|
// quindi si vede scorrere davvero, e restano un ventesimo delle notifiche
|
|
// che servirebbero per seguire il decimo esatto.
|
|
_timerTicker = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };
|
|
_timerTicker.Tick += (_, _) => TickTimers();
|
|
_timerTicker.Start();
|
|
|
|
RefreshMonitorHeader();
|
|
}
|
|
|
|
private void TickTimers()
|
|
{
|
|
var vms = _auctionViewModels;
|
|
|
|
for (var i = 0; i < vms.Count; i++)
|
|
{
|
|
try { vms[i].RefreshTimeDisplay(); }
|
|
catch { /* una riga che non si aggiorna non deve fermare le altre */ }
|
|
}
|
|
}
|
|
|
|
private void RefreshMonitorHeader()
|
|
{
|
|
try
|
|
{
|
|
var vms = _auctionViewModels;
|
|
|
|
var active = 0;
|
|
var watch = 0;
|
|
var stopped = 0;
|
|
var won = 0;
|
|
var lost = 0;
|
|
|
|
foreach (var vm in vms)
|
|
{
|
|
switch (vm.StatusKind)
|
|
{
|
|
case "won": won++; break;
|
|
case "lost": lost++; break;
|
|
case "leading":
|
|
case "active": active++; break;
|
|
case "watch": watch++; break;
|
|
default: stopped++; break;
|
|
}
|
|
}
|
|
|
|
AuctionMonitor.UpdateCounters(vms.Count, active, watch, stopped, won, lost);
|
|
|
|
var clock = _auctionMonitor.Clock;
|
|
AuctionMonitor.UpdateEngineStatus(
|
|
_auctionMonitor.RequestsSent, clock.IsSynced, clock.SampleCount, clock.AverageLatencyMs);
|
|
|
|
RefreshAccountPills();
|
|
|
|
// Lo stato dei pulsanti globali dipende da tutte le aste: ricalcolarlo a
|
|
// ogni risposta di ogni asta era uno scorrimento completo dell'elenco
|
|
// decine di volte al secondo. Una volta al secondo è più che sufficiente.
|
|
UpdateGlobalControlButtons();
|
|
|
|
if (_selectedAuction != null) RefreshSelectedStats(_selectedAuction);
|
|
}
|
|
catch { /* la barra informativa non deve mai disturbare il resto */ }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Puntate, credito e vincite da confermare, riletti dalla sessione viva a ogni
|
|
/// battito.
|
|
///
|
|
/// <para>Prima ciascuno di questi numeri veniva scritto da chi lo aggiornava —
|
|
/// il ripristino della sessione, il timer di rinfresco, il codice dopo la puntata —
|
|
/// e bastava che uno dei tre non passasse perché la barra restasse su un valore
|
|
/// vecchio. Rileggerli qui costa una lettura di campo al secondo e toglie di mezzo
|
|
/// tutta quella categoria di errori.</para>
|
|
/// </summary>
|
|
private void RefreshAccountPills()
|
|
{
|
|
var session = _auctionMonitor.GetSession();
|
|
|
|
var connected = session != null && !string.IsNullOrEmpty(session.Username);
|
|
|
|
AuctionMonitor.UpdateAccountStatus(
|
|
connected ? session!.RemainingBids : null,
|
|
connected ? (decimal)session!.ShopCredit : null,
|
|
AuctionsToConfirm);
|
|
|
|
UpdateMinBidsIndicator(SettingsManager.Load().MinimumRemainingBids);
|
|
}
|
|
|
|
/// <summary>Contatori del motore relativi alla sola asta selezionata.</summary>
|
|
private void RefreshSelectedStats(AuctionViewModel vm)
|
|
{
|
|
try
|
|
{
|
|
var info = vm.AuctionInfo;
|
|
AuctionMonitor.UpdateSelectedStats(
|
|
info.ResetCount,
|
|
info.BidsUsedOnThisAuction ?? vm.MyClicks,
|
|
info.AverageLatencyMs,
|
|
info.PollCount,
|
|
info.PollErrors);
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scheda Prodotto: costo totale se si vince e verdetto di convenienza.
|
|
/// </summary>
|
|
private void RefreshProductVerdict(AuctionViewModel? vm)
|
|
{
|
|
try
|
|
{
|
|
var totalBox = AuctionMonitor.FindName("ProductTotalCostText") as System.Windows.Controls.TextBlock;
|
|
var value = vm?.AuctionInfo.CalculatedValue;
|
|
|
|
if (value == null)
|
|
{
|
|
if (totalBox != null) totalBox.Text = "—";
|
|
AuctionMonitor.SetVerdict("Convenienza", "non calcolabile", "neutral");
|
|
return;
|
|
}
|
|
|
|
if (totalBox != null) totalBox.Text = $"{value.TotalCostIfWin:F2} €";
|
|
|
|
if (value.Savings is not { } savings)
|
|
{
|
|
AuctionMonitor.SetVerdict("Convenienza", "valore prodotto ignoto", "neutral");
|
|
return;
|
|
}
|
|
|
|
var pct = Math.Abs(value.SavingsPercentage ?? 0);
|
|
AuctionMonitor.SetVerdict(
|
|
savings > 0 ? "Risparmio" : "Sovrapprezzo",
|
|
$"{Math.Abs(savings):F2} € ({pct:F1}%)",
|
|
savings > 0 ? "ok" : "danger");
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// "Rimuovi le aste concluse": ripulisce l'elenco da ciò che non serve più
|
|
/// senza toccare quelle ancora in corso.
|
|
/// </summary>
|
|
private void AuctionMonitor_RemoveFinishedClicked(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
var finished = _auctionViewModels
|
|
.Where(a => a.StatusKind is "won" or "lost")
|
|
.ToList();
|
|
|
|
if (finished.Count == 0)
|
|
{
|
|
MessageBox.Show(this, "Nessuna asta conclusa da rimuovere.", "Aste concluse",
|
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
|
return;
|
|
}
|
|
|
|
foreach (var vm in finished)
|
|
{
|
|
_auctionMonitor.RemoveAuction(vm.AuctionId);
|
|
_auctionViewModels.Remove(vm);
|
|
}
|
|
|
|
if (_selectedAuction != null && !_auctionViewModels.Contains(_selectedAuction))
|
|
_selectedAuction = null;
|
|
|
|
SaveAuctions();
|
|
UpdateTotalCount();
|
|
UpdateGlobalControlButtons();
|
|
RefreshMonitorHeader();
|
|
|
|
Log($"[REMOVE] Rimosse {finished.Count} aste concluse", LogLevel.Info);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log($"[ERRORE] Rimozione aste concluse: {ex.Message}", LogLevel.Error);
|
|
}
|
|
}
|
|
}
|
|
}
|