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