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