C++ Builder XE4, 10.2 Tokyo > UDP > 遠隔地PCから問い合わせがあった時にフォーム上のTLabel Captionを返信する v0.1,v0.2

```txt:動作環境
C++ Builder XE4

RAD Studio 10.2 Tokyo Update 2 (追記: 2017/12/28)
   v0.1を動作確認した
```

## 実装内容

- UDPでTLabelのコンポーネント名を受信する
- 取得したコンポーネント名を元にTLabelコンポーネントを探す
- 見つかったコンポーネントのCaption情報を返信する

## v0.1

### 実装

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

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <IdBaseComponent.hpp>
#include <IdComponent.hpp>
#include <IdSocketHandle.hpp>
#include <IdUDPBase.hpp>
#include <IdUDPServer.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TLabel *Label1;
	TIdUDPServer *IdUDPServer1;
	TMemo *Memo1;
	TLabel *L_myIP;
	void __fastcall IdUDPServer1UDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData,
          TIdSocketHandle *ABinding);
	void __fastcall FormShow(TObject *Sender);
private:	// ユーザー宣言
	String __fastcall getLabelCaption(String componentName);
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)
{
	IdUDPServer1->DefaultPort = 7000;
	IdUDPServer1->Active = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::IdUDPServer1UDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData,
		  TIdSocketHandle *ABinding)
{
	// 1. Receive command (Component name)
	String rcvdStr;
	Idglobal::_di_IIdTextEncoding encSJIS;
	encSJIS = IndyTextEncoding(932);
	int init_pos = 0;
	rcvdStr = encSJIS->GetString(AData, init_pos, AData.Length);

	// for debug
	Memo1->Lines->Add(L"rcvd:" + rcvdStr);

	// 2. return Caption of the component
	String peerIP = ABinding->PeerIP;
	int peerPort = ABinding->PeerPort;
	String retStr = getLabelCaption(/* componentName=*/rcvdStr);
	IdUDPServer1->Send(peerIP, peerPort, retStr, encSJIS);
}
//---------------------------------------------------------------------------

String __fastcall TForm1::getLabelCaption(String componentName)
{
	TLabel *lblPtr = (TLabel *)FindComponent(componentName);
	if (lblPtr == NULL) {
		return L"";
	}

	return lblPtr->Caption;
}

void __fastcall TForm1::FormShow(TObject *Sender)
{
	Label1->Caption = L"From Label1";

	// show IP address
	TIdStack::IncUsage();
	L_myIP->Caption = GStack->LocalAddress;
	TIdStack::DecUsage();
}
//---------------------------------------------------------------------------
```

### 実行例

`L_myIP`というコマンドの応答

```
192.168.0.31
```

`Label1`というコマンドの応答

```
From Label1
```

任意のTLabelコンポーネントに対してCaptionを取得できる。


## v0.2 > STX, ETX, BCC追加

### 変更点

- 応答文字列にコマンド文字列(と",")を追加
- `<STX>`,`<ETX>`でペイロードを囲み、`<ETX>`の後に`<BCC>`を付けるようにした。
  - `<BCC>`の計算は`ペイロード+<ETX>`をunsigned charで総和とったもの

### 実装

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

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <IdBaseComponent.hpp>
#include <IdComponent.hpp>
#include <IdSocketHandle.hpp>
#include <IdUDPBase.hpp>
#include <IdUDPServer.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TLabel *Label1;
	TIdUDPServer *IdUDPServer1;
	TMemo *Memo1;
	TLabel *L_myIP;
	void __fastcall IdUDPServer1UDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData,
          TIdSocketHandle *ABinding);
	void __fastcall FormShow(TObject *Sender);
private:	// ユーザー宣言
	String __fastcall getLabelCaption(String componentName);
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;
//---------------------------------------------------------------------------


/*
v0.2 2017/02/21
	- add getWrappedString()
	- add calc_bcc()
v0.1 2017/02/21
	- add IdUDPServer1UDPRead()
	- add getLabelCaption()
	- add FormShow()
*/


__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
	IdUDPServer1->DefaultPort = 7000;
	IdUDPServer1->Active = true;
}
//---------------------------------------------------------------------------

static unsigned char calc_bcc(char *srcPtr, int len)
{
	// srcPtr: <STX>ペイロード<ETX>の文字列
	// ペイロードと<ETX>から<BCC>を計算する
	//

	unsigned char bcc = 0;

	for(int idx=0; idx<len; idx++) {
		if (idx==0) {
			continue; // <STX>はスキップする
		}
		bcc += srcPtr[idx];
	}

	return bcc;
}

static AnsiString getWrappedString(AnsiString srcStr)
{
	AnsiString retStr = srcStr;
	char szbuf[50] = {0};
	szbuf[0] = 0x02; // STX
	strncpy(&szbuf[1], srcStr.c_str(), srcStr.Length());
	int last = srcStr.Length();
	szbuf[++last] = 0x03; // ETX

	unsigned char bcc = calc_bcc(szbuf, strlen(szbuf));
	szbuf[++last] = bcc;

	return AnsiString(szbuf);
}

void __fastcall TForm1::IdUDPServer1UDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData,
		  TIdSocketHandle *ABinding)
{
	// 1. Receive command (Component name)
	AnsiString rcvdStr;
	Idglobal::_di_IIdTextEncoding encSJIS;
	encSJIS = IndyTextEncoding(932);
	int init_pos = 0;
	rcvdStr = encSJIS->GetString(AData, init_pos, AData.Length);

	// for debug
	Memo1->Lines->Add(L"rcvd:" + rcvdStr);

	// 2. return Caption of the component
	AnsiString peerIP = ABinding->PeerIP;
	int peerPort = ABinding->PeerPort;
	AnsiString capStr = getLabelCaption(/* componentName=*/rcvdStr);
	AnsiString wrpStr = getWrappedString(rcvdStr + L"," + capStr);
	IdUDPServer1->Send(peerIP, peerPort, wrpStr, encSJIS);
}
//---------------------------------------------------------------------------

String __fastcall TForm1::getLabelCaption(String componentName)
{
	TLabel *lblPtr = (TLabel *)FindComponent(componentName);
	if (lblPtr == NULL) {
		return L"";
	}

	return lblPtr->Caption;
}

void __fastcall TForm1::FormShow(TObject *Sender)
{
	Label1->Caption = L"From Label1";

	// show IP address
	TIdStack::IncUsage();
	L_myIP->Caption = GStack->LocalAddress;
	TIdStack::DecUsage();
}
//---------------------------------------------------------------------------
```

### 動作確認

NonSoftさんの「UDP/IPテストツール」を使わせていただきました。
http://nonsoft.la.coocan.jp/Download/UdpTool/index.html

このツールは、ASCII文字列に加えて、`<STX>`などの文字を"`<STX>`"として表示してくれます。
このため、上記のような通信の確認がしやすくなります。


```txt:結果
送-> 192.168.0.31 (7000 )L_myIP
->受 192.168.0.31 (7000 )<STX>L_myIP,192.168.0.31<ETX><b2>
送-> 192.168.0.31 (7000 )L_myIP
->受 192.168.0.31 (7000 )<STX>L_myIP,192.168.0.31<ETX><b2>
```