Pytanie dotyczące usprawnienia (pod Siebie) timeru.

0

Witam.

Potrzebuję pomocy w użyciu timera.
Wiem, że gdy interval timeru ustawię na np. 1000, to dana funkcja będzie się powtarzała co sekundę.
Mi zależy na tym, by timer czytał mi określoną wartość z prędkością dużo szybszą, jednak gdy ona będzie dla mnie nie odpowiednia żeby wykonywał daną funkcję, którą będzie mógł powtórzyć najwcześniej za sekundę.
Chodzi mi o cyferki, które się bardzo szybko zmieniają.

Postaram się to bardziej opisać na przykładzie:

if STRTOINT(LABEL1.CAPTION)  <= SpinEdit1.value then
begin
JAKASFUNKCJA();
end;
end;

Niby spoko było by zastosować funkcję sleep(), ale to niestety zacina cały program.

1

Mi zależy na tym, by timer czytał mi określoną wartość z prędkością dużo szybszą [...]

Co to znaczy "dużo szybszą"? Czyli jaką? Co ile milisekund? Dolną granicą jest 16ms - z większą częstotliwością standardowy timer nie może pracować. Sleep też nie umożliwia krótszego zatrzymania programu.

[...] jednak gdy ona będzie dla mnie nie odpowiednia żeby wykonywał daną funkcję, którą będzie mógł powtórzyć najwcześniej za sekundę.

Ta część nie ma sensu - gdy nie będzie odpowiednia to nie napisałeś co ma się stać.

Niby spoko było by zastosować funkcję sleep(), ale to niestety zacina cały program.

Nie będzie zamrażać interfejsu, jeśli użyjesz jej w wątku pobocznym.


Nic z tego opisu nie wiadomo, bo nie wiadomo co chcesz zrobić. Zamiast opisać to co jest problemem, Ty opisujesz ułomność timera czy funkcji Sleep. Napisz dokładnie co chcesz zrobić, a wtedy wybierze się odpowiednie rozwiązanie.

Poza tym wyłącz CapsLock - słowa kluczowe piszemy wyłącznie małymi literami, a identyfikatory zmiennych, właściwości, funkcji, metod itd. piszemy w stylu PascalCase.

0

Chcę pobierać dane z zewnętrznego programu i zapisywać je załóżmy do label1.caption. Byłby to numer.
Mam takową funkcję zapisaną w timerze1. Te liczby mogą być różne ( od zera do 100 tys). Częstotliwość timera zapisującego te liczby do labelu jest mała, powiedzmy 20 ms.

W drugim timerze chciałym sprawdzać czy liczba z labelu pierwszego jest mniejsza niż spinedit1.
Jeżeli zdaży się, że liczba będzie mniejsza (może się to zdarzyć nawet kilka razy w ciągu sekundy)
to chcę, by została wykorzystana funkcja, którą Sobie wcześniej zapisałem i żeby funkcję ponownie powtórzył co najwcześniej za sekundę jeżeli liczba znowu będzie mniejsza niż ta ze spinedit.

0

Czy ten zewnętrzny program jest Twój? Bo jeśli nie to dochodzi jeszcze problem pozyskania danych, czyli ich odczytu w oknie innego procesu - a to też może nie być trywialne.

0

W sumie najlepiej, jak pokażę screen tego co chcę osiągnąć:

screen.png

Pierwsze, tam gdzie jest "delay action this time" ma być wykorzystywane niemal automatycznie wtedy, gdy ta liczba będzie mniejsza.
Drugie to czas jaki ma czekać na powtórzenie tej funkcji jeżeli ta liczba znowu będzie mniejsza.

Można to wpierniczyć w timera jak napisałem, ale wtedy nie będzie to tak dokładne, tylko za każdym razem będzie delay.

0

Musisz zrozumieć że akcje podpięte pod Timery wykonują się w wątku głównym aplikacji i procedura sleep zawarta w timerze zawsze zamrozi ci na chwilę apkę Nie wchodzę w szczegóły, ale może rozwiązaniem mogło by być przeniesienie akcji Timerów do oddzielnych wątków. Ale wtedy pojawia się problem synchronizacji dostępu przez wątki do współdzielonych zasobów , czyli np.
użycie muteków czy sekcji krytycznych.
Jeśli bezkonfliktowe współdzielenie dostępu do wspólnych zasobów zamyka się w obrębie jednej aplikacji, a tak chyba jest w Twoim problemie, to wystarczą sekcje krytyczne. Nie mam pewności co będzie jeśli współdzielone zasoby są typami prostymi, atomowo przetwarzanymi przez CPU jak np. int czy boolean. Czy ich współdzielenie może być realizowane sprzętowo na poziome CPU i wtedy niepotrzebne są sowftarowe mechanizmy synchronizacji dostępu, czy jednak trzeba się posiłkować metodami programowymi ?

0

@grzegorz_so: z typami prostymi nie ma problemu, bo ich odczyt lub modyfikacja nie musi być synchronizowana. Poza tym, coś zbyt pesymistycznie podchodzisz do tego problemu :]

Z tego co widzę na zrzucie, dane przechowywane są w polu typu TEdit, więc za pomocą funkcji Win32 API można się do nich bez problemu dobrać. Wystarczy znaleźć uchwyt okna, a następnie uchwyt kontrolki. Jeśli dobrze pamiętam, potem pozostanie jedynie obsługa komunikatu WM_GETTEXT w celu pobrania tekstu.

Oczywiście można do tego wykorzystać standardowy timer, ale w nieco niestandardowy sposób. Przyda się ustawić mu Interval na 1, aby uzyskać zawsze minimum - czyli mniej więcej 16ms. W zdarzeniu OnTimer pobrać tekst z tego cudzego programu, wykonać swój kod. Aby timer poczekał sekundę i przy tym nie zamroził aplikacji, zdarzenie OnTimer musi być wykonywane cyklicznie, bez opóźnień. Czyli trzeba sprawić, aby to zdarzenie nadal wywoływane było co te 16ms, ale żeby przez ~60 wywołań (co da mniej więcej sekundę) żaden kod nie był wykonywany.

Mały przyklad:

const
  WAIT_MS = 1000;

type
  TMainForm = class(TForm)
  {..}
  private
    FTimer: TTimer;
    FWait: Boolean;
    FWaitTo: Integer;
  {..}
  end;
  
constructor TMainForm.FormCreate(Sender: TObject);
begin
  FWait := False;
end;

procedure TMainForm.FTimerTimer(Sender: TObject);
var
  LTime, LValue: Integer;
begin
  if FWait then
    FWait := GetTickCount() <= FWaitTo;

  if not FWait then
  begin
    LValue := GetValueFromExternalApp();
    
    if LValue < SpinEdit.Value then
    begin
      DoMyStuff();
      
      FWait := True;
      FWaitTo := GetTickCount() + WAIT_MS;
    end;
  end;
end;

FWait to zmienna logiczna, określająca czy timer ma oczekiwać, czy może działać normalnie. FWaitTo zawiera licznik ticków, do którego timer ma nic nie robić. WAIT_MS zawiera liczbę milisekund oczekiwania timera.

I teraz zdarzenie OnTimer. Najpierw sprawdzane jest czy timer ma oczekiwać - domyślnie pole FWait zawiera False, więc timer nie znajduje się w stanie oczekiwania. Następnie sprawdzane jest czy timer oczekuje - jeśli tak to dalszy kod jest pomijany. Jeśli nie oczekuje to pobierana jest liczba z zewnętrznego programu, po czym jest porównywana z wartością ze SpinEdit. Jeśli jest mniejsza to wykonywany jest odpowiedni kod (w miejscu hipotetycznej metody DoMyStuff), a na koniec zostaje ustawiona flaga oczekiwania oraz stan licznika ticków na bieżący plus określona liczba milisekund.

W kolejnych wywołaniach tego zdarzenia, sprawdzane będzie czy timer ma czekać czy nie. Jeśli timer znajduje się w stanie oczekiwania to sprawdzane jest, czy już upłynął ten czas i jeśli tak - flaga zostaje ustawiona na False. A jeśli nie upłynął to nadal zawiera wartość True, dzięki czemu drugi warunek nie zostanie spełniony i timer nic więcej nie wykona.

Pisane z głowy, ale powinno działać.

0

Bardzo dziękuję furious programming.
To było własnie to, czego potrzebowałem.
Problem rozwiązany.

1 użytkowników online, w tym zalogowanych: 0, gości: 1