commit
4ee1259d51
@ -0,0 +1,19 @@ |
|||||||
|
tcp-protobuf-app/ |
||||||
|
├── proto/ |
||||||
|
│ └── service/ |
||||||
|
│ └── v1/ |
||||||
|
│ └── service.proto # Схема Protobuf |
||||||
|
├── buf.yaml # Конфігурація Buf |
||||||
|
├── buf.gen.yaml # Налаштування генерації |
||||||
|
├── server/ |
||||||
|
│ └── main.go |
||||||
|
└── client/ |
||||||
|
└── main.go |
||||||
|
|
||||||
|
|
||||||
|
go install github.com/bufbuild/buf/cmd/buf@latest |
||||||
|
|
||||||
|
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest |
||||||
|
|
||||||
|
|
||||||
|
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest |
@ -0,0 +1,46 @@ |
|||||||
|
package bin |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"net" |
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto" |
||||||
|
) |
||||||
|
|
||||||
|
func WriteMessage(conn net.Conn, m proto.Message) error { |
||||||
|
data, err := proto.Marshal(m) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("proto.Marshal: %w", err) |
||||||
|
} |
||||||
|
var ( |
||||||
|
length = len(data) |
||||||
|
w = bytes.NewBuffer(nil) |
||||||
|
) |
||||||
|
w.Write([]byte{ |
||||||
|
byte(length), |
||||||
|
byte(length >> 8), |
||||||
|
}) |
||||||
|
w.Write(data) |
||||||
|
if _, err := conn.Write(w.Bytes()); err != nil { |
||||||
|
return fmt.Errorf("conn.Write: %w", err) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func ReadMessage(conn net.Conn, m proto.Message) error { |
||||||
|
buf := make([]byte, 2) |
||||||
|
if _, err := io.ReadFull(conn, buf); err != nil { |
||||||
|
return fmt.Errorf("io.ReadFull: %w", err) |
||||||
|
} |
||||||
|
length := int(buf[0]) | (int(buf[1]) << 8) |
||||||
|
buf = make([]byte, length) |
||||||
|
if _, err := io.ReadFull(conn, buf); err != nil { |
||||||
|
return fmt.Errorf("io.ReadFull: %w", err) |
||||||
|
} |
||||||
|
if err := proto.Unmarshal(buf, m); err != nil { |
||||||
|
return fmt.Errorf("proto.Unmarshal: %w", err) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
version: v1 |
||||||
|
plugins: |
||||||
|
- name: go |
||||||
|
out: gen |
||||||
|
opt: paths=source_relative |
@ -0,0 +1,8 @@ |
|||||||
|
version: v1 |
||||||
|
name: buf.build/dima/tcp-service |
||||||
|
lint: |
||||||
|
use: |
||||||
|
- DEFAULT |
||||||
|
breaking: |
||||||
|
use: |
||||||
|
- FILE |
@ -0,0 +1,82 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"log" |
||||||
|
"net" |
||||||
|
"time" |
||||||
|
|
||||||
|
pb "gordenko.dev/dima/protolab/gen/service/v1" |
||||||
|
|
||||||
|
"gordenko.dev/dima/protolab/bin" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
ServerAddr = "localhost:8080" |
||||||
|
) |
||||||
|
|
||||||
|
func main() { |
||||||
|
conn, err := net.Dial("tcp", ServerAddr) |
||||||
|
if err != nil { |
||||||
|
log.Fatalf("net.Dial: %v\n", err) |
||||||
|
} |
||||||
|
defer conn.Close() |
||||||
|
|
||||||
|
log.Println("connected to server") |
||||||
|
|
||||||
|
for { |
||||||
|
// PING
|
||||||
|
pingReq := &pb.RequestWrapper{ |
||||||
|
Payload: &pb.RequestWrapper_Ping{ |
||||||
|
Ping: &pb.PingRequest{ |
||||||
|
Timestamp: time.Now().Unix(), |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
err = bin.WriteMessage(conn, pingReq) |
||||||
|
if err != nil { |
||||||
|
log.Fatalf("send ping: %v", err) |
||||||
|
} else { |
||||||
|
log.Println("ping sent") |
||||||
|
} |
||||||
|
|
||||||
|
var pingResp pb.ResponseWrapper |
||||||
|
err = bin.ReadMessage(conn, &pingResp) |
||||||
|
if err != nil { |
||||||
|
log.Fatalf("read ping response: %v", err) |
||||||
|
} |
||||||
|
if resp := pingResp.GetPingResp(); resp != nil { |
||||||
|
log.Printf("pong received, server time: %s\n", |
||||||
|
time.Unix(resp.GetServerTimestamp(), 0).Format(time.RFC3339)) |
||||||
|
} |
||||||
|
|
||||||
|
time.Sleep(3 * time.Second) |
||||||
|
// TELEMETRY
|
||||||
|
telemetryReq := &pb.RequestWrapper{ |
||||||
|
Payload: &pb.RequestWrapper_SendTelemetry{ |
||||||
|
SendTelemetry: &pb.TelemetryData{ |
||||||
|
DeviceId: "device-123", |
||||||
|
Timestamp: time.Now().Unix(), |
||||||
|
Temperature: 25, |
||||||
|
BatteryLevel: 70, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
err = bin.WriteMessage(conn, telemetryReq) |
||||||
|
if err != nil { |
||||||
|
log.Fatalf("send telemetry: %v", err) |
||||||
|
} else { |
||||||
|
log.Println("telemetry sent") |
||||||
|
} |
||||||
|
|
||||||
|
var telemetryResp pb.ResponseWrapper |
||||||
|
err = bin.ReadMessage(conn, &telemetryResp) |
||||||
|
if err != nil { |
||||||
|
log.Fatalf("bin.ReadMessage: %v", err) |
||||||
|
} |
||||||
|
if resp := telemetryResp.GetTelemetryResp(); resp != nil { |
||||||
|
log.Printf("server response on TelemetryData: %s\n", resp.GetResultCode().String()) |
||||||
|
} |
||||||
|
time.Sleep(3 * time.Second) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,536 @@ |
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: service/v1/service.proto
|
||||||
|
|
||||||
|
package servicev1 |
||||||
|
|
||||||
|
import ( |
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect" |
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl" |
||||||
|
reflect "reflect" |
||||||
|
sync "sync" |
||||||
|
unsafe "unsafe" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) |
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) |
||||||
|
) |
||||||
|
|
||||||
|
type TelemetryResponse_ResultCode int32 |
||||||
|
|
||||||
|
const ( |
||||||
|
TelemetryResponse_RESULT_CODE_UNSPECIFIED TelemetryResponse_ResultCode = 0 |
||||||
|
TelemetryResponse_RESULT_CODE_SUCCEEDED TelemetryResponse_ResultCode = 1 |
||||||
|
TelemetryResponse_RESULT_CODE_FAILED TelemetryResponse_ResultCode = 2 |
||||||
|
) |
||||||
|
|
||||||
|
// Enum value maps for TelemetryResponse_ResultCode.
|
||||||
|
var ( |
||||||
|
TelemetryResponse_ResultCode_name = map[int32]string{ |
||||||
|
0: "RESULT_CODE_UNSPECIFIED", |
||||||
|
1: "RESULT_CODE_SUCCEEDED", |
||||||
|
2: "RESULT_CODE_FAILED", |
||||||
|
} |
||||||
|
TelemetryResponse_ResultCode_value = map[string]int32{ |
||||||
|
"RESULT_CODE_UNSPECIFIED": 0, |
||||||
|
"RESULT_CODE_SUCCEEDED": 1, |
||||||
|
"RESULT_CODE_FAILED": 2, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func (x TelemetryResponse_ResultCode) Enum() *TelemetryResponse_ResultCode { |
||||||
|
p := new(TelemetryResponse_ResultCode) |
||||||
|
*p = x |
||||||
|
return p |
||||||
|
} |
||||||
|
|
||||||
|
func (x TelemetryResponse_ResultCode) String() string { |
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) |
||||||
|
} |
||||||
|
|
||||||
|
func (TelemetryResponse_ResultCode) Descriptor() protoreflect.EnumDescriptor { |
||||||
|
return file_service_v1_service_proto_enumTypes[0].Descriptor() |
||||||
|
} |
||||||
|
|
||||||
|
func (TelemetryResponse_ResultCode) Type() protoreflect.EnumType { |
||||||
|
return &file_service_v1_service_proto_enumTypes[0] |
||||||
|
} |
||||||
|
|
||||||
|
func (x TelemetryResponse_ResultCode) Number() protoreflect.EnumNumber { |
||||||
|
return protoreflect.EnumNumber(x) |
||||||
|
} |
||||||
|
|
||||||
|
// Deprecated: Use TelemetryResponse_ResultCode.Descriptor instead.
|
||||||
|
func (TelemetryResponse_ResultCode) EnumDescriptor() ([]byte, []int) { |
||||||
|
return file_service_v1_service_proto_rawDescGZIP(), []int{3, 0} |
||||||
|
} |
||||||
|
|
||||||
|
type TelemetryData struct { |
||||||
|
state protoimpl.MessageState `protogen:"open.v1"` |
||||||
|
DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` |
||||||
|
Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` |
||||||
|
Temperature float64 `protobuf:"fixed64,3,opt,name=temperature,proto3" json:"temperature,omitempty"` |
||||||
|
BatteryLevel int32 `protobuf:"varint,4,opt,name=battery_level,json=batteryLevel,proto3" json:"battery_level,omitempty"` |
||||||
|
unknownFields protoimpl.UnknownFields |
||||||
|
sizeCache protoimpl.SizeCache |
||||||
|
} |
||||||
|
|
||||||
|
func (x *TelemetryData) Reset() { |
||||||
|
*x = TelemetryData{} |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[0] |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
|
||||||
|
func (x *TelemetryData) String() string { |
||||||
|
return protoimpl.X.MessageStringOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
func (*TelemetryData) ProtoMessage() {} |
||||||
|
|
||||||
|
func (x *TelemetryData) ProtoReflect() protoreflect.Message { |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[0] |
||||||
|
if x != nil { |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
if ms.LoadMessageInfo() == nil { |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
return ms |
||||||
|
} |
||||||
|
return mi.MessageOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
// Deprecated: Use TelemetryData.ProtoReflect.Descriptor instead.
|
||||||
|
func (*TelemetryData) Descriptor() ([]byte, []int) { |
||||||
|
return file_service_v1_service_proto_rawDescGZIP(), []int{0} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *TelemetryData) GetDeviceId() string { |
||||||
|
if x != nil { |
||||||
|
return x.DeviceId |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
func (x *TelemetryData) GetTimestamp() int64 { |
||||||
|
if x != nil { |
||||||
|
return x.Timestamp |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
func (x *TelemetryData) GetTemperature() float64 { |
||||||
|
if x != nil { |
||||||
|
return x.Temperature |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
func (x *TelemetryData) GetBatteryLevel() int32 { |
||||||
|
if x != nil { |
||||||
|
return x.BatteryLevel |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
type PingRequest struct { |
||||||
|
state protoimpl.MessageState `protogen:"open.v1"` |
||||||
|
Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` |
||||||
|
unknownFields protoimpl.UnknownFields |
||||||
|
sizeCache protoimpl.SizeCache |
||||||
|
} |
||||||
|
|
||||||
|
func (x *PingRequest) Reset() { |
||||||
|
*x = PingRequest{} |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[1] |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
|
||||||
|
func (x *PingRequest) String() string { |
||||||
|
return protoimpl.X.MessageStringOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
func (*PingRequest) ProtoMessage() {} |
||||||
|
|
||||||
|
func (x *PingRequest) ProtoReflect() protoreflect.Message { |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[1] |
||||||
|
if x != nil { |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
if ms.LoadMessageInfo() == nil { |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
return ms |
||||||
|
} |
||||||
|
return mi.MessageOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
// Deprecated: Use PingRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PingRequest) Descriptor() ([]byte, []int) { |
||||||
|
return file_service_v1_service_proto_rawDescGZIP(), []int{1} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *PingRequest) GetTimestamp() int64 { |
||||||
|
if x != nil { |
||||||
|
return x.Timestamp |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
type RequestWrapper struct { |
||||||
|
state protoimpl.MessageState `protogen:"open.v1"` |
||||||
|
// Types that are valid to be assigned to Payload:
|
||||||
|
//
|
||||||
|
// *RequestWrapper_SendTelemetry
|
||||||
|
// *RequestWrapper_Ping
|
||||||
|
Payload isRequestWrapper_Payload `protobuf_oneof:"payload"` |
||||||
|
unknownFields protoimpl.UnknownFields |
||||||
|
sizeCache protoimpl.SizeCache |
||||||
|
} |
||||||
|
|
||||||
|
func (x *RequestWrapper) Reset() { |
||||||
|
*x = RequestWrapper{} |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[2] |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
|
||||||
|
func (x *RequestWrapper) String() string { |
||||||
|
return protoimpl.X.MessageStringOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
func (*RequestWrapper) ProtoMessage() {} |
||||||
|
|
||||||
|
func (x *RequestWrapper) ProtoReflect() protoreflect.Message { |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[2] |
||||||
|
if x != nil { |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
if ms.LoadMessageInfo() == nil { |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
return ms |
||||||
|
} |
||||||
|
return mi.MessageOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
// Deprecated: Use RequestWrapper.ProtoReflect.Descriptor instead.
|
||||||
|
func (*RequestWrapper) Descriptor() ([]byte, []int) { |
||||||
|
return file_service_v1_service_proto_rawDescGZIP(), []int{2} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *RequestWrapper) GetPayload() isRequestWrapper_Payload { |
||||||
|
if x != nil { |
||||||
|
return x.Payload |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (x *RequestWrapper) GetSendTelemetry() *TelemetryData { |
||||||
|
if x != nil { |
||||||
|
if x, ok := x.Payload.(*RequestWrapper_SendTelemetry); ok { |
||||||
|
return x.SendTelemetry |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (x *RequestWrapper) GetPing() *PingRequest { |
||||||
|
if x != nil { |
||||||
|
if x, ok := x.Payload.(*RequestWrapper_Ping); ok { |
||||||
|
return x.Ping |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
type isRequestWrapper_Payload interface { |
||||||
|
isRequestWrapper_Payload() |
||||||
|
} |
||||||
|
|
||||||
|
type RequestWrapper_SendTelemetry struct { |
||||||
|
SendTelemetry *TelemetryData `protobuf:"bytes,1,opt,name=send_telemetry,json=sendTelemetry,proto3,oneof"` |
||||||
|
} |
||||||
|
|
||||||
|
type RequestWrapper_Ping struct { |
||||||
|
Ping *PingRequest `protobuf:"bytes,2,opt,name=ping,proto3,oneof"` |
||||||
|
} |
||||||
|
|
||||||
|
func (*RequestWrapper_SendTelemetry) isRequestWrapper_Payload() {} |
||||||
|
|
||||||
|
func (*RequestWrapper_Ping) isRequestWrapper_Payload() {} |
||||||
|
|
||||||
|
type TelemetryResponse struct { |
||||||
|
state protoimpl.MessageState `protogen:"open.v1"` |
||||||
|
ResultCode TelemetryResponse_ResultCode `protobuf:"varint,1,opt,name=result_code,json=resultCode,proto3,enum=service.v1.TelemetryResponse_ResultCode" json:"result_code,omitempty"` |
||||||
|
unknownFields protoimpl.UnknownFields |
||||||
|
sizeCache protoimpl.SizeCache |
||||||
|
} |
||||||
|
|
||||||
|
func (x *TelemetryResponse) Reset() { |
||||||
|
*x = TelemetryResponse{} |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[3] |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
|
||||||
|
func (x *TelemetryResponse) String() string { |
||||||
|
return protoimpl.X.MessageStringOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
func (*TelemetryResponse) ProtoMessage() {} |
||||||
|
|
||||||
|
func (x *TelemetryResponse) ProtoReflect() protoreflect.Message { |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[3] |
||||||
|
if x != nil { |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
if ms.LoadMessageInfo() == nil { |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
return ms |
||||||
|
} |
||||||
|
return mi.MessageOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
// Deprecated: Use TelemetryResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*TelemetryResponse) Descriptor() ([]byte, []int) { |
||||||
|
return file_service_v1_service_proto_rawDescGZIP(), []int{3} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *TelemetryResponse) GetResultCode() TelemetryResponse_ResultCode { |
||||||
|
if x != nil { |
||||||
|
return x.ResultCode |
||||||
|
} |
||||||
|
return TelemetryResponse_RESULT_CODE_UNSPECIFIED |
||||||
|
} |
||||||
|
|
||||||
|
type PingResponse struct { |
||||||
|
state protoimpl.MessageState `protogen:"open.v1"` |
||||||
|
ServerTimestamp int64 `protobuf:"varint,1,opt,name=server_timestamp,json=serverTimestamp,proto3" json:"server_timestamp,omitempty"` |
||||||
|
unknownFields protoimpl.UnknownFields |
||||||
|
sizeCache protoimpl.SizeCache |
||||||
|
} |
||||||
|
|
||||||
|
func (x *PingResponse) Reset() { |
||||||
|
*x = PingResponse{} |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[4] |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
|
||||||
|
func (x *PingResponse) String() string { |
||||||
|
return protoimpl.X.MessageStringOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
func (*PingResponse) ProtoMessage() {} |
||||||
|
|
||||||
|
func (x *PingResponse) ProtoReflect() protoreflect.Message { |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[4] |
||||||
|
if x != nil { |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
if ms.LoadMessageInfo() == nil { |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
return ms |
||||||
|
} |
||||||
|
return mi.MessageOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
// Deprecated: Use PingResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PingResponse) Descriptor() ([]byte, []int) { |
||||||
|
return file_service_v1_service_proto_rawDescGZIP(), []int{4} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *PingResponse) GetServerTimestamp() int64 { |
||||||
|
if x != nil { |
||||||
|
return x.ServerTimestamp |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
type ResponseWrapper struct { |
||||||
|
state protoimpl.MessageState `protogen:"open.v1"` |
||||||
|
// Types that are valid to be assigned to Payload:
|
||||||
|
//
|
||||||
|
// *ResponseWrapper_TelemetryResp
|
||||||
|
// *ResponseWrapper_PingResp
|
||||||
|
Payload isResponseWrapper_Payload `protobuf_oneof:"payload"` |
||||||
|
unknownFields protoimpl.UnknownFields |
||||||
|
sizeCache protoimpl.SizeCache |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ResponseWrapper) Reset() { |
||||||
|
*x = ResponseWrapper{} |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[5] |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ResponseWrapper) String() string { |
||||||
|
return protoimpl.X.MessageStringOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
func (*ResponseWrapper) ProtoMessage() {} |
||||||
|
|
||||||
|
func (x *ResponseWrapper) ProtoReflect() protoreflect.Message { |
||||||
|
mi := &file_service_v1_service_proto_msgTypes[5] |
||||||
|
if x != nil { |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
if ms.LoadMessageInfo() == nil { |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
return ms |
||||||
|
} |
||||||
|
return mi.MessageOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
// Deprecated: Use ResponseWrapper.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ResponseWrapper) Descriptor() ([]byte, []int) { |
||||||
|
return file_service_v1_service_proto_rawDescGZIP(), []int{5} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ResponseWrapper) GetPayload() isResponseWrapper_Payload { |
||||||
|
if x != nil { |
||||||
|
return x.Payload |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ResponseWrapper) GetTelemetryResp() *TelemetryResponse { |
||||||
|
if x != nil { |
||||||
|
if x, ok := x.Payload.(*ResponseWrapper_TelemetryResp); ok { |
||||||
|
return x.TelemetryResp |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ResponseWrapper) GetPingResp() *PingResponse { |
||||||
|
if x != nil { |
||||||
|
if x, ok := x.Payload.(*ResponseWrapper_PingResp); ok { |
||||||
|
return x.PingResp |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
type isResponseWrapper_Payload interface { |
||||||
|
isResponseWrapper_Payload() |
||||||
|
} |
||||||
|
|
||||||
|
type ResponseWrapper_TelemetryResp struct { |
||||||
|
TelemetryResp *TelemetryResponse `protobuf:"bytes,1,opt,name=telemetry_resp,json=telemetryResp,proto3,oneof"` |
||||||
|
} |
||||||
|
|
||||||
|
type ResponseWrapper_PingResp struct { |
||||||
|
PingResp *PingResponse `protobuf:"bytes,2,opt,name=ping_resp,json=pingResp,proto3,oneof"` |
||||||
|
} |
||||||
|
|
||||||
|
func (*ResponseWrapper_TelemetryResp) isResponseWrapper_Payload() {} |
||||||
|
|
||||||
|
func (*ResponseWrapper_PingResp) isResponseWrapper_Payload() {} |
||||||
|
|
||||||
|
var File_service_v1_service_proto protoreflect.FileDescriptor |
||||||
|
|
||||||
|
const file_service_v1_service_proto_rawDesc = "" + |
||||||
|
"\n" + |
||||||
|
"\x18service/v1/service.proto\x12\n" + |
||||||
|
"service.v1\"\x91\x01\n" + |
||||||
|
"\rTelemetryData\x12\x1b\n" + |
||||||
|
"\tdevice_id\x18\x01 \x01(\tR\bdeviceId\x12\x1c\n" + |
||||||
|
"\ttimestamp\x18\x02 \x01(\x03R\ttimestamp\x12 \n" + |
||||||
|
"\vtemperature\x18\x03 \x01(\x01R\vtemperature\x12#\n" + |
||||||
|
"\rbattery_level\x18\x04 \x01(\x05R\fbatteryLevel\"+\n" + |
||||||
|
"\vPingRequest\x12\x1c\n" + |
||||||
|
"\ttimestamp\x18\x01 \x01(\x03R\ttimestamp\"\x8e\x01\n" + |
||||||
|
"\x0eRequestWrapper\x12B\n" + |
||||||
|
"\x0esend_telemetry\x18\x01 \x01(\v2\x19.service.v1.TelemetryDataH\x00R\rsendTelemetry\x12-\n" + |
||||||
|
"\x04ping\x18\x02 \x01(\v2\x17.service.v1.PingRequestH\x00R\x04pingB\t\n" + |
||||||
|
"\apayload\"\xbc\x01\n" + |
||||||
|
"\x11TelemetryResponse\x12I\n" + |
||||||
|
"\vresult_code\x18\x01 \x01(\x0e2(.service.v1.TelemetryResponse.ResultCodeR\n" + |
||||||
|
"resultCode\"\\\n" + |
||||||
|
"\n" + |
||||||
|
"ResultCode\x12\x1b\n" + |
||||||
|
"\x17RESULT_CODE_UNSPECIFIED\x10\x00\x12\x19\n" + |
||||||
|
"\x15RESULT_CODE_SUCCEEDED\x10\x01\x12\x16\n" + |
||||||
|
"\x12RESULT_CODE_FAILED\x10\x02\"9\n" + |
||||||
|
"\fPingResponse\x12)\n" + |
||||||
|
"\x10server_timestamp\x18\x01 \x01(\x03R\x0fserverTimestamp\"\x9d\x01\n" + |
||||||
|
"\x0fResponseWrapper\x12F\n" + |
||||||
|
"\x0etelemetry_resp\x18\x01 \x01(\v2\x1d.service.v1.TelemetryResponseH\x00R\rtelemetryResp\x127\n" + |
||||||
|
"\tping_resp\x18\x02 \x01(\v2\x18.service.v1.PingResponseH\x00R\bpingRespB\t\n" + |
||||||
|
"\apayloadB#Z!protolab/gen/service/v1;servicev1b\x06proto3" |
||||||
|
|
||||||
|
var ( |
||||||
|
file_service_v1_service_proto_rawDescOnce sync.Once |
||||||
|
file_service_v1_service_proto_rawDescData []byte |
||||||
|
) |
||||||
|
|
||||||
|
func file_service_v1_service_proto_rawDescGZIP() []byte { |
||||||
|
file_service_v1_service_proto_rawDescOnce.Do(func() { |
||||||
|
file_service_v1_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_service_v1_service_proto_rawDesc), len(file_service_v1_service_proto_rawDesc))) |
||||||
|
}) |
||||||
|
return file_service_v1_service_proto_rawDescData |
||||||
|
} |
||||||
|
|
||||||
|
var file_service_v1_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) |
||||||
|
var file_service_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6) |
||||||
|
var file_service_v1_service_proto_goTypes = []any{ |
||||||
|
(TelemetryResponse_ResultCode)(0), // 0: service.v1.TelemetryResponse.ResultCode
|
||||||
|
(*TelemetryData)(nil), // 1: service.v1.TelemetryData
|
||||||
|
(*PingRequest)(nil), // 2: service.v1.PingRequest
|
||||||
|
(*RequestWrapper)(nil), // 3: service.v1.RequestWrapper
|
||||||
|
(*TelemetryResponse)(nil), // 4: service.v1.TelemetryResponse
|
||||||
|
(*PingResponse)(nil), // 5: service.v1.PingResponse
|
||||||
|
(*ResponseWrapper)(nil), // 6: service.v1.ResponseWrapper
|
||||||
|
} |
||||||
|
var file_service_v1_service_proto_depIdxs = []int32{ |
||||||
|
1, // 0: service.v1.RequestWrapper.send_telemetry:type_name -> service.v1.TelemetryData
|
||||||
|
2, // 1: service.v1.RequestWrapper.ping:type_name -> service.v1.PingRequest
|
||||||
|
0, // 2: service.v1.TelemetryResponse.result_code:type_name -> service.v1.TelemetryResponse.ResultCode
|
||||||
|
4, // 3: service.v1.ResponseWrapper.telemetry_resp:type_name -> service.v1.TelemetryResponse
|
||||||
|
5, // 4: service.v1.ResponseWrapper.ping_resp:type_name -> service.v1.PingResponse
|
||||||
|
5, // [5:5] is the sub-list for method output_type
|
||||||
|
5, // [5:5] is the sub-list for method input_type
|
||||||
|
5, // [5:5] is the sub-list for extension type_name
|
||||||
|
5, // [5:5] is the sub-list for extension extendee
|
||||||
|
0, // [0:5] is the sub-list for field type_name
|
||||||
|
} |
||||||
|
|
||||||
|
func init() { file_service_v1_service_proto_init() } |
||||||
|
func file_service_v1_service_proto_init() { |
||||||
|
if File_service_v1_service_proto != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
file_service_v1_service_proto_msgTypes[2].OneofWrappers = []any{ |
||||||
|
(*RequestWrapper_SendTelemetry)(nil), |
||||||
|
(*RequestWrapper_Ping)(nil), |
||||||
|
} |
||||||
|
file_service_v1_service_proto_msgTypes[5].OneofWrappers = []any{ |
||||||
|
(*ResponseWrapper_TelemetryResp)(nil), |
||||||
|
(*ResponseWrapper_PingResp)(nil), |
||||||
|
} |
||||||
|
type x struct{} |
||||||
|
out := protoimpl.TypeBuilder{ |
||||||
|
File: protoimpl.DescBuilder{ |
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), |
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_service_v1_service_proto_rawDesc), len(file_service_v1_service_proto_rawDesc)), |
||||||
|
NumEnums: 1, |
||||||
|
NumMessages: 6, |
||||||
|
NumExtensions: 0, |
||||||
|
NumServices: 0, |
||||||
|
}, |
||||||
|
GoTypes: file_service_v1_service_proto_goTypes, |
||||||
|
DependencyIndexes: file_service_v1_service_proto_depIdxs, |
||||||
|
EnumInfos: file_service_v1_service_proto_enumTypes, |
||||||
|
MessageInfos: file_service_v1_service_proto_msgTypes, |
||||||
|
}.Build() |
||||||
|
File_service_v1_service_proto = out.File |
||||||
|
file_service_v1_service_proto_goTypes = nil |
||||||
|
file_service_v1_service_proto_depIdxs = nil |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
module gordenko.dev/dima/protolab |
||||||
|
|
||||||
|
go 1.24.2 |
||||||
|
|
||||||
|
require google.golang.org/protobuf v1.36.10 |
@ -0,0 +1,4 @@ |
|||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= |
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= |
||||||
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= |
||||||
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= |
@ -0,0 +1,90 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"log" |
||||||
|
"net" |
||||||
|
"time" |
||||||
|
|
||||||
|
pb "gordenko.dev/dima/protolab/gen/service/v1" |
||||||
|
|
||||||
|
"gordenko.dev/dima/protolab/bin" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
Port = 8080 |
||||||
|
) |
||||||
|
|
||||||
|
func handleConnection(conn net.Conn) { |
||||||
|
defer conn.Close() |
||||||
|
log.Printf("client %s connected\n", conn.RemoteAddr().String()) |
||||||
|
|
||||||
|
for { |
||||||
|
var ( |
||||||
|
reqWrapper pb.RequestWrapper |
||||||
|
respWrapper *pb.ResponseWrapper |
||||||
|
) |
||||||
|
|
||||||
|
err := bin.ReadMessage(conn, &reqWrapper) |
||||||
|
if err != nil { |
||||||
|
log.Fatalf("read request: %v\n", err) |
||||||
|
} |
||||||
|
|
||||||
|
switch p := reqWrapper.Payload.(type) { |
||||||
|
case *pb.RequestWrapper_SendTelemetry: |
||||||
|
data := p.SendTelemetry |
||||||
|
log.Printf("telemetry received from %s: Temp %.2f°C, BatteryLevel %v%%\n", |
||||||
|
data.GetDeviceId(), data.GetTemperature(), data.GetBatteryLevel()) |
||||||
|
|
||||||
|
respWrapper = &pb.ResponseWrapper{ |
||||||
|
Payload: &pb.ResponseWrapper_TelemetryResp{ |
||||||
|
TelemetryResp: &pb.TelemetryResponse{ |
||||||
|
ResultCode: pb.TelemetryResponse_RESULT_CODE_SUCCEEDED, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
case *pb.RequestWrapper_Ping: |
||||||
|
data := p.Ping |
||||||
|
log.Printf("ping received, client time %s\n", |
||||||
|
time.Unix(data.GetTimestamp(), 0).Format(time.RFC3339)) |
||||||
|
|
||||||
|
respWrapper = &pb.ResponseWrapper{ |
||||||
|
Payload: &pb.ResponseWrapper_PingResp{ |
||||||
|
PingResp: &pb.PingResponse{ |
||||||
|
ServerTimestamp: time.Now().Unix(), |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
default: |
||||||
|
log.Printf("unknown request received: %T\n", p) |
||||||
|
} |
||||||
|
|
||||||
|
err = bin.WriteMessage(conn, respWrapper) |
||||||
|
if err != nil { |
||||||
|
log.Fatalf("send response: %v\n", err) |
||||||
|
} else { |
||||||
|
log.Println("response sent") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func main() { |
||||||
|
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", Port)) |
||||||
|
if err != nil { |
||||||
|
log.Fatalf("net.Listen: %v\n", err) |
||||||
|
} |
||||||
|
defer listener.Close() |
||||||
|
|
||||||
|
log.Printf("server started on port %d", Port) |
||||||
|
|
||||||
|
for { |
||||||
|
conn, err := listener.Accept() |
||||||
|
if err != nil { |
||||||
|
log.Printf("listener.Accept: %v", err) |
||||||
|
continue |
||||||
|
} |
||||||
|
go handleConnection(conn) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package service.v1; |
||||||
|
|
||||||
|
option go_package = "protolab/gen/service/v1;servicev1"; |
||||||
|
|
||||||
|
message TelemetryData { |
||||||
|
string device_id = 1; |
||||||
|
int64 timestamp = 2; |
||||||
|
double temperature = 3; |
||||||
|
int32 battery_level = 4; |
||||||
|
} |
||||||
|
|
||||||
|
message PingRequest { |
||||||
|
int64 timestamp = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message RequestWrapper { |
||||||
|
oneof payload { |
||||||
|
TelemetryData send_telemetry = 1; |
||||||
|
PingRequest ping = 2; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
message TelemetryResponse { |
||||||
|
enum ResultCode { |
||||||
|
RESULT_CODE_UNSPECIFIED = 0; |
||||||
|
RESULT_CODE_SUCCEEDED = 1; |
||||||
|
RESULT_CODE_FAILED = 2; |
||||||
|
} |
||||||
|
ResultCode result_code = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message PingResponse { |
||||||
|
int64 server_timestamp = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message ResponseWrapper { |
||||||
|
oneof payload { |
||||||
|
TelemetryResponse telemetry_resp = 1; |
||||||
|
PingResponse ping_resp = 2; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue