Skip to content

Commit 1ae3f4c

Browse files
committed
feat(grpc): can set buffer config on monitor open
Ref: arduino/pluggable-monitor-protocol-handler#33 Signed-off-by: dankeboy36 <[email protected]>
1 parent 461b668 commit 1ae3f4c

File tree

10 files changed

+467
-89
lines changed

10 files changed

+467
-89
lines changed

commands/service_monitor.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"errors"
2121
"fmt"
2222
"io"
23+
"strconv"
2324
"sync/atomic"
2425

2526
"github.com/arduino/arduino-cli/commands/cmderrors"
@@ -95,6 +96,38 @@ func (s *monitorPipeClient) Close() error {
9596
return nil
9697
}
9798

99+
// configurer is the minimal subset of the monitor used to apply settings.
100+
type configurer interface {
101+
Configure(parameterName, value string) error
102+
}
103+
104+
// applyBufferConfig translates the gRPC buffer config into pluggable-monitor CONFIGURE calls.
105+
func applyBufferConfig(c configurer, cfg *rpc.MonitorBufferConfig) {
106+
if cfg == nil {
107+
return
108+
}
109+
if v := cfg.GetHighWaterMarkBytes(); v > 0 {
110+
_ = c.Configure("_buffer.hwm", strconv.Itoa(int(v)))
111+
}
112+
// Interval (0 disables)
113+
_ = c.Configure("_buffer.interval_ms", strconv.Itoa(int(cfg.GetFlushIntervalMs())))
114+
// Line buffering
115+
_ = c.Configure("_buffer.line", strconv.FormatBool(cfg.GetLineBuffering()))
116+
// Queue capacity
117+
if v := cfg.GetFlushQueueCapacity(); v > 0 {
118+
_ = c.Configure("_buffer.queue", strconv.Itoa(int(v)))
119+
}
120+
// Overflow strategy (default to drop if unspecified)
121+
switch cfg.GetOverflowStrategy() {
122+
case rpc.BufferOverflowStrategy_BUFFER_OVERFLOW_STRATEGY_WAIT:
123+
_ = c.Configure("_buffer.overflow", "wait")
124+
default: // unspecified or drop
125+
_ = c.Configure("_buffer.overflow", "drop")
126+
}
127+
// Bounded wait for overflow (ms)
128+
_ = c.Configure("_buffer.overflow_wait_ms", strconv.Itoa(int(cfg.GetOverflowWaitMs())))
129+
}
130+
98131
// MonitorServerToReadWriteCloser creates a monitor server that proxies the data to a ReadWriteCloser.
99132
// The server is returned along with the ReadWriteCloser that can be used to send and receive data
100133
// to the server. The MonitorPortOpenRequest is used to configure the monitor.
@@ -148,6 +181,7 @@ func (s *arduinoCoreServerImpl) Monitor(stream rpc.ArduinoCoreService_MonitorSer
148181
for setting, value := range boardSettings.AsMap() {
149182
monitor.Configure(setting, value)
150183
}
184+
applyBufferConfig(monitor, openReq.GetBufferConfig())
151185
monitorIO, err := monitor.Open(openReq.GetPort().GetAddress(), openReq.GetPort().GetProtocol())
152186
if err != nil {
153187
monitor.Quit()
@@ -165,7 +199,7 @@ func (s *arduinoCoreServerImpl) Monitor(stream rpc.ArduinoCoreService_MonitorSer
165199

166200
ctx, cancel := context.WithCancel(stream.Context())
167201
gracefulCloseInitiated := &atomic.Bool{}
168-
gracefuleCloseCtx, gracefulCloseCancel := context.WithCancel(context.Background())
202+
gracefulCloseCtx, gracefulCloseCancel := context.WithCancel(context.Background())
169203

170204
// gRPC stream receiver (gRPC data -> monitor, config, close)
171205
go func() {
@@ -230,7 +264,7 @@ func (s *arduinoCoreServerImpl) Monitor(stream rpc.ArduinoCoreService_MonitorSer
230264
<-ctx.Done()
231265
if gracefulCloseInitiated.Load() {
232266
// Port closing has been initiated in the receiver
233-
<-gracefuleCloseCtx.Done()
267+
<-gracefulCloseCtx.Done()
234268
} else {
235269
monitorClose()
236270
}

commands/service_monitor_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2025 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package commands
17+
18+
import (
19+
"testing"
20+
21+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
22+
)
23+
24+
type fakeConfigurer struct{ calls [][2]string }
25+
26+
func (f *fakeConfigurer) Configure(k, v string) error {
27+
f.calls = append(f.calls, [2]string{k, v})
28+
return nil
29+
}
30+
31+
func haveCall(calls [][2]string, k, v string) bool {
32+
for _, kv := range calls {
33+
if kv[0] == k && kv[1] == v {
34+
return true
35+
}
36+
}
37+
return false
38+
}
39+
40+
// Test that we correctly read all buffer_config fields from the gRPC request
41+
// and emit the expected CONFIGURE _buffer.* key/value pairs.
42+
func Test_applyBufferConfig_AllFields(t *testing.T) {
43+
f := &fakeConfigurer{}
44+
cfg := &rpc.MonitorBufferConfig{
45+
HighWaterMarkBytes: 64,
46+
FlushIntervalMs: 16,
47+
LineBuffering: true,
48+
FlushQueueCapacity: 256,
49+
OverflowStrategy: rpc.BufferOverflowStrategy_BUFFER_OVERFLOW_STRATEGY_WAIT,
50+
OverflowWaitMs: 50,
51+
}
52+
applyBufferConfig(f, cfg)
53+
54+
want := map[string]string{
55+
"_buffer.hwm": "64",
56+
"_buffer.interval_ms": "16",
57+
"_buffer.line": "true",
58+
"_buffer.queue": "256",
59+
"_buffer.overflow": "wait",
60+
"_buffer.overflow_wait_ms": "50",
61+
}
62+
for k, v := range want {
63+
if !haveCall(f.calls, k, v) {
64+
t.Fatalf("missing or wrong CONFIGURE %s=%s; calls=%v", k, v, f.calls)
65+
}
66+
}
67+
}
68+
69+
// Test that zeros/defaults are handled as intended: we still emit interval/line/overflow,
70+
// default overflow to 'drop', and omit hwm/queue when zero.
71+
func Test_applyBufferConfig_DefaultsAndZeros(t *testing.T) {
72+
f := &fakeConfigurer{}
73+
cfg := &rpc.MonitorBufferConfig{ // zeros/unset
74+
HighWaterMarkBytes: 0,
75+
FlushIntervalMs: 0,
76+
LineBuffering: false,
77+
FlushQueueCapacity: 0,
78+
OverflowStrategy: rpc.BufferOverflowStrategy_BUFFER_OVERFLOW_STRATEGY_UNSPECIFIED,
79+
OverflowWaitMs: 0,
80+
}
81+
applyBufferConfig(f, cfg)
82+
83+
expects := map[string]string{
84+
"_buffer.interval_ms": "0",
85+
"_buffer.line": "false",
86+
"_buffer.overflow": "drop",
87+
"_buffer.overflow_wait_ms": "0",
88+
}
89+
for k, v := range expects {
90+
if !haveCall(f.calls, k, v) {
91+
t.Fatalf("expected CONFIGURE %s=%s not found; calls=%v", k, v, f.calls)
92+
}
93+
}
94+
for _, dis := range []string{"_buffer.hwm", "_buffer.queue"} {
95+
for _, kv := range f.calls {
96+
if kv[0] == dis {
97+
t.Fatalf("did not expect CONFIGURE for %s when value is zero", dis)
98+
}
99+
}
100+
}
101+
}

internal/locales/locale.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ func findMatchingLanguage(language string, supportedLocales []string) string {
5454
}
5555

5656
func findMatchingLocale(locale string, supportedLocales []string) string {
57-
for _, suportedLocale := range supportedLocales {
58-
if locale == suportedLocale {
59-
return suportedLocale
57+
for _, supportedLocale := range supportedLocales {
58+
if locale == supportedLocale {
59+
return supportedLocale
6060
}
6161
}
6262

rpc/cc/arduino/cli/commands/v1/commands.pb.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rpc/cc/arduino/cli/commands/v1/commands.proto

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ service ArduinoCoreService {
161161
// Start a debug session and communicate with the debugger tool.
162162
rpc Debug(stream DebugRequest) returns (stream DebugResponse) {}
163163

164-
// Determine if debugging is suported given a specific configuration.
164+
// Determine if debugging is supported given a specific configuration.
165165
rpc IsDebugSupported(IsDebugSupportedRequest) returns (IsDebugSupportedResponse) {}
166166

167167
// Query the debugger information given a specific configuration.
@@ -336,7 +336,7 @@ message NewSketchRequest {
336336
// Default Sketchbook directory "directories.User" is used if sketch_dir is
337337
// empty.
338338
string sketch_dir = 3;
339-
// Specificies if an existing .ino sketch should be overwritten.
339+
// Specifies if an existing .ino sketch should be overwritten.
340340
bool overwrite = 4;
341341

342342
reserved 1;
@@ -387,7 +387,7 @@ message SetSketchDefaultsRequest {
387387
}
388388

389389
message SetSketchDefaultsResponse {
390-
// The value of default_fqnn that has been written in project file
390+
// The value of default_fqbn that has been written in project file
391391
// (sketch.yaml).
392392
string default_fqbn = 1;
393393
// The value of default_port that has been written in project file

rpc/cc/arduino/cli/commands/v1/commands_grpc.pb.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rpc/cc/arduino/cli/commands/v1/lib.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rpc/cc/arduino/cli/commands/v1/lib.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ message LibraryResolveDependenciesRequest {
155155
// The version of the library to check dependencies of. If no version is
156156
// specified, dependencies of the newest version will be listed.
157157
string version = 3;
158-
// If true the computed solution will try to keep exising libraries
158+
// If true the computed solution will try to keep existing libraries
159159
// at their current version.
160160
bool do_not_update_installed_libraries = 4;
161161
}

0 commit comments

Comments
 (0)