refactor: replace GetRandom with IoT sensing data update scenario

This commit is contained in:
2026-07-17 15:51:10 +09:00
parent 50edea0225
commit 902af18555
10 changed files with 446 additions and 346 deletions
+3
View File
@@ -13,3 +13,6 @@ update.sh
# Workspace-specific scripts # Workspace-specific scripts
/resume_all.sh /resume_all.sh
# Local downloaded binary tools
/bin/
+3 -2
View File
@@ -89,8 +89,9 @@ Serving requests...
Client: Client:
Server Date and Time: 2026-07-12 20:00:00.123456789 +0900 KST m=+1.000000001 Server Date and Time: 2026-07-12 20:00:00.123456789 +0900 KST m=+1.000000001
Random Password: &c(D7f/G#s%d Random Password: &c(D7f/G#s%d
Random Integer 1: 42 Received sensing data - Device: sensor-room-01, Temp: 24.50°C, Humid: 52.30%
Random Integer 2: 87 Sensing Update Success: true
Sensing Update Message: Sensing data updated successfully for device sensor-room-01
``` ```
### 4. 트러블슈팅 ### 4. 트러블슈팅
+26 -24
View File
@@ -28,19 +28,19 @@ gRPC는 HTTP/2를 기반으로 구축된 구글의 고성능 오픈소스 원격
--- ---
## 2. 실습 프로젝트 소개: Random 데이터 서비스 ## 2. 실습 프로젝트 소개: IoT 센싱 데이터 수집 서비스
본 튜토리얼에서는 gRPC 분산 통신 기법을 실증적으로 학습하기 위해 가상의 **Random 데이터 API 서비스** 프로젝트를 직접 설계하고 구현해 나갑니다. 본 튜토리얼에서는 gRPC 분산 통신 기법을 실증적으로 학습하기 위해 현업에서 가장 범용적으로 쓰이는 가상의 **IoT 센싱 데이터 수집 및 기기 관리 서비스** 프로젝트를 직접 설계하고 구현해 나갑니다.
### 2.1 프로젝트 시나리오 ### 2.1 프로젝트 시나리오
사물인터넷(IoT) 센서 노드나 지능형 멀티 에이전트 분산 환경에서는 기기들이 중앙 서버에 접속해 상태 정보(날짜/시간)를 동기화하거나, 보안 패킷 전송을 위해 임시 원격 패스워드를 발급받고, 연산용 고유 난수를 안전하게 질의해야 하는 현실적인 통신 요건이 존재합니다. 사물인터넷(IoT) 센서 노드나 분산 멀티 에이전트 환경에서는 엣지 기기들이 중앙 서버에 접속해 통신 가능 여부를 검증하고 상태 정보(날짜/시간)를 수집하거나, 임시 보안 인증을 위한 비밀번호 발급을 요청하고, 실시간으로 센싱한 환경 정보(온도, 습도 등)를 지속적으로 업데이트해야 하는 현실적인 시나리오가 요구됩니다.
우리가 개발할 `Random` 서비스는 이에 대응하는 다음 3가지 원격 프로시저(RPC)를 구현합니다: 우리가 개발할 `IoTService`는 이에 대응하는 다음 3가지 핵심 원격 프로시저(RPC)를 수행합니다:
1. **서버 시간 및 날짜 조회 (`GetDate`)**: 클라이언트가 요청 시 서버는 내부 시스템의 포맷팅된 시간 문자열을 가공하여 반환합니다. 1. **서버 시간 및 날짜 조회 (`GetDate`)**: 기기가 접속 상태를 확인하며 동기화를 위해 서버의 현재 날짜와 시간 포맷 문자열을 반환받습니다.
2. **일회성 보안 패스워드 발급 (`GetRandomPass`)**: 클라이언트가 난수 시드와 바이트 길이를 명시하여 요청하면, 서버는 안전한 ASCII 비밀번호 문자열을 조립해 응답합니다. 2. **센싱 데이터 업데이트 (`UpdateSensingData`)**: 센서 노드가 주기적으로 수집한 물리 데이터(온도, 습도) 및 기기 식별자(Device ID)를 전달하면, 서버는 데이터 정합성을 검증한 후 성공 여부를 반환합니다.
3. **의사 난수 정수 생성 (`GetRandom`)**: 재현성을 위해 시드값과 시퀀스 위치를 인자로 전달받아 계산에 부합하는 정수 난수값을 연산해 반환합니다. 3. **일회성 보안 패스워드 발급 (`GetRandomPass`)**: 기기가 임시 통신 세션 수립을 위해 난수 생성 시드와 보안 문자열 길이를 전달하면, 무작위 ASCII 임시 패스워드를 연산하여 응답받습니다.
### 2.2 학습 목표 및 진행 방법 ### 2.2 학습 목표 및 진행 방법
간단하면서도 긴밀한 데이터 파이프라인을 구축하는 실습을 통해 학습자는 다음 gRPC 지식 체계를 단계별로 마스터하게 됩니다: 유기적인 IoT 데이터 통신 모듈을 구축하는 실습을 통해 학습자는 다음 gRPC 지식 체계를 단계별로 마스터하게 됩니다:
* **스키마 설계**: `.proto` IDL 문법을 활용해 데이터 형식(Message)과 원격 함수(Service RPC) 계약을 강제하는 법을 습득합니다. * **스키마 설계**: `.proto` IDL 문법을 활용해 데이터 형식(Message)과 원격 함수(Service RPC) 계약을 강제하는 법을 습득합니다.
* **Stub 컴파일**: `protoc` 도구 체인을 가동하여 Go 프로그래밍 언어 소스코드를 안전하게 생성하고 프로젝트 빌드에 바인딩하는 기법을 배웁니다. * **Stub 컴파일**: `protoc` 도구 체인을 가동하여 Go 프로그래밍 언어 소스코드를 안전하게 생성하고 프로젝트 빌드에 바인딩하는 기법을 배웁니다.
* **네트워크 구현**: 실제로 TCP 소켓을 확보하여 gRPC 서버를 실행하고, 클라이언트가 평문 커넥션을 수립하여 실시간 동기식 원격 프로시저를 직접 기동하는 엔드투엔드 구동 구조를 체득합니다. * **네트워크 구현**: 실제로 TCP 소켓을 확보하여 gRPC 서버를 실행하고, 클라이언트가 평문 커넥션을 수립하여 실시간 동기식 원격 프로시저를 직접 기동하는 엔드투엔드 구동 구조를 체득합니다.
@@ -49,26 +49,28 @@ gRPC는 HTTP/2를 기반으로 구축된 구글의 고성능 오픈소스 원격
## 3. 인터페이스 명세서 (`protoapi.proto`) ## 3. 인터페이스 명세서 (`protoapi.proto`)
저장소 루트의 [protoapi.proto](../protoapi.proto) 파일은 앞서 설계한 `Random` 서비스를 구축하기 위해 아래와 같이 사양을 선언해 둡니다. 저장소 루트의 [protoapi.proto](../protoapi.proto) 파일은 앞서 설계한 `IoTService`를 구축하기 위해 아래와 같이 사양을 선언해 둡니다.
```proto ```proto
syntax = "proto3"; syntax = "proto3";
option go_package = "./protoapi/;protoapi"; option go_package = "./protoapi/;protoapi";
service Random { service IoTService {
rpc GetDate (RequestDateTime) returns (DateTime); rpc GetDate (RequestDateTime) returns (DateTime);
rpc GetRandom (RandomParams) returns (RandomInt); rpc UpdateSensingData (SensingData) returns (SensingResponse);
rpc GetRandomPass (RequestPass) returns (RandomPass); rpc GetRandomPass (RequestPass) returns (RandomPass);
} }
message RandomParams { message SensingData {
int64 Seed = 1; string DeviceId = 1;
int64 Place = 2; double Temperature = 2;
double Humidity = 3;
} }
message RandomInt { message SensingResponse {
int64 Value = 1; bool Success = 1;
string Message = 2;
} }
message DateTime { message DateTime {
@@ -124,16 +126,16 @@ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
* **구조체 정의**: * **구조체 정의**:
```go ```go
type RandomServer struct { type IoTServer struct {
protoapi.UnimplementedRandomServer protoapi.UnimplementedIoTServiceServer
} }
``` ```
`UnimplementedRandomServer`를 임베딩하여, 향후 메서드가 새로 추가되더라도 기존 서버가 빌드 에러 없이 최소한의 호환(unimplemented 에러 응답)을 가지게 강제합니다. `UnimplementedIoTServiceServer`를 임베딩하여, 향후 메서드가 새로 추가되더라도 기존 서버가 빌드 에러 없이 최소한의 호환(unimplemented 에러 응답)을 가지게 강제합니다.
* **서버 기동 흐름**: * **서버 기동 흐름**:
```go ```go
server := grpc.NewServer() server := grpc.NewServer()
var randomServer RandomServer var iotServer IoTServer
protoapi.RegisterRandomServer(server, randomServer) protoapi.RegisterIoTServiceServer(server, iotServer)
reflection.Register(server) // grpcurl 등 외부 디버깅 목적 reflection.Register(server) // grpcurl 등 외부 디버깅 목적
listen, _ := net.Listen("tcp", port) listen, _ := net.Listen("tcp", port)
server.Serve(listen) server.Serve(listen)
@@ -145,17 +147,17 @@ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
```go ```go
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
defer conn.Close() defer conn.Close()
client := protoapi.NewRandomClient(conn) client := protoapi.NewIoTServiceClient(conn)
``` ```
`insecure.NewCredentials()`를 전달하여 TLS를 건너뛴 채 평문으로 빠르고 간단한 로컬 테스트 환경을 구축합니다. `insecure.NewCredentials()`를 전달하여 TLS를 건너뛴 채 평문으로 빠르고 간단한 로컬 테스트 환경을 구축합니다.
* **원격 호출**: * **원격 호출**:
`client.GetDate()`, `client.GetRandom()`, `client.GetRandomPass()`를 차례로 호출하여 매개변수와 결과를 콘솔로 확인합니다. `client.GetDate()`, `client.GetRandomPass()`, `client.UpdateSensingData()`를 차례로 호출하여 매개변수와 결과를 콘솔로 확인합니다.
### 5.3 gRPC 실습 예제 동작 흐름 ### 5.3 gRPC 실습 예제 동작 흐름
예제가 구동되면 서버와 클라이언트 간에 다음과 같은 호출 시퀀스가 순차적으로 실행됩니다: 예제가 구동되면 서버와 클라이언트 간에 다음과 같은 호출 시퀀스가 순차적으로 실행됩니다:
1. **날짜 조회 (`GetDate`)**: 클라이언트가 서버에 날짜 조회를 요청하고, 서버는 자체의 현재 날짜와 시간 문자열을 포맷하여 반환합니다. 1. **날짜 조회 (`GetDate`)**: 클라이언트가 서버에 날짜 조회를 요청하고, 서버는 자체의 현재 날짜와 시간 문자열을 포맷하여 반환합니다.
2. **비밀번호 생성 (`GetRandomPass`)**: 클라이언트가 생성할 무작위 비밀번호의 길이(기본 8자)와 난수 생성 시드값을 전달하면, 서버는 지정된 사양의 임의 문자열을 작성해 반환합니다. 2. **비밀번호 생성 (`GetRandomPass`)**: 클라이언트가 생성할 무작위 비밀번호의 길이(기본 8자)와 난수 생성 시드값을 전달하면, 서버는 지정된 사양의 임의 문자열을 작성해 반환합니다.
3. **난수 생성 (`GetRandom`)**: 서로 다른 위치(Place) 및 시드(Seed) 값을 전달하여 각각 1회씩, 총 2회의 독립적인 난수 생성(의사 난수 정수값) 결과를 반환받아 출력합니다. 3. **센싱 데이터 업데이트 (`UpdateSensingData`)**: 클라이언트가 기기 ID("sensor-room-01")와 수집한 온/습도 환경 변수를 전달하면, 서버는 이를 수신하여 화면에 상세 센싱 값을 출력하고 업데이트 완료 성공 응답을 전송합니다.
--- ---
+13 -21
View File
@@ -11,7 +11,7 @@ import (
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
) )
func AskingDateTime(ctx context.Context, m protoapi.RandomClient) (*protoapi.DateTime, error) { func AskingDateTime(ctx context.Context, m protoapi.IoTServiceClient) (*protoapi.DateTime, error) {
request := &protoapi.RequestDateTime{ request := &protoapi.RequestDateTime{
Value: "Please send me the date and time", Value: "Please send me the date and time",
} }
@@ -19,7 +19,7 @@ func AskingDateTime(ctx context.Context, m protoapi.RandomClient) (*protoapi.Dat
return m.GetDate(ctx, request) return m.GetDate(ctx, request)
} }
func AskPass(ctx context.Context, m protoapi.RandomClient, seed int64, length int64) (*protoapi.RandomPass, error) { func AskPass(ctx context.Context, m protoapi.IoTServiceClient, seed int64, length int64) (*protoapi.RandomPass, error) {
request := &protoapi.RequestPass{ request := &protoapi.RequestPass{
Seed: seed, Seed: seed,
Length: length, Length: length,
@@ -28,13 +28,14 @@ func AskPass(ctx context.Context, m protoapi.RandomClient, seed int64, length in
return m.GetRandomPass(ctx, request) return m.GetRandomPass(ctx, request)
} }
func AskRandom(ctx context.Context, m protoapi.RandomClient, seed int64, place int64) (*protoapi.RandomInt, error) { func AskUpdateSensingData(ctx context.Context, m protoapi.IoTServiceClient, deviceId string, temp float64, humid float64) (*protoapi.SensingResponse, error) {
request := &protoapi.RandomParams{ request := &protoapi.SensingData{
Seed: seed, DeviceId: deviceId,
Place: place, Temperature: temp,
Humidity: humid,
} }
return m.GetRandom(ctx, request) return m.UpdateSensingData(ctx, request)
} }
func ClientRun(addr string) { func ClientRun(addr string) {
@@ -44,10 +45,7 @@ func ClientRun(addr string) {
return return
} }
rand.Seed(time.Now().Unix()) client := protoapi.NewIoTServiceClient(conn)
seed := int64(rand.Intn(100))
client := protoapi.NewRandomClient(conn)
r, err := AskingDateTime(context.Background(), client) r, err := AskingDateTime(context.Background(), client)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@@ -55,6 +53,7 @@ func ClientRun(addr string) {
} }
fmt.Println("Server Date and Time:", r.Value) fmt.Println("Server Date and Time:", r.Value)
rand.Seed(time.Now().Unix())
length := int64(rand.Intn(20)) length := int64(rand.Intn(20))
p, err := AskPass(context.Background(), client, 100, length+1) p, err := AskPass(context.Background(), client, 100, length+1)
if err != nil { if err != nil {
@@ -63,18 +62,11 @@ func ClientRun(addr string) {
} }
fmt.Println("Random Password:", p.Password) fmt.Println("Random Password:", p.Password)
place := int64(rand.Intn(100)) res, err := AskUpdateSensingData(context.Background(), client, "sensor-room-01", 24.5, 52.3)
i, err := AskRandom(context.Background(), client, seed, place)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
fmt.Println("Random Integer 1:", i.Value) fmt.Println("Sensing Update Success:", res.Success)
fmt.Println("Sensing Update Message:", res.Message)
k, err := AskRandom(context.Background(), client, seed, place-1)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Random Integer 2:", k.Value)
} }
+11 -19
View File
@@ -50,11 +50,11 @@ func getString(len int64) string {
return temp return temp
} }
type RandomServer struct { type IoTServer struct {
protoapi.UnimplementedRandomServer protoapi.UnimplementedIoTServiceServer
} }
func (RandomServer) GetDate(ctx context.Context, r *protoapi.RequestDateTime) (*protoapi.DateTime, error) { func (IoTServer) GetDate(ctx context.Context, r *protoapi.RequestDateTime) (*protoapi.DateTime, error) {
currentTime := time.Now() currentTime := time.Now()
response := &protoapi.DateTime{ response := &protoapi.DateTime{
Value: currentTime.String(), Value: currentTime.String(),
@@ -63,26 +63,18 @@ func (RandomServer) GetDate(ctx context.Context, r *protoapi.RequestDateTime) (*
return response, nil return response, nil
} }
func (RandomServer) GetRandom(ctx context.Context, r *protoapi.RandomParams) (*protoapi.RandomInt, error) { func (IoTServer) UpdateSensingData(ctx context.Context, r *protoapi.SensingData) (*protoapi.SensingResponse, error) {
src := rand.NewSource(r.GetSeed()) fmt.Printf("Received sensing data - Device: %s, Temp: %.2f°C, Humid: %.2f%%\n", r.GetDeviceId(), r.GetTemperature(), r.GetHumidity())
place := r.GetPlace()
temp := random(min, max, src)
for {
place--
if place <= 0 {
break
}
temp = random(min, max, src)
}
response := &protoapi.RandomInt{ response := &protoapi.SensingResponse{
Value: int64(temp), Success: true,
Message: fmt.Sprintf("Sensing data updated successfully for device %s", r.GetDeviceId()),
} }
return response, nil return response, nil
} }
func (RandomServer) GetRandomPass(ctx context.Context, r *protoapi.RequestPass) (*protoapi.RandomPass, error) { func (IoTServer) GetRandomPass(ctx context.Context, r *protoapi.RequestPass) (*protoapi.RandomPass, error) {
rand.Seed(r.GetSeed()) rand.Seed(r.GetSeed())
temp := getString(r.GetLength()) temp := getString(r.GetLength())
@@ -95,8 +87,8 @@ func (RandomServer) GetRandomPass(ctx context.Context, r *protoapi.RequestPass)
func ServerRun(addr string) { func ServerRun(addr string) {
server := grpc.NewServer() server := grpc.NewServer()
var randomServer RandomServer var iotServer IoTServer
protoapi.RegisterRandomServer(server, randomServer) protoapi.RegisterIoTServiceServer(server, iotServer)
reflection.Register(server) reflection.Register(server)
+151
View File
@@ -0,0 +1,151 @@
# 프로젝트 분석 보고서: grpccanary
## 1. 개요
`grpccanary`는 **Go 언어로 JSON 처리 → HTTP 서버 → gRPC 서버/클라이언트로 이어지는 학습 여정을 다루는 한국어 튜토리얼 저장소**입니다. 저장소 이름(`grpccanary`)과 달리 실제 내용은 gRPC 단일 주제가 아니라, "데이터 직렬화(JSON) → 웹 서버(HTTP/gin) → 원격 프로시저 호출(gRPC)"로 난이도를 높여가는 3단계 실습 커리큘럼이며, 현재는 **gRPC 파트가 가장 완성도 높게 구현**되어 있습니다.
저장소 루트에는 이 학습 프로젝트와는 성격이 다른 **`multi-agent-mux`라는 다중 에이전트(Claude Code) 오케스트레이션 인프라**가 함께 자리 잡고 있습니다(`.agents/`, `.mam/` 디렉토리). 이 인프라는 tmux 세션 + MQTT 메시지 브로커를 이용해 여러 AI 에이전트(팀장/리뷰어 역할)가 하나의 작업 저장소를 공유하며 협업하도록 설계된 별도의 도구 체계이며, 실제로 이 보고서를 작성하는 작업(`job 702ea1d8`) 자체도 이 인프라를 통해 위임되었습니다.
즉 이 저장소는 **"gRPC를 배우기 위한 Go 예제 코드"**와 **"그 예제 코드를 여러 AI 에이전트가 함께 작업하도록 돕는 협업 프레임워크"**가 한 저장소 안에 공존하는 구조입니다.
---
## 2. 저장소 구조
```
grpccanary/
├── README.md # 메인 튜토리얼 (JSON/HTTP/gRPC 개념 설명 + 실행 가이드, 한국어)
├── AGENTS.md # LLM 코딩 행동 지침 (일반 원칙)
├── go.mod / go.sum # Go 모듈 정의 (module grpccanary, go 1.25.4)
├── protoapi.proto # gRPC 서비스 IDL(Protocol Buffers) 정의 원본
├── protoapi/ # protoc로 생성된 Go stub 코드
│ ├── protoapi.pb.go # 메시지 타입 (protoc-gen-go)
│ └── protoapi_grpc.pb.go # 서비스/클라이언트 stub (protoc-gen-go-grpc)
├── obj.json # JSON 예제용 샘플 데이터 파일
├── examples/
│ ├── main.go # 실행 진입점 (현재는 JSON 예제만 호출하도록 설정됨)
│ ├── jsonexample/
│ │ └── json_parser.go # encoding/json 마샬링·언마샬링 예제
│ ├── httpentity/
│ │ ├── server.go # gin 기반 HTTP 서버 (전체가 주석 처리된 미완성 스텁)
│ │ └── client.go # 패키지 선언만 있는 빈 파일
│ └── grpcentity/
│ ├── server.go # gRPC 서버 구현 (Random 서비스)
│ ├── client.go # gRPC 클라이언트 구현
│ └── README.md # gRPC 서버/클라이언트 구현 상세 해설 (한국어)
├── docs/
│ └── Working with JSON/
│ ├── README.md # JSON 관련 학습 자료 (영문)
│ └── README-kr.md # JSON 관련 학습 자료 (한글)
├── scripts/
│ └── generate-env.sh # .env.example → .env 복사 스크립트 (멀티에이전트 인프라용)
├── .env.example # 멀티에이전트 인프라(MQTT 브로커 등) 설정 템플릿
├── .agents/ # 멀티에이전트 오케스트레이션 규칙·스킬 (아래 4절 참고)
└── .mam/ # 멀티에이전트 잡(Job) 레지스트리 및 세션 상태 (런타임 산출물)
```
빌드 확인 결과 `go build ./...``go vet ./...` 모두 오류 없이 통과했으며, 테스트 파일(`*_test.go`)은 저장소에 존재하지 않습니다.
---
## 3. 핵심 구성 요소 (학습용 Go 코드)
### 3.1 진입점 — `examples/main.go`
- 프로젝트의 유일한 `main()` 함수. `var port = ":8080"`을 정의하고 있으며, 기본 상태에서는 `jsonexample.JsonParsingExample()`만 호출합니다.
- gRPC 예제를 실행하려면 README.md 안내에 따라 `main()` 내부를 수동으로 편집해 `grpcSample()`을 호출하도록 바꿔야 합니다(미사용 import 오류 방지를 위해 `jsonexample` import를 주석 처리해야 함). 즉 **하나의 코드베이스 안에서 학습 단계별로 진입점을 수동 전환하는 방식**으로 설계되어 있습니다.
- `grpcSample()` 함수는 `entity.ServerRun(port)`를 고루틴으로 백그라운드 실행한 뒤 1초 대기 후 `entity.ClientRun(...)`을 호출해, 같은 프로세스 안에서 서버·클라이언트가 통신하는 데모를 구성합니다.
### 3.2 JSON 파싱 예제 — `examples/jsonexample/json_parser.go`
- `encoding/json` 표준 라이브러리를 이용해 (1) `map[string]interface{}` ↔ JSON 문자열 변환, (2) 구조체(`Person`) ↔ JSON 변환의 마샬링/언마샬링을 시연합니다.
- 외부 의존성 없이 표준 라이브러리만 사용하는 가장 단순한 예제로, 커리큘럼의 1단계 역할을 합니다.
### 3.3 HTTP 서버 예제 — `examples/httpentity/`
- `server.go``gin-gonic/gin`을 이용한 REST API 서버(정적 파일 서빙 + `/api/randomNumber`, `/api/randomPassword`, `/api/randomDate` 라우트) 초안이 **전체 주석 처리**된 상태로만 존재합니다. 즉 코드는 작성되어 있으나 활성화되지 않은 미완성/보류 상태입니다.
- `client.go``package httpentity` 선언 한 줄만 있는 빈 파일입니다.
- `go.mod`에는 `gin-gonic/gin` 의존성이 여전히 선언되어 있어, 이 파트가 완전히 폐기된 것이 아니라 추후 재개를 염두에 둔 진행 중(work-in-progress) 상태로 보입니다.
### 3.4 gRPC 서비스 정의 — `protoapi.proto`
- `proto3` 문법으로 `Random`이라는 gRPC 서비스를 정의하며 3개의 RPC 메서드를 제공합니다.
- `GetDate(RequestDateTime) returns (DateTime)` — 서버의 현재 날짜/시간 반환
- `GetRandom(RandomParams) returns (RandomInt)` — 시드(Seed)와 위치(Place)를 기반으로 한 의사난수 생성
- `GetRandomPass(RequestPass) returns (RandomPass)` — 시드와 길이(Length)를 받아 무작위 ASCII 비밀번호 생성
- `option go_package = "./protoapi/;protoapi"`로 지정되어 있어, `protoc` 컴파일 시 `protoapi/` 디렉토리에 Go 패키지 `protoapi`가 생성됩니다.
- 이미 컴파일된 stub(`protoapi/protoapi.pb.go`, `protoapi/protoapi_grpc.pb.go`)이 저장소에 커밋되어 있어 `protoc` 재설치 없이 바로 빌드/실행이 가능합니다.
### 3.5 gRPC 서버 구현 — `examples/grpcentity/server.go`
- `RandomServer` 구조체가 `protoapi.UnimplementedRandomServer`를 임베딩하여 3개 RPC 메서드(`GetDate`, `GetRandom`, `GetRandomPass`)를 구현합니다.
- `getString(len int64)`은 ASCII 코드 `!`(33)부터 94개 범위 내에서 문자를 뽑아 임의 문자열(비밀번호)을 생성하는 헬퍼입니다.
- `ServerRun(addr string)``grpc.NewServer()`로 서버를 만들고 `reflection.Register(server)`로 gRPC reflection(예: `grpcurl` 같은 외부 CLI 디버깅 도구 지원)을 활성화한 뒤 TCP 포트를 리슨합니다.
- **주의(코드상 특이점)**: `ServerRun`은 인자로 받은 `addr`을 실제로 사용하지 않고 패키지 전역 변수 `port = ":8080"`으로 리슨합니다(`net.Listen("tcp", port)`). 따라서 현재 코드는 항상 `:8080`에서만 동작하며, 호출부에서 다른 포트를 넘겨도 무시됩니다.
- `rand.Seed(r.GetSeed())`(패키지 전역 시드 설정, Go 1.20+에서는 deprecated)와 `rand.NewSource`를 혼용하고 있어 스레드 안전성이나 API 일관성 측면에서 다소 오래된 패턴을 보입니다(학습용 예제이므로 의도된 단순화로 판단됨).
### 3.6 gRPC 클라이언트 구현 — `examples/grpcentity/client.go`
- `grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))`로 평문(TLS 미적용) 채널을 생성합니다(로컬 테스트 목적).
- `AskingDateTime`, `AskPass`, `AskRandom` 세 개의 래퍼 함수가 각각 대응하는 RPC를 호출합니다.
- `ClientRun(addr)`이 실행 엔트리포인트로, 날짜/시간 조회 1회, 비밀번호 생성 1회, 서로 다른 파라미터로 난수 생성 2회를 순차 호출하며 결과를 표준 출력으로 출력합니다.
### 3.7 문서 자료
- **`README.md`(루트)**: 이야기체(스타트업 개발자 '민우'의 사례)로 gRPC 도입 배경(REST/JSON의 한계, Protobuf 계약, HTTP/2)을 설명한 뒤, Go 버전 요구사항(1.25.4+), 의존성 설치, `main.go` 수동 편집 방법, 실행 명령(`go run ./examples`), 기대 출력 예시, 트러블슈팅(포트 충돌, 모듈 로드 오류)까지 안내하는 실행 가이드로 구성되어 있습니다.
- **`examples/grpcentity/README.md`**: `.proto` 파일 문법 요소별 상세 해설, `protoc`/`protoc-gen-go`/`protoc-gen-go-grpc` 설치 및 컴파일 절차, 서버·클라이언트 코드의 구현 단계별 설명을 담은 심화 가이드입니다.
- **`docs/Working with JSON/`**: JSON 관련 별도 학습 자료(영/한 병기)가 준비되어 있으나, 루트 README와 직접 링크되어 있지는 않습니다.
---
## 4. 두 번째 레이어 — 멀티에이전트 오케스트레이션 인프라
저장소에는 학습 콘텐츠와 무관한 **AI 에이전트 협업 인프라**가 함께 포함되어 있습니다.
- **`AGENTS.md`**: 일반적인 LLM 코딩 행동 지침(가정하지 말 것, 최소 변경, 외과적 수정 등)을 규정합니다.
- **`.agents/MULTI_AGENT_RULES.md`(+ 한국어판)**: MQTT 메시징 백플레인과 tmux 기반 다중 에이전트 협업 프로토콜을 정의합니다. 총괄 매니저(Orchestrator) → 팀장(개발/리뷰) → 작업 위임 및 리뷰 루프 → 완료 보고로 이어지는 워크플로우, Job 레지스트리(`.mam/jobs/<id>.json`, `fcntl` 파일 락), 세션 레지스트리(`.mam/agent-sessions.db`, SQLite WAL), HMAC-SHA256 기반 메시지 인증(PoC 모드에서는 비활성) 등을 상세히 규정합니다.
- **`.agents/skills/`**: `multi-agent-mux-{create,delegate-job,loop,monitor,resume,status,stop}` 등 실제 세션 생성·작업 위임·모니터링·중지를 수행하는 스킬(스크립트) 모음입니다.
- **`.mam/`**: 위 인프라의 런타임 상태 저장소로, `jobs/`(작업 레지스트리 및 브리프 파일), `agent-sessions.yaml`/`.db`(세션 상태), `agent_homes/`(에이전트별 메모리), `delegate_job_logs/`(위임 감사 로그)를 포함합니다. 이번 작업 브리프(`.mam/jobs/702ea1d8/brief.md`) 역시 이 구조를 통해 전달되었습니다.
- **`scripts/generate-env.sh`, `.env.example`**: 이 인프라가 사용하는 MQTT 브로커 접속 정보, 경로 설정 등을 `.env`로 초기화하는 헬퍼입니다. 실제 비밀 값은 `.env`(git-ignored)에만 두고 `.env.example`에는 플레이스홀더만 커밋하도록 설계되어 있습니다.
**참고**: 새로 추가된 `.gitignore``.agents/`, `.mam/`, `.env`, `AGENTS.md` 등을 앞으로 git 추적 대상에서 제외하도록 지정하고 있습니다. 다만 `git log`를 보면 `AGENTS.md`, `examples/grpcentity/README.md` 등은 과거 커밋(`init 251117`, `update 260609`, 이후 docs 커밋들)에서 이미 저장소에 커밋되어 있으므로, 이번 `.gitignore` 추가는 향후 신규/변경 파일이 실수로 다시 커밋되는 것을 막기 위한 조치로 보입니다(기존에 추적 중인 파일을 소급 제외하지는 않음).
---
## 5. 실행 방법
### 5.1 사전 준비
```bash
go version # Go 1.25.4 이상 필요
go mod download # 의존성 다운로드
```
### 5.2 JSON 예제 실행 (기본값, 별도 수정 불필요)
```bash
go run ./examples
```
`examples/main.go`가 기본적으로 `jsonexample.JsonParsingExample()`만 호출하므로, 별도 수정 없이 바로 JSON 마샬링/언마샬링 결과가 콘솔에 출력됩니다.
### 5.3 gRPC 예제 실행 (수동 편집 필요)
1. `examples/main.go`를 열어 `jsonexample` import를 주석 처리하고 `main()` 안에서 `grpcSample()`을 호출하도록 변경.
2. 저장 후 실행:
```bash
go run ./examples
```
3. 내부적으로 `:8080` 포트에서 gRPC 서버(고루틴)가 기동되고, 1초 후 클라이언트가 `GetDate`, `GetRandomPass`, `GetRandom`(2회) 순으로 RPC를 호출하며 결과를 출력합니다.
4. 포트 충돌 시(`bind: address already in use`) `examples/grpcentity/server.go`의 `port` 전역 변수를 변경해야 합니다(3.5절에서 언급했듯 `ServerRun`의 `addr` 인자는 무시되므로, 포트 변경은 이 전역 변수 수정으로만 가능).
### 5.4 HTTP 예제
`httpentity` 패키지는 서버 로직 전체가 주석 처리되어 있고 클라이언트 파일은 비어 있어, 현재 상태로는 실행 가능한 산출물이 없습니다. 향후 주석을 해제하고 `main.go`에 호출부를 추가해야 실습이 가능합니다.
### 5.5 `.proto` 재컴파일 (선택)
`.proto` 정의를 수정하고 stub을 재생성하려면:
```bash
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
--go-grpc_opt=paths=source_relative protoapi.proto
```
사전에 `protoc`, `protoc-gen-go`, `protoc-gen-go-grpc`가 설치되어 있어야 합니다(설치 방법은 `examples/grpcentity/README.md` 참고).
---
## 6. 주요 관찰 사항 및 특이점
1. **단일 진입점, 수동 전환 방식**: `examples/main.go`가 유일한 실행 지점이며, 학습 단계(JSON/HTTP/gRPC)를 전환하려면 코드를 직접 편집해야 합니다. 각 예제를 독립적으로 실행할 수 있는 별도 커맨드나 플래그는 없습니다.
2. **`ServerRun(addr)`의 `addr` 인자 미사용**: gRPC 서버는 전달받은 인자 대신 패키지 전역 `port` 변수로 리슨하므로, 함수 시그니처와 실제 동작이 일치하지 않는 잠재적 혼동 요소입니다.
3. **HTTP 파트 미완성**: `gin` 의존성은 `go.mod`에 존재하지만 실제 코드는 비활성 상태로, 커리큘럼상 "다음 실습 예정" 단계로 보입니다.
4. **테스트 부재**: 저장소 전체에 자동화된 테스트(`*_test.go`)가 없어, 코드 정상 동작 여부는 수동 실행(`go run`)과 콘솔 출력 확인에 의존합니다. `go build ./...`, `go vet ./...`는 통과합니다.
5. **레거시 API 사용**: `rand.Seed(...)`(전역 시드 설정) 등 Go 최신 버전에서 권장되지 않는(deprecated) 패턴이 사용되고 있으나, 학습용 예제의 단순성을 위한 의도적 선택으로 보입니다.
6. **이중 성격의 저장소**: 순수 학습 콘텐츠(gRPC/JSON/HTTP 튜토리얼)와 AI 에이전트 협업 인프라(multi-agent-mux)가 한 저장소에 공존하며, 두 영역은 서로 기능적으로 독립적입니다. 협업 인프라(`.agents/`, `.mam/`)는 이 학습 프로젝트를 대상으로 여러 AI 에이전트가 작업을 위임받고 결과를 보고하는 용도로 사용되고 있습니다(본 보고서 작성 작업 자체가 그 예시).
+9 -10
View File
@@ -2,23 +2,23 @@ syntax = "proto3";
option go_package = "./protoapi/;protoapi"; option go_package = "./protoapi/;protoapi";
service Random { service IoTService {
rpc GetDate (RequestDateTime) returns (DateTime); rpc GetDate (RequestDateTime) returns (DateTime);
rpc GetRandom (RandomParams) returns (RandomInt); rpc UpdateSensingData (SensingData) returns (SensingResponse);
rpc GetRandomPass (RequestPass) returns (RandomPass); rpc GetRandomPass (RequestPass) returns (RandomPass);
} }
// For random number message SensingData {
message RandomParams { string DeviceId = 1;
int64 Seed = 1; double Temperature = 2;
int64 Place = 2; double Humidity = 3;
} }
message RandomInt { message SensingResponse {
int64 Value = 1; bool Success = 1;
string Message = 2;
} }
// For date time
message DateTime { message DateTime {
string Value = 1; string Value = 1;
} }
@@ -27,7 +27,6 @@ message RequestDateTime {
string Value = 2; string Value = 2;
} }
// For random password
message RequestPass { message RequestPass {
int64 Seed = 1; int64 Seed = 1;
int64 Length = 8; int64 Length = 8;
+124 -209
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.33.0 // protoc-gen-go v1.36.11
// protoc v3.21.12 // protoc v5.27.2
// source: protoapi.proto // source: protoapi.proto
package protoapi package protoapi
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -20,34 +21,31 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// For random number type SensingData struct {
type RandomParams struct { state protoimpl.MessageState `protogen:"open.v1"`
state protoimpl.MessageState DeviceId string `protobuf:"bytes,1,opt,name=DeviceId,proto3" json:"DeviceId,omitempty"`
sizeCache protoimpl.SizeCache Temperature float64 `protobuf:"fixed64,2,opt,name=Temperature,proto3" json:"Temperature,omitempty"`
Humidity float64 `protobuf:"fixed64,3,opt,name=Humidity,proto3" json:"Humidity,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
Seed int64 `protobuf:"varint,1,opt,name=Seed,proto3" json:"Seed,omitempty"`
Place int64 `protobuf:"varint,2,opt,name=Place,proto3" json:"Place,omitempty"`
} }
func (x *RandomParams) Reset() { func (x *SensingData) Reset() {
*x = RandomParams{} *x = SensingData{}
if protoimpl.UnsafeEnabled { mi := &file_protoapi_proto_msgTypes[0]
mi := &file_protoapi_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi)
ms.StoreMessageInfo(mi)
}
} }
func (x *RandomParams) String() string { func (x *SensingData) String() string {
return protoimpl.X.MessageStringOf(x) return protoimpl.X.MessageStringOf(x)
} }
func (*RandomParams) ProtoMessage() {} func (*SensingData) ProtoMessage() {}
func (x *RandomParams) ProtoReflect() protoreflect.Message { func (x *SensingData) ProtoReflect() protoreflect.Message {
mi := &file_protoapi_proto_msgTypes[0] mi := &file_protoapi_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -57,51 +55,56 @@ func (x *RandomParams) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x) return mi.MessageOf(x)
} }
// Deprecated: Use RandomParams.ProtoReflect.Descriptor instead. // Deprecated: Use SensingData.ProtoReflect.Descriptor instead.
func (*RandomParams) Descriptor() ([]byte, []int) { func (*SensingData) Descriptor() ([]byte, []int) {
return file_protoapi_proto_rawDescGZIP(), []int{0} return file_protoapi_proto_rawDescGZIP(), []int{0}
} }
func (x *RandomParams) GetSeed() int64 { func (x *SensingData) GetDeviceId() string {
if x != nil { if x != nil {
return x.Seed return x.DeviceId
}
return ""
}
func (x *SensingData) GetTemperature() float64 {
if x != nil {
return x.Temperature
} }
return 0 return 0
} }
func (x *RandomParams) GetPlace() int64 { func (x *SensingData) GetHumidity() float64 {
if x != nil { if x != nil {
return x.Place return x.Humidity
} }
return 0 return 0
} }
type RandomInt struct { type SensingResponse struct {
state protoimpl.MessageState state protoimpl.MessageState `protogen:"open.v1"`
sizeCache protoimpl.SizeCache Success bool `protobuf:"varint,1,opt,name=Success,proto3" json:"Success,omitempty"`
Message string `protobuf:"bytes,2,opt,name=Message,proto3" json:"Message,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
Value int64 `protobuf:"varint,1,opt,name=Value,proto3" json:"Value,omitempty"`
} }
func (x *RandomInt) Reset() { func (x *SensingResponse) Reset() {
*x = RandomInt{} *x = SensingResponse{}
if protoimpl.UnsafeEnabled { mi := &file_protoapi_proto_msgTypes[1]
mi := &file_protoapi_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi)
ms.StoreMessageInfo(mi)
}
} }
func (x *RandomInt) String() string { func (x *SensingResponse) String() string {
return protoimpl.X.MessageStringOf(x) return protoimpl.X.MessageStringOf(x)
} }
func (*RandomInt) ProtoMessage() {} func (*SensingResponse) ProtoMessage() {}
func (x *RandomInt) ProtoReflect() protoreflect.Message { func (x *SensingResponse) ProtoReflect() protoreflect.Message {
mi := &file_protoapi_proto_msgTypes[1] mi := &file_protoapi_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -111,34 +114,37 @@ func (x *RandomInt) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x) return mi.MessageOf(x)
} }
// Deprecated: Use RandomInt.ProtoReflect.Descriptor instead. // Deprecated: Use SensingResponse.ProtoReflect.Descriptor instead.
func (*RandomInt) Descriptor() ([]byte, []int) { func (*SensingResponse) Descriptor() ([]byte, []int) {
return file_protoapi_proto_rawDescGZIP(), []int{1} return file_protoapi_proto_rawDescGZIP(), []int{1}
} }
func (x *RandomInt) GetValue() int64 { func (x *SensingResponse) GetSuccess() bool {
if x != nil { if x != nil {
return x.Value return x.Success
} }
return 0 return false
} }
// For date time func (x *SensingResponse) GetMessage() string {
type DateTime struct { if x != nil {
state protoimpl.MessageState return x.Message
sizeCache protoimpl.SizeCache }
unknownFields protoimpl.UnknownFields return ""
}
Value string `protobuf:"bytes,1,opt,name=Value,proto3" json:"Value,omitempty"` type DateTime struct {
state protoimpl.MessageState `protogen:"open.v1"`
Value string `protobuf:"bytes,1,opt,name=Value,proto3" json:"Value,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *DateTime) Reset() { func (x *DateTime) Reset() {
*x = DateTime{} *x = DateTime{}
if protoimpl.UnsafeEnabled { mi := &file_protoapi_proto_msgTypes[2]
mi := &file_protoapi_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi)
ms.StoreMessageInfo(mi)
}
} }
func (x *DateTime) String() string { func (x *DateTime) String() string {
@@ -149,7 +155,7 @@ func (*DateTime) ProtoMessage() {}
func (x *DateTime) ProtoReflect() protoreflect.Message { func (x *DateTime) ProtoReflect() protoreflect.Message {
mi := &file_protoapi_proto_msgTypes[2] mi := &file_protoapi_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -172,20 +178,17 @@ func (x *DateTime) GetValue() string {
} }
type RequestDateTime struct { type RequestDateTime struct {
state protoimpl.MessageState state protoimpl.MessageState `protogen:"open.v1"`
sizeCache protoimpl.SizeCache Value string `protobuf:"bytes,2,opt,name=Value,proto3" json:"Value,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
Value string `protobuf:"bytes,2,opt,name=Value,proto3" json:"Value,omitempty"`
} }
func (x *RequestDateTime) Reset() { func (x *RequestDateTime) Reset() {
*x = RequestDateTime{} *x = RequestDateTime{}
if protoimpl.UnsafeEnabled { mi := &file_protoapi_proto_msgTypes[3]
mi := &file_protoapi_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi)
ms.StoreMessageInfo(mi)
}
} }
func (x *RequestDateTime) String() string { func (x *RequestDateTime) String() string {
@@ -196,7 +199,7 @@ func (*RequestDateTime) ProtoMessage() {}
func (x *RequestDateTime) ProtoReflect() protoreflect.Message { func (x *RequestDateTime) ProtoReflect() protoreflect.Message {
mi := &file_protoapi_proto_msgTypes[3] mi := &file_protoapi_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -218,23 +221,19 @@ func (x *RequestDateTime) GetValue() string {
return "" return ""
} }
// For random password
type RequestPass struct { type RequestPass struct {
state protoimpl.MessageState state protoimpl.MessageState `protogen:"open.v1"`
sizeCache protoimpl.SizeCache Seed int64 `protobuf:"varint,1,opt,name=Seed,proto3" json:"Seed,omitempty"`
Length int64 `protobuf:"varint,8,opt,name=Length,proto3" json:"Length,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
Seed int64 `protobuf:"varint,1,opt,name=Seed,proto3" json:"Seed,omitempty"`
Length int64 `protobuf:"varint,8,opt,name=Length,proto3" json:"Length,omitempty"`
} }
func (x *RequestPass) Reset() { func (x *RequestPass) Reset() {
*x = RequestPass{} *x = RequestPass{}
if protoimpl.UnsafeEnabled { mi := &file_protoapi_proto_msgTypes[4]
mi := &file_protoapi_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi)
ms.StoreMessageInfo(mi)
}
} }
func (x *RequestPass) String() string { func (x *RequestPass) String() string {
@@ -245,7 +244,7 @@ func (*RequestPass) ProtoMessage() {}
func (x *RequestPass) ProtoReflect() protoreflect.Message { func (x *RequestPass) ProtoReflect() protoreflect.Message {
mi := &file_protoapi_proto_msgTypes[4] mi := &file_protoapi_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -275,20 +274,17 @@ func (x *RequestPass) GetLength() int64 {
} }
type RandomPass struct { type RandomPass struct {
state protoimpl.MessageState state protoimpl.MessageState `protogen:"open.v1"`
sizeCache protoimpl.SizeCache Password string `protobuf:"bytes,1,opt,name=Password,proto3" json:"Password,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
Password string `protobuf:"bytes,1,opt,name=Password,proto3" json:"Password,omitempty"`
} }
func (x *RandomPass) Reset() { func (x *RandomPass) Reset() {
*x = RandomPass{} *x = RandomPass{}
if protoimpl.UnsafeEnabled { mi := &file_protoapi_proto_msgTypes[5]
mi := &file_protoapi_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi)
ms.StoreMessageInfo(mi)
}
} }
func (x *RandomPass) String() string { func (x *RandomPass) String() string {
@@ -299,7 +295,7 @@ func (*RandomPass) ProtoMessage() {}
func (x *RandomPass) ProtoReflect() protoreflect.Message { func (x *RandomPass) ProtoReflect() protoreflect.Message {
mi := &file_protoapi_proto_msgTypes[5] mi := &file_protoapi_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
@@ -323,66 +319,60 @@ func (x *RandomPass) GetPassword() string {
var File_protoapi_proto protoreflect.FileDescriptor var File_protoapi_proto protoreflect.FileDescriptor
var file_protoapi_proto_rawDesc = []byte{ const file_protoapi_proto_rawDesc = "" +
0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, "\n" +
0x22, 0x38, 0x0a, 0x0c, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, "\x0eprotoapi.proto\"g\n" +
0x12, 0x12, 0x0a, 0x04, 0x53, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, "\vSensingData\x12\x1a\n" +
0x53, 0x65, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, "\bDeviceId\x18\x01 \x01(\tR\bDeviceId\x12 \n" +
0x01, 0x28, 0x03, 0x52, 0x05, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x22, 0x21, 0x0a, 0x09, 0x52, 0x61, "\vTemperature\x18\x02 \x01(\x01R\vTemperature\x12\x1a\n" +
0x6e, 0x64, 0x6f, 0x6d, 0x49, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, "\bHumidity\x18\x03 \x01(\x01R\bHumidity\"E\n" +
0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x20, 0x0a, "\x0fSensingResponse\x12\x18\n" +
0x08, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, "\aSuccess\x18\x01 \x01(\bR\aSuccess\x12\x18\n" +
0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, "\aMessage\x18\x02 \x01(\tR\aMessage\" \n" +
0x27, 0x0a, 0x0f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, "\bDateTime\x12\x14\n" +
0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, "\x05Value\x18\x01 \x01(\tR\x05Value\"'\n" +
0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x39, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, "\x0fRequestDateTime\x12\x14\n" +
0x65, 0x73, 0x74, 0x50, 0x61, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x65, 0x65, 0x64, 0x18, "\x05Value\x18\x02 \x01(\tR\x05Value\"9\n" +
0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x53, 0x65, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x4c, "\vRequestPass\x12\x12\n" +
0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4c, 0x65, 0x6e, "\x04Seed\x18\x01 \x01(\x03R\x04Seed\x12\x16\n" +
0x67, 0x74, 0x68, 0x22, 0x28, 0x0a, 0x0a, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x61, 0x73, "\x06Length\x18\b \x01(\x03R\x06Length\"(\n" +
0x73, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, "\n" +
0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x32, 0x84, 0x01, "RandomPass\x12\x1a\n" +
0x0a, 0x06, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x12, 0x26, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x44, "\bPassword\x18\x01 \x01(\tR\bPassword2\x95\x01\n" +
0x61, 0x74, 0x65, 0x12, 0x10, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, "\n" +
0x65, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x09, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, "IoTService\x12&\n" +
0x12, 0x26, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x12, 0x0d, 0x2e, "\aGetDate\x12\x10.RequestDateTime\x1a\t.DateTime\x123\n" +
0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0a, 0x2e, 0x52, "\x11UpdateSensingData\x12\f.SensingData\x1a\x10.SensingResponse\x12*\n" +
0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x49, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x52, "\rGetRandomPass\x12\f.RequestPass\x1a\v.RandomPassB\x16Z\x14./protoapi/;protoapib\x06proto3"
0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x61, 0x73, 0x73, 0x12, 0x0c, 0x2e, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x50, 0x61, 0x73, 0x73, 0x1a, 0x0b, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d,
0x50, 0x61, 0x73, 0x73, 0x42, 0x16, 0x5a, 0x14, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61,
0x70, 0x69, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_protoapi_proto_rawDescOnce sync.Once file_protoapi_proto_rawDescOnce sync.Once
file_protoapi_proto_rawDescData = file_protoapi_proto_rawDesc file_protoapi_proto_rawDescData []byte
) )
func file_protoapi_proto_rawDescGZIP() []byte { func file_protoapi_proto_rawDescGZIP() []byte {
file_protoapi_proto_rawDescOnce.Do(func() { file_protoapi_proto_rawDescOnce.Do(func() {
file_protoapi_proto_rawDescData = protoimpl.X.CompressGZIP(file_protoapi_proto_rawDescData) file_protoapi_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_protoapi_proto_rawDesc), len(file_protoapi_proto_rawDesc)))
}) })
return file_protoapi_proto_rawDescData return file_protoapi_proto_rawDescData
} }
var file_protoapi_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_protoapi_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_protoapi_proto_goTypes = []interface{}{ var file_protoapi_proto_goTypes = []any{
(*RandomParams)(nil), // 0: RandomParams (*SensingData)(nil), // 0: SensingData
(*RandomInt)(nil), // 1: RandomInt (*SensingResponse)(nil), // 1: SensingResponse
(*DateTime)(nil), // 2: DateTime (*DateTime)(nil), // 2: DateTime
(*RequestDateTime)(nil), // 3: RequestDateTime (*RequestDateTime)(nil), // 3: RequestDateTime
(*RequestPass)(nil), // 4: RequestPass (*RequestPass)(nil), // 4: RequestPass
(*RandomPass)(nil), // 5: RandomPass (*RandomPass)(nil), // 5: RandomPass
} }
var file_protoapi_proto_depIdxs = []int32{ var file_protoapi_proto_depIdxs = []int32{
3, // 0: Random.GetDate:input_type -> RequestDateTime 3, // 0: IoTService.GetDate:input_type -> RequestDateTime
0, // 1: Random.GetRandom:input_type -> RandomParams 0, // 1: IoTService.UpdateSensingData:input_type -> SensingData
4, // 2: Random.GetRandomPass:input_type -> RequestPass 4, // 2: IoTService.GetRandomPass:input_type -> RequestPass
2, // 3: Random.GetDate:output_type -> DateTime 2, // 3: IoTService.GetDate:output_type -> DateTime
1, // 4: Random.GetRandom:output_type -> RandomInt 1, // 4: IoTService.UpdateSensingData:output_type -> SensingResponse
5, // 5: Random.GetRandomPass:output_type -> RandomPass 5, // 5: IoTService.GetRandomPass:output_type -> RandomPass
3, // [3:6] is the sub-list for method output_type 3, // [3:6] is the sub-list for method output_type
0, // [0:3] is the sub-list for method input_type 0, // [0:3] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension type_name
@@ -395,85 +385,11 @@ func file_protoapi_proto_init() {
if File_protoapi_proto != nil { if File_protoapi_proto != nil {
return return
} }
if !protoimpl.UnsafeEnabled {
file_protoapi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RandomParams); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_protoapi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RandomInt); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_protoapi_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DateTime); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_protoapi_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RequestDateTime); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_protoapi_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RequestPass); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_protoapi_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RandomPass); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_protoapi_proto_rawDesc, RawDescriptor: unsafe.Slice(unsafe.StringData(file_protoapi_proto_rawDesc), len(file_protoapi_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 6, NumMessages: 6,
NumExtensions: 0, NumExtensions: 0,
@@ -484,7 +400,6 @@ func file_protoapi_proto_init() {
MessageInfos: file_protoapi_proto_msgTypes, MessageInfos: file_protoapi_proto_msgTypes,
}.Build() }.Build()
File_protoapi_proto = out.File File_protoapi_proto = out.File
file_protoapi_proto_rawDesc = nil
file_protoapi_proto_goTypes = nil file_protoapi_proto_goTypes = nil
file_protoapi_proto_depIdxs = nil file_protoapi_proto_depIdxs = nil
} }
+61 -61
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.5.1 // - protoc-gen-go-grpc v1.6.2
// - protoc v3.21.12 // - protoc v5.27.2
// source: protoapi.proto // source: protoapi.proto
package protoapi package protoapi
@@ -19,177 +19,177 @@ import (
const _ = grpc.SupportPackageIsVersion9 const _ = grpc.SupportPackageIsVersion9
const ( const (
Random_GetDate_FullMethodName = "/Random/GetDate" IoTService_GetDate_FullMethodName = "/IoTService/GetDate"
Random_GetRandom_FullMethodName = "/Random/GetRandom" IoTService_UpdateSensingData_FullMethodName = "/IoTService/UpdateSensingData"
Random_GetRandomPass_FullMethodName = "/Random/GetRandomPass" IoTService_GetRandomPass_FullMethodName = "/IoTService/GetRandomPass"
) )
// RandomClient is the client API for Random service. // IoTServiceClient is the client API for IoTService service.
// //
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type RandomClient interface { type IoTServiceClient interface {
GetDate(ctx context.Context, in *RequestDateTime, opts ...grpc.CallOption) (*DateTime, error) GetDate(ctx context.Context, in *RequestDateTime, opts ...grpc.CallOption) (*DateTime, error)
GetRandom(ctx context.Context, in *RandomParams, opts ...grpc.CallOption) (*RandomInt, error) UpdateSensingData(ctx context.Context, in *SensingData, opts ...grpc.CallOption) (*SensingResponse, error)
GetRandomPass(ctx context.Context, in *RequestPass, opts ...grpc.CallOption) (*RandomPass, error) GetRandomPass(ctx context.Context, in *RequestPass, opts ...grpc.CallOption) (*RandomPass, error)
} }
type randomClient struct { type ioTServiceClient struct {
cc grpc.ClientConnInterface cc grpc.ClientConnInterface
} }
func NewRandomClient(cc grpc.ClientConnInterface) RandomClient { func NewIoTServiceClient(cc grpc.ClientConnInterface) IoTServiceClient {
return &randomClient{cc} return &ioTServiceClient{cc}
} }
func (c *randomClient) GetDate(ctx context.Context, in *RequestDateTime, opts ...grpc.CallOption) (*DateTime, error) { func (c *ioTServiceClient) GetDate(ctx context.Context, in *RequestDateTime, opts ...grpc.CallOption) (*DateTime, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DateTime) out := new(DateTime)
err := c.cc.Invoke(ctx, Random_GetDate_FullMethodName, in, out, cOpts...) err := c.cc.Invoke(ctx, IoTService_GetDate_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return out, nil return out, nil
} }
func (c *randomClient) GetRandom(ctx context.Context, in *RandomParams, opts ...grpc.CallOption) (*RandomInt, error) { func (c *ioTServiceClient) UpdateSensingData(ctx context.Context, in *SensingData, opts ...grpc.CallOption) (*SensingResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RandomInt) out := new(SensingResponse)
err := c.cc.Invoke(ctx, Random_GetRandom_FullMethodName, in, out, cOpts...) err := c.cc.Invoke(ctx, IoTService_UpdateSensingData_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return out, nil return out, nil
} }
func (c *randomClient) GetRandomPass(ctx context.Context, in *RequestPass, opts ...grpc.CallOption) (*RandomPass, error) { func (c *ioTServiceClient) GetRandomPass(ctx context.Context, in *RequestPass, opts ...grpc.CallOption) (*RandomPass, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RandomPass) out := new(RandomPass)
err := c.cc.Invoke(ctx, Random_GetRandomPass_FullMethodName, in, out, cOpts...) err := c.cc.Invoke(ctx, IoTService_GetRandomPass_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return out, nil return out, nil
} }
// RandomServer is the server API for Random service. // IoTServiceServer is the server API for IoTService service.
// All implementations must embed UnimplementedRandomServer // All implementations must embed UnimplementedIoTServiceServer
// for forward compatibility. // for forward compatibility.
type RandomServer interface { type IoTServiceServer interface {
GetDate(context.Context, *RequestDateTime) (*DateTime, error) GetDate(context.Context, *RequestDateTime) (*DateTime, error)
GetRandom(context.Context, *RandomParams) (*RandomInt, error) UpdateSensingData(context.Context, *SensingData) (*SensingResponse, error)
GetRandomPass(context.Context, *RequestPass) (*RandomPass, error) GetRandomPass(context.Context, *RequestPass) (*RandomPass, error)
mustEmbedUnimplementedRandomServer() mustEmbedUnimplementedIoTServiceServer()
} }
// UnimplementedRandomServer must be embedded to have // UnimplementedIoTServiceServer must be embedded to have
// forward compatible implementations. // forward compatible implementations.
// //
// NOTE: this should be embedded by value instead of pointer to avoid a nil // NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called. // pointer dereference when methods are called.
type UnimplementedRandomServer struct{} type UnimplementedIoTServiceServer struct{}
func (UnimplementedRandomServer) GetDate(context.Context, *RequestDateTime) (*DateTime, error) { func (UnimplementedIoTServiceServer) GetDate(context.Context, *RequestDateTime) (*DateTime, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetDate not implemented") return nil, status.Error(codes.Unimplemented, "method GetDate not implemented")
} }
func (UnimplementedRandomServer) GetRandom(context.Context, *RandomParams) (*RandomInt, error) { func (UnimplementedIoTServiceServer) UpdateSensingData(context.Context, *SensingData) (*SensingResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRandom not implemented") return nil, status.Error(codes.Unimplemented, "method UpdateSensingData not implemented")
} }
func (UnimplementedRandomServer) GetRandomPass(context.Context, *RequestPass) (*RandomPass, error) { func (UnimplementedIoTServiceServer) GetRandomPass(context.Context, *RequestPass) (*RandomPass, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetRandomPass not implemented") return nil, status.Error(codes.Unimplemented, "method GetRandomPass not implemented")
} }
func (UnimplementedRandomServer) mustEmbedUnimplementedRandomServer() {} func (UnimplementedIoTServiceServer) mustEmbedUnimplementedIoTServiceServer() {}
func (UnimplementedRandomServer) testEmbeddedByValue() {} func (UnimplementedIoTServiceServer) testEmbeddedByValue() {}
// UnsafeRandomServer may be embedded to opt out of forward compatibility for this service. // UnsafeIoTServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to RandomServer will // Use of this interface is not recommended, as added methods to IoTServiceServer will
// result in compilation errors. // result in compilation errors.
type UnsafeRandomServer interface { type UnsafeIoTServiceServer interface {
mustEmbedUnimplementedRandomServer() mustEmbedUnimplementedIoTServiceServer()
} }
func RegisterRandomServer(s grpc.ServiceRegistrar, srv RandomServer) { func RegisterIoTServiceServer(s grpc.ServiceRegistrar, srv IoTServiceServer) {
// If the following call pancis, it indicates UnimplementedRandomServer was // If the following call panics, it indicates UnimplementedIoTServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue() t.testEmbeddedByValue()
} }
s.RegisterService(&Random_ServiceDesc, srv) s.RegisterService(&IoTService_ServiceDesc, srv)
} }
func _Random_GetDate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _IoTService_GetDate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RequestDateTime) in := new(RequestDateTime)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
if interceptor == nil { if interceptor == nil {
return srv.(RandomServer).GetDate(ctx, in) return srv.(IoTServiceServer).GetDate(ctx, in)
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: Random_GetDate_FullMethodName, FullMethod: IoTService_GetDate_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RandomServer).GetDate(ctx, req.(*RequestDateTime)) return srv.(IoTServiceServer).GetDate(ctx, req.(*RequestDateTime))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Random_GetRandom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _IoTService_UpdateSensingData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RandomParams) in := new(SensingData)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
if interceptor == nil { if interceptor == nil {
return srv.(RandomServer).GetRandom(ctx, in) return srv.(IoTServiceServer).UpdateSensingData(ctx, in)
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: Random_GetRandom_FullMethodName, FullMethod: IoTService_UpdateSensingData_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RandomServer).GetRandom(ctx, req.(*RandomParams)) return srv.(IoTServiceServer).UpdateSensingData(ctx, req.(*SensingData))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Random_GetRandomPass_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _IoTService_GetRandomPass_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RequestPass) in := new(RequestPass)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
if interceptor == nil { if interceptor == nil {
return srv.(RandomServer).GetRandomPass(ctx, in) return srv.(IoTServiceServer).GetRandomPass(ctx, in)
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: Random_GetRandomPass_FullMethodName, FullMethod: IoTService_GetRandomPass_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RandomServer).GetRandomPass(ctx, req.(*RequestPass)) return srv.(IoTServiceServer).GetRandomPass(ctx, req.(*RequestPass))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
// Random_ServiceDesc is the grpc.ServiceDesc for Random service. // IoTService_ServiceDesc is the grpc.ServiceDesc for IoTService service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
var Random_ServiceDesc = grpc.ServiceDesc{ var IoTService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "Random", ServiceName: "IoTService",
HandlerType: (*RandomServer)(nil), HandlerType: (*IoTServiceServer)(nil),
Methods: []grpc.MethodDesc{ Methods: []grpc.MethodDesc{
{ {
MethodName: "GetDate", MethodName: "GetDate",
Handler: _Random_GetDate_Handler, Handler: _IoTService_GetDate_Handler,
}, },
{ {
MethodName: "GetRandom", MethodName: "UpdateSensingData",
Handler: _Random_GetRandom_Handler, Handler: _IoTService_UpdateSensingData_Handler,
}, },
{ {
MethodName: "GetRandomPass", MethodName: "GetRandomPass",
Handler: _Random_GetRandomPass_Handler, Handler: _IoTService_GetRandomPass_Handler,
}, },
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
# generate-env.sh — create a local .env from the committed .env.example template.
#
# Behaviour:
# - .env absent → copy .env.example to .env, print the path.
# - .env present → no-op (leaves your edits intact), exit 0.
# - .env present --force → overwrite .env from .env.example (backs up to .env.bak).
#
# Paths are resolved relative to this script (repo root = parent of deploy/),
# so it works regardless of the caller's cwd.
#
# Usage: deploy/generate-env.sh [--force] [-h|--help]
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SRC="$REPO_ROOT/.env.example"
DST="$REPO_ROOT/.env"
FORCE=0
while [ $# -gt 0 ]; do
case "$1" in
--force) FORCE=1; shift ;;
-h|--help)
echo "Usage: $0 [--force]"
echo " Create .env from .env.example. --force overwrites an existing .env."
exit 0 ;;
*) echo "ERROR: unknown arg: $1" >&2; echo "Usage: $0 [--force]" >&2; exit 2 ;;
esac
done
[ -f "$SRC" ] || { echo "ERROR: template not found: $SRC" >&2; exit 1; }
if [ -f "$DST" ] && [ "$FORCE" != "1" ]; then
echo "no-op: $DST already exists (use --force to overwrite)"
exit 0
fi
if [ -f "$DST" ] && [ "$FORCE" = "1" ]; then
cp -p "$DST" "$DST.bak"
echo "backed up existing .env -> $DST.bak"
fi
cp "$SRC" "$DST"
echo "created: $DST"
echo "Next: edit $DST and fill in any secrets (look for 'replace_me')."