Avatar billede safl Nybegynder
05. februar 2006 - 13:53 Der er 19 kommentarer og
1 løsning

Udføre en procedure på et givent tidspunkt

Hello,

Jeg har brug for at kunne starte en procedure på et givent tidspunkt.
Jeg har tænkt mig at gøre det på denne måde:

Smide en Timer ind der hvert sekund tjekker om det er blevet tid til at køre proceduren.

Jeg vil blot høre om der er nogen her der har en bedre ide til hvordan jeg kan schedule min procedure?

Det virker lidt for ineffektivt at lave et tjek hvert evigt eneste sekund. Når den kun skal køre proceduren en gang i døgnet på et givent tidspunkt.

Løsningen skal være en mulighed med kode i programmet ikke noget med at bruge windows scheduling eller lignende.
Pointene gives altså til den der kan foreslå en mere effektiv måde at schedule en procedure end den jeg har foreslået ovenover.

på forhånd tak,

Simon
Avatar billede kroning Nybegynder
05. februar 2006 - 13:56 #1
Brug en timer men sæt interval til det antal milisekunder der er til proceduren skal kaldes, dvs. du tager tidspunktet proceduren skal kaldes og trækker aktuel tid fra den tid du så får regner du om til milisekunder og sætter Timer.interval til denne.
Der er også en anden mulighed så jeg vender lige tilbage om et øjeblik.
Avatar billede safl Nybegynder
05. februar 2006 - 14:02 #2
kroning> den mulighed havde jeg også tænkt lidt på men fandt den lidt for svævende. Er meget spændt at høre på dit næste forslag :)
Avatar billede kroning Nybegynder
05. februar 2006 - 14:18 #3
Jeg tænkte på at bruge ting som:
CreateWaitableTimer
SetWaitableTimer
og evt. WaitForSingleObject
men jeg bruger selv Timer løsningen på den måde jeg beskrev først og det virker ganske fint sålænge der ikke er længere end 49,7 dage til at proceduren skal kaldes. 49,7 dage er jo det længste timer.interval som er en cardinal kan sættes til.
Avatar billede kroning Nybegynder
05. februar 2006 - 14:20 #4
Hvad mener du forresten med for svævende?
Avatar billede safl Nybegynder
05. februar 2006 - 14:54 #5
Hmm metoden med at tjekke om tiden er korrekt igen igen igen igen. er smart på den måde at jeg kan tjekke for flere forskellige procedures ekserkvering. Desværre er det ikke særlig effektivt.
Hvorimod det med at sætte en timer til at ekserkvere en gang ude i fremtiden gør at timeren kun kan bruges til ekserkvering af en enkelt procedure men den er til gengæld mere effektiv.
Men hvis min procedure f.eks. i en konfiguration skal ekserkveres tirsdag og torsdag kl. 13 og i en anden konfiguration alle ugens dage kl. 00:00 så er den første metode det mest fleksible da jeg kan lave en timer der styrer det hele.
Hvorimod jeg skal lave flere timere til at styre hvert tidspunkt, hvis jeg benytter din første anbefaling, korrekt?
Avatar billede stoney Nybegynder
05. februar 2006 - 15:00 #6
Hvad med at bruge windows indbyggede  scheduler ?

Altså kald dit prog, læs  dit prog's configuration og udfør
den procedure du vil.

Stoney
Avatar billede kroning Nybegynder
05. februar 2006 - 15:00 #7
Næ, jeg bruger kun en timer. Jeg har en liste med "ting der skal ske" sorteret i tids orden, det der skal ske først står øverst, programmet kikker så på den første i listen og beregner hvor lang tid der til at denne event skal ske og sætter så timer.interval derefter. Når Timer så når sin tid eller hvis der sker ændringer i event listen så kikkes der igen på event listen og timer.interval sættes igen.
Avatar billede kroning Nybegynder
05. februar 2006 - 15:04 #8
CreateWaitableTimer og SetWaitableTimer kan også kun håndtere en tid af gangen.
Avatar billede safl Nybegynder
05. februar 2006 - 15:06 #9
kroning> Er det muligt at se en implementation at din prioritering af opgaver?
Det lyder ret interessant. Jeg har udover de ting der skal ske på bestemte tidspunkter også en række at der ting der skal eksveres på intervaller (hvert 5. minut, hvert kvarter).
Dette ville egentlig være smart at skære alt der er afhængigt at tid over en kam og lave ligesom du har gjort en kø af jobs der skal ekserkveres.

stoney> Hvis du læser mit spørgsmål så vil jeg ikke benytte styresystemets scheduling, er kun interesseret i implementationer i koden. Ellers tak for foreslaget :)
Avatar billede stoney Nybegynder
05. februar 2006 - 15:22 #10
ok, men med mit forslag vil din procedure blive udført uanset om
dit prog kører eller ej.

Stoney
Avatar billede kroning Nybegynder
05. februar 2006 - 15:40 #11
Lige den måde jeg gør det på kan du nok ikke bruge, jeg bruger en synlig ListView som indeholder opgaverne. Det er det eneste program der kører på computeren som bare hænger i et hjørne uden skærm og tastatur og styere adgangs kontrollen til forskellige enheder på en campingplads.
Opgaverne smides ind i ListViewen på denne måde:

procedure TKDSatMainForm.OpretKommando(Tid : Double; Cmd,Addr,Port : string;CmdType : byte);
var
    ListItem : TListItem;
begin
    ListItem:=ListViewCmd.Items.Add;
  ListItem.Caption:=FloatToStr(Tid);
  ListItem.SubItems.Add(Cmd);
    ListItem.SubItems.Add(Addr);
  ListItem.SubItems.Add(Port);
    ListItem.SubItems.Add(IntToStr(CmdType));
end;

Caption i listviewen indeholder tiden for næste opgave og alle opgaverne sorteres så i caption (tids) orden dvs. at den tid der kommer først ligger øverst.

Hvis det skal være enkelt kunne du evt. bruge en simpel TStringList der indeholder:
Tid=opgave,noget andet,blabla

Tid skal så være i et format således at når TStrngListen sorteres ved at sættes sorted=true så bliver rækkefølgen rigtig, det må være noget med yyyy-mm-dd hh:mm:ss
for de opgaver der skal udføres for hver 5 minutter er det så bare ligge 5 minutter til tiden så vil den automatisk ryge ned i listen.
Man kunne evt. også lave et dynamisk array af records, eller lave en speciel klasse til det, men TStringList er hurtig og enkelt.
Avatar billede safl Nybegynder
05. februar 2006 - 16:46 #12
kroning> Har du nogen håndtering af hvis 2 opgaver f.eks. bliver ekserkveret på samme tid?
Avatar billede kroning Nybegynder
05. februar 2006 - 17:26 #13
Så er det tilfældig hvilken en der bliver udført først, men du kan evt. ligge en priotering ind efter tiden f.eks. yyyy-mm-dd hh:ss:mm:p hvor p er priotering f.eks. 1-9.
Men måske ville det være bedre at bruge en ListView så kan du følge med i hvad der sker og du kan oprette felter til de enkelt punkter tid, priotering, opgave osv. Og den kan så bare sættes til visible=false og flyttes uden for formen når programmet er færdig.
Avatar billede safl Nybegynder
05. februar 2006 - 17:30 #14
Hmm ja det er selvfølgelig en mulighed.
Jeg har dog nu valgt at benytte en timer på 1 minut og lave de her konstante ineffektive checks efter opgaver. Det er nemt og temmelig fleksibelt og kan implementeres hurtigt i det nuværende program.
I fremtiden vil jeg nok skrive det om og tænke på denne lille diskussion om måder at implementere det på. Jeg takker for hjælpen.
Skriv et svar kroning. Så får du pointene.
Avatar billede kroning Nybegynder
05. februar 2006 - 17:49 #15
jow, nu er det jo søndag så hvis du venter lidt så kan det være andre har nogle bedre forslag.
Avatar billede safl Nybegynder
05. februar 2006 - 17:53 #16
Jeg vil lade den stå i en 3 dage, og alternative løsnings forslag modtages med kyshånd.
Indlægget her handler aller mest om design af løsning og ikke så meget om implementationen.
Så alt hvad i delphi mennesker derude har af erfaringer må i hjertens gerne dele.
Avatar billede kroning Nybegynder
05. februar 2006 - 21:27 #17
Jeg kunne ikke lade være med at rode lidt med at lave en klasse, det kan sikkert laves bedre. Der mangler stadig en del bla. er der ikke check for om den tid man sætter er før aktuel tid og man kunne også lave mulighed for at kunne sætte en Antal værdi dvs. hvor mange gange opgaven skal udføres eller en Repeat værdi således at en opgave bliver udført hver dag på samme tidspunkt eller for hver f.eks. 10 minutter.
Men kik på det det kan måske give dig nogle ideer:

Den nederste kode "Button1Click" opretter 3 opgaver der skal aktiveres om 10,20 og 30 sekunder. Som du kan se er det ganske enkelt at oprette en opgave blot ved at kalde Opgaver.Add(Tid1,OpgaveBeep1); så kaldes proceduren OpgaveBeep1 på det angivne tidspunkt.

-----------------------------
unit OpgaverClassUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
    TOpgaveProcedure = procedure of object;

  TOpgave = class(TCollectionItem)
  private
      pTid : TDateTime;
    pOpgaveProcedure : TOpgaveProcedure;
  protected
  public
      destructor Destroy; override;
    constructor Create(Collection: TCollection); override;
  end;

  TOpgaver = class(TOwnedCollection)
  private
      OpgaveTimer : TTimer;
    NextOpgave : TOpgave;

    procedure SetTimer;
    procedure OpgaveTimerTimer(Sender: TObject);
  protected
    function GetItem(Index: Integer): TOpgave;
    procedure SetItem(Index: Integer; const Value: TOpgave);
  public
    function Add(Tid : TDateTime; OpgaveProcedure : TOpgaveProcedure): TOpgave; reintroduce;
        constructor Create;
    destructor Destroy; override;

        property Items[Index: Integer]: TOpgave read GetItem write SetItem; default;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure OpgaveBeep1;
    procedure OpgaveBeep2;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Opgaver : TOpgaver;

implementation

{$R *.dfm}

constructor TOpgave.Create(Collection: TCollection);
begin
  inherited Create(Collection);
end;

destructor TOpgave.Destroy;
begin
  inherited Destroy;
end;

//********************

constructor TOpgaver.Create;
begin
    inherited Create(nil, TOpgave);
  OpgaveTimer:=TTimer.Create(nil);
  OpgaveTimer.OnTimer:=OpgaveTimerTimer;
    OpgaveTimer.Enabled:=false;
end;

destructor TOpgaver.Destroy;
begin
  inherited Destroy;
end;

procedure TOpgaver.OpgaveTimerTimer(Sender: TObject);
var
        TempOpgaveProcedure : TOpgaveProcedure;
begin
    OpgaveTimer.Enabled:=false;
  TempOpgaveProcedure:=NextOpgave.pOpgaveProcedure;

  // der sættes gang i timeren inden pOpgaveProcedure kaldes
    Delete(NextOpgave.Index);
    SetTimer;

    if Assigned(TempOpgaveProcedure) then
        TempOpgaveProcedure;
end;

function TOpgaver.GetItem(Index: Integer): TOpgave;
begin
  Result:=TOpgave(inherited Items[index]);
end;

procedure TOpgaver.SetItem(Index: Integer; const Value: TOpgave);
begin
  inherited SetItem(Index, Value);
end;

procedure TOpgaver.SetTimer;
var
    i : integer;
  NextTime : TDateTime;
begin
    OpgaveTimer.Enabled:=false;
    NextTime:=MaxDateTime;
    for i:=0 to Count-1 do
      if Items[i].pTid<NextTime then
    begin
        NextOpgave:=Items[i];
        NextTime:=Items[i].pTid;
    end;

    if NextTime=MaxDateTime then
      exit; // ingen opgaver
    OpgaveTimer.Interval:=round((NextTime-Now)*86400*1000);
  OpgaveTimer.Enabled:=true;
end;

function TOpgaver.Add(Tid : TDateTime; OpgaveProcedure : TOpgaveProcedure): TOpgave;
begin
  Result:=Inherited Add as TOpgave;
    Result.pTid:=Tid;
    Result.pOpgaveProcedure:=OpgaveProcedure;
  SetTimer;
end;

//********************************

procedure TForm1.FormCreate(Sender: TObject);
begin
    Opgaver:=TOpgaver.Create;
end;

procedure TForm1.OpgaveBeep1;
begin
    Beep;
    showmessage('10 sekunder opgaven');
end;

procedure TForm1.OpgaveBeep2;
begin
    Beep;
    showmessage('20 og 30 sekunder opgaven');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
    Tid1,Tid2,Tid3 : TDateTime;
begin
    Tid1:=Now+( (1/86400)*10); // 10 sekunder
  Tid2:=Now+( (1/86400)*20); // 20 sekunder
  Tid3:=Now+( (1/86400)*30); // 30 sekunder

    Opgaver.Add(Tid1,OpgaveBeep1); // 10 sekunder
  Opgaver.Add(Tid3,OpgaveBeep2); // 30 sekunder
  Opgaver.Add(Tid2,OpgaveBeep2); // 20 sekunder
end;

end.
Avatar billede safl Nybegynder
05. februar 2006 - 22:32 #18
kroning> Det ser jo spændende ud.

Det kunne egentlig være rigtig fedt at lave en meget simplificeret udgave af unix cron.
Altså mulighed for at oprette en liste af periodiske gentagne opgaver.
Der kan gentage efter formen:

#minute hour  mday  month wday
*/5 */2-4 * */0-2 *

Hvor * siger "hver" altså hver minut, time osv. og et / efter * betyder hver time 5 minutter over. Og et interval som 2-4 betyder hver time i timerne fra 2 til 4.
Og en fast værdi som 0 istedet for stjerne ville hver gang minut, time osv. blev præcis det.
Avatar billede hugopedersen Nybegynder
03. marts 2008 - 11:57 #19
kroning>  jeg sidder og roder med noget schedulering i Delphi (er ikke nogen ørn til det - er Access programmør) og falder så lige over dit indlæg her som ser ud til at være noget af det jeg kunne bruge.
Har du evt. videreudviklet på det så vil jeg da meget gerne se det :-)

Jeg har 2 ting jeg gerne vil kunne gøre som jeg ikke umiddelbart kan se om din kode kan håndtere.
1.  Jeg vil gerne på en eller anden måde visualisere hvilke opgaver der er scheduleret (og evt. hvor lang tid der er til de starter)
2.  Jeg har brug for at kunne genindlæse listen hvis der skal ændres noget. Jeg lægger tidspunkterne ind i en .ini fil og læser der fra ugedag og tid.

Bær over med mig hvis mine spørgsmål virker stupide :-)
Avatar billede kroning Nybegynder
03. marts 2008 - 12:34 #20
Nej det er ikke noget jeg har arbejdet videre med.
Men prøv at oprette en ny tråd så andre også kan komme med input, og lav evt. et link til denne tråd.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester