Skip to content

Commit 0e4004f

Browse files
committed
Made this repo a library
1 parent f2d7d51 commit 0e4004f

File tree

6 files changed

+464
-397
lines changed

6 files changed

+464
-397
lines changed

Taskfile.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ version: "3"
22

33
tasks:
44
build:
5-
desc: Build the project
5+
desc: Build the dummy-discovery client example
66
cmds:
7-
- go build -v -ldflags '{{.LDFLAGS}}'
7+
- go build -o dist/dummy -v -ldflags '{{.LDFLAGS}}' ./dummy-discovery
88

99
vars:
1010
PROJECT_NAME: "dummy-discovery"
@@ -14,5 +14,5 @@ vars:
1414
TIMESTAMP:
1515
sh: echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
1616
LDFLAGS: >
17-
-X github.com/arduino/dummy-discovery/version.Tag={{.VERSION}}
18-
-X github.com/arduino/dummy-discovery/version.Timestamp={{.TIMESTAMP}}
17+
-X github.com/arduino/dummy-discovery/dummy-discovery/args.Tag={{.VERSION}}
18+
-X github.com/arduino/dummy-discovery/dummy-discovery/args.Timestamp={{.TIMESTAMP}}

discovery_server.go

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
//
2+
// This file is part of dummy-discovery.
3+
//
4+
// Copyright 2021 ARDUINO SA (http://www.arduino.cc/)
5+
//
6+
// This software is released under the GNU General Public License version 3,
7+
// which covers the main part of arduino-cli.
8+
// The terms of this license can be found at:
9+
// https://www.gnu.org/licenses/gpl-3.0.en.html
10+
//
11+
// You can be released from the requirements of the above licenses by purchasing
12+
// a commercial license. Buying such a license is mandatory if you want to modify or
13+
// otherwise use the software for commercial activities involving the Arduino
14+
// software without disclosing the source code of your own applications. To purchase
15+
// a commercial license, send an email to license@arduino.cc.
16+
//
17+
18+
package discovery
19+
20+
import (
21+
"bufio"
22+
"encoding/json"
23+
"fmt"
24+
"io"
25+
"regexp"
26+
"strconv"
27+
"strings"
28+
"sync"
29+
30+
"github.com/arduino/go-properties-orderedmap"
31+
)
32+
33+
type Port struct {
34+
Address string `json:"address"`
35+
AddressLabel string `json:"label,omitempty"`
36+
Protocol string `json:"protocol,omitempty"`
37+
ProtocolLabel string `json:"protocolLabel,omitempty"`
38+
Properties *properties.Map `json:"properties,omitempty"`
39+
}
40+
41+
type EventCallback func(event string, port *Port)
42+
43+
type Discovery interface {
44+
Hello(userAgent string, protocolVersion int) error
45+
Start() error
46+
Stop() error
47+
List() ([]*Port, error)
48+
StartSync(eventCB EventCallback) (chan<- bool, error)
49+
}
50+
51+
type DiscoveryServer struct {
52+
impl Discovery
53+
out io.Writer
54+
outMutex sync.Mutex
55+
userAgent string
56+
reqProtocolVersion int
57+
initialized bool
58+
started bool
59+
syncStarted bool
60+
syncCloseChan chan<- bool
61+
}
62+
63+
func NewDiscoveryServer(impl Discovery) *DiscoveryServer {
64+
return &DiscoveryServer{
65+
impl: impl,
66+
}
67+
}
68+
69+
func (d *DiscoveryServer) Run(in io.Reader, out io.Writer) error {
70+
d.out = out
71+
reader := bufio.NewReader(in)
72+
for {
73+
fullCmd, err := reader.ReadString('\n')
74+
if err != nil {
75+
d.output(&genericMessageJSON{
76+
EventType: "command_error",
77+
Error: true,
78+
Message: err.Error(),
79+
})
80+
return err
81+
}
82+
split := strings.Split(fullCmd, " ")
83+
cmd := strings.ToUpper(strings.TrimSpace(split[0]))
84+
85+
if !d.initialized && cmd != "HELLO" {
86+
d.output(&genericMessageJSON{
87+
EventType: "command_error",
88+
Error: true,
89+
Message: fmt.Sprintf("First command must be HELLO, but got '%s'", cmd),
90+
})
91+
continue
92+
}
93+
94+
switch cmd {
95+
case "HELLO":
96+
if d.initialized {
97+
d.output(&genericMessageJSON{
98+
EventType: "hello",
99+
Error: true,
100+
Message: "HELLO already called",
101+
})
102+
continue
103+
}
104+
re := regexp.MustCompile(`(\d+) "([^"]+)"`)
105+
matches := re.FindStringSubmatch(fullCmd[6:])
106+
if len(matches) != 3 {
107+
d.output(&genericMessageJSON{
108+
EventType: "hello",
109+
Error: true,
110+
Message: "Invalid HELLO command",
111+
})
112+
continue
113+
}
114+
d.userAgent = matches[2]
115+
if v, err := strconv.ParseInt(matches[1], 10, 64); err != nil {
116+
d.output(&genericMessageJSON{
117+
EventType: "hello",
118+
Error: true,
119+
Message: "Invalid protocol version: " + matches[2],
120+
})
121+
continue
122+
} else {
123+
d.reqProtocolVersion = int(v)
124+
}
125+
if err := d.impl.Hello(d.userAgent, 1); err != nil {
126+
d.output(&genericMessageJSON{
127+
EventType: "hello",
128+
Error: true,
129+
Message: err.Error(),
130+
})
131+
continue
132+
}
133+
d.output(&genericMessageJSON{
134+
EventType: "hello",
135+
ProtocolVersion: 1, // Protocol version 1 is the only supported for now...
136+
Message: "OK",
137+
})
138+
d.initialized = true
139+
140+
case "START":
141+
if d.started {
142+
d.output(&genericMessageJSON{
143+
EventType: "start",
144+
Error: true,
145+
Message: "Discovery already STARTed",
146+
})
147+
continue
148+
}
149+
if d.syncStarted {
150+
d.output(&genericMessageJSON{
151+
EventType: "start",
152+
Error: true,
153+
Message: "Discovery already START_SYNCed, cannot START",
154+
})
155+
continue
156+
}
157+
if err := d.impl.Start(); err != nil {
158+
d.output(&genericMessageJSON{
159+
EventType: "start",
160+
Error: true,
161+
Message: "Cannot START: " + err.Error(),
162+
})
163+
continue
164+
}
165+
d.started = true
166+
d.output(&genericMessageJSON{
167+
EventType: "start",
168+
Message: "OK",
169+
})
170+
171+
case "LIST":
172+
if !d.started {
173+
d.output(&genericMessageJSON{
174+
EventType: "list",
175+
Error: true,
176+
Message: "Discovery not STARTed",
177+
})
178+
continue
179+
}
180+
if d.syncStarted {
181+
d.output(&genericMessageJSON{
182+
EventType: "list",
183+
Error: true,
184+
Message: "discovery already START_SYNCed, LIST not allowed",
185+
})
186+
continue
187+
}
188+
if ports, err := d.impl.List(); err != nil {
189+
d.output(&genericMessageJSON{
190+
EventType: "list",
191+
Error: true,
192+
Message: "LIST error: " + err.Error(),
193+
})
194+
continue
195+
} else {
196+
type listOutputJSON struct {
197+
EventType string `json:"eventType"`
198+
Ports []*Port `json:"ports"`
199+
}
200+
d.output(&listOutputJSON{
201+
EventType: "list",
202+
Ports: ports,
203+
})
204+
}
205+
206+
case "START_SYNC":
207+
if d.syncStarted {
208+
d.output(&genericMessageJSON{
209+
EventType: "start_sync",
210+
Error: true,
211+
Message: "Discovery already START_SYNCed",
212+
})
213+
continue
214+
}
215+
if d.started {
216+
d.output(&genericMessageJSON{
217+
EventType: "start_sync",
218+
Error: true,
219+
Message: "Discovery already STARTed, cannot START_SYNC",
220+
})
221+
continue
222+
}
223+
if c, err := d.impl.StartSync(d.syncEvent); err != nil {
224+
d.output(&genericMessageJSON{
225+
EventType: "start_sync",
226+
Error: true,
227+
Message: "Cannot START_SYNC: " + err.Error(),
228+
})
229+
continue
230+
} else {
231+
d.syncCloseChan = c
232+
d.syncStarted = true
233+
d.output(&genericMessageJSON{
234+
EventType: "start_sync",
235+
Message: "OK",
236+
})
237+
}
238+
239+
case "STOP":
240+
if !d.syncStarted && !d.started {
241+
d.output(&genericMessageJSON{
242+
EventType: "stop",
243+
Error: true,
244+
Message: "Discovery already STOPped",
245+
})
246+
continue
247+
}
248+
if err := d.impl.Stop(); err != nil {
249+
d.output(&genericMessageJSON{
250+
EventType: "stop",
251+
Error: true,
252+
Message: "Cannot STOP: " + err.Error(),
253+
})
254+
continue
255+
}
256+
if d.started {
257+
d.started = false
258+
}
259+
if d.syncStarted {
260+
d.syncCloseChan <- true
261+
close(d.syncCloseChan)
262+
d.syncStarted = false
263+
}
264+
d.output(&genericMessageJSON{
265+
EventType: "stop",
266+
Message: "OK",
267+
})
268+
269+
case "QUIT":
270+
d.output(&genericMessageJSON{
271+
EventType: "quit",
272+
Message: "OK",
273+
})
274+
return nil
275+
276+
default:
277+
d.output(&genericMessageJSON{
278+
EventType: "command_error",
279+
Error: true,
280+
Message: fmt.Sprintf("Command %s not supported", cmd),
281+
})
282+
}
283+
}
284+
}
285+
286+
func (d *DiscoveryServer) syncEvent(event string, port *Port) {
287+
type syncOutputJSON struct {
288+
EventType string `json:"eventType"`
289+
Port *Port `json:"port"`
290+
}
291+
d.output(&syncOutputJSON{
292+
EventType: event,
293+
Port: port,
294+
})
295+
}
296+
297+
type genericMessageJSON struct {
298+
EventType string `json:"eventType"`
299+
Message string `json:"message"`
300+
Error bool `json:"error,omitempty"`
301+
ProtocolVersion int `json:"protocolVersion,omitempty"`
302+
}
303+
304+
func (d *DiscoveryServer) output(msg interface{}) {
305+
data, err := json.MarshalIndent(msg, "", " ")
306+
if err != nil {
307+
d.output(&genericMessageJSON{
308+
EventType: "command_error",
309+
Error: true,
310+
Message: err.Error(),
311+
})
312+
} else {
313+
d.outMutex.Lock()
314+
fmt.Println(string(data))
315+
d.outMutex.Unlock()
316+
}
317+
}

args.go renamed to dummy-discovery/args/args.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,27 @@
1515
// a commercial license, send an email to license@arduino.cc.
1616
//
1717

18-
package main
18+
package args
1919

2020
import (
2121
"fmt"
2222
"os"
2323
)
2424

25-
var args struct {
26-
showVersion bool
27-
}
25+
// Tag is the current git tag
26+
var Tag = "snapshot"
27+
28+
// Timestamp is the current timestamp
29+
var Timestamp = "unknown"
2830

29-
func parseArgs() {
31+
func ParseArgs() {
3032
for _, arg := range os.Args[1:] {
3133
if arg == "" {
3234
continue
3335
}
3436
if arg == "-v" || arg == "--version" {
35-
args.showVersion = true
36-
continue
37+
fmt.Printf("serial-discovery %s (build timestamp: %s)\n", Tag, Timestamp)
38+
os.Exit(0)
3739
}
3840
fmt.Fprintf(os.Stderr, "invalid argument: %s\n", arg)
3941
os.Exit(1)

0 commit comments

Comments
 (0)