first commit
This commit is contained in:
19
README
Normal file
19
README
Normal file
@@ -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
|
||||||
46
bin/bin.go
Normal file
46
bin/bin.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
5
buf.gen.yaml
Normal file
5
buf.gen.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
version: v1
|
||||||
|
plugins:
|
||||||
|
- name: go
|
||||||
|
out: gen
|
||||||
|
opt: paths=source_relative
|
||||||
8
buf.yaml
Normal file
8
buf.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
version: v1
|
||||||
|
name: buf.build/dima/tcp-service
|
||||||
|
lint:
|
||||||
|
use:
|
||||||
|
- DEFAULT
|
||||||
|
breaking:
|
||||||
|
use:
|
||||||
|
- FILE
|
||||||
82
client/main.go
Normal file
82
client/main.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
536
gen/service/v1/service.pb.go
Normal file
536
gen/service/v1/service.pb.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module gordenko.dev/dima/protolab
|
||||||
|
|
||||||
|
go 1.24.2
|
||||||
|
|
||||||
|
require google.golang.org/protobuf v1.36.10
|
||||||
4
go.sum
Normal file
4
go.sum
Normal file
@@ -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=
|
||||||
90
server/main.go
Normal file
90
server/main.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
43
service/v1/service.proto
Normal file
43
service/v1/service.proto
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user