c++ builder XE4, 10.2 Tokyo > TTimer > N回リトライする

```txt:動作確認
C++ Builder XE4
RAD Studio 10.2 Tokyo Update 2 (追記: 2017/12/27)
```

## 動作仕様

- ある指定のインターバルごとに処理をする
- 最大N回のリトライをする
- 成功した時点で処理をやめる

なんらかのインターバル処理が必要となる。

スレッド実装か、TTimer実装など考えられる。

## code v0.1

TTimer実装した。

```Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TButton *btnStart;
	TCheckBox *chkOK;
	TTimer *Timer1;
	TMemo *Memo1;
	void __fastcall btnStartClick(TObject *Sender);
	void __fastcall Timer1Timer(TObject *Sender);
private:	// ユーザー宣言
public:		// ユーザー宣言
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
```

```Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------

static int s_index;
static const int kMaxRetry = 6;

void __fastcall TForm1::btnStartClick(TObject *Sender)
{
	s_index = 0;
	Memo1->Clear();

	Timer1->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
	String msg = DateTimeToStr(Now());
	Memo1->Lines->Add(msg);

	s_index++;


	if (chkOK->Checked || s_index >= kMaxRetry) {
		Timer1->Enabled = false;
		Memo1->Lines->Add(L"Stop");
	}
}
//---------------------------------------------------------------------------
```

## 結果

例として、Timer1->Interval = 5000; // msecとした。


### 最大回数リトライ

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/c8777e3a-2380-b009-4684-5c401fa9c7e6.png)

### 途中で成功した場合

chkOKをチェックして、途中で成功させた場合。

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/15313128-8014-6cb9-b045-3a06cea54865.png)


## 備考

TTimer->Enabled=true;してから最初の1回が開始されるまで`インターバルmsec`待つことになる。
TTimer->Enabled=true;した時点で処理をしたい場合は、別途処理をコールすることになる。

上記の例ではchkOKによる処理の成功判別としたが、TTimer内の処理自体の成功を判別するようにして使うのを想定している。

TTimer内の処理を長くする設計はまずいので、通常はTTimer内はフラグを立てるだけにしておいて、そのフラグが立っていることを判別して処理を始める、というような使い方も検討するのがいい。組込み系の割込み処理と同じ考え。

## code v0.2

処理の成功判断を別クラスから通知することを検討して以下のように変更した。

```Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------

static int s_index;
static const int kMaxRetry = 6;

void __fastcall TForm1::btnStartClick(TObject *Sender)
{
	s_index = 0;
	Memo1->Clear();

	chkOK->Checked = false;
	Timer1->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
	if (chkOK->Checked == false) {
		String msg = DateTimeToStr(Now());
		Memo1->Lines->Add(msg);
	}

	s_index++;

	if (chkOK->Checked || s_index >= kMaxRetry) {
		Timer1->Enabled = false;
		Memo1->Lines->Add(L"Stop");
	}
}
//---------------------------------------------------------------------------
```

以下の二通りの方法がある。

1. chkOK->Checkedを外部クラスからtrueにする
2. Timer1->Enabledを外部クラスからfalseにする

1の場合は、処理成功後に1回Timer1Timer()がコールされる分、余分である。