From d7e92429c8c2e8b1280cc69021b3c89ff99aa978 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 31 Jul 2023 12:16:48 +0200 Subject: [PATCH 01/18] UploadResponse now has 'oneof' clause for better API design --- commands/daemon/daemon.go | 12 +- rpc/cc/arduino/cli/commands/v1/upload.pb.go | 279 +++++++++++--------- rpc/cc/arduino/cli/commands/v1/upload.proto | 10 +- 3 files changed, 171 insertions(+), 130 deletions(-) diff --git a/commands/daemon/daemon.go b/commands/daemon/daemon.go index bcf173381c7..f18509c250f 100644 --- a/commands/daemon/daemon.go +++ b/commands/daemon/daemon.go @@ -297,8 +297,16 @@ func (s *ArduinoCoreServerImpl) PlatformList(ctx context.Context, req *rpc.Platf // Upload FIXMEDOC func (s *ArduinoCoreServerImpl) Upload(req *rpc.UploadRequest, stream rpc.ArduinoCoreService_UploadServer) error { syncSend := NewSynchronizedSend(stream.Send) - outStream := feedStreamTo(func(data []byte) { syncSend.Send(&rpc.UploadResponse{OutStream: data}) }) - errStream := feedStreamTo(func(data []byte) { syncSend.Send(&rpc.UploadResponse{ErrStream: data}) }) + outStream := feedStreamTo(func(data []byte) { + syncSend.Send(&rpc.UploadResponse{ + Message: &rpc.UploadResponse_OutStream{OutStream: data}, + }) + }) + errStream := feedStreamTo(func(data []byte) { + syncSend.Send(&rpc.UploadResponse{ + Message: &rpc.UploadResponse_ErrStream{ErrStream: data}, + }) + }) err := upload.Upload(stream.Context(), req, outStream, errStream) outStream.Close() errStream.Close() diff --git a/rpc/cc/arduino/cli/commands/v1/upload.pb.go b/rpc/cc/arduino/cli/commands/v1/upload.pb.go index 50176927a47..ed7be460aa3 100644 --- a/rpc/cc/arduino/cli/commands/v1/upload.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/upload.pb.go @@ -192,10 +192,11 @@ type UploadResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The output of the upload process. - OutStream []byte `protobuf:"bytes,1,opt,name=out_stream,json=outStream,proto3" json:"out_stream,omitempty"` - // The error output of the upload process. - ErrStream []byte `protobuf:"bytes,2,opt,name=err_stream,json=errStream,proto3" json:"err_stream,omitempty"` + // Types that are assignable to Message: + // + // *UploadResponse_OutStream + // *UploadResponse_ErrStream + Message isUploadResponse_Message `protobuf_oneof:"message"` } func (x *UploadResponse) Reset() { @@ -230,20 +231,45 @@ func (*UploadResponse) Descriptor() ([]byte, []int) { return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{1} } +func (m *UploadResponse) GetMessage() isUploadResponse_Message { + if m != nil { + return m.Message + } + return nil +} + func (x *UploadResponse) GetOutStream() []byte { - if x != nil { + if x, ok := x.GetMessage().(*UploadResponse_OutStream); ok { return x.OutStream } return nil } func (x *UploadResponse) GetErrStream() []byte { - if x != nil { + if x, ok := x.GetMessage().(*UploadResponse_ErrStream); ok { return x.ErrStream } return nil } +type isUploadResponse_Message interface { + isUploadResponse_Message() +} + +type UploadResponse_OutStream struct { + // The output of the upload process. + OutStream []byte `protobuf:"bytes,1,opt,name=out_stream,json=outStream,proto3,oneof"` +} + +type UploadResponse_ErrStream struct { + // The error output of the upload process. + ErrStream []byte `protobuf:"bytes,2,opt,name=err_stream,json=errStream,proto3,oneof"` +} + +func (*UploadResponse_OutStream) isUploadResponse_Message() {} + +func (*UploadResponse_ErrStream) isUploadResponse_Message() {} + type ProgrammerIsRequiredForUploadError struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1000,130 +1026,131 @@ var file_cc_arduino_cli_commands_v1_upload_proto_rawDesc = []byte{ 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4e, 0x0a, 0x0e, 0x55, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5d, 0x0a, 0x0e, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x65, - 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x22, 0x24, 0x0a, 0x22, 0x50, 0x72, - 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x49, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x64, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x22, 0xa0, 0x04, 0x0a, 0x1c, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x73, 0x69, 0x6e, 0x67, - 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, - 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6b, 0x65, 0x74, 0x63, - 0x68, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6b, - 0x65, 0x74, 0x63, 0x68, 0x50, 0x61, 0x74, 0x68, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, - 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, - 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, - 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x69, 0x72, - 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, - 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x69, 0x0a, 0x0b, 0x75, 0x73, 0x65, - 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x48, - 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, - 0x61, 0x64, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x5d, 0x0a, 0x1d, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x73, 0x69, - 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x22, 0xb1, 0x03, 0x0a, 0x15, 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, 0x6f, 0x74, 0x6c, - 0x6f, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, - 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, - 0x62, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, - 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, - 0x72, 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, - 0x6f, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, - 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, - 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, - 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, - 0x52, 0x75, 0x6e, 0x12, 0x62, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, - 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, - 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x75, 0x73, 0x65, - 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x46, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x56, 0x0a, 0x16, 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, - 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, - 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x22, 0x80, - 0x01, 0x0a, 0x28, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, - 0x72, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x55, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, + 0x48, 0x00, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1f, 0x0a, + 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x48, 0x00, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x09, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x24, 0x0a, 0x22, 0x50, 0x72, 0x6f, + 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x49, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, + 0xa0, 0x04, 0x0a, 0x1c, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x50, + 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, + 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6b, 0x65, + 0x74, 0x63, 0x68, 0x50, 0x61, 0x74, 0x68, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, + 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, + 0x1f, 0x0a, 0x0b, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x69, 0x72, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x12, + 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x69, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, + 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x48, 0x2e, + 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x5d, 0x0a, 0x1d, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x73, 0x69, 0x6e, + 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x22, 0xb1, 0x03, 0x0a, 0x15, 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, + 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, - 0x6e, 0x22, 0x75, 0x0a, 0x29, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, - 0x6d, 0x65, 0x72, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, - 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, - 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x0b, 0x70, 0x72, 0x6f, - 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x1a, 0x53, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, - 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, - 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x66, 0x0a, 0x09, 0x55, 0x73, 0x65, - 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x6f, 0x6f, 0x6c, 0x49, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x22, 0x65, 0x0a, 0x1b, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, - 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x46, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, - 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a, 0x75, 0x73, - 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, - 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, - 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, + 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, + 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, + 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, + 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, + 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, + 0x75, 0x6e, 0x12, 0x62, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, + 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x56, 0x0a, 0x16, 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, 0x6f, + 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, + 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x22, 0x80, 0x01, + 0x0a, 0x28, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, + 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, + 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, + 0x22, 0x75, 0x0a, 0x29, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, + 0x65, 0x72, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, + 0x0b, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, + 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x1a, 0x53, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, + 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1a, 0x0a, 0x08, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x66, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x6f, 0x6f, 0x6c, 0x49, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x22, 0x65, 0x0a, 0x1b, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, + 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x46, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a, 0x75, 0x73, 0x65, + 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, + 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, + 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1333,6 +1360,10 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } } + file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[1].OneofWrappers = []interface{}{ + (*UploadResponse_OutStream)(nil), + (*UploadResponse_ErrStream)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/rpc/cc/arduino/cli/commands/v1/upload.proto b/rpc/cc/arduino/cli/commands/v1/upload.proto index d0aa108c0ef..4ea99c0147c 100644 --- a/rpc/cc/arduino/cli/commands/v1/upload.proto +++ b/rpc/cc/arduino/cli/commands/v1/upload.proto @@ -62,10 +62,12 @@ message UploadRequest { } message UploadResponse { - // The output of the upload process. - bytes out_stream = 1; - // The error output of the upload process. - bytes err_stream = 2; + oneof message { + // The output of the upload process. + bytes out_stream = 1; + // The error output of the upload process. + bytes err_stream = 2; + } } message ProgrammerIsRequiredForUploadError {} From 97d9e2417dbd63ec317cfb70aa10d9f74bf5aff7 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 31 Jul 2023 12:59:29 +0200 Subject: [PATCH 02/18] Added scaffolding to return updated-port after upload --- commands/daemon/daemon.go | 12 +- commands/upload/burnbootloader.go | 2 +- commands/upload/upload.go | 61 +-- commands/upload/upload_test.go | 2 +- internal/cli/compile/compile.go | 8 +- internal/cli/upload/upload.go | 27 +- rpc/cc/arduino/cli/commands/v1/upload.pb.go | 484 ++++++++++++-------- rpc/cc/arduino/cli/commands/v1/upload.proto | 8 + 8 files changed, 372 insertions(+), 232 deletions(-) diff --git a/commands/daemon/daemon.go b/commands/daemon/daemon.go index f18509c250f..1cb5a667b60 100644 --- a/commands/daemon/daemon.go +++ b/commands/daemon/daemon.go @@ -307,13 +307,17 @@ func (s *ArduinoCoreServerImpl) Upload(req *rpc.UploadRequest, stream rpc.Arduin Message: &rpc.UploadResponse_ErrStream{ErrStream: data}, }) }) - err := upload.Upload(stream.Context(), req, outStream, errStream) + res, err := upload.Upload(stream.Context(), req, outStream, errStream) outStream.Close() errStream.Close() - if err != nil { - return convertErrorToRPCStatus(err) + if res != nil { + syncSend.Send(&rpc.UploadResponse{ + Message: &rpc.UploadResponse_Result{ + Result: res, + }, + }) } - return nil + return convertErrorToRPCStatus(err) } // UploadUsingProgrammer FIXMEDOC diff --git a/commands/upload/burnbootloader.go b/commands/upload/burnbootloader.go index f47e22fd239..99efe2be757 100644 --- a/commands/upload/burnbootloader.go +++ b/commands/upload/burnbootloader.go @@ -39,7 +39,7 @@ func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStre } defer release() - err := runProgramAction( + _, err := runProgramAction( pme, nil, // sketch "", // importFile diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 8de4bc009f6..a8678d38e27 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -123,7 +123,7 @@ func getUserFields(toolID string, platformRelease *cores.PlatformRelease) []*rpc } // Upload FIXMEDOC -func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, errStream io.Writer) error { +func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, errStream io.Writer) (*rpc.UploadResult, error) { logrus.Tracef("Upload %s on %s started", req.GetSketchPath(), req.GetFqbn()) // TODO: make a generic function to extract sketch from request @@ -131,16 +131,16 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er sketchPath := paths.New(req.GetSketchPath()) sk, err := sketch.New(sketchPath) if err != nil && req.GetImportDir() == "" && req.GetImportFile() == "" { - return &arduino.CantOpenSketchError{Cause: err} + return nil, &arduino.CantOpenSketchError{Cause: err} } pme, release := commands.GetPackageManagerExplorer(req) if pme == nil { - return &arduino.InvalidInstanceError{} + return nil, &arduino.InvalidInstanceError{} } defer release() - if err := runProgramAction( + updatedPort, err := runProgramAction( pme, sk, req.GetImportFile(), @@ -155,11 +155,14 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er errStream, req.GetDryRun(), req.GetUserFields(), - ); err != nil { - return err + ) + if err != nil { + return nil, err } - return nil + return &rpc.UploadResult{ + UpdatedUploadPort: updatedPort, + }, nil } // UsingProgrammer FIXMEDOC @@ -169,7 +172,7 @@ func UsingProgrammer(ctx context.Context, req *rpc.UploadUsingProgrammerRequest, if req.GetProgrammer() == "" { return &arduino.MissingProgrammerError{} } - err := Upload(ctx, &rpc.UploadRequest{ + _, err := Upload(ctx, &rpc.UploadRequest{ Instance: req.GetInstance(), SketchPath: req.GetSketchPath(), ImportFile: req.GetImportFile(), @@ -190,10 +193,10 @@ func runProgramAction(pme *packagemanager.Explorer, programmerID string, verbose, verify, burnBootloader bool, outStream, errStream io.Writer, - dryRun bool, userFields map[string]string) error { + dryRun bool, userFields map[string]string) (*rpc.Port, error) { if burnBootloader && programmerID == "" { - return &arduino.MissingProgrammerError{} + return nil, &arduino.MissingProgrammerError{} } if port == nil || (port.Address == "" && port.Protocol == "") { // For no-port uploads use "default" protocol @@ -203,19 +206,19 @@ func runProgramAction(pme *packagemanager.Explorer, fqbn, err := cores.ParseFQBN(fqbnIn) if err != nil { - return &arduino.InvalidFQBNError{Cause: err} + return nil, &arduino.InvalidFQBNError{Cause: err} } logrus.WithField("fqbn", fqbn).Tracef("Detected FQBN") // Find target board and board properties _, boardPlatform, board, boardProperties, buildPlatform, err := pme.ResolveFQBN(fqbn) if boardPlatform == nil { - return &arduino.PlatformNotFoundError{ + return nil, &arduino.PlatformNotFoundError{ Platform: fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch), Cause: err, } } else if err != nil { - return &arduino.UnknownFQBNError{Cause: err} + return nil, &arduino.UnknownFQBNError{Cause: err} } logrus. WithField("boardPlatform", boardPlatform). @@ -232,7 +235,7 @@ func runProgramAction(pme *packagemanager.Explorer, programmer = buildPlatform.Programmers[programmerID] } if programmer == nil { - return &arduino.ProgrammerNotFoundError{Programmer: programmerID} + return nil, &arduino.ProgrammerNotFoundError{Programmer: programmerID} } } @@ -253,7 +256,7 @@ func runProgramAction(pme *packagemanager.Explorer, } uploadToolID, err := getToolID(props, action, port.Protocol) if err != nil { - return err + return nil, err } var uploadToolPlatform *cores.PlatformRelease @@ -268,7 +271,7 @@ func runProgramAction(pme *packagemanager.Explorer, Trace("Upload tool") if split := strings.Split(uploadToolID, ":"); len(split) > 2 { - return &arduino.InvalidPlatformPropertyError{ + return nil, &arduino.InvalidPlatformPropertyError{ Property: fmt.Sprintf("%s.tool.%s", action, port.Protocol), // TODO: Can be done better, maybe inline getToolID(...) Value: uploadToolID} } else if len(split) == 2 { @@ -277,12 +280,12 @@ func runProgramAction(pme *packagemanager.Explorer, PlatformArchitecture: boardPlatform.Platform.Architecture, }) if p == nil { - return &arduino.PlatformNotFoundError{Platform: split[0] + ":" + boardPlatform.Platform.Architecture} + return nil, &arduino.PlatformNotFoundError{Platform: split[0] + ":" + boardPlatform.Platform.Architecture} } uploadToolID = split[1] uploadToolPlatform = pme.GetInstalledPlatformRelease(p) if uploadToolPlatform == nil { - return &arduino.PlatformNotFoundError{Platform: split[0] + ":" + boardPlatform.Platform.Architecture} + return nil, &arduino.PlatformNotFoundError{Platform: split[0] + ":" + boardPlatform.Platform.Architecture} } } @@ -309,7 +312,7 @@ func runProgramAction(pme *packagemanager.Explorer, } if !uploadProperties.ContainsKey("upload.protocol") && programmer == nil { - return &arduino.ProgrammerRequiredForUploadError{} + return nil, &arduino.ProgrammerRequiredForUploadError{} } // Set properties for verbose upload @@ -357,13 +360,13 @@ func runProgramAction(pme *packagemanager.Explorer, if !burnBootloader { importPath, sketchName, err := determineBuildPathAndSketchName(importFile, importDir, sk, fqbn) if err != nil { - return &arduino.NotFoundError{Message: tr("Error finding build artifacts"), Cause: err} + return nil, &arduino.NotFoundError{Message: tr("Error finding build artifacts"), Cause: err} } if !importPath.Exist() { - return &arduino.NotFoundError{Message: tr("Compiled sketch not found in %s", importPath)} + return nil, &arduino.NotFoundError{Message: tr("Compiled sketch not found in %s", importPath)} } if !importPath.IsDir() { - return &arduino.NotFoundError{Message: tr("Expected compiled sketch in directory %s, but is a file instead", importPath)} + return nil, &arduino.NotFoundError{Message: tr("Expected compiled sketch in directory %s, but is a file instead", importPath)} } uploadProperties.SetPath("build.path", importPath) uploadProperties.Set("build.project_name", sketchName) @@ -466,22 +469,28 @@ func runProgramAction(pme *packagemanager.Explorer, toolEnv := pme.GetEnvVarsForSpawnedProcess() if burnBootloader { if err := runTool("erase.pattern", uploadProperties, outStream, errStream, verbose, dryRun, toolEnv); err != nil { - return &arduino.FailedUploadError{Message: tr("Failed chip erase"), Cause: err} + return nil, &arduino.FailedUploadError{Message: tr("Failed chip erase"), Cause: err} } if err := runTool("bootloader.pattern", uploadProperties, outStream, errStream, verbose, dryRun, toolEnv); err != nil { - return &arduino.FailedUploadError{Message: tr("Failed to burn bootloader"), Cause: err} + return nil, &arduino.FailedUploadError{Message: tr("Failed to burn bootloader"), Cause: err} } } else if programmer != nil { if err := runTool("program.pattern", uploadProperties, outStream, errStream, verbose, dryRun, toolEnv); err != nil { - return &arduino.FailedUploadError{Message: tr("Failed programming"), Cause: err} + return nil, &arduino.FailedUploadError{Message: tr("Failed programming"), Cause: err} } } else { if err := runTool("upload.pattern", uploadProperties, outStream, errStream, verbose, dryRun, toolEnv); err != nil { - return &arduino.FailedUploadError{Message: tr("Failed uploading"), Cause: err} + return nil, &arduino.FailedUploadError{Message: tr("Failed uploading"), Cause: err} } } logrus.Tracef("Upload successful") + return nil, nil // TODO: return new port +} + +func detectNewUploadPort(oldPort *rpc.Port) *rpc.Port { + logrus.Tracef("Detecting new board port") + // TODO return nil } diff --git a/commands/upload/upload_test.go b/commands/upload/upload_test.go index d3f69fd1baf..e1f87cd9832 100644 --- a/commands/upload/upload_test.go +++ b/commands/upload/upload_test.go @@ -184,7 +184,7 @@ func TestUploadPropertiesComposition(t *testing.T) { testRunner := func(t *testing.T, test test, verboseVerify bool) { outStream := &bytes.Buffer{} errStream := &bytes.Buffer{} - err := runProgramAction( + _, err := runProgramAction( pme, nil, // sketch "", // importFile diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index b208cc79dfd..baa1dff5f89 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -236,6 +236,8 @@ func runCompileCommand(cmd *cobra.Command, args []string) { DoNotExpandBuildProperties: showProperties == arguments.ShowPropertiesUnexpanded, } compileRes, compileError := compile.Compile(context.Background(), compileRequest, stdOut, stdErr, nil) + + var uploadRes *rpc.UploadResult if compileError == nil && uploadAfterCompile { userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{ Instance: inst, @@ -268,8 +270,10 @@ func runCompileCommand(cmd *cobra.Command, args []string) { UserFields: fields, } - if err := upload.Upload(context.Background(), uploadRequest, stdOut, stdErr); err != nil { + if res, err := upload.Upload(context.Background(), uploadRequest, stdOut, stdErr); err != nil { feedback.Fatal(tr("Error during Upload: %v", err), feedback.ErrGeneric) + } else { + uploadRes = res } } @@ -330,6 +334,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { CompilerOut: stdIO.Stdout, CompilerErr: stdIO.Stderr, BuilderResult: compileRes, + UploadResult: uploadRes, ProfileOut: profileOut, Success: compileError == nil, showPropertiesMode: showProperties, @@ -375,6 +380,7 @@ type compileResult struct { CompilerOut string `json:"compiler_out"` CompilerErr string `json:"compiler_err"` BuilderResult *rpc.CompileResponse `json:"builder_result"` + UploadResult *rpc.UploadResult `json:"upload_result"` Success bool `json:"success"` ProfileOut string `json:"profile_out,omitempty"` Error string `json:"error,omitempty"` diff --git a/internal/cli/upload/upload.go b/internal/cli/upload/upload.go index 739bb7d29e4..1fa91086ac0 100644 --- a/internal/cli/upload/upload.go +++ b/internal/cli/upload/upload.go @@ -168,8 +168,31 @@ func runUploadCommand(command *cobra.Command, args []string) { DryRun: dryRun, UserFields: fields, } - if err := upload.Upload(context.Background(), req, stdOut, stdErr); err != nil { + if res, err := upload.Upload(context.Background(), req, stdOut, stdErr); err != nil { feedback.FatalError(err, feedback.ErrGeneric) + } else { + io := stdIOResult() + feedback.PrintResult(&uploadResult{ + Stdout: io.Stdout, + Stderr: io.Stderr, + UpdatedUploadPort: res.UpdatedUploadPort, + }) } - feedback.PrintResult(stdIOResult()) +} + +type uploadResult struct { + Stdout string `json:"stdout"` + Stderr string `json:"stderr"` + UpdatedUploadPort *rpc.Port `json:"updated_upload_port,omitempty"` +} + +func (r *uploadResult) Data() interface{} { + return r +} + +func (r *uploadResult) String() string { + if r.UpdatedUploadPort == nil { + return "" + } + return tr("New upload port: %[1]s (%[2]s)", r.UpdatedUploadPort.Address, r.UpdatedUploadPort.Protocol) } diff --git a/rpc/cc/arduino/cli/commands/v1/upload.pb.go b/rpc/cc/arduino/cli/commands/v1/upload.pb.go index ed7be460aa3..0398a460ba1 100644 --- a/rpc/cc/arduino/cli/commands/v1/upload.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/upload.pb.go @@ -196,6 +196,7 @@ type UploadResponse struct { // // *UploadResponse_OutStream // *UploadResponse_ErrStream + // *UploadResponse_Result Message isUploadResponse_Message `protobuf_oneof:"message"` } @@ -252,6 +253,13 @@ func (x *UploadResponse) GetErrStream() []byte { return nil } +func (x *UploadResponse) GetResult() *UploadResult { + if x, ok := x.GetMessage().(*UploadResponse_Result); ok { + return x.Result + } + return nil +} + type isUploadResponse_Message interface { isUploadResponse_Message() } @@ -266,10 +274,66 @@ type UploadResponse_ErrStream struct { ErrStream []byte `protobuf:"bytes,2,opt,name=err_stream,json=errStream,proto3,oneof"` } +type UploadResponse_Result struct { + // The upload result + Result *UploadResult `protobuf:"bytes,3,opt,name=result,proto3,oneof"` +} + func (*UploadResponse_OutStream) isUploadResponse_Message() {} func (*UploadResponse_ErrStream) isUploadResponse_Message() {} +func (*UploadResponse_Result) isUploadResponse_Message() {} + +type UploadResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // When a board requires a port disconnection to perform the upload, this + // field returns the port where the board reconnects after the upload. + UpdatedUploadPort *Port `protobuf:"bytes,1,opt,name=updated_upload_port,json=updatedUploadPort,proto3" json:"updated_upload_port,omitempty"` +} + +func (x *UploadResult) Reset() { + *x = UploadResult{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadResult) ProtoMessage() {} + +func (x *UploadResult) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadResult.ProtoReflect.Descriptor instead. +func (*UploadResult) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{2} +} + +func (x *UploadResult) GetUpdatedUploadPort() *Port { + if x != nil { + return x.UpdatedUploadPort + } + return nil +} + type ProgrammerIsRequiredForUploadError struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -279,7 +343,7 @@ type ProgrammerIsRequiredForUploadError struct { func (x *ProgrammerIsRequiredForUploadError) Reset() { *x = ProgrammerIsRequiredForUploadError{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[2] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -292,7 +356,7 @@ func (x *ProgrammerIsRequiredForUploadError) String() string { func (*ProgrammerIsRequiredForUploadError) ProtoMessage() {} func (x *ProgrammerIsRequiredForUploadError) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[2] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -305,7 +369,7 @@ func (x *ProgrammerIsRequiredForUploadError) ProtoReflect() protoreflect.Message // Deprecated: Use ProgrammerIsRequiredForUploadError.ProtoReflect.Descriptor instead. func (*ProgrammerIsRequiredForUploadError) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{2} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{3} } type UploadUsingProgrammerRequest struct { @@ -352,7 +416,7 @@ type UploadUsingProgrammerRequest struct { func (x *UploadUsingProgrammerRequest) Reset() { *x = UploadUsingProgrammerRequest{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[3] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -365,7 +429,7 @@ func (x *UploadUsingProgrammerRequest) String() string { func (*UploadUsingProgrammerRequest) ProtoMessage() {} func (x *UploadUsingProgrammerRequest) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[3] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -378,7 +442,7 @@ func (x *UploadUsingProgrammerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UploadUsingProgrammerRequest.ProtoReflect.Descriptor instead. func (*UploadUsingProgrammerRequest) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{3} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{4} } func (x *UploadUsingProgrammerRequest) GetInstance() *Instance { @@ -472,7 +536,7 @@ type UploadUsingProgrammerResponse struct { func (x *UploadUsingProgrammerResponse) Reset() { *x = UploadUsingProgrammerResponse{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[4] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -485,7 +549,7 @@ func (x *UploadUsingProgrammerResponse) String() string { func (*UploadUsingProgrammerResponse) ProtoMessage() {} func (x *UploadUsingProgrammerResponse) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[4] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -498,7 +562,7 @@ func (x *UploadUsingProgrammerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UploadUsingProgrammerResponse.ProtoReflect.Descriptor instead. func (*UploadUsingProgrammerResponse) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{4} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{5} } func (x *UploadUsingProgrammerResponse) GetOutStream() []byte { @@ -547,7 +611,7 @@ type BurnBootloaderRequest struct { func (x *BurnBootloaderRequest) Reset() { *x = BurnBootloaderRequest{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[5] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -560,7 +624,7 @@ func (x *BurnBootloaderRequest) String() string { func (*BurnBootloaderRequest) ProtoMessage() {} func (x *BurnBootloaderRequest) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[5] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -573,7 +637,7 @@ func (x *BurnBootloaderRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BurnBootloaderRequest.ProtoReflect.Descriptor instead. func (*BurnBootloaderRequest) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{5} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{6} } func (x *BurnBootloaderRequest) GetInstance() *Instance { @@ -646,7 +710,7 @@ type BurnBootloaderResponse struct { func (x *BurnBootloaderResponse) Reset() { *x = BurnBootloaderResponse{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[6] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -659,7 +723,7 @@ func (x *BurnBootloaderResponse) String() string { func (*BurnBootloaderResponse) ProtoMessage() {} func (x *BurnBootloaderResponse) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[6] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -672,7 +736,7 @@ func (x *BurnBootloaderResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BurnBootloaderResponse.ProtoReflect.Descriptor instead. func (*BurnBootloaderResponse) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{6} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{7} } func (x *BurnBootloaderResponse) GetOutStream() []byte { @@ -701,7 +765,7 @@ type ListProgrammersAvailableForUploadRequest struct { func (x *ListProgrammersAvailableForUploadRequest) Reset() { *x = ListProgrammersAvailableForUploadRequest{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[7] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -714,7 +778,7 @@ func (x *ListProgrammersAvailableForUploadRequest) String() string { func (*ListProgrammersAvailableForUploadRequest) ProtoMessage() {} func (x *ListProgrammersAvailableForUploadRequest) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[7] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -727,7 +791,7 @@ func (x *ListProgrammersAvailableForUploadRequest) ProtoReflect() protoreflect.M // Deprecated: Use ListProgrammersAvailableForUploadRequest.ProtoReflect.Descriptor instead. func (*ListProgrammersAvailableForUploadRequest) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{7} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{8} } func (x *ListProgrammersAvailableForUploadRequest) GetInstance() *Instance { @@ -755,7 +819,7 @@ type ListProgrammersAvailableForUploadResponse struct { func (x *ListProgrammersAvailableForUploadResponse) Reset() { *x = ListProgrammersAvailableForUploadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[8] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -768,7 +832,7 @@ func (x *ListProgrammersAvailableForUploadResponse) String() string { func (*ListProgrammersAvailableForUploadResponse) ProtoMessage() {} func (x *ListProgrammersAvailableForUploadResponse) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[8] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -781,7 +845,7 @@ func (x *ListProgrammersAvailableForUploadResponse) ProtoReflect() protoreflect. // Deprecated: Use ListProgrammersAvailableForUploadResponse.ProtoReflect.Descriptor instead. func (*ListProgrammersAvailableForUploadResponse) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{8} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{9} } func (x *ListProgrammersAvailableForUploadResponse) GetProgrammers() []*Programmer { @@ -807,7 +871,7 @@ type SupportedUserFieldsRequest struct { func (x *SupportedUserFieldsRequest) Reset() { *x = SupportedUserFieldsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[9] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -820,7 +884,7 @@ func (x *SupportedUserFieldsRequest) String() string { func (*SupportedUserFieldsRequest) ProtoMessage() {} func (x *SupportedUserFieldsRequest) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[9] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -833,7 +897,7 @@ func (x *SupportedUserFieldsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SupportedUserFieldsRequest.ProtoReflect.Descriptor instead. func (*SupportedUserFieldsRequest) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{9} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{10} } func (x *SupportedUserFieldsRequest) GetInstance() *Instance { @@ -876,7 +940,7 @@ type UserField struct { func (x *UserField) Reset() { *x = UserField{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[10] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -889,7 +953,7 @@ func (x *UserField) String() string { func (*UserField) ProtoMessage() {} func (x *UserField) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[10] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -902,7 +966,7 @@ func (x *UserField) ProtoReflect() protoreflect.Message { // Deprecated: Use UserField.ProtoReflect.Descriptor instead. func (*UserField) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{10} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{11} } func (x *UserField) GetToolId() string { @@ -946,7 +1010,7 @@ type SupportedUserFieldsResponse struct { func (x *SupportedUserFieldsResponse) Reset() { *x = SupportedUserFieldsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[11] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -959,7 +1023,7 @@ func (x *SupportedUserFieldsResponse) String() string { func (*SupportedUserFieldsResponse) ProtoMessage() {} func (x *SupportedUserFieldsResponse) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[11] + mi := &file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -972,7 +1036,7 @@ func (x *SupportedUserFieldsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SupportedUserFieldsResponse.ProtoReflect.Descriptor instead. func (*SupportedUserFieldsResponse) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{11} + return file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP(), []int{12} } func (x *SupportedUserFieldsResponse) GetUserFields() []*UserField { @@ -1026,131 +1090,141 @@ var file_cc_arduino_cli_commands_v1_upload_proto_rawDesc = []byte{ 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5d, 0x0a, 0x0e, 0x55, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0a, - 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x48, 0x00, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1f, 0x0a, - 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x48, 0x00, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x09, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x24, 0x0a, 0x22, 0x50, 0x72, 0x6f, - 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x49, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, - 0xa0, 0x04, 0x0a, 0x1c, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x50, - 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, - 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, - 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6b, 0x65, - 0x74, 0x63, 0x68, 0x50, 0x61, 0x74, 0x68, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, - 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, - 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, - 0x1f, 0x0a, 0x0b, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x69, 0x72, 0x12, - 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x12, - 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x69, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, - 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x48, 0x2e, - 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x5d, 0x0a, 0x1d, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x73, 0x69, 0x6e, - 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x22, 0xb1, 0x03, 0x0a, 0x15, 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, - 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, - 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, - 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, - 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, - 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, - 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, - 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, - 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, - 0x75, 0x6e, 0x12, 0x62, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, - 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, - 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x56, 0x0a, 0x16, 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, 0x6f, - 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa1, 0x01, 0x0a, 0x0e, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, + 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1f, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x22, 0x80, 0x01, - 0x0a, 0x28, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, - 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, + 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, + 0x42, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x60, + 0x0a, 0x0c, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x50, + 0x0a, 0x13, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, + 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x11, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, 0x72, 0x74, + 0x22, 0x24, 0x0a, 0x22, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x49, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xa0, 0x04, 0x0a, 0x1c, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, + 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, + 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x50, 0x61, 0x74, 0x68, 0x12, 0x34, + 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, - 0x22, 0x75, 0x0a, 0x29, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, - 0x65, 0x72, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, - 0x0b, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, - 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x67, - 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x1a, 0x53, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, - 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1a, 0x0a, 0x08, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x66, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, - 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x6f, 0x6f, 0x6c, 0x49, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x22, 0x65, 0x0a, 0x1b, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, - 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x46, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, + 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x44, 0x69, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, + 0x6d, 0x6d, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, + 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, + 0x69, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0b, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x48, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a, 0x75, 0x73, 0x65, - 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, - 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, - 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, + 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, + 0x75, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x55, 0x73, + 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5d, 0x0a, 0x1d, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x75, + 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, + 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, + 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x22, 0xb1, 0x03, 0x0a, 0x15, 0x42, 0x75, 0x72, + 0x6e, 0x42, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, + 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, + 0x66, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, + 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x62, 0x0a, 0x0b, 0x75, 0x73, 0x65, + 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, + 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x72, 0x6e, + 0x42, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x3d, 0x0a, + 0x0f, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x56, 0x0a, 0x16, + 0x42, 0x75, 0x72, 0x6e, 0x42, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x22, 0x80, 0x01, 0x0a, 0x28, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, + 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, + 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x22, 0x75, 0x0a, 0x29, 0x4c, 0x69, 0x73, 0x74, 0x50, + 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, + 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x63, 0x2e, 0x61, + 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, + 0x72, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x73, 0x22, 0x8e, + 0x01, 0x0a, 0x1a, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, + 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, + 0x71, 0x62, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, + 0x66, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x17, 0x0a, 0x07, + 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, + 0x6f, 0x6f, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x65, 0x0a, 0x1b, 0x53, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x63, + 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x42, 0x48, + 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, + 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, + 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, + 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1165,46 +1239,49 @@ func file_cc_arduino_cli_commands_v1_upload_proto_rawDescGZIP() []byte { return file_cc_arduino_cli_commands_v1_upload_proto_rawDescData } -var file_cc_arduino_cli_commands_v1_upload_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_cc_arduino_cli_commands_v1_upload_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_cc_arduino_cli_commands_v1_upload_proto_goTypes = []interface{}{ (*UploadRequest)(nil), // 0: cc.arduino.cli.commands.v1.UploadRequest (*UploadResponse)(nil), // 1: cc.arduino.cli.commands.v1.UploadResponse - (*ProgrammerIsRequiredForUploadError)(nil), // 2: cc.arduino.cli.commands.v1.ProgrammerIsRequiredForUploadError - (*UploadUsingProgrammerRequest)(nil), // 3: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest - (*UploadUsingProgrammerResponse)(nil), // 4: cc.arduino.cli.commands.v1.UploadUsingProgrammerResponse - (*BurnBootloaderRequest)(nil), // 5: cc.arduino.cli.commands.v1.BurnBootloaderRequest - (*BurnBootloaderResponse)(nil), // 6: cc.arduino.cli.commands.v1.BurnBootloaderResponse - (*ListProgrammersAvailableForUploadRequest)(nil), // 7: cc.arduino.cli.commands.v1.ListProgrammersAvailableForUploadRequest - (*ListProgrammersAvailableForUploadResponse)(nil), // 8: cc.arduino.cli.commands.v1.ListProgrammersAvailableForUploadResponse - (*SupportedUserFieldsRequest)(nil), // 9: cc.arduino.cli.commands.v1.SupportedUserFieldsRequest - (*UserField)(nil), // 10: cc.arduino.cli.commands.v1.UserField - (*SupportedUserFieldsResponse)(nil), // 11: cc.arduino.cli.commands.v1.SupportedUserFieldsResponse - nil, // 12: cc.arduino.cli.commands.v1.UploadRequest.UserFieldsEntry - nil, // 13: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.UserFieldsEntry - nil, // 14: cc.arduino.cli.commands.v1.BurnBootloaderRequest.UserFieldsEntry - (*Instance)(nil), // 15: cc.arduino.cli.commands.v1.Instance - (*Port)(nil), // 16: cc.arduino.cli.commands.v1.Port - (*Programmer)(nil), // 17: cc.arduino.cli.commands.v1.Programmer + (*UploadResult)(nil), // 2: cc.arduino.cli.commands.v1.UploadResult + (*ProgrammerIsRequiredForUploadError)(nil), // 3: cc.arduino.cli.commands.v1.ProgrammerIsRequiredForUploadError + (*UploadUsingProgrammerRequest)(nil), // 4: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest + (*UploadUsingProgrammerResponse)(nil), // 5: cc.arduino.cli.commands.v1.UploadUsingProgrammerResponse + (*BurnBootloaderRequest)(nil), // 6: cc.arduino.cli.commands.v1.BurnBootloaderRequest + (*BurnBootloaderResponse)(nil), // 7: cc.arduino.cli.commands.v1.BurnBootloaderResponse + (*ListProgrammersAvailableForUploadRequest)(nil), // 8: cc.arduino.cli.commands.v1.ListProgrammersAvailableForUploadRequest + (*ListProgrammersAvailableForUploadResponse)(nil), // 9: cc.arduino.cli.commands.v1.ListProgrammersAvailableForUploadResponse + (*SupportedUserFieldsRequest)(nil), // 10: cc.arduino.cli.commands.v1.SupportedUserFieldsRequest + (*UserField)(nil), // 11: cc.arduino.cli.commands.v1.UserField + (*SupportedUserFieldsResponse)(nil), // 12: cc.arduino.cli.commands.v1.SupportedUserFieldsResponse + nil, // 13: cc.arduino.cli.commands.v1.UploadRequest.UserFieldsEntry + nil, // 14: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.UserFieldsEntry + nil, // 15: cc.arduino.cli.commands.v1.BurnBootloaderRequest.UserFieldsEntry + (*Instance)(nil), // 16: cc.arduino.cli.commands.v1.Instance + (*Port)(nil), // 17: cc.arduino.cli.commands.v1.Port + (*Programmer)(nil), // 18: cc.arduino.cli.commands.v1.Programmer } var file_cc_arduino_cli_commands_v1_upload_proto_depIdxs = []int32{ - 15, // 0: cc.arduino.cli.commands.v1.UploadRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance - 16, // 1: cc.arduino.cli.commands.v1.UploadRequest.port:type_name -> cc.arduino.cli.commands.v1.Port - 12, // 2: cc.arduino.cli.commands.v1.UploadRequest.user_fields:type_name -> cc.arduino.cli.commands.v1.UploadRequest.UserFieldsEntry - 15, // 3: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance - 16, // 4: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.port:type_name -> cc.arduino.cli.commands.v1.Port - 13, // 5: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.user_fields:type_name -> cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.UserFieldsEntry - 15, // 6: cc.arduino.cli.commands.v1.BurnBootloaderRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance - 16, // 7: cc.arduino.cli.commands.v1.BurnBootloaderRequest.port:type_name -> cc.arduino.cli.commands.v1.Port - 14, // 8: cc.arduino.cli.commands.v1.BurnBootloaderRequest.user_fields:type_name -> cc.arduino.cli.commands.v1.BurnBootloaderRequest.UserFieldsEntry - 15, // 9: cc.arduino.cli.commands.v1.ListProgrammersAvailableForUploadRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance - 17, // 10: cc.arduino.cli.commands.v1.ListProgrammersAvailableForUploadResponse.programmers:type_name -> cc.arduino.cli.commands.v1.Programmer - 15, // 11: cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance - 10, // 12: cc.arduino.cli.commands.v1.SupportedUserFieldsResponse.user_fields:type_name -> cc.arduino.cli.commands.v1.UserField - 13, // [13:13] is the sub-list for method output_type - 13, // [13:13] is the sub-list for method input_type - 13, // [13:13] is the sub-list for extension type_name - 13, // [13:13] is the sub-list for extension extendee - 0, // [0:13] is the sub-list for field type_name + 16, // 0: cc.arduino.cli.commands.v1.UploadRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance + 17, // 1: cc.arduino.cli.commands.v1.UploadRequest.port:type_name -> cc.arduino.cli.commands.v1.Port + 13, // 2: cc.arduino.cli.commands.v1.UploadRequest.user_fields:type_name -> cc.arduino.cli.commands.v1.UploadRequest.UserFieldsEntry + 2, // 3: cc.arduino.cli.commands.v1.UploadResponse.result:type_name -> cc.arduino.cli.commands.v1.UploadResult + 17, // 4: cc.arduino.cli.commands.v1.UploadResult.updated_upload_port:type_name -> cc.arduino.cli.commands.v1.Port + 16, // 5: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance + 17, // 6: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.port:type_name -> cc.arduino.cli.commands.v1.Port + 14, // 7: cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.user_fields:type_name -> cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest.UserFieldsEntry + 16, // 8: cc.arduino.cli.commands.v1.BurnBootloaderRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance + 17, // 9: cc.arduino.cli.commands.v1.BurnBootloaderRequest.port:type_name -> cc.arduino.cli.commands.v1.Port + 15, // 10: cc.arduino.cli.commands.v1.BurnBootloaderRequest.user_fields:type_name -> cc.arduino.cli.commands.v1.BurnBootloaderRequest.UserFieldsEntry + 16, // 11: cc.arduino.cli.commands.v1.ListProgrammersAvailableForUploadRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance + 18, // 12: cc.arduino.cli.commands.v1.ListProgrammersAvailableForUploadResponse.programmers:type_name -> cc.arduino.cli.commands.v1.Programmer + 16, // 13: cc.arduino.cli.commands.v1.SupportedUserFieldsRequest.instance:type_name -> cc.arduino.cli.commands.v1.Instance + 11, // 14: cc.arduino.cli.commands.v1.SupportedUserFieldsResponse.user_fields:type_name -> cc.arduino.cli.commands.v1.UserField + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_cc_arduino_cli_commands_v1_upload_proto_init() } @@ -1240,7 +1317,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProgrammerIsRequiredForUploadError); i { + switch v := v.(*UploadResult); i { case 0: return &v.state case 1: @@ -1252,7 +1329,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UploadUsingProgrammerRequest); i { + switch v := v.(*ProgrammerIsRequiredForUploadError); i { case 0: return &v.state case 1: @@ -1264,7 +1341,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UploadUsingProgrammerResponse); i { + switch v := v.(*UploadUsingProgrammerRequest); i { case 0: return &v.state case 1: @@ -1276,7 +1353,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BurnBootloaderRequest); i { + switch v := v.(*UploadUsingProgrammerResponse); i { case 0: return &v.state case 1: @@ -1288,7 +1365,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BurnBootloaderResponse); i { + switch v := v.(*BurnBootloaderRequest); i { case 0: return &v.state case 1: @@ -1300,7 +1377,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListProgrammersAvailableForUploadRequest); i { + switch v := v.(*BurnBootloaderResponse); i { case 0: return &v.state case 1: @@ -1312,7 +1389,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListProgrammersAvailableForUploadResponse); i { + switch v := v.(*ListProgrammersAvailableForUploadRequest); i { case 0: return &v.state case 1: @@ -1324,7 +1401,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SupportedUserFieldsRequest); i { + switch v := v.(*ListProgrammersAvailableForUploadResponse); i { case 0: return &v.state case 1: @@ -1336,7 +1413,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserField); i { + switch v := v.(*SupportedUserFieldsRequest); i { case 0: return &v.state case 1: @@ -1348,6 +1425,18 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { } } file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserField); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SupportedUserFieldsResponse); i { case 0: return &v.state @@ -1363,6 +1452,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { file_cc_arduino_cli_commands_v1_upload_proto_msgTypes[1].OneofWrappers = []interface{}{ (*UploadResponse_OutStream)(nil), (*UploadResponse_ErrStream)(nil), + (*UploadResponse_Result)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -1370,7 +1460,7 @@ func file_cc_arduino_cli_commands_v1_upload_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_cc_arduino_cli_commands_v1_upload_proto_rawDesc, NumEnums: 0, - NumMessages: 15, + NumMessages: 16, NumExtensions: 0, NumServices: 0, }, diff --git a/rpc/cc/arduino/cli/commands/v1/upload.proto b/rpc/cc/arduino/cli/commands/v1/upload.proto index 4ea99c0147c..8a2b6b0215d 100644 --- a/rpc/cc/arduino/cli/commands/v1/upload.proto +++ b/rpc/cc/arduino/cli/commands/v1/upload.proto @@ -67,9 +67,17 @@ message UploadResponse { bytes out_stream = 1; // The error output of the upload process. bytes err_stream = 2; + // The upload result + UploadResult result = 3; } } +message UploadResult { + // When a board requires a port disconnection to perform the upload, this + // field returns the port where the board reconnects after the upload. + Port updated_upload_port = 1; +} + message ProgrammerIsRequiredForUploadError {} message UploadUsingProgrammerRequest { From bfb2fb4447ae179375a6cb9517ef72c668c1c41c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 1 Aug 2023 13:48:02 +0200 Subject: [PATCH 03/18] Upload port change detection (first draft) --- commands/upload/burnbootloader.go | 1 + commands/upload/upload.go | 114 ++++++++++++++++++++++--- commands/upload/upload_test.go | 1 + internal/algorithms/channels.go | 22 +++++ rpc/cc/arduino/cli/commands/v1/port.go | 22 +++++ 5 files changed, 150 insertions(+), 10 deletions(-) create mode 100644 internal/algorithms/channels.go create mode 100644 rpc/cc/arduino/cli/commands/v1/port.go diff --git a/commands/upload/burnbootloader.go b/commands/upload/burnbootloader.go index 99efe2be757..2b369b95e4f 100644 --- a/commands/upload/burnbootloader.go +++ b/commands/upload/burnbootloader.go @@ -41,6 +41,7 @@ func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStre _, err := runProgramAction( pme, + nil, // watcher nil, // sketch "", // importFile "", // importDir diff --git a/commands/upload/upload.go b/commands/upload/upload.go index a8678d38e27..48fcc0c7f81 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -21,6 +21,8 @@ import ( "io" "path/filepath" "strings" + "sync" + "time" "github.com/arduino/arduino-cli/arduino" "github.com/arduino/arduino-cli/arduino/cores" @@ -29,8 +31,10 @@ import ( "github.com/arduino/arduino-cli/arduino/serialutils" "github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/arduino-cli/commands" + "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/executils" "github.com/arduino/arduino-cli/i18n" + f "github.com/arduino/arduino-cli/internal/algorithms" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" @@ -134,15 +138,23 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er return nil, &arduino.CantOpenSketchError{Cause: err} } - pme, release := commands.GetPackageManagerExplorer(req) + pme, pmeRelease := commands.GetPackageManagerExplorer(req) if pme == nil { return nil, &arduino.InvalidInstanceError{} } - defer release() + defer pmeRelease() + + watch, watchRelease, err := board.Watch(&rpc.BoardListWatchRequest{Instance: req.GetInstance()}) + if err != nil { + logrus.WithError(err).Error("Error watching board ports") + } else { + defer watchRelease() + } updatedPort, err := runProgramAction( pme, sk, + watch, req.GetImportFile(), req.GetImportDir(), req.GetFqbn(), @@ -189,21 +201,43 @@ func UsingProgrammer(ctx context.Context, req *rpc.UploadUsingProgrammerRequest, func runProgramAction(pme *packagemanager.Explorer, sk *sketch.Sketch, + watch <-chan *rpc.BoardListWatchResponse, importFile, importDir, fqbnIn string, port *rpc.Port, programmerID string, verbose, verify, burnBootloader bool, outStream, errStream io.Writer, dryRun bool, userFields map[string]string) (*rpc.Port, error) { - if burnBootloader && programmerID == "" { - return nil, &arduino.MissingProgrammerError{} - } if port == nil || (port.Address == "" && port.Protocol == "") { // For no-port uploads use "default" protocol port = &rpc.Port{Protocol: "default"} } logrus.WithField("port", port).Tracef("Upload port") + // Default newPort + uploadCompleted := func() *rpc.Port { return nil } + if watch != nil { + // Run port detector + uploadCompletedCtx, cancel := context.WithCancel(context.Background()) + var newUploadPort *rpc.Port + var wg sync.WaitGroup + wg.Add(1) + go func() { + newUploadPort = detectUploadPort(port, watch, uploadCompletedCtx) + wg.Done() + }() + uploadCompleted = func() *rpc.Port { + cancel() + wg.Wait() + return newUploadPort + } + defer uploadCompleted() // defer in case of exit on error (ensures goroutine completion) + } + + if burnBootloader && programmerID == "" { + return nil, &arduino.MissingProgrammerError{} + } + fqbn, err := cores.ParseFQBN(fqbnIn) if err != nil { return nil, &arduino.InvalidFQBNError{Cause: err} @@ -485,13 +519,73 @@ func runProgramAction(pme *packagemanager.Explorer, } logrus.Tracef("Upload successful") - return nil, nil // TODO: return new port + return uploadCompleted(), nil } -func detectNewUploadPort(oldPort *rpc.Port) *rpc.Port { - logrus.Tracef("Detecting new board port") - // TODO - return nil +func detectUploadPort(uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResponse, uploadCtx context.Context) *rpc.Port { + log := logrus.WithField("task", "port_detection") + log.Tracef("Detecting new board port after upload") + + defer func() { + // On exit, discard all events until the watcher is closed + go f.DiscardCh(watch) + }() + + // Ignore all events during the upload + for { + select { + case ev, ok := <-watch: + if !ok { + log.Error("Upload port detection failed, watcher closed") + return nil + } + log.WithField("event", ev).Trace("Ignored watcher event before upload") + continue + case <-uploadCtx.Done(): + // Upload completed, move to the next phase + } + break + } + + // Pick the first port that is detected after the upload + desiredHwID := uploadPort.HardwareId + timeout := time.After(5 * time.Second) + var candidate *rpc.Port + for { + select { + case ev, ok := <-watch: + if !ok { + log.Error("Upload port detection failed, watcher closed") + return candidate + } + if ev.EventType == "remove" && candidate != nil { + if candidate.Equals(ev.Port.GetPort()) { + log.WithField("event", ev).Trace("Candidate port is no more available") + candidate = nil + continue + } + } + if ev.EventType != "add" { + log.WithField("event", ev).Trace("Ignored non-add event") + continue + } + candidate = ev.Port.GetPort() + log.WithField("event", ev).Trace("New upload port candidate") + + // If the current candidate port does not have the desired HW-ID do + // not return it immediately. + if desiredHwID != "" && candidate.GetHardwareId() != desiredHwID { + log.Trace("New candidate port did not match desired HW ID, keep watching...") + continue + } + + log.Trace("Found new upload port!") + return candidate + case <-timeout: + log.Trace("Timeout waiting for candidate port") + return candidate + } + } } func runTool(recipeID string, props *properties.Map, outStream, errStream io.Writer, verbose bool, dryRun bool, toolEnv []string) error { diff --git a/commands/upload/upload_test.go b/commands/upload/upload_test.go index e1f87cd9832..771af042c42 100644 --- a/commands/upload/upload_test.go +++ b/commands/upload/upload_test.go @@ -187,6 +187,7 @@ func TestUploadPropertiesComposition(t *testing.T) { _, err := runProgramAction( pme, nil, // sketch + nil, // board watcher "", // importFile test.importDir.String(), // importDir test.fqbn, // FQBN diff --git a/internal/algorithms/channels.go b/internal/algorithms/channels.go new file mode 100644 index 00000000000..9ffa2174cee --- /dev/null +++ b/internal/algorithms/channels.go @@ -0,0 +1,22 @@ +// This file is part of arduino-cli. +// +// Copyright 2023 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package f + +// DiscardCh consume all incoming messages from the given channel until its closed. +func DiscardCh[T any](ch <-chan T) { + for range ch { + } +} diff --git a/rpc/cc/arduino/cli/commands/v1/port.go b/rpc/cc/arduino/cli/commands/v1/port.go new file mode 100644 index 00000000000..530636ad194 --- /dev/null +++ b/rpc/cc/arduino/cli/commands/v1/port.go @@ -0,0 +1,22 @@ +// This file is part of arduino-cli. +// +// Copyright 2023 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package commands + +// Equals return true if the given port has the same address and protocol +// of the current port. +func (p *Port) Equals(o *Port) bool { + return p.GetAddress() == o.GetAddress() && p.GetProtocol() == o.GetProtocol() +} From f7eef3b4d80308a3d31517d2199996165fd75f13 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 2 Aug 2023 11:42:07 +0200 Subject: [PATCH 04/18] Simplified port detection using a Future-style abstraction --- commands/upload/upload.go | 26 ++++++++++---------------- internal/algorithms/channels.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 48fcc0c7f81..a2c4b3d8934 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -21,7 +21,6 @@ import ( "io" "path/filepath" "strings" - "sync" "time" "github.com/arduino/arduino-cli/arduino" @@ -219,17 +218,11 @@ func runProgramAction(pme *packagemanager.Explorer, if watch != nil { // Run port detector uploadCompletedCtx, cancel := context.WithCancel(context.Background()) - var newUploadPort *rpc.Port - var wg sync.WaitGroup - wg.Add(1) - go func() { - newUploadPort = detectUploadPort(port, watch, uploadCompletedCtx) - wg.Done() - }() + newUploadPort := f.NewFuture[*rpc.Port]() + go detectUploadPort(port, watch, uploadCompletedCtx, newUploadPort) uploadCompleted = func() *rpc.Port { cancel() - wg.Wait() - return newUploadPort + return newUploadPort.Await() } defer uploadCompleted() // defer in case of exit on error (ensures goroutine completion) } @@ -522,13 +515,15 @@ func runProgramAction(pme *packagemanager.Explorer, return uploadCompleted(), nil } -func detectUploadPort(uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResponse, uploadCtx context.Context) *rpc.Port { +func detectUploadPort(uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResponse, uploadCtx context.Context, result f.Future[*rpc.Port]) { log := logrus.WithField("task", "port_detection") log.Tracef("Detecting new board port after upload") + var candidate *rpc.Port defer func() { // On exit, discard all events until the watcher is closed go f.DiscardCh(watch) + result.Send(candidate) }() // Ignore all events during the upload @@ -537,7 +532,7 @@ func detectUploadPort(uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResp case ev, ok := <-watch: if !ok { log.Error("Upload port detection failed, watcher closed") - return nil + return } log.WithField("event", ev).Trace("Ignored watcher event before upload") continue @@ -550,13 +545,12 @@ func detectUploadPort(uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResp // Pick the first port that is detected after the upload desiredHwID := uploadPort.HardwareId timeout := time.After(5 * time.Second) - var candidate *rpc.Port for { select { case ev, ok := <-watch: if !ok { log.Error("Upload port detection failed, watcher closed") - return candidate + return } if ev.EventType == "remove" && candidate != nil { if candidate.Equals(ev.Port.GetPort()) { @@ -580,10 +574,10 @@ func detectUploadPort(uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResp } log.Trace("Found new upload port!") - return candidate + return case <-timeout: log.Trace("Timeout waiting for candidate port") - return candidate + return } } } diff --git a/internal/algorithms/channels.go b/internal/algorithms/channels.go index 9ffa2174cee..dfd9db22ab0 100644 --- a/internal/algorithms/channels.go +++ b/internal/algorithms/channels.go @@ -15,8 +15,41 @@ package f +import "sync" + // DiscardCh consume all incoming messages from the given channel until its closed. func DiscardCh[T any](ch <-chan T) { for range ch { } } + +// Future is an object that holds a result value. The value may be read and +// written asynchronously. +type Future[T any] interface { + Send(T) + Await() T +} + +type future[T any] struct { + wg sync.WaitGroup + value T +} + +// NewFuture creates a new Future[T] +func NewFuture[T any]() Future[T] { + res := &future[T]{} + res.wg.Add(1) + return res +} + +// Send a result in the Future. Threads waiting for result will be unlocked. +func (f *future[T]) Send(value T) { + f.value = value + f.wg.Done() +} + +// Await for a result from the Future, blocks until a result is available. +func (f *future[T]) Await() T { + f.wg.Wait() + return f.value +} From 83dcade7fe80de840e1cf9b324f04af55aab1a99 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 2 Aug 2023 11:55:12 +0200 Subject: [PATCH 05/18] Perform watcher-flush higher in the call tree --- commands/upload/upload.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index a2c4b3d8934..36890434162 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -207,6 +207,11 @@ func runProgramAction(pme *packagemanager.Explorer, outStream, errStream io.Writer, dryRun bool, userFields map[string]string) (*rpc.Port, error) { + defer func() { + // On exit, discard all events until the watcher is closed + go f.DiscardCh(watch) + }() + if port == nil || (port.Address == "" && port.Protocol == "") { // For no-port uploads use "default" protocol port = &rpc.Port{Protocol: "default"} @@ -521,8 +526,6 @@ func detectUploadPort(uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResp var candidate *rpc.Port defer func() { - // On exit, discard all events until the watcher is closed - go f.DiscardCh(watch) result.Send(candidate) }() From 33126273d94b852e411ddb2a2c2d5e6ad7171c22 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 2 Aug 2023 12:59:14 +0200 Subject: [PATCH 06/18] Do not infer upload port if 'upload.wait_for_upload_port' is false --- commands/upload/upload.go | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 36890434162..eda2fbc560e 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -207,8 +207,8 @@ func runProgramAction(pme *packagemanager.Explorer, outStream, errStream io.Writer, dryRun bool, userFields map[string]string) (*rpc.Port, error) { + // Ensure watcher events consumption in case of exit on error defer func() { - // On exit, discard all events until the watcher is closed go f.DiscardCh(watch) }() @@ -218,20 +218,6 @@ func runProgramAction(pme *packagemanager.Explorer, } logrus.WithField("port", port).Tracef("Upload port") - // Default newPort - uploadCompleted := func() *rpc.Port { return nil } - if watch != nil { - // Run port detector - uploadCompletedCtx, cancel := context.WithCancel(context.Background()) - newUploadPort := f.NewFuture[*rpc.Port]() - go detectUploadPort(port, watch, uploadCompletedCtx, newUploadPort) - uploadCompleted = func() *rpc.Port { - cancel() - return newUploadPort.Await() - } - defer uploadCompleted() // defer in case of exit on error (ensures goroutine completion) - } - if burnBootloader && programmerID == "" { return nil, &arduino.MissingProgrammerError{} } @@ -404,6 +390,24 @@ func runProgramAction(pme *packagemanager.Explorer, uploadProperties.Set("build.project_name", sketchName) } + // By default do not return any new port... + uploadCompleted := func() *rpc.Port { return nil } + // ...but if there is an expected port change then run the detector... + if uploadProperties.GetBoolean("upload.wait_for_upload_port") && watch != nil { + uploadCompletedCtx, cancel := context.WithCancel(context.Background()) + newUploadPort := f.NewFuture[*rpc.Port]() + go detectUploadPort(port, watch, uploadCompletedCtx, newUploadPort) + uploadCompleted = func() *rpc.Port { + cancel() + return newUploadPort.Await() + } + + // Ensures goroutines completion in case of exit on error + defer uploadCompleted() + } else { + go f.DiscardCh(watch) + } + // Force port wait to make easier to unbrick boards like the Arduino Leonardo, or similar with native USB, // when a sketch causes a crash and the native USB serial port is lost. // See https://github.com/arduino/arduino-cli/issues/1943 for the details. From a087862dbaaf0d6fdff09fc87c4aba42f6674e80 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 2 Aug 2023 13:07:28 +0200 Subject: [PATCH 07/18] Further simplified port detection subroutine structure --- commands/upload/upload.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index eda2fbc560e..c3a0d12775f 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -390,21 +390,17 @@ func runProgramAction(pme *packagemanager.Explorer, uploadProperties.Set("build.project_name", sketchName) } - // By default do not return any new port... - uploadCompleted := func() *rpc.Port { return nil } - // ...but if there is an expected port change then run the detector... - if uploadProperties.GetBoolean("upload.wait_for_upload_port") && watch != nil { - uploadCompletedCtx, cancel := context.WithCancel(context.Background()) - newUploadPort := f.NewFuture[*rpc.Port]() - go detectUploadPort(port, watch, uploadCompletedCtx, newUploadPort) - uploadCompleted = func() *rpc.Port { - cancel() - return newUploadPort.Await() - } + // This context is kept alive for the entire duration of the upload + uploadCtx, uploadCompleted := context.WithCancel(context.Background()) + defer uploadCompleted() - // Ensures goroutines completion in case of exit on error - defer uploadCompleted() + // By default do not return any new port but if there is an + // expected port change then run the detector. + updatedUploadPort := f.NewFuture[*rpc.Port]() + if uploadProperties.GetBoolean("upload.wait_for_upload_port") && watch != nil { + go detectUploadPort(port, watch, uploadCtx, updatedUploadPort) } else { + updatedUploadPort.Send(nil) go f.DiscardCh(watch) } @@ -520,8 +516,10 @@ func runProgramAction(pme *packagemanager.Explorer, } } + uploadCompleted() logrus.Tracef("Upload successful") - return uploadCompleted(), nil + + return updatedUploadPort.Await(), nil } func detectUploadPort(uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResponse, uploadCtx context.Context, result f.Future[*rpc.Port]) { From 9f8d266cf3c0b57d5f6783268ef89e61d34097ad Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 2 Aug 2023 13:08:13 +0200 Subject: [PATCH 08/18] fixed linter issue --- commands/upload/upload.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index c3a0d12775f..b0dedee0c42 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -398,7 +398,7 @@ func runProgramAction(pme *packagemanager.Explorer, // expected port change then run the detector. updatedUploadPort := f.NewFuture[*rpc.Port]() if uploadProperties.GetBoolean("upload.wait_for_upload_port") && watch != nil { - go detectUploadPort(port, watch, uploadCtx, updatedUploadPort) + go detectUploadPort(uploadCtx, port, watch, updatedUploadPort) } else { updatedUploadPort.Send(nil) go f.DiscardCh(watch) @@ -522,7 +522,7 @@ func runProgramAction(pme *packagemanager.Explorer, return updatedUploadPort.Await(), nil } -func detectUploadPort(uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResponse, uploadCtx context.Context, result f.Future[*rpc.Port]) { +func detectUploadPort(uploadCtx context.Context, uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResponse, result f.Future[*rpc.Port]) { log := logrus.WithField("task", "port_detection") log.Tracef("Detecting new board port after upload") From 5896fba900db441a1a3a2a3af30536c0d8cb3425 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 9 Aug 2023 14:31:39 +0200 Subject: [PATCH 09/18] Always return an updatedUploadPort. Arduino CLI should always return the port after an upload, even in the case where no port change is expected. The consumer shouldn't be required to implement "if not updated_upload_port, use original port" logic. The whole point is that all the logic for determining which port should be selected after an upload should be implemented in Arduino CLI. The consumer should be able to simply select the port Arduino CLI tells it to select in all cases. --- commands/upload/upload.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index b0dedee0c42..227166d07c8 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -201,7 +201,7 @@ func UsingProgrammer(ctx context.Context, req *rpc.UploadUsingProgrammerRequest, func runProgramAction(pme *packagemanager.Explorer, sk *sketch.Sketch, watch <-chan *rpc.BoardListWatchResponse, - importFile, importDir, fqbnIn string, port *rpc.Port, + importFile, importDir, fqbnIn string, userPort *rpc.Port, programmerID string, verbose, verify, burnBootloader bool, outStream, errStream io.Writer, @@ -212,6 +212,7 @@ func runProgramAction(pme *packagemanager.Explorer, go f.DiscardCh(watch) }() + port := userPort if port == nil || (port.Address == "" && port.Protocol == "") { // For no-port uploads use "default" protocol port = &rpc.Port{Protocol: "default"} @@ -519,7 +520,11 @@ func runProgramAction(pme *packagemanager.Explorer, uploadCompleted() logrus.Tracef("Upload successful") - return updatedUploadPort.Await(), nil + updatedPort := updatedUploadPort.Await() + if updatedPort == nil { + updatedPort = userPort + } + return userPort, nil } func detectUploadPort(uploadCtx context.Context, uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResponse, result f.Future[*rpc.Port]) { From 990d93a5ed5feb407e8ac2398e21c3f89e024852 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 9 Aug 2023 14:57:32 +0200 Subject: [PATCH 10/18] Updated docs --- docs/UPGRADING.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md index 31490513af2..d1821cf87fa 100644 --- a/docs/UPGRADING.md +++ b/docs/UPGRADING.md @@ -4,6 +4,58 @@ Here you can find a list of migration guides to handle breaking changes between ## 0.34.0 +### The gRPC `cc.arduino.cli.commands.v1.UploadRepsonse` command response has been changed. + +Previously the `UploadResponse` was used only to stream the tool output: + +``` +message UploadResponse { + // The output of the upload process. + bytes out_stream = 1; + // The error output of the upload process. + bytes err_stream = 2; +} +``` + +Now the API logic has been clarified using the `oneof` clause and another field has been added providing an +`UploadResult` message that is sent when a successful upload completes. + +``` +message UploadResponse { + oneof message { + // The output of the upload process. + bytes out_stream = 1; + // The error output of the upload process. + bytes err_stream = 2; + // The upload result + UploadResult result = 3; + } +} + +message UploadResult { + // When a board requires a port disconnection to perform the upload, this + // field returns the port where the board reconnects after the upload. + Port updated_upload_port = 1; +} +``` + +### golang API: method `github.com/arduino/arduino-cli/commands/upload.Upload` changed signature + +The `Upload` method signature has been changed from: + +```go +func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, errStream io.Writer) error { ... } +``` + +to: + +```go +func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, errStream io.Writer) (*rpc.UploadResult, error) { ... } +``` + +Now an `UploadResult` structure is returned together with the error. If you are not interested in the information +contained in the structure you can safely ignore it. + ### golang package `github.com/arduino/arduino-cli/inventory` removed from public API The package `inventory` is no more a public golang API. From a750bdc69a7b835bdd82d61f392e666fa54e04ec Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 11 Aug 2023 13:07:19 +0200 Subject: [PATCH 11/18] Perform a deep-copy of upload ports where needed. Previously only the pointer was copied, thus making changes in `actualPort` to be reflected also to `port`. This lead to some weird result in the `updatedUploadPort` result: { "stdout": "Verify 11344 bytes of flash with checksum.\nVerify successful\ndone in 0.010 seconds\nCPU reset.\n", "stderr": "", "updated_upload_port": { "address": "/dev/tty.usbmodem14101", <------- this address... "label": "/dev/cu.usbmodem14101", <------- ...is different from the label "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x804E", "serialNumber": "94A3397C5150435437202020FF150838", "vid": "0x2341" }, "hardware_id": "94A3397C5150435437202020FF150838" } } --- arduino/discovery/discovery.go | 25 +++++++++++++++++++++++++ commands/upload/upload.go | 15 ++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/arduino/discovery/discovery.go b/arduino/discovery/discovery.go index b8c3537b5f4..3aeb3890110 100644 --- a/arduino/discovery/discovery.go +++ b/arduino/discovery/discovery.go @@ -113,6 +113,21 @@ func (p *Port) ToRPC() *rpc.Port { } } +// PortFromRPCPort converts an *rpc.Port to a *Port +func PortFromRPCPort(o *rpc.Port) (p *Port) { + if o == nil { + return nil + } + return &Port{ + Address: o.Address, + AddressLabel: o.Label, + Protocol: o.Protocol, + ProtocolLabel: o.ProtocolLabel, + HardwareID: o.HardwareId, + Properties: properties.NewFromHashmap(o.Properties), + } +} + func (p *Port) String() string { if p == nil { return "none" @@ -120,6 +135,16 @@ func (p *Port) String() string { return p.Address } +// Clone creates a copy of this Port +func (p *Port) Clone() *Port { + if p == nil { + return nil + } + var res Port = *p + res.Properties = p.Properties.Clone() + return &res +} + // Event is a pluggable discovery event type Event struct { Type string diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 227166d07c8..47a4a9cac61 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -26,6 +26,7 @@ import ( "github.com/arduino/arduino-cli/arduino" "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" + "github.com/arduino/arduino-cli/arduino/discovery" "github.com/arduino/arduino-cli/arduino/globals" "github.com/arduino/arduino-cli/arduino/serialutils" "github.com/arduino/arduino-cli/arduino/sketch" @@ -212,10 +213,10 @@ func runProgramAction(pme *packagemanager.Explorer, go f.DiscardCh(watch) }() - port := userPort + port := discovery.PortFromRPCPort(userPort) if port == nil || (port.Address == "" && port.Protocol == "") { // For no-port uploads use "default" protocol - port = &rpc.Port{Protocol: "default"} + port = &discovery.Port{Protocol: "default"} } logrus.WithField("port", port).Tracef("Upload port") @@ -421,7 +422,7 @@ func runProgramAction(pme *packagemanager.Explorer, // If not using programmer perform some action required // to set the board in bootloader mode - actualPort := port + actualPort := port.Clone() if programmer == nil && !burnBootloader && (port.Protocol == "serial" || forcedSerialPortWait) { // Perform reset via 1200bps touch if requested and wait for upload port also if requested. touch := uploadProperties.GetBoolean("upload.use_1200bps_touch") @@ -491,10 +492,10 @@ func runProgramAction(pme *packagemanager.Explorer, // Get Port properties gathered using pluggable discovery uploadProperties.Set("upload.port.address", port.Address) - uploadProperties.Set("upload.port.label", port.Label) + uploadProperties.Set("upload.port.label", port.AddressLabel) uploadProperties.Set("upload.port.protocol", port.Protocol) uploadProperties.Set("upload.port.protocolLabel", port.ProtocolLabel) - for prop, value := range actualPort.Properties { + for prop, value := range actualPort.Properties.AsMap() { uploadProperties.Set(fmt.Sprintf("upload.port.properties.%s", prop), value) } @@ -527,7 +528,7 @@ func runProgramAction(pme *packagemanager.Explorer, return userPort, nil } -func detectUploadPort(uploadCtx context.Context, uploadPort *rpc.Port, watch <-chan *rpc.BoardListWatchResponse, result f.Future[*rpc.Port]) { +func detectUploadPort(uploadCtx context.Context, uploadPort *discovery.Port, watch <-chan *rpc.BoardListWatchResponse, result f.Future[*rpc.Port]) { log := logrus.WithField("task", "port_detection") log.Tracef("Detecting new board port after upload") @@ -553,7 +554,7 @@ func detectUploadPort(uploadCtx context.Context, uploadPort *rpc.Port, watch <-c } // Pick the first port that is detected after the upload - desiredHwID := uploadPort.HardwareId + desiredHwID := uploadPort.HardwareID timeout := time.After(5 * time.Second) for { select { From d1869b6664ff6db9612519d7b00652e4bbf29110 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 11 Aug 2023 13:13:40 +0200 Subject: [PATCH 12/18] When updating `actualPort` address, update also the address label. --- commands/upload/upload.go | 1 + 1 file changed, 1 insertion(+) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 47a4a9cac61..31d7770c077 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -476,6 +476,7 @@ func runProgramAction(pme *packagemanager.Explorer, } else { if newPortAddress != "" { actualPort.Address = newPortAddress + actualPort.AddressLabel = newPortAddress } } } From af873e0105af7a2a0fb409737c7fa60a26592b5c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 11 Aug 2023 13:36:16 +0200 Subject: [PATCH 13/18] Fixed some potential nil pointer exceptions --- arduino/discovery/discovery.go | 11 ++++++++--- arduino/discovery/discovery_client/main.go | 10 ++++++---- commands/board/list.go | 13 ++++++++----- commands/board/list_test.go | 5 +---- commands/upload/upload.go | 6 ++++-- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/arduino/discovery/discovery.go b/arduino/discovery/discovery.go index 3aeb3890110..40767fb38e7 100644 --- a/arduino/discovery/discovery.go +++ b/arduino/discovery/discovery.go @@ -118,14 +118,17 @@ func PortFromRPCPort(o *rpc.Port) (p *Port) { if o == nil { return nil } - return &Port{ + res := &Port{ Address: o.Address, AddressLabel: o.Label, Protocol: o.Protocol, ProtocolLabel: o.ProtocolLabel, HardwareID: o.HardwareId, - Properties: properties.NewFromHashmap(o.Properties), } + if o.Properties != nil { + res.Properties = properties.NewFromHashmap(o.Properties) + } + return res } func (p *Port) String() string { @@ -141,7 +144,9 @@ func (p *Port) Clone() *Port { return nil } var res Port = *p - res.Properties = p.Properties.Clone() + if p.Properties != nil { + res.Properties = p.Properties.Clone() + } return &res } diff --git a/arduino/discovery/discovery_client/main.go b/arduino/discovery/discovery_client/main.go index dd556691fe4..b9e1cfd0988 100644 --- a/arduino/discovery/discovery_client/main.go +++ b/arduino/discovery/discovery_client/main.go @@ -51,10 +51,12 @@ func main() { fmt.Printf(" Address: %s\n", port.Address) fmt.Printf(" Protocol: %s\n", port.Protocol) if ev.Type == "add" { - keys := port.Properties.Keys() - sort.Strings(keys) - for _, k := range keys { - fmt.Printf(" %s=%s\n", k, port.Properties.Get(k)) + if port.Properties != nil { + keys := port.Properties.Keys() + sort.Strings(keys) + for _, k := range keys { + fmt.Printf(" %s=%s\n", k, port.Properties.Get(k)) + } } } fmt.Println() diff --git a/commands/board/list.go b/commands/board/list.go index 5098ced4cfd..1ac055253b9 100644 --- a/commands/board/list.go +++ b/commands/board/list.go @@ -34,6 +34,7 @@ import ( "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/inventory" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/go-properties-orderedmap" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -128,20 +129,22 @@ func apiByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) { }, nil } -func identifyViaCloudAPI(port *discovery.Port) ([]*rpc.BoardListItem, error) { +func identifyViaCloudAPI(props *properties.Map) ([]*rpc.BoardListItem, error) { // If the port is not USB do not try identification via cloud - id := port.Properties - if !id.ContainsKey("vid") || !id.ContainsKey("pid") { + if !props.ContainsKey("vid") || !props.ContainsKey("pid") { return nil, nil } logrus.Debug("Querying builder API for board identification...") - return cachedAPIByVidPid(id.Get("vid"), id.Get("pid")) + return cachedAPIByVidPid(props.Get("vid"), props.Get("pid")) } // identify returns a list of boards checking first the installed platforms or the Cloud API func identify(pme *packagemanager.Explorer, port *discovery.Port) ([]*rpc.BoardListItem, error) { boards := []*rpc.BoardListItem{} + if port.Properties == nil { + return boards, nil + } // first query installed cores through the Package Manager logrus.Debug("Querying installed cores for board identification...") @@ -167,7 +170,7 @@ func identify(pme *packagemanager.Explorer, port *discovery.Port) ([]*rpc.BoardL // if installed cores didn't recognize the board, try querying // the builder API if the board is a USB device port if len(boards) == 0 { - items, err := identifyViaCloudAPI(port) + items, err := identifyViaCloudAPI(port.Properties) if err != nil { // this is bad, but keep going logrus.WithError(err).Debug("Error querying builder API") diff --git a/commands/board/list_test.go b/commands/board/list_test.go index cf1dca37e4c..f99d942cf29 100644 --- a/commands/board/list_test.go +++ b/commands/board/list_test.go @@ -103,10 +103,7 @@ func TestGetByVidPidMalformedResponse(t *testing.T) { } func TestBoardDetectionViaAPIWithNonUSBPort(t *testing.T) { - port := &discovery.Port{ - Properties: properties.NewMap(), - } - items, err := identifyViaCloudAPI(port) + items, err := identifyViaCloudAPI(properties.NewMap()) require.NoError(t, err) require.Empty(t, items) } diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 31d7770c077..abf261b1fe3 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -496,8 +496,10 @@ func runProgramAction(pme *packagemanager.Explorer, uploadProperties.Set("upload.port.label", port.AddressLabel) uploadProperties.Set("upload.port.protocol", port.Protocol) uploadProperties.Set("upload.port.protocolLabel", port.ProtocolLabel) - for prop, value := range actualPort.Properties.AsMap() { - uploadProperties.Set(fmt.Sprintf("upload.port.properties.%s", prop), value) + if actualPort.Properties != nil { + for prop, value := range actualPort.Properties.AsMap() { + uploadProperties.Set(fmt.Sprintf("upload.port.properties.%s", prop), value) + } } // Run recipes for upload From f8394fb6998510d44e32807be2ac5b30bb7f84b9 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 11 Aug 2023 15:39:52 +0200 Subject: [PATCH 14/18] Further simplified board watcher We must acesss the gRPC API only until we cross the `command` package border. Once we are inside the `command` package we should use the internal API only. --- arduino/discovery/discovery.go | 6 ++++ commands/upload/burnbootloader.go | 1 - commands/upload/upload.go | 50 +++++++++++--------------- commands/upload/upload_test.go | 1 - rpc/cc/arduino/cli/commands/v1/port.go | 22 ------------ 5 files changed, 26 insertions(+), 54 deletions(-) delete mode 100644 rpc/cc/arduino/cli/commands/v1/port.go diff --git a/arduino/discovery/discovery.go b/arduino/discovery/discovery.go index 40767fb38e7..5a50d564583 100644 --- a/arduino/discovery/discovery.go +++ b/arduino/discovery/discovery.go @@ -97,6 +97,12 @@ type Port struct { var tr = i18n.Tr +// Equals return true if the given port has the same address and protocol +// of the current port. +func (p *Port) Equals(o *Port) bool { + return p.Address == o.Address && p.Protocol == o.Protocol +} + // ToRPC converts Port into rpc.Port func (p *Port) ToRPC() *rpc.Port { props := p.Properties diff --git a/commands/upload/burnbootloader.go b/commands/upload/burnbootloader.go index 2b369b95e4f..99efe2be757 100644 --- a/commands/upload/burnbootloader.go +++ b/commands/upload/burnbootloader.go @@ -41,7 +41,6 @@ func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStre _, err := runProgramAction( pme, - nil, // watcher nil, // sketch "", // importFile "", // importDir diff --git a/commands/upload/upload.go b/commands/upload/upload.go index abf261b1fe3..decf2b72293 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -31,7 +31,6 @@ import ( "github.com/arduino/arduino-cli/arduino/serialutils" "github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/arduino-cli/commands" - "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/executils" "github.com/arduino/arduino-cli/i18n" f "github.com/arduino/arduino-cli/internal/algorithms" @@ -144,17 +143,9 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er } defer pmeRelease() - watch, watchRelease, err := board.Watch(&rpc.BoardListWatchRequest{Instance: req.GetInstance()}) - if err != nil { - logrus.WithError(err).Error("Error watching board ports") - } else { - defer watchRelease() - } - updatedPort, err := runProgramAction( pme, sk, - watch, req.GetImportFile(), req.GetImportDir(), req.GetFqbn(), @@ -201,18 +192,12 @@ func UsingProgrammer(ctx context.Context, req *rpc.UploadUsingProgrammerRequest, func runProgramAction(pme *packagemanager.Explorer, sk *sketch.Sketch, - watch <-chan *rpc.BoardListWatchResponse, importFile, importDir, fqbnIn string, userPort *rpc.Port, programmerID string, verbose, verify, burnBootloader bool, outStream, errStream io.Writer, - dryRun bool, userFields map[string]string) (*rpc.Port, error) { - - // Ensure watcher events consumption in case of exit on error - defer func() { - go f.DiscardCh(watch) - }() - + dryRun bool, userFields map[string]string, +) (*rpc.Port, error) { port := discovery.PortFromRPCPort(userPort) if port == nil || (port.Address == "" && port.Protocol == "") { // For no-port uploads use "default" protocol @@ -398,12 +383,17 @@ func runProgramAction(pme *packagemanager.Explorer, // By default do not return any new port but if there is an // expected port change then run the detector. - updatedUploadPort := f.NewFuture[*rpc.Port]() - if uploadProperties.GetBoolean("upload.wait_for_upload_port") && watch != nil { - go detectUploadPort(uploadCtx, port, watch, updatedUploadPort) + updatedUploadPort := f.NewFuture[*discovery.Port]() + if uploadProperties.GetBoolean("upload.wait_for_upload_port") { + watcher, err := pme.DiscoveryManager().Watch() + if err != nil { + return nil, err + } + defer watcher.Close() + + go detectUploadPort(uploadCtx, port, watcher.Feed(), updatedUploadPort) } else { updatedUploadPort.Send(nil) - go f.DiscardCh(watch) } // Force port wait to make easier to unbrick boards like the Arduino Leonardo, or similar with native USB, @@ -526,16 +516,16 @@ func runProgramAction(pme *packagemanager.Explorer, updatedPort := updatedUploadPort.Await() if updatedPort == nil { - updatedPort = userPort + return userPort, nil } - return userPort, nil + return updatedPort.ToRPC(), nil } -func detectUploadPort(uploadCtx context.Context, uploadPort *discovery.Port, watch <-chan *rpc.BoardListWatchResponse, result f.Future[*rpc.Port]) { +func detectUploadPort(uploadCtx context.Context, uploadPort *discovery.Port, watch <-chan *discovery.Event, result f.Future[*discovery.Port]) { log := logrus.WithField("task", "port_detection") log.Tracef("Detecting new board port after upload") - var candidate *rpc.Port + var candidate *discovery.Port defer func() { result.Send(candidate) }() @@ -566,23 +556,23 @@ func detectUploadPort(uploadCtx context.Context, uploadPort *discovery.Port, wat log.Error("Upload port detection failed, watcher closed") return } - if ev.EventType == "remove" && candidate != nil { - if candidate.Equals(ev.Port.GetPort()) { + if ev.Type == "remove" && candidate != nil { + if candidate.Equals(ev.Port) { log.WithField("event", ev).Trace("Candidate port is no more available") candidate = nil continue } } - if ev.EventType != "add" { + if ev.Type != "add" { log.WithField("event", ev).Trace("Ignored non-add event") continue } - candidate = ev.Port.GetPort() + candidate = ev.Port log.WithField("event", ev).Trace("New upload port candidate") // If the current candidate port does not have the desired HW-ID do // not return it immediately. - if desiredHwID != "" && candidate.GetHardwareId() != desiredHwID { + if desiredHwID != "" && candidate.HardwareID != desiredHwID { log.Trace("New candidate port did not match desired HW ID, keep watching...") continue } diff --git a/commands/upload/upload_test.go b/commands/upload/upload_test.go index 771af042c42..e1f87cd9832 100644 --- a/commands/upload/upload_test.go +++ b/commands/upload/upload_test.go @@ -187,7 +187,6 @@ func TestUploadPropertiesComposition(t *testing.T) { _, err := runProgramAction( pme, nil, // sketch - nil, // board watcher "", // importFile test.importDir.String(), // importDir test.fqbn, // FQBN diff --git a/rpc/cc/arduino/cli/commands/v1/port.go b/rpc/cc/arduino/cli/commands/v1/port.go deleted file mode 100644 index 530636ad194..00000000000 --- a/rpc/cc/arduino/cli/commands/v1/port.go +++ /dev/null @@ -1,22 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2023 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package commands - -// Equals return true if the given port has the same address and protocol -// of the current port. -func (p *Port) Equals(o *Port) bool { - return p.GetAddress() == o.GetAddress() && p.GetProtocol() == o.GetProtocol() -} From e9e5fbdd824ab3fb918809697353a8f88677bd98 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 14 Aug 2023 11:28:28 +0200 Subject: [PATCH 15/18] Before returning from upload, check if the port is still alive Now the upload detects cases when the upload port is "unstable", i.e. the port changes even if it shouldn't (because the wait_for_upload_port property in boards.txt is set to false). This change should make the upload process more resilient. --- commands/upload/upload.go | 63 +++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index decf2b72293..ebc609b3439 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -381,20 +381,18 @@ func runProgramAction(pme *packagemanager.Explorer, uploadCtx, uploadCompleted := context.WithCancel(context.Background()) defer uploadCompleted() - // By default do not return any new port but if there is an - // expected port change then run the detector. - updatedUploadPort := f.NewFuture[*discovery.Port]() - if uploadProperties.GetBoolean("upload.wait_for_upload_port") { - watcher, err := pme.DiscoveryManager().Watch() - if err != nil { - return nil, err - } - defer watcher.Close() - - go detectUploadPort(uploadCtx, port, watcher.Feed(), updatedUploadPort) - } else { - updatedUploadPort.Send(nil) + // Start the upload port change detector. + watcher, err := pme.DiscoveryManager().Watch() + if err != nil { + return nil, err } + defer watcher.Close() + updatedUploadPort := f.NewFuture[*discovery.Port]() + go detectUploadPort( + uploadCtx, + port, watcher.Feed(), + uploadProperties.GetBoolean("upload.wait_for_upload_port"), + updatedUploadPort) // Force port wait to make easier to unbrick boards like the Arduino Leonardo, or similar with native USB, // when a sketch causes a crash and the native USB serial port is lost. @@ -514,18 +512,19 @@ func runProgramAction(pme *packagemanager.Explorer, uploadCompleted() logrus.Tracef("Upload successful") - updatedPort := updatedUploadPort.Await() - if updatedPort == nil { - return userPort, nil - } - return updatedPort.ToRPC(), nil + return updatedUploadPort.Await().ToRPC(), nil } -func detectUploadPort(uploadCtx context.Context, uploadPort *discovery.Port, watch <-chan *discovery.Event, result f.Future[*discovery.Port]) { +func detectUploadPort( + uploadCtx context.Context, + uploadPort *discovery.Port, watch <-chan *discovery.Event, + waitForUploadPort bool, + result f.Future[*discovery.Port], +) { log := logrus.WithField("task", "port_detection") log.Tracef("Detecting new board port after upload") - var candidate *discovery.Port + candidate := uploadPort.Clone() defer func() { result.Send(candidate) }() @@ -538,7 +537,13 @@ func detectUploadPort(uploadCtx context.Context, uploadPort *discovery.Port, wat log.Error("Upload port detection failed, watcher closed") return } - log.WithField("event", ev).Trace("Ignored watcher event before upload") + if candidate != nil && ev.Type == "remove" && ev.Port.Equals(candidate) { + log.WithField("event", ev).Trace("User-specified port has been disconnected, forcing waiting for upload port") + waitForUploadPort = true + candidate = nil + } else { + log.WithField("event", ev).Trace("Ignored watcher event before upload") + } continue case <-uploadCtx.Done(): // Upload completed, move to the next phase @@ -549,6 +554,9 @@ func detectUploadPort(uploadCtx context.Context, uploadPort *discovery.Port, wat // Pick the first port that is detected after the upload desiredHwID := uploadPort.HardwareID timeout := time.After(5 * time.Second) + if !waitForUploadPort { + timeout = time.After(time.Second) + } for { select { case ev, ok := <-watch: @@ -556,12 +564,15 @@ func detectUploadPort(uploadCtx context.Context, uploadPort *discovery.Port, wat log.Error("Upload port detection failed, watcher closed") return } - if ev.Type == "remove" && candidate != nil { - if candidate.Equals(ev.Port) { - log.WithField("event", ev).Trace("Candidate port is no more available") - candidate = nil - continue + if candidate != nil && ev.Type == "remove" && candidate.Equals(ev.Port) { + log.WithField("event", ev).Trace("Candidate port is no more available") + candidate = nil + if !waitForUploadPort { + waitForUploadPort = true + timeout = time.After(5 * time.Second) + log.Trace("User-specified port has been disconnected, now waiting for upload port, timeout exteneded by 5 seconds") } + continue } if ev.Type != "add" { log.WithField("event", ev).Trace("Ignored non-add event") From 69d9ae7e9d1285e596e814462b60e0bcbed344fe Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 16 Aug 2023 10:12:30 +0200 Subject: [PATCH 16/18] Apply suggestions from code review Co-authored-by: per1234 --- arduino/discovery/discovery.go | 2 +- commands/upload/upload.go | 6 +++--- internal/algorithms/channels.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arduino/discovery/discovery.go b/arduino/discovery/discovery.go index 5a50d564583..1832f1e77d3 100644 --- a/arduino/discovery/discovery.go +++ b/arduino/discovery/discovery.go @@ -97,7 +97,7 @@ type Port struct { var tr = i18n.Tr -// Equals return true if the given port has the same address and protocol +// Equals returns true if the given port has the same address and protocol // of the current port. func (p *Port) Equals(o *Port) bool { return p.Address == o.Address && p.Protocol == o.Protocol diff --git a/commands/upload/upload.go b/commands/upload/upload.go index ebc609b3439..920248992ae 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -538,7 +538,7 @@ func detectUploadPort( return } if candidate != nil && ev.Type == "remove" && ev.Port.Equals(candidate) { - log.WithField("event", ev).Trace("User-specified port has been disconnected, forcing waiting for upload port") + log.WithField("event", ev).Trace("User-specified port has been disconnected, forcing wait for upload port") waitForUploadPort = true candidate = nil } else { @@ -565,12 +565,12 @@ func detectUploadPort( return } if candidate != nil && ev.Type == "remove" && candidate.Equals(ev.Port) { - log.WithField("event", ev).Trace("Candidate port is no more available") + log.WithField("event", ev).Trace("Candidate port is no longer available") candidate = nil if !waitForUploadPort { waitForUploadPort = true timeout = time.After(5 * time.Second) - log.Trace("User-specified port has been disconnected, now waiting for upload port, timeout exteneded by 5 seconds") + log.Trace("User-specified port has been disconnected, now waiting for upload port, timeout extended by 5 seconds") } continue } diff --git a/internal/algorithms/channels.go b/internal/algorithms/channels.go index dfd9db22ab0..982925322cb 100644 --- a/internal/algorithms/channels.go +++ b/internal/algorithms/channels.go @@ -17,7 +17,7 @@ package f import "sync" -// DiscardCh consume all incoming messages from the given channel until its closed. +// DiscardCh consumes all incoming messages from the given channel until it's closed. func DiscardCh[T any](ch <-chan T) { for range ch { } From e12a068128d82e5b13745f0598bf32e85eab8399 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 16 Aug 2023 10:21:02 +0200 Subject: [PATCH 17/18] Fixed nil exception --- commands/upload/upload.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 920248992ae..5f2755e6dad 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -512,7 +512,11 @@ func runProgramAction(pme *packagemanager.Explorer, uploadCompleted() logrus.Tracef("Upload successful") - return updatedUploadPort.Await().ToRPC(), nil + updatedPort := updatedUploadPort.Await() + if updatedPort == nil { + return nil, nil + } + return updatedPort.ToRPC(), nil } func detectUploadPort( From 862bbe243b0762ca92a41c9de12797f8195e1cf4 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 16 Aug 2023 16:31:23 +0200 Subject: [PATCH 18/18] Improved tracking algorithm for upload-port reconnection The new algorithm takes into account the case where a single board may expose multiple ports, in this case the selection will increase priority to ports that: 1. have the same HW id as the user specified port for upload 2. have the same protocol as the user specified port for upload 3. have the same address as the user specified port for upload --- commands/upload/upload.go | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 5f2755e6dad..75f8aae9238 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -556,7 +556,6 @@ func detectUploadPort( } // Pick the first port that is detected after the upload - desiredHwID := uploadPort.HardwareID timeout := time.After(5 * time.Second) if !waitForUploadPort { timeout = time.After(time.Second) @@ -582,20 +581,41 @@ func detectUploadPort( log.WithField("event", ev).Trace("Ignored non-add event") continue } + + portPriority := func(port *discovery.Port) int { + if port == nil { + return 0 + } + prio := 0 + if port.HardwareID == uploadPort.HardwareID { + prio += 1000 + } + if port.Protocol == uploadPort.Protocol { + prio += 100 + } + if port.Address == uploadPort.Address { + prio += 10 + } + return prio + } + evPortPriority := portPriority(ev.Port) + candidatePriority := portPriority(candidate) + if evPortPriority <= candidatePriority { + log.WithField("event", ev).Tracef("New upload port candidate is worse than the current one (prio=%d)", evPortPriority) + continue + } + log.WithField("event", ev).Tracef("Found new upload port candidate (prio=%d)", evPortPriority) candidate = ev.Port - log.WithField("event", ev).Trace("New upload port candidate") - // If the current candidate port does not have the desired HW-ID do - // not return it immediately. - if desiredHwID != "" && candidate.HardwareID != desiredHwID { - log.Trace("New candidate port did not match desired HW ID, keep watching...") + // If the current candidate have the desired HW-ID return it quickly. + if candidate.HardwareID == ev.Port.HardwareID { + timeout = time.After(time.Second) + log.Trace("New candidate port match the desired HW ID, timeout reduced to 1 second.") continue } - log.Trace("Found new upload port!") - return case <-timeout: - log.Trace("Timeout waiting for candidate port") + log.WithField("selected_port", candidate).Trace("Timeout waiting for candidate port") return } }