-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.cpp
More file actions
261 lines (233 loc) · 10.1 KB
/
app.cpp
File metadata and controls
261 lines (233 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#include "app.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h> // ソケット関連の関数を使うために必要
#include <sys/un.h> // UNIXドメインソケット用のヘッダ
#include <sys/stat.h>
#include <Light.h>
#include "spikeapi.h"
using namespace spikeapi;
// ==========================================
// ソケット通信用の定義
// ==========================================
// UNIXドメインソケット:同じマシン上のプロセス間通信に使う通信方式
// ファイルシステムのパスで識別されるため、異なるプロセス同士が通信できる
#define QR_SOCKET_PATH "/tmp/qr_socket" // QRサーバーの所在地(ソケットファイルのパス)
#define BUFFER_SIZE 1024 // 1回の通信で受け取るデータの最大サイズ
#define HANDSHAKE_MSG "ASP_QR_CLIENT" // このプロセスのハンドシェイク送信メッセージ
#define HANDSHAKE_ACK "QR_SERVER" // 期待する応答メッセージ
// ==========================================
// ハンドシェイク:プロセス間通信の初期化処理
// 2つのプロセスが正しく通信できることを確認する儀式のようなもの
// ==========================================
/**
* QRサーバーへの接続を試みる
*
* 説明:
* UNIXドメインソケットを作成して、QRサーバーに接続します。
* サーバーがまだ起動していない場合に備えて、3秒間にわたって
* リトライします(100msごとに10回試行)。
*
* ソケットの流れ:
* 1. socket() - ソケットを作成(電話を用意するようなもの)
* 2. connect() - サーバーに接続(番号を入力して相手に電話をかける)
*
* @return ソケットディスクリプタ(接続成功時は0以上、失敗時は-1)
* ディスクリプタ:リソースを管理するための整数ID
*/
int connect_to_qr_server() {
// ステップ1:ソケット作成
// AF_UNIX - UNIXドメインソケット(同じマシン上での通信)
// SOCK_STREAM - ストリーム型(確実な順序で受信できる通信)
// 0 - プロトコル(自動選択)
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket"); // エラー理由を表示
return -1;
}
// ステップ2:サーバーアドレスの設定
// UNIXドメインソケットではファイルパスがアドレスになる
struct sockaddr_un server_addr;
memset(&server_addr, 0, sizeof(struct sockaddr_un)); // メモリをクリア
server_addr.sun_family = AF_UNIX; // UNIXドメインソケット指定
strncpy(server_addr.sun_path, QR_SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
// ステップ3:サーバーへの接続を試みる(リトライロジック)
// サーバーがまだ起動していないことを想定し、複数回試行
for (int retry = 0; retry < 30; retry++) {
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un)) == 0) {
printf("Connected to QR server\n");
return sock; // 接続成功
}
if (retry < 29) {
usleep(100000); // 100ms(= 0.1秒)待機してから次を試す
}
}
printf("Failed to connect to QR server\n");
close(sock); // 接続失敗時はソケットを閉じる
return -1;
}
/**
* ハンドシェイクを実行する
*
* 説明:
* ASPプロセスとQRサーバーが正しく相互認識できることを確認します。
*
* 手順:
* 1. このプロセスが "ASP_QR_CLIENT" メッセージを送信
* 2. サーバーが "QR_SERVER" メッセージを返してくることを確認
* 3. 両者が正しく応答したら通信を開始
*
* @param sock ソケットディスクリプタ
* @return 成功時は1、失敗時は0
*/
int perform_handshake(int sock) {
// ステップ1:ハンドシェイクメッセージ送信
// send() - データをソケットを通じて送信
// strlen() - 文字列の長さを計算
if (send(sock, HANDSHAKE_MSG, strlen(HANDSHAKE_MSG), 0) < 0) {
perror("send handshake");
return 0;
}
printf("Sent handshake message\n");
// ステップ2:ハンドシェイク応答受信
// recv() - ソケットからデータを受信
// BUFFER_SIZE - 最大何バイト受け取るか
char buffer[BUFFER_SIZE];
int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (n < 0) {
perror("recv handshake");
return 0;
}
buffer[n] = '\0'; // 文字列の終端を明示的に設定(重要!)
// ステップ3:応答メッセージが期待値と一致するか確認
if (strcmp(buffer, HANDSHAKE_ACK) == 0) {
printf("Handshake successful\n");
return 1;
}
printf("Invalid handshake response: %s\n", buffer);
return 0;
}
/**
* 色文字列をLight::Colorに変換する
*
* 説明:
* QRコードから読み取った"Red"や"Green"などの文字列に対応する
* Light::Colorの値に変換します。テキストデータを列挙型に変換する
* 典型的な"パーサ"と呼ばれる処理です。
*
* 対応する色:
* - Red / GREEN / Blue / Yellow / Orange / White / Black
* - 大文字小文字は区別しません
*
* @param color_str 色文字列("Red", "Green", "Blue"など)
* @return Light::Color列挙値
*/
Light::EColor parse_color(const char *color_str) {
// strncmp() - 文字列を比較・マッチングさせる
// 複数の色パターン(大文字小文字両方)に対応
if (strncmp(color_str, "Red", 3) == 0 || strncmp(color_str, "RED", 3) == 0) {
return Light::EColor::RED;
} else if (strncmp(color_str, "Green", 5) == 0 || strncmp(color_str, "GREEN", 5) == 0) {
return Light::EColor::GREEN;
} else if (strncmp(color_str, "Blue", 4) == 0 || strncmp(color_str, "BLUE", 4) == 0) {
return Light::EColor::BLUE;
} else if (strncmp(color_str, "Yellow", 6) == 0 || strncmp(color_str, "YELLOW", 6) == 0) {
return Light::EColor::YELLOW;
} else if (strncmp(color_str, "Orange", 6) == 0 || strncmp(color_str, "ORANGE", 6) == 0) {
return Light::EColor::ORANGE;
} else if (strncmp(color_str, "White", 5) == 0 || strncmp(color_str, "WHITE", 5) == 0) {
return Light::EColor::WHITE;
} else if (strncmp(color_str, "Black", 5) == 0 || strncmp(color_str, "BLACK", 5) == 0) {
return Light::EColor::BLACK;
}
// 文字列が該当しない場合はデフォルトでWHITEを返す
return Light::EColor::WHITE;
}
/**
* メインタスク - ASP内で実行されるメイン処理
*
* 説明:
* このタスクはASP(リアルタイムOS)の中で実行されます。
* QRサーバーにアクセスして、色データを受け取り、
* LED(Light)の色を制御する処理の中心です。
*
* 処理手順:
* 1. Lightクラスのインスタンス生成(LED制御準備)
* 2. QRサーバーに接続
* 3. ハンドシェイク実行
* 4. メインループ:
* - QRコード(色文字列)を受信
* - 前回と異なる色なら、LightクラスのsetColorで変更
* - 100ms待機してから繰り返し
*/
void main_task(intptr_t unused) {
printf("Start!\n");
// ==========================================
// ステップ1:LED制御オブジェクトの準備
// ==========================================
// Light spikeapi:RaSpikeで提供される、ロボットのLED光源を操作するクラス
Light light;
// ==========================================
// ステップ2:QRサーバーへの接続試行
// ==========================================
int sock = connect_to_qr_server();
if (sock < 0) {
printf("Failed to connect to QR server. Running without QR input.\n");
ext_tsk(); // タスク終了
return;
}
// ==========================================
// ステップ3:ハンドシェイク(初期設定)実行
// ==========================================
if (!perform_handshake(sock)) {
printf("Handshake failed\n");
close(sock);
ext_tsk(); // タスク終了
return;
}
// ==========================================
// ステップ4:メインループ - QRコード受取と色変更
// ==========================================
// 前回受け取った色を記録(同じ色が連続する場合を検出するため)
char last_color[BUFFER_SIZE] = "";
char buffer[BUFFER_SIZE];
while (true) {
// QRコードプロセスからのデータを受信
// recv() - ブロッキング受け取り(データ到着まで待つ)
int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (n < 0) {
perror("recv");
break; // エラー時はループを抜ける
} else if (n == 0) {
printf("QR server disconnected\n");
break; // サーバー切断時はループを抜ける
}
// 受け取ったデータを文字列として終端を設定
buffer[n] = '\0';
printf("Received QR data: %s\n", buffer);
// 取得したQRコードが前回表示のものと異なるか確認
// strcmp() - 2つの文字列が同じなら0を返す
if (strcmp(last_color, buffer) != 0) {
// 新しい色なので、前回の色を更新
strncpy(last_color, buffer, sizeof(last_color) - 1);
last_color[sizeof(last_color) - 1] = '\0';
// 新しい色をLight::Colorに変換
Light::EColor color = parse_color(buffer);
// LED(Light)の色を変更
// setColor() - Light クラスのメンバ関数
light.turnOnColor(color);
printf("Color changed to: %s\n", buffer);
}
// CPU負荷軽減のため、100ms待機してから次のデータを受信
// dly_tsk() - ASP提供のマイクロ秒単位の遅延関数
// 100 * 1000 = 100,000マイクロ秒 = 100ms
dly_tsk(100 * 1000);
}
// ==========================================
// クリーンアップ
// ==========================================
close(sock); // ソケットを閉じる
ext_tsk(); // タスク終了
}