package basic import ( "context" "fmt" "grpccanary/lib/grpc/basic/protoapi" "io" "math/rand" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) func AskingDateTime(ctx context.Context, m protoapi.IoTServiceClient) (*protoapi.DateTime, error) { request := &protoapi.RequestDateTime{ Value: "Please send me the date and time", } return m.GetDate(ctx, request) } func AskPass(ctx context.Context, m protoapi.IoTServiceClient, seed int64, length int64) (*protoapi.RandomPass, error) { request := &protoapi.RequestPass{ Seed: seed, Length: length, } return m.GetRandomPass(ctx, request) } func AskUpdateSensingData(ctx context.Context, m protoapi.IoTServiceClient, deviceId string, temp float64, humid float64) (*protoapi.SensingResponse, error) { request := &protoapi.SensingData{ DeviceId: deviceId, Temperature: temp, Humidity: humid, } return m.UpdateSensingData(ctx, request) } func AskUploadFile(ctx context.Context, m protoapi.IoTServiceClient, fileName string, fileData []byte) (*protoapi.UploadStatus, error) { stream, err := m.UploadFile(ctx) if err != nil { return nil, err } chunkSize := 1024 // 1KB ๋‹จ์œ„ ์ฒญํฌ totalBytes := len(fileData) for i := 0; i < totalBytes; i += chunkSize { end := i + chunkSize if end > totalBytes { end = totalBytes } err := stream.Send(&protoapi.FileChunk{ FileName: fileName, Content: fileData[i:end], }) if err != nil { return nil, err } } return stream.CloseAndRecv() } func AskListFiles(ctx context.Context, m protoapi.IoTServiceClient) (*protoapi.FileList, error) { return m.ListFiles(ctx, &protoapi.EmptyRequest{}) } func AskDownloadFile(ctx context.Context, m protoapi.IoTServiceClient, fileName string) ([]byte, error) { stream, err := m.DownloadFile(ctx, &protoapi.DownloadRequest{FileName: fileName}) if err != nil { return nil, err } var buffer []byte for { chunk, err := stream.Recv() if err == io.EOF { break } if err != nil { return nil, err } buffer = append(buffer, chunk.GetContent()...) } return buffer, nil } func AskSubscribeAlerts(ctx context.Context, m protoapi.IoTServiceClient, clientId string, topic string) { stream, err := m.SubscribeAlerts(ctx, &protoapi.AlertSubscription{ ClientId: clientId, Topic: topic, }) if err != nil { fmt.Println("Failed to subscribe alerts:", err) return } for { alert, err := stream.Recv() if err == io.EOF { fmt.Println("Alert subscription stream closed by server.") break } if err != nil { break } fmt.Printf("\n๐Ÿ”” [ALERT RECEIVED] ID: %s | Device: %s | Msg: %s | Time: %s\n\n", alert.GetAlertId(), alert.GetDeviceId(), alert.GetMessage(), time.Unix(alert.GetTimestamp(), 0).Format("15:04:05")) } } func ClientRun(addr string) { conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { fmt.Println("NewClient error:", err) return } client := protoapi.NewIoTServiceClient(conn) // ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹ค์‹œ๊ฐ„ ๊ฒฝ๋ณด ๊ตฌ๋… ๊ธฐ๋™ alertCtx, alertCancel := context.WithCancel(context.Background()) defer alertCancel() go AskSubscribeAlerts(alertCtx, client, "client-app-01", "temperature_warnings") // ๊ตฌ๋… ๋ฆฌ์‹œ๋ฒ„ ์ฑ„๋„ ๋“ฑ๋ก์„ ์œ„ํ•ด 50ms ์Šฌ๋ฆฝ time.Sleep(50 * time.Millisecond) r, err := AskingDateTime(context.Background(), client) if err != nil { fmt.Println(err) return } fmt.Println("Server Date and Time:", r.Value) length := int64(rand.Intn(20)) p, err := AskPass(context.Background(), client, 100, length+1) if err != nil { fmt.Println(err) return } fmt.Println("Random Password:", p.Password) res, err := AskUpdateSensingData(context.Background(), client, "sensor-room-01", 24.5, 52.3) if err != nil { fmt.Println(err) return } fmt.Println("Sensing Update Success:", res.Success) fmt.Println("Sensing Update Message:", res.Message) // 4๋‹จ๊ณ„: ํŒŒ์ผ ์—…๋กœ๋“œ ์ŠคํŠธ๋ฆฌ๋ฐ ์‹คํ–‰ ์˜ˆ์ œ dummyData := make([]byte, 10240) // 10KB ๊ฐ€์ƒ ๋”๋ฏธ ๋ฐ์ดํ„ฐ for i := range dummyData { dummyData[i] = byte(rand.Intn(256)) } fmt.Println("Uploading dummy file (10KB) via Client Streaming...") status, err := AskUploadFile(context.Background(), client, "firmware.bin", dummyData) if err != nil { fmt.Println("File upload failed:", err) return } fmt.Println("Upload Success:", status.Success) fmt.Println("Upload Message:", status.Message) fmt.Printf("Uploaded Bytes: %d bytes\n", status.BytesUploaded) // 5๋‹จ๊ณ„: ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ ์‹คํ–‰ ์˜ˆ์ œ fmt.Println("Querying uploaded files metadata from Server...") list, err := AskListFiles(context.Background(), client) if err != nil { fmt.Println("Failed to list files:", err) return } for i, f := range list.GetFiles() { fmt.Printf("[%d] Name: %s, Size: %d bytes, UploadedAt: %s\n", i+1, f.GetFileName(), f.GetFileSize(), time.Unix(f.GetUploadedAt(), 0).Format("2006-01-02 15:04:05")) } // 6๋‹จ๊ณ„: ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ์ŠคํŠธ๋ฆฌ๋ฐ ์‹คํ–‰ ์˜ˆ์ œ fmt.Println("Downloading file 'firmware.bin' via Server Streaming...") downloadedData, err := AskDownloadFile(context.Background(), client, "firmware.bin") if err != nil { fmt.Println("Failed to download file:", err) return } fmt.Printf("Download completed. Received %d bytes.\n", len(downloadedData)) // ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ(Integrity) ๊ฒ€์ฆ isMatch := true if len(dummyData) != len(downloadedData) { isMatch = false } else { for i := range dummyData { if dummyData[i] != downloadedData[i] { isMatch = false break } } } fmt.Printf("Data Integrity Checked (Upload vs Download matches?): %t\n", isMatch) // 7๋‹จ๊ณ„: ์ž„๊ณ„๊ฐ’ ์ดˆ๊ณผ ์˜จ์Šต๋„ ์ „์†ก์„ ํ†ตํ•œ Pub/Sub ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ ์œ ๋ฐœ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ fmt.Println("Sending abnormal high-temperature sensing data (45.8ยฐC)...") alertRes, err := AskUpdateSensingData(context.Background(), client, "sensor-room-01", 45.8, 60.1) if err != nil { fmt.Println(err) return } fmt.Println("Abnormal Sensing Update Success:", alertRes.Success) // ์•Œ๋ฆผ ์ด๋ฒคํŠธ๊ฐ€ ๋น„๋™๊ธฐ๋กœ ํ™”๋ฉด์— ์ถœ๋ ฅ๋  ์‹œ๊ฐ„์„ ํ™•๋ณดํ•˜๊ธฐ ์œ„ํ•ด 100ms ๋Œ€๊ธฐ time.Sleep(100 * time.Millisecond) }