1#!/usr/bin/env python3
2"""
3ํด๋ผ์ฐ๋ IoT ํตํฉ - ์๋ฎฌ๋ ์ด์
๋ชจ๋
4AWS IoT Core ๋ฐ GCP Pub/Sub ์ฐ๋ ์๋ฎฌ๋ ์ด์
5
6์ค์ ํด๋ผ์ฐ๋ ๊ณ์ ๋ฐ ์๊ฒฉ ์ฆ๋ช
์์ด ๋์
7"""
8
9import json
10import time
11import threading
12import uuid
13from datetime import datetime
14from typing import Dict, List, Optional, Callable
15from dataclasses import dataclass, asdict
16from enum import Enum
17import queue
18import random
19
20
21# ==============================================================================
22# ๋ฐ์ดํฐ ๋ชจ๋ธ
23# ==============================================================================
24
25class CloudProvider(Enum):
26 """ํด๋ผ์ฐ๋ ์ ๊ณต์"""
27 AWS_IOT = "aws_iot"
28 GCP_PUBSUB = "gcp_pubsub"
29 SIMULATION = "simulation"
30
31
32class MessageQoS(Enum):
33 """MQTT QoS ๋ ๋ฒจ"""
34 AT_MOST_ONCE = 0
35 AT_LEAST_ONCE = 1
36 EXACTLY_ONCE = 2
37
38
39@dataclass
40class IoTMessage:
41 """IoT ๋ฉ์์ง"""
42 topic: str
43 payload: Dict
44 timestamp: datetime
45 message_id: str
46 qos: MessageQoS = MessageQoS.AT_LEAST_ONCE
47
48 def to_json(self) -> str:
49 """JSON ์ง๋ ฌํ"""
50 data = {
51 "topic": self.topic,
52 "payload": self.payload,
53 "timestamp": self.timestamp.isoformat(),
54 "message_id": self.message_id,
55 "qos": self.qos.value
56 }
57 return json.dumps(data)
58
59
60@dataclass
61class DeviceInfo:
62 """๋๋ฐ์ด์ค ์ ๋ณด"""
63 device_id: str
64 device_type: str
65 location: str
66 firmware_version: str
67 registered_at: datetime
68
69
70@dataclass
71class TelemetryData:
72 """ํ
๋ ๋ฉํธ๋ฆฌ ๋ฐ์ดํฐ"""
73 device_id: str
74 temperature: float
75 humidity: float
76 pressure: float
77 battery_level: float
78 timestamp: datetime
79
80 def to_dict(self) -> Dict:
81 return {
82 "device_id": self.device_id,
83 "temperature": self.temperature,
84 "humidity": self.humidity,
85 "pressure": self.pressure,
86 "battery_level": self.battery_level,
87 "timestamp": self.timestamp.isoformat()
88 }
89
90
91# ==============================================================================
92# AWS IoT Core ์๋ฎฌ๋ ์ด์
93# ==============================================================================
94
95class SimulatedAWSIoTClient:
96 """AWS IoT Core ํด๋ผ์ด์ธํธ (์๋ฎฌ๋ ์ด์
)"""
97
98 def __init__(self, endpoint: str, cert_path: str, key_path: str,
99 ca_path: str, client_id: str):
100 self.endpoint = endpoint
101 self.cert_path = cert_path
102 self.key_path = key_path
103 self.ca_path = ca_path
104 self.client_id = client_id
105 self.connected = False
106 self.subscriptions: Dict[str, Callable] = {}
107 self.message_queue = queue.Queue()
108
109 print(f"[AWS IoT ์๋ฎฌ๋ ์ด์
] ํด๋ผ์ด์ธํธ ์์ฑ")
110 print(f" - ์๋ํฌ์ธํธ: {endpoint}")
111 print(f" - ํด๋ผ์ด์ธํธ ID: {client_id}")
112 print(f" - ์ธ์ฆ์: {cert_path}")
113
114 def connect(self):
115 """์ฐ๊ฒฐ (์๋ฎฌ๋ ์ด์
)"""
116 print(f"\n[AWS IoT ์๋ฎฌ๋ ์ด์
] ์ฐ๊ฒฐ ์ค...")
117 time.sleep(0.5) # ์ฐ๊ฒฐ ์ง์ฐ ์๋ฎฌ๋ ์ด์
118
119 # ์ธ์ฆ์ ๊ฒ์ฆ ์๋ฎฌ๋ ์ด์
120 print(" - TLS ํธ๋์
ฐ์ดํฌ...")
121 time.sleep(0.2)
122 print(" - ์ธ์ฆ์ ๊ฒ์ฆ...")
123 time.sleep(0.2)
124 print(" - MQTT ์ฐ๊ฒฐ...")
125 time.sleep(0.2)
126
127 self.connected = True
128 print("โ ์ฐ๊ฒฐ ์ฑ๊ณต!\n")
129
130 def disconnect(self):
131 """์ฐ๊ฒฐ ํด์ """
132 if self.connected:
133 print("[AWS IoT ์๋ฎฌ๋ ์ด์
] ์ฐ๊ฒฐ ํด์ ")
134 self.connected = False
135
136 def publish(self, topic: str, payload: Dict, qos: MessageQoS = MessageQoS.AT_LEAST_ONCE):
137 """๋ฉ์์ง ๋ฐํ"""
138 if not self.connected:
139 raise RuntimeError("์ฐ๊ฒฐ๋์ง ์์")
140
141 message = IoTMessage(
142 topic=topic,
143 payload=payload,
144 timestamp=datetime.now(),
145 message_id=str(uuid.uuid4()),
146 qos=qos
147 )
148
149 # ๋ฐํ ์๋ฎฌ๋ ์ด์
150 print(f"[AWS IoT ๋ฐํ] {topic}")
151 print(f" ๋ฉ์์ง ID: {message.message_id[:8]}...")
152 print(f" QoS: {qos.name}")
153 print(f" ํ์ด๋ก๋: {json.dumps(payload, indent=2)}")
154
155 # ๋คํธ์ํฌ ์ง์ฐ ์๋ฎฌ๋ ์ด์
156 time.sleep(random.uniform(0.01, 0.05))
157
158 return message.message_id
159
160 def subscribe(self, topic: str, callback: Callable):
161 """ํ ํฝ ๊ตฌ๋
"""
162 if not self.connected:
163 raise RuntimeError("์ฐ๊ฒฐ๋์ง ์์")
164
165 self.subscriptions[topic] = callback
166 print(f"[AWS IoT ๊ตฌ๋
] {topic}")
167
168 def _simulate_incoming_message(self, topic: str, payload: Dict):
169 """์์ ๋ฉ์์ง ์๋ฎฌ๋ ์ด์
(๋ด๋ถ ํ
์คํธ์ฉ)"""
170 if topic in self.subscriptions:
171 callback = self.subscriptions[topic]
172 callback(topic, payload)
173
174
175class AWSIoTDeviceManager:
176 """AWS IoT ๋๋ฐ์ด์ค ๊ด๋ฆฌ์"""
177
178 def __init__(self, client: SimulatedAWSIoTClient):
179 self.client = client
180 self.device_info: Optional[DeviceInfo] = None
181
182 def register_device(self, device_info: DeviceInfo):
183 """๋๋ฐ์ด์ค ๋ฑ๋ก"""
184 self.device_info = device_info
185
186 print("\n[AWS IoT] ๋๋ฐ์ด์ค ๋ฑ๋ก")
187 print(f" - ID: {device_info.device_id}")
188 print(f" - ํ์
: {device_info.device_type}")
189 print(f" - ์์น: {device_info.location}")
190 print(f" - ํ์จ์ด: {device_info.firmware_version}")
191
192 # Thing ์์ฑ ์๋ฎฌ๋ ์ด์
193 print(" - Thing ์์ฑ ์ค...")
194 time.sleep(0.3)
195
196 # ์ธ์ฆ์ ์ฐ๊ฒฐ ์๋ฎฌ๋ ์ด์
197 print(" - ์ธ์ฆ์ ์ฐ๊ฒฐ ์ค...")
198 time.sleep(0.3)
199
200 # ์ ์ฑ
์ฐ๊ฒฐ ์๋ฎฌ๋ ์ด์
201 print(" - IoT ์ ์ฑ
์ฐ๊ฒฐ ์ค...")
202 time.sleep(0.3)
203
204 print("โ ๋๋ฐ์ด์ค ๋ฑ๋ก ์๋ฃ\n")
205
206 def publish_telemetry(self, data: TelemetryData):
207 """ํ
๋ ๋ฉํธ๋ฆฌ ๋ฐํ"""
208 topic = f"device/{data.device_id}/telemetry"
209 payload = data.to_dict()
210
211 self.client.publish(topic, payload)
212
213 def update_device_shadow(self, state: Dict):
214 """Device Shadow ์
๋ฐ์ดํธ"""
215 if not self.device_info:
216 raise ValueError("๋๋ฐ์ด์ค ์ ๋ณด ์์")
217
218 topic = f"$aws/things/{self.device_info.device_id}/shadow/update"
219
220 shadow_payload = {
221 "state": {
222 "reported": state
223 },
224 "metadata": {
225 "reported": {
226 k: {"timestamp": int(time.time())}
227 for k in state.keys()
228 }
229 }
230 }
231
232 print(f"\n[AWS IoT] Device Shadow ์
๋ฐ์ดํธ")
233 print(f" ์ํ: {json.dumps(state, indent=2)}")
234
235 self.client.publish(topic, shadow_payload)
236
237
238# ==============================================================================
239# GCP Pub/Sub ์๋ฎฌ๋ ์ด์
240# ==============================================================================
241
242class SimulatedGCPPubSubPublisher:
243 """GCP Pub/Sub Publisher (์๋ฎฌ๋ ์ด์
)"""
244
245 def __init__(self, project_id: str, topic_id: str):
246 self.project_id = project_id
247 self.topic_id = topic_id
248 self.topic_path = f"projects/{project_id}/topics/{topic_id}"
249
250 print(f"[GCP Pub/Sub ์๋ฎฌ๋ ์ด์
] Publisher ์์ฑ")
251 print(f" - ํ๋ก์ ํธ: {project_id}")
252 print(f" - ํ ํฝ: {topic_id}")
253 print(f" - ๊ฒฝ๋ก: {self.topic_path}")
254
255 def publish(self, data: Dict, **attributes) -> str:
256 """๋ฉ์์ง ๋ฐํ"""
257 message_id = str(uuid.uuid4())
258
259 print(f"\n[GCP Pub/Sub ๋ฐํ]")
260 print(f" ํ ํฝ: {self.topic_id}")
261 print(f" ๋ฉ์์ง ID: {message_id[:8]}...")
262
263 if attributes:
264 print(f" ์์ฑ: {attributes}")
265
266 print(f" ๋ฐ์ดํฐ: {json.dumps(data, indent=2)}")
267
268 # ๋คํธ์ํฌ ์ง์ฐ ์๋ฎฌ๋ ์ด์
269 time.sleep(random.uniform(0.01, 0.05))
270
271 print(f"โ ๋ฐํ ์๋ฃ\n")
272 return message_id
273
274 def publish_batch(self, messages: List[Dict]) -> List[str]:
275 """๋ฐฐ์น ๋ฐํ"""
276 print(f"\n[GCP Pub/Sub ๋ฐฐ์น ๋ฐํ] {len(messages)}๊ฐ ๋ฉ์์ง")
277
278 message_ids = []
279 for i, data in enumerate(messages):
280 message_id = str(uuid.uuid4())
281 message_ids.append(message_id)
282 print(f" [{i+1}] {message_id[:8]}...")
283
284 time.sleep(random.uniform(0.05, 0.1))
285 print(f"โ ๋ฐฐ์น ๋ฐํ ์๋ฃ\n")
286
287 return message_ids
288
289
290class SimulatedGCPPubSubSubscriber:
291 """GCP Pub/Sub Subscriber (์๋ฎฌ๋ ์ด์
)"""
292
293 def __init__(self, project_id: str, subscription_id: str):
294 self.project_id = project_id
295 self.subscription_id = subscription_id
296 self.subscription_path = f"projects/{project_id}/subscriptions/{subscription_id}"
297 self.message_queue = queue.Queue()
298
299 print(f"[GCP Pub/Sub ์๋ฎฌ๋ ์ด์
] Subscriber ์์ฑ")
300 print(f" - ๊ตฌ๋
: {subscription_id}")
301 print(f" - ๊ฒฝ๋ก: {self.subscription_path}")
302
303 def pull(self, max_messages: int = 10) -> List[Dict]:
304 """๋ฉ์์ง ํ (๋๊ธฐ)"""
305 print(f"\n[GCP Pub/Sub Pull] ์ต๋ {max_messages}๊ฐ ๋ฉ์์ง")
306
307 messages = []
308
309 # ์๋ฎฌ๋ ์ด์
: ํ์์ ๋ฉ์์ง ๊ฐ์ ธ์ค๊ธฐ
310 for _ in range(min(max_messages, self.message_queue.qsize())):
311 try:
312 msg = self.message_queue.get_nowait()
313 messages.append(msg)
314 except queue.Empty:
315 break
316
317 print(f" ์์ : {len(messages)}๊ฐ ๋ฉ์์ง")
318
319 # ACK ์๋ฎฌ๋ ์ด์
320 if messages:
321 print(f" ACK ์ ์ก ์ค...")
322 time.sleep(0.1)
323
324 return messages
325
326 def subscribe(self, callback: Callable):
327 """์คํธ๋ฆฌ๋ฐ ๊ตฌ๋
(๋น๋๊ธฐ)"""
328 print(f"\n[GCP Pub/Sub] ์คํธ๋ฆฌ๋ฐ ๊ตฌ๋
์์")
329
330 def streaming_loop():
331 while True:
332 try:
333 msg = self.message_queue.get(timeout=1)
334 callback(msg, {})
335 except queue.Empty:
336 continue
337 except Exception as e:
338 print(f"๊ตฌ๋
์ค๋ฅ: {e}")
339 break
340
341 thread = threading.Thread(target=streaming_loop, daemon=True)
342 thread.start()
343
344 return thread
345
346
347# ==============================================================================
348# MQTT ๋ฉ์์ง ํฌ๋งท
349# ==============================================================================
350
351class MQTTMessageFormat:
352 """MQTT ๋ฉ์์ง ํฌ๋งท ํ์ค"""
353
354 @staticmethod
355 def create_telemetry(device_id: str, sensor_data: Dict) -> Dict:
356 """ํ
๋ ๋ฉํธ๋ฆฌ ๋ฉ์์ง ์์ฑ"""
357 return {
358 "device_id": device_id,
359 "message_type": "telemetry",
360 "data": sensor_data,
361 "timestamp": datetime.now().isoformat(),
362 "version": "1.0"
363 }
364
365 @staticmethod
366 def create_event(device_id: str, event_type: str, event_data: Dict) -> Dict:
367 """์ด๋ฒคํธ ๋ฉ์์ง ์์ฑ"""
368 return {
369 "device_id": device_id,
370 "message_type": "event",
371 "event_type": event_type,
372 "data": event_data,
373 "timestamp": datetime.now().isoformat(),
374 "version": "1.0"
375 }
376
377 @staticmethod
378 def create_command(device_id: str, command: str, parameters: Dict) -> Dict:
379 """๋ช
๋ น ๋ฉ์์ง ์์ฑ"""
380 return {
381 "device_id": device_id,
382 "message_type": "command",
383 "command": command,
384 "parameters": parameters,
385 "timestamp": datetime.now().isoformat(),
386 "command_id": str(uuid.uuid4()),
387 "version": "1.0"
388 }
389
390 @staticmethod
391 def create_response(device_id: str, command_id: str, status: str, result: Dict) -> Dict:
392 """๋ช
๋ น ์๋ต ๋ฉ์์ง ์์ฑ"""
393 return {
394 "device_id": device_id,
395 "message_type": "response",
396 "command_id": command_id,
397 "status": status, # "success", "error", "timeout"
398 "result": result,
399 "timestamp": datetime.now().isoformat(),
400 "version": "1.0"
401 }
402
403
404# ==============================================================================
405# ๋๋ฐ์ด์ค ํ๋ก๋น์ ๋
406# ==============================================================================
407
408class DeviceProvisioning:
409 """๋๋ฐ์ด์ค ํ๋ก๋น์ ๋ (์๋ฎฌ๋ ์ด์
)"""
410
411 def __init__(self, provider: CloudProvider):
412 self.provider = provider
413 self.provisioned_devices: Dict[str, DeviceInfo] = {}
414
415 print(f"\n[ํ๋ก๋น์ ๋] ์ด๊ธฐํ ({provider.value})")
416
417 def provision_device(self, device_id: str, device_type: str, location: str) -> DeviceInfo:
418 """๋๋ฐ์ด์ค ํ๋ก๋น์ ๋"""
419 print(f"\n[ํ๋ก๋น์ ๋] ๋๋ฐ์ด์ค ๋ฑ๋ก ์์")
420 print(f" - ID: {device_id}")
421 print(f" - ํ์
: {device_type}")
422 print(f" - ์์น: {location}")
423
424 # ๋จ๊ณ 1: ๋๋ฐ์ด์ค ์์ฑ
425 print("\n [1/4] ๋๋ฐ์ด์ค ์์ฑ ์ค...")
426 time.sleep(0.3)
427
428 # ๋จ๊ณ 2: ์ธ์ฆ์ ์์ฑ
429 print(" [2/4] ์ธ์ฆ์ ์์ฑ ์ค...")
430 cert_arn = f"arn:aws:iot:region:account:cert/{uuid.uuid4()}"
431 print(f" ์ธ์ฆ์ ARN: {cert_arn}")
432 time.sleep(0.3)
433
434 # ๋จ๊ณ 3: ์ ์ฑ
์ฐ๊ฒฐ
435 print(" [3/4] ์ ์ฑ
์ฐ๊ฒฐ ์ค...")
436 print(" ์ ์ฑ
: IoTDevicePolicy")
437 time.sleep(0.3)
438
439 # ๋จ๊ณ 4: Thing ์์ฑ ๋ฐ ์ฐ๊ฒฐ
440 print(" [4/4] Thing ์์ฑ ๋ฐ ์ฐ๊ฒฐ ์ค...")
441 time.sleep(0.3)
442
443 device_info = DeviceInfo(
444 device_id=device_id,
445 device_type=device_type,
446 location=location,
447 firmware_version="1.0.0",
448 registered_at=datetime.now()
449 )
450
451 self.provisioned_devices[device_id] = device_info
452
453 print("\nโ ํ๋ก๋น์ ๋ ์๋ฃ!")
454 print(f" ์ธ์ฆ์๊ฐ ์์ฑ๋์์ต๋๋ค: certs/{device_id}.cert.pem")
455 print(f" ๊ฐ์ธํค๊ฐ ์์ฑ๋์์ต๋๋ค: certs/{device_id}.private.key\n")
456
457 return device_info
458
459 def deprovision_device(self, device_id: str):
460 """๋๋ฐ์ด์ค ํด์ """
461 if device_id not in self.provisioned_devices:
462 raise ValueError(f"๋๋ฐ์ด์ค ์์: {device_id}")
463
464 print(f"\n[ํ๋ก๋น์ ๋] ๋๋ฐ์ด์ค ํด์ : {device_id}")
465
466 # ์ธ์ฆ์ ๋นํ์ฑํ
467 print(" - ์ธ์ฆ์ ๋นํ์ฑํ ์ค...")
468 time.sleep(0.2)
469
470 # Thing ์ญ์
471 print(" - Thing ์ญ์ ์ค...")
472 time.sleep(0.2)
473
474 del self.provisioned_devices[device_id]
475 print("โ ํด์ ์๋ฃ\n")
476
477
478# ==============================================================================
479# ํตํฉ IoT ํด๋ผ์ด์ธํธ
480# ==============================================================================
481
482class CloudIoTClient:
483 """ํตํฉ ํด๋ผ์ฐ๋ IoT ํด๋ผ์ด์ธํธ"""
484
485 def __init__(self, provider: CloudProvider, config: Dict):
486 self.provider = provider
487 self.config = config
488
489 print("\n" + "="*60)
490 print(f"ํด๋ผ์ฐ๋ IoT ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ({provider.value})")
491 print("="*60)
492
493 if provider == CloudProvider.AWS_IOT:
494 self.client = SimulatedAWSIoTClient(
495 endpoint=config.get('endpoint', 'simulated-endpoint.iot.region.amazonaws.com'),
496 cert_path=config.get('cert_path', 'certs/device.cert.pem'),
497 key_path=config.get('key_path', 'certs/device.private.key'),
498 ca_path=config.get('ca_path', 'certs/root-CA.crt'),
499 client_id=config.get('client_id', 'device-001')
500 )
501
502 elif provider == CloudProvider.GCP_PUBSUB:
503 self.publisher = SimulatedGCPPubSubPublisher(
504 project_id=config.get('project_id', 'my-iot-project'),
505 topic_id=config.get('topic_id', 'iot-telemetry')
506 )
507 self.subscriber = SimulatedGCPPubSubSubscriber(
508 project_id=config.get('project_id', 'my-iot-project'),
509 subscription_id=config.get('subscription_id', 'iot-telemetry-sub')
510 )
511
512 self.message_stats = {
513 "published": 0,
514 "received": 0,
515 "errors": 0
516 }
517
518 def connect(self):
519 """์ฐ๊ฒฐ"""
520 if self.provider == CloudProvider.AWS_IOT:
521 self.client.connect()
522
523 def disconnect(self):
524 """์ฐ๊ฒฐ ํด์ """
525 if self.provider == CloudProvider.AWS_IOT:
526 self.client.disconnect()
527
528 def publish_telemetry(self, device_id: str, sensor_data: Dict):
529 """ํ
๋ ๋ฉํธ๋ฆฌ ๋ฐํ"""
530 message = MQTTMessageFormat.create_telemetry(device_id, sensor_data)
531
532 if self.provider == CloudProvider.AWS_IOT:
533 topic = f"device/{device_id}/telemetry"
534 self.client.publish(topic, message)
535 elif self.provider == CloudProvider.GCP_PUBSUB:
536 self.publisher.publish(message, device_id=device_id, message_type="telemetry")
537
538 self.message_stats["published"] += 1
539
540 def subscribe_commands(self, device_id: str, callback: Callable):
541 """๋ช
๋ น ๊ตฌ๋
"""
542 def command_handler(topic: str, payload: Dict):
543 print(f"\n[๋ช
๋ น ์์ ] {topic}")
544 print(f" ๋ช
๋ น: {payload.get('command')}")
545 print(f" ํ๋ผ๋ฏธํฐ: {payload.get('parameters')}")
546
547 # ์ฝ๋ฐฑ ์คํ
548 callback(payload)
549
550 # ์๋ต ์ ์ก
551 response = MQTTMessageFormat.create_response(
552 device_id=device_id,
553 command_id=payload.get('command_id'),
554 status="success",
555 result={"executed": True}
556 )
557
558 response_topic = f"device/{device_id}/response"
559 self.client.publish(response_topic, response)
560
561 if self.provider == CloudProvider.AWS_IOT:
562 topic = f"device/{device_id}/command"
563 self.client.subscribe(topic, command_handler)
564
565 def get_statistics(self) -> Dict:
566 """ํต๊ณ ์กฐํ"""
567 return self.message_stats.copy()
568
569
570# ==============================================================================
571# ์ผ์ ๋ฐ์ดํฐ ์๋ฎฌ๋ ์ดํฐ
572# ==============================================================================
573
574class SensorSimulator:
575 """์ผ์ ๋ฐ์ดํฐ ์๋ฎฌ๋ ์ดํฐ"""
576
577 def __init__(self, device_id: str):
578 self.device_id = device_id
579 self.base_temp = 25.0
580 self.base_humidity = 60.0
581 self.base_pressure = 1013.25
582 self.battery_level = 100.0
583
584 def generate_reading(self) -> TelemetryData:
585 """์ผ์ ์ฝ๊ธฐ ์์ฑ"""
586 # ๋๋ค ๋ณ๋ ์ถ๊ฐ
587 temp = self.base_temp + random.uniform(-2, 2)
588 humidity = self.base_humidity + random.uniform(-5, 5)
589 pressure = self.base_pressure + random.uniform(-2, 2)
590
591 # ๋ฐฐํฐ๋ฆฌ ์๋ชจ
592 self.battery_level = max(0, self.battery_level - random.uniform(0.01, 0.05))
593
594 return TelemetryData(
595 device_id=self.device_id,
596 temperature=round(temp, 2),
597 humidity=round(humidity, 2),
598 pressure=round(pressure, 2),
599 battery_level=round(self.battery_level, 2),
600 timestamp=datetime.now()
601 )
602
603
604# ==============================================================================
605# ๋ฐ๋ชจ ์๋๋ฆฌ์ค
606# ==============================================================================
607
608def demo_aws_iot():
609 """AWS IoT Core ๋ฐ๋ชจ"""
610 print("\n" + "="*60)
611 print("AWS IoT Core ๋ฐ๋ชจ")
612 print("="*60)
613
614 # ํ๋ก๋น์ ๋
615 provisioning = DeviceProvisioning(CloudProvider.AWS_IOT)
616 device_info = provisioning.provision_device(
617 device_id="raspberry-pi-001",
618 device_type="sensor-hub",
619 location="Seoul, Korea"
620 )
621
622 # ํด๋ผ์ด์ธํธ ์์ฑ
623 config = {
624 'endpoint': 'a1b2c3d4e5f6g7.iot.ap-northeast-2.amazonaws.com',
625 'cert_path': f'certs/{device_info.device_id}.cert.pem',
626 'key_path': f'certs/{device_info.device_id}.private.key',
627 'ca_path': 'certs/AmazonRootCA1.pem',
628 'client_id': device_info.device_id
629 }
630
631 client = CloudIoTClient(CloudProvider.AWS_IOT, config)
632 client.connect()
633
634 # ๋๋ฐ์ด์ค ๊ด๋ฆฌ์
635 device_manager = AWSIoTDeviceManager(client.client)
636 device_manager.register_device(device_info)
637
638 # ์ผ์ ์๋ฎฌ๋ ์ดํฐ
639 sensor = SensorSimulator(device_info.device_id)
640
641 # ๋ช
๋ น ๊ตฌ๋
642 def on_command(payload: Dict):
643 command = payload.get('command')
644 print(f"\n๋ช
๋ น ์คํ: {command}")
645
646 client.subscribe_commands(device_info.device_id, on_command)
647
648 # ํ
๋ ๋ฉํธ๋ฆฌ ๋ฐํ
649 print("\n" + "-"*60)
650 print("ํ
๋ ๋ฉํธ๋ฆฌ ๋ฐํ ์์")
651 print("-"*60)
652
653 for i in range(5):
654 print(f"\n[{i+1}/5] ์ผ์ ๋ฐ์ดํฐ ๋ฐํ")
655
656 # ์ผ์ ์ฝ๊ธฐ
657 data = sensor.generate_reading()
658 print(f" ์จ๋: {data.temperature}ยฐC")
659 print(f" ์ต๋: {data.humidity}%")
660 print(f" ์๋ ฅ: {data.pressure} hPa")
661 print(f" ๋ฐฐํฐ๋ฆฌ: {data.battery_level}%")
662
663 # ๋ฐํ
664 device_manager.publish_telemetry(data)
665
666 # Device Shadow ์
๋ฐ์ดํธ
667 if i % 2 == 0:
668 shadow_state = {
669 "temperature": data.temperature,
670 "humidity": data.humidity,
671 "battery": data.battery_level
672 }
673 device_manager.update_device_shadow(shadow_state)
674
675 time.sleep(2)
676
677 # ํต๊ณ
678 print("\n" + "="*60)
679 print("AWS IoT ๋ฐ๋ชจ ์๋ฃ")
680 print("="*60)
681 stats = client.get_statistics()
682 print(f"๋ฐํ๋ ๋ฉ์์ง: {stats['published']}๊ฐ")
683
684 client.disconnect()
685
686
687def demo_gcp_pubsub():
688 """GCP Pub/Sub ๋ฐ๋ชจ"""
689 print("\n" + "="*60)
690 print("GCP Pub/Sub ๋ฐ๋ชจ")
691 print("="*60)
692
693 # ํด๋ผ์ด์ธํธ ์์ฑ
694 config = {
695 'project_id': 'my-iot-project-123456',
696 'topic_id': 'iot-telemetry',
697 'subscription_id': 'iot-telemetry-sub'
698 }
699
700 client = CloudIoTClient(CloudProvider.GCP_PUBSUB, config)
701
702 # ์ผ์ ์๋ฎฌ๋ ์ดํฐ
703 sensor = SensorSimulator("gcp-device-001")
704
705 # ํ
๋ ๋ฉํธ๋ฆฌ ๋ฐํ
706 print("\n" + "-"*60)
707 print("ํ
๋ ๋ฉํธ๋ฆฌ ๋ฐํ ์์")
708 print("-"*60)
709
710 messages = []
711 for i in range(5):
712 data = sensor.generate_reading()
713 print(f"\n[{i+1}/5] ์ผ์ ๋ฐ์ดํฐ ์์ฑ")
714 print(f" ์จ๋: {data.temperature}ยฐC")
715 print(f" ์ต๋: {data.humidity}%")
716
717 # ๊ฐ๋ณ ๋ฐํ
718 sensor_data = {
719 "temperature": data.temperature,
720 "humidity": data.humidity,
721 "pressure": data.pressure,
722 "battery_level": data.battery_level
723 }
724
725 client.publish_telemetry("gcp-device-001", sensor_data)
726 messages.append(data.to_dict())
727
728 time.sleep(1)
729
730 # ๋ฐฐ์น ๋ฐํ
731 print("\n" + "-"*60)
732 print("๋ฐฐ์น ๋ฐํ")
733 print("-"*60)
734 client.publisher.publish_batch(messages)
735
736 # ํต๊ณ
737 print("\n" + "="*60)
738 print("GCP Pub/Sub ๋ฐ๋ชจ ์๋ฃ")
739 print("="*60)
740 stats = client.get_statistics()
741 print(f"๋ฐํ๋ ๋ฉ์์ง: {stats['published']}๊ฐ")
742
743
744def demo_command_control():
745 """๋ช
๋ น ๋ฐ ์ ์ด ๋ฐ๋ชจ"""
746 print("\n" + "="*60)
747 print("๋ช
๋ น ๋ฐ ์ ์ด ๋ฐ๋ชจ")
748 print("="*60)
749
750 device_id = "smart-device-001"
751
752 # ๋ช
๋ น ์์ฑ ์์
753 print("\n[ํด๋ผ์ฐ๋ โ ๋๋ฐ์ด์ค] ๋ช
๋ น ์ ์ก")
754
755 # 1. LED ์ ์ด ๋ช
๋ น
756 led_command = MQTTMessageFormat.create_command(
757 device_id=device_id,
758 command="set_led",
759 parameters={"color": "red", "brightness": 80}
760 )
761 print(f"\n1. LED ์ ์ด ๋ช
๋ น:")
762 print(json.dumps(led_command, indent=2))
763
764 # 2. ์ค์ ๋ณ๊ฒฝ ๋ช
๋ น
765 config_command = MQTTMessageFormat.create_command(
766 device_id=device_id,
767 command="update_config",
768 parameters={"report_interval": 60, "threshold_temp": 30}
769 )
770 print(f"\n2. ์ค์ ๋ณ๊ฒฝ ๋ช
๋ น:")
771 print(json.dumps(config_command, indent=2))
772
773 # 3. ํ์จ์ด ์
๋ฐ์ดํธ ๋ช
๋ น
774 firmware_command = MQTTMessageFormat.create_command(
775 device_id=device_id,
776 command="update_firmware",
777 parameters={"version": "2.0.0", "url": "https://example.com/firmware.bin"}
778 )
779 print(f"\n3. ํ์จ์ด ์
๋ฐ์ดํธ ๋ช
๋ น:")
780 print(json.dumps(firmware_command, indent=2))
781
782 # ์๋ต ์์
783 print("\n[๋๋ฐ์ด์ค โ ํด๋ผ์ฐ๋] ๋ช
๋ น ์๋ต")
784
785 response = MQTTMessageFormat.create_response(
786 device_id=device_id,
787 command_id=led_command["command_id"],
788 status="success",
789 result={"led_state": "on", "color": "red", "brightness": 80}
790 )
791 print(json.dumps(response, indent=2))
792
793
794# ==============================================================================
795# ๋ฉ์ธ ์คํ
796# ==============================================================================
797
798def main():
799 """๋ฉ์ธ ํจ์"""
800 print("ํด๋ผ์ฐ๋ IoT ํตํฉ - ์๋ฎฌ๋ ์ด์
๋ชจ๋")
801 print("="*60)
802 print("์ด ํ๋ก๊ทธ๋จ์ ์ค์ ํด๋ผ์ฐ๋ ๊ณ์ ์์ด ์๋ฎฌ๋ ์ด์
์ผ๋ก ๋์ํฉ๋๋ค.")
803 print()
804
805 # ๋ฉ๋ด
806 print("๋ฐ๋ชจ ์๋๋ฆฌ์ค:")
807 print(" 1. AWS IoT Core")
808 print(" 2. GCP Pub/Sub")
809 print(" 3. ๋ช
๋ น ๋ฐ ์ ์ด")
810 print(" 4. ์ ์ฒด ์คํ")
811 print()
812
813 choice = input("์ ํ (1-4, ๊ธฐ๋ณธ๊ฐ=4): ").strip() or "4"
814
815 if choice == "1":
816 demo_aws_iot()
817 elif choice == "2":
818 demo_gcp_pubsub()
819 elif choice == "3":
820 demo_command_control()
821 elif choice == "4":
822 demo_aws_iot()
823 time.sleep(2)
824 demo_gcp_pubsub()
825 time.sleep(2)
826 demo_command_control()
827 else:
828 print("์๋ชป๋ ์ ํ")
829
830
831if __name__ == "__main__":
832 main()