|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
[.net]Coda(FIFO), EF e 'timer'...
Ariekkime ... credetemi, mi piacerebbe tanto essere uno di quelli che i problemi li risolve anzichè chiedere sempre aiuto.... btw...
Ho una tabella mappata su EF che contiene valori come questi: Codice:
id=21; fkID=15; startJob = 1; jobLenght = 100; isLast = 0; workerID = 10; id=22; fkID=15; startJob = 101; jobLenght = 100; isLast = 0; workerID = 19; id=23; fkID=15; startJob = 201; jobLenght = 0; isLast = 1; workerID = 61; Per fare un esempio: 1) Viene inserito un ordine 2) i prodotti che sono nell'ordine hanno una lavorazione 3) la lavorazione ha varie fasi 4) la prima fase di lavorazione viene inviata all'operatore preposto 5) la seconda fase di lavorazione, deve durare 'jobLenght' prima di essere inviata sul terminale dell'altro eventuale operatore preposto 6) ... e così via fin quando non si arriva all'ultima fase che valorizza il campo "isLast" a 1 e chiude quindi la lavorazione del prodotto. L'ordine di inserimento e di uscita dei vari "ordini" è di tipo FIFO. Avevo quindi pensato di creare un business object con i campi che mi interessano e gestire una o più "Queue<T>". Ho capito che devo gestire una sorta di "timer" o di metodo System.Threading.Sleep... che prenda il valore di jobLenght, ma non riesco a 'rappresentarmelo' come codice.. Mi dareste qualche dritta ? Grazie sempre. RaouL.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
sono riuscito ad implementare quasi tutto (la coda e i vari eventi ad essa legata).
Mi manca soltanto un ultimo pezzettino ma che davvero non ho idea di come implementare e avrei davvero bisogno di aiuto: Non riesco neanche a pensare come "scalare" di posizione fra una fase e l'altra ed a far partire un "timer" che in base alla durata della fase invii i dati fra un terminale e l'altro ![]()
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
![]() |
![]() |
![]() |
#3 |
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Io lo vedevo un lavoro perfetto per Workflow Foundation.
Non ho capito la tua ultima richiesta. Cosa intendi di preciso per "inviare i dati fra un terminale e l'altro"? Questi terminali non leggono le informazioni da un db? Ricevono una qualche forma di file? Hanno un socket in ascolto? Fanno polling da qualche parte? |
![]() |
![]() |
![]() |
#4 | |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Quote:
![]() Si, leggono dati solo da un db. Non c'è nessun servizio che gestisce l'applicazione. Provo a spiegarmi meglio, ieri ero davvero stanchissimo ![]() Codice:
//i nomi di metodi ed entità sono solo concettuali private void Foo(int prodottoID, int terminaleID, bool segnaleRicevuto) { int counter = context.ProdottoSchedaLavorazioni.Where(x => x.id_prodotto == prodottoID).Count(); //ora prendo la prima fase di lavorazione var primoStep = context.FaseLavori.First(x => x.ProdottoSchedaLavorazioni.id_prodotto == prodottoID && x.id_terminale == terminaleID); if(segnaleRicevuto) //segnale ricevuto dal terminale che ha la prima fase: vuol dire "ho iniziato a lavorare il prodotto" { for(int i = primoStep.progressivo; i <= counter; i++) { //prendo la fase successiva var nextStep = context.FaseLavori.Single(x => x.progressivo == i); //in ogni step c'è un tempo che indica la durata della lavorazione //ad esempio 100 //devo utilizzare questo tempo per mettere in "pausa" la query che verrà visualizzata sul terminale di chi ha lo stepSuccessivo //nel senso che se il primo step dura 100, il bind dei dati //sul terminale di chi ha il secondo step deve apparire dopo 100 //il secondo step avrà anche lui una durata, ad es. 200 //quindi se c'è un terzo (o N) dovranno visualizzare i dati a partire //dalla durata (tempo1 + tempo2 + tempoN). } } }
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
|
![]() |
![]() |
![]() |
#5 | |
Senior Member
Iscritto dal: Dec 2001
Città: Lecce
Messaggi: 3936
|
Quote:
![]() |
|
![]() |
![]() |
![]() |
#7 |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Ok, direi che ci sono quasi:
Codice:
namespace JobHelper.ServiceManager public static class JobsQueue { public static Queue<ObjJob> QListener = new Queue<ObjJob>(); public static void OnSignal(int productID, int termID, int sequence, int signal) if(signal == 0) { ObjJob obj = new ObjJob(productID, termID, sequence); QListener.Enqueue(obj); } else QListener.Dequeue(); } public class ObjJob { public int ProductID {get; set;} public int TermID {get; set;} public int Sequence {get; set;} //blah costruttore che prende come argomenti productID, termID e sequence } //somewhere in the horror code... public IQueryable JobSequence(int termID) { IQueryable result = null; int timerTick = 10; foreach(var job in JobsQueue.QListener) { var currentJob = objContext.Job.Single(x => x.productID == job.ProductID); var jobLine = currentJob.JobLines.Single(x => x.termID == job.TermID && x.sequence == job.Sequence); if(jobLine.sequence > 1) { timerTick = jobLine.TotalMinutes; } result = //blablbalba Thread.Sleep(timerTick); return result; } } Ma.... (c'è sempre uno stramaledetto 'ma').... ovviamente quando uso Thread.Sleep, l'applicazione (giustamente) è "ferma" e l'utente non può interagire. Cosa posso utilizzare per fare in modo che durante i vari "refresh" l'utente abbia la possibilità di interagire con l'applicazione ? Mi viene in mente un backgroundworker, ma io mi trovo ancora lato "logica" e non UI. Grazie RaouL.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
![]() |
![]() |
![]() |
#8 |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
....
mi sono comunque accorto che così, non è che funzioni come dovrebbe... in pratica fa un semplice refresh per un intervallo di tempo costante.... ![]() ![]() ![]()
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
![]() |
![]() |
![]() |
#9 |
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Non capisco il perchè di questo codice:
Codice:
Thread.Sleep(timerTick); return result; Non potresti intanto leggere i dati metterli in un oggetto che solleva un evento di "DataReady" quando è passato timerTick? Ovviamente questo prevede che tu abbia un software multithread in cui un thread principale parallelizza l'esecuzione del tuo foreach e riceve eventi dai thread avviati per poi segnalare ulterioriormente a chi di dovere il fatto che sono arrivati dati. Questa struttura multi-thread probabilmente cozza con la natura statica di JobsQueue (forse ti serve una ConcurrentQueue). Inoltre dovresti stare attento ai timeout. Mi sembra di capire che leggi qualcosa da db, ma se poi vai a riutilizzarlo magari un bel po' di tempo dopo potresti avere problemi con la connessione. |
![]() |
![]() |
![]() |
#10 | |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Quote:
Allora, lo scenario è questo: sul server gira soltanto il db (è sql server) l'applicazione è su ogni pc, quindi, ogni unità ha il suo eseguibile. Adesso... ovviamente l'applicazione è la stessa per ogni pc. Quando l'applicazione parte, l'utente fa un login ed in base a dei parametri viene riconosciuto come tipo di unità, ad esempio: passa-filo decoratore etc... c'è un'altra unità che invece gestisce gli ordini: viene immesso l'ordine di un prodotto; il prodotto ha delle fasi di lavorazione; ciascuna fase di lavorazione ha una sola competenza(passa-filo, decoratore, etc..) ed ha un ordine cronologico di lavorazione, ad es.: 1) passa-filo 2) decoratore 3) imballaggio ciascuna lavorazione ha un tempo di lavorazione: prima fase 2 minuti seconda fase 3 minuti terza fase 1 minuto a livello di applicazione deve succedere questo: il pc che corrisponde alla competenza della prima fase di lavorazione deve ricevere a video che deve iniziare appunto la lavorazione finito il tempo di lavorazione della prima fase, il pc che corrisponde alla competenza successiva riceve a video la notifica che la fase uno è terminata e deve iniziare la sua lavorazione.... e così via fin quando non si arriva all'ultima fase (l'imballaggio) Quando l'ultima fase è completata, l'oggetto può essere rimosso dalla coda e non essere più visualizzato sui vari terminali.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
|
![]() |
![]() |
![]() |
#11 |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Ok, mi sto documentando sul namespace System.Collections.Concurrent.
Ho letto che in pratica il mio è il classico scenario "producer - consumer" e mi si suggerisce di usare una blockingcollection<T>. Io sto procedendo così ma mi manca proprio la capacità di capire come redirigere i dati a tempo ed in sequenza: Codice:
public static BlockingCollection<MyObject> queue = new BlockingCollection<MyObject>(); public static void Test(MyObject obj) { this.obj = obj; queue.Add(obj); } public void Test2() { foreach(var item in queue.GetConsumingEnumerable()) MessageBox.Show(item.BlahProperty...); } Plz... datemi una mano .... ![]() RaouL.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
![]() |
![]() |
![]() |
#12 |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Ok... tralascio per il momento il mondo "parallelo". E' un mondo nuovo per me e mi richiede tempi di apprendimento che per ora non ho.
Butterei quindi qualsiasi considerazione fatta su code et similia e mi concentrerei banalmente su query classiche. in pseudo codice quindi: Codice:
//per qualsiasi terminale (pc... o monitor che dir si voglia) la sorgente dati è public IQueryable FillData(int computerID) { var result = from a in myContext.MyData.Where(x => x.computerID == computerID) select new { //blabladati }; return result; } //in questi dati, per ciascuna fase di lavoro ci sono l'inizio e la fine (quindi la durata) della lavorazione //come faccio a passare da una fase all'altra allo scadere dei rispettivi tempi di lavorazione ? ![]() ![]() ![]()
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
![]() |
![]() |
![]() |
#13 |
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Però scusa se hai istanze del software differenti che gestiscono le varie fasi cosa ti impedisce di impostare uno stato su db alla fine di una fase di lavorazione?
L'istanza del software che gestisce la fase X che ha durata T1 esegue e al termine del tempo T imposta lo stato su db. Il software che gestisce la fase X+1 controlla lo stato su db e se è quello giusto fa partire la lavorazione, alla scadenza del tempo T2 imposta su db lo stato successivo e così via. Questo stato può essere monitorato tramite polling su db(soluzione più semplice e immediata) dalle altre istanze (ti ci vorrà anche lo stato di "work in progress"). |
![]() |
![]() |
![]() |
#14 | |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Quote:
Posso provare a simularlo su una replica in locale. Grazie comunque per il suggerimento.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
|
![]() |
![]() |
![]() |
#15 | |
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Quote:
Però non avresti bisogno dell'appoggio del db, se proprio non hai modo di fartelo modificare. |
|
![]() |
![]() |
![]() |
#16 |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Mmm... mi sento sempre più confuso...
al momento sto simulando l'avanzamento delle sequenze in modo manuale, nel senso che quando un terminale indica che ha finito la sua lavorazione, la query viene visualizzata sul terminale successivo: Codice:
public IQueryable QueueSequence(int termID) { IQueryable coda = null; var items = context.VQueue.Where(x => x.termID == termID); foreach (var item in items) { if (item.jobSequence < 2 && item.progressivo == 1) { coda = from jobs in context.VQueue.Where(x => x.jobSequence < 2 && x.progressivo == 1) select new { idr = jobs.id_productOrder, hSend = jobs.hSend, linea = jobs.lineaoloDesc, product = jobs.product, id_product = jobs.productID, pritem = jobs.progressivo, sequence = jobs.jobSequence, term = jobs.termID }; } if (item.jobSequence >= 2 && item.jobSequence < 4 && item.progressivo == 2 && !item.isLast) { coda = from jobs in context.VQueue.Where(x => x.jobSequence >= 2 && x.jobSequence < 4 && x.progressivo == 2 && !item.isLast) select new { idr = jobs.id_productOrder, hSend = jobs.hSend, linea = jobs.lineaoloDesc, product = jobs.producticoloDesc, id_product = jobs.producticoloPfID, pritem = jobs.progressivo, sequence = jobs.jobSequence, term = jobs.termID }; } if (item.jobSequence >= 4 && item.progressivo == 3 && item.isLast) { coda = from jobs in context.VQueue.Where(x => x.jobSequence >= 4 && x.progressivo == 3 && x.isLast) select new { idr = jobs.id_productOrder, hSend = jobs.hSend, linea = jobs.lineaoloDesc, product = jobs.producticoloDesc, id_product = jobs.producticoloPfID, pritem = jobs.progressivo, sequence = jobs.jobSequence, term = jobs.termID }; } } return coda; } ad ogni modo: nel caso di sopra, simulo un prodotto che ha tre sequenze di lavoro e delle quali la terza è quella di chiusura; vi chiedo: posso fare in modo che si comporti allo stesso modo a prescindere da quante sequenze ci sono ? Intendo che al momento è tale la confusione che non riesco neanche a pensare a come ciclare all'interno.. Grazie sempre per il supporto. RaouL.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
![]() |
![]() |
![]() |
#17 |
Senior Member
Iscritto dal: Feb 2003
Città: Stockholm (SE)
Messaggi: 1343
|
non so molto del tuo problema, ma se hai un problema di gestione dello stato, potresti pensare di aiutarti con Workflow Foundation. Ha una curva di apprendimento un po' troppo ripida all'inizio ma potrebbe valerne la pena
![]() |
![]() |
![]() |
![]() |
#18 | |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Ciao Raoul, riprendendo il tuo esempio:
Quote:
EDIT: Inoltre: i vari terminali (i vari worker, se ho capito bene) possono comunicare tra loro (LAN)? Ho capito che non puoi toccare il db, ma i worker ci possono accedere, questo mi porta a chiederti: volendo puoi installare un applicazione sul pc dove c'è il db per comunicare con i worker?
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) Ultima modifica di banryu79 : 03-10-2012 alle 08:22. |
|
![]() |
![]() |
![]() |
#19 | ||
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Quote:
![]() Rispondendo alla prima domanda: Il worker che ha finito la sua lavorazione può proseguire. Quote:
1)gli eseguibili risiedono sul server e vengono eseguiti dai clients. 2)Ogni client ha il suo eseguibile in locale e tutti i clients ovviamente hanno in comune soltanto il db. Si, sui clients posso installare qualunque cosa.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
||
![]() |
![]() |
![]() |
#20 |
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Leggendone la documentazione mi rendo conto che è esattamente ciò che serve per risolvere . Ma al momento i miei problemi derivano anche dal fatto che lavoro su codice già esistente e l'applicazione è già in produzione.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek ![]() |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 21:49.