/
core.go
402 lines (379 loc) · 11.3 KB
/
core.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/*
The core component is the central node managing the other components.
The core component connects to the chronicle and receives the details of the new images via RPC.
The core also connects to the display component and sends the new visuals again via RPC.
The rpc address and port for all the components can be configured at startup via command line arguments.
The core also expose a RPC interface to sort and group the visuals and sent the updated list to the display.
This is called by the gesture component.
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"math"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"github.com/UniversityofTromso/mrclean"
)
var (
//IP:PORT of thr rpc server
rpcserver string
//Display component RPC server address
displayrpc string
//the display component rpc client
client *rpc.Client
//config file name
configfile string
//config is map of configuration options
config map[string]interface{}
//netconn is the transport protocol for the connection
netconn string
)
func init() {
flag.StringVar(&displayrpc,
"displayrpc", mrclean.DisplayAddr,
"IP:PORT of the Display RPC server")
flag.StringVar(&rpcserver,
"rpcserver", mrclean.CoreAddr, "IP:PORT of the rpc server, defaults to localhost:32123")
flag.StringVar(&configfile,
"configfile", "config.json", "Configuration file for Mr. Clean")
flag.StringVar(&netconn, "net", "tcp", "Specifies the connection protocol: tcp, udp, unix etc..")
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func main() {
flag.Parse()
var err error
config, err = ReadConfig(configfile)
if err != nil {
log.Fatal(err)
}
core := &Core{Visuals: make(map[string]*mrclean.Visual)}
go srunService(core)
client, err = jsonrpc.Dial(netconn, displayrpc)
if err != nil {
log.Fatal("dialing:", err)
}
var (
farg int
reply [2]float64
)
err = client.Call("Display.Size", farg, &reply)
if err != nil {
log.Fatal("Display error:", err)
}
core.DispW = reply[0]
core.DispH = reply[1]
log.Printf("core.DispW: %f, core.DispH: %f\n", core.DispW, core.DispH)
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT)
<-sigc
}
//All method have rpc signature
type Core struct {
Visuals map[string]*mrclean.Visual
DispW, DispH float64
Lock sync.Mutex
// max width and height of the visuals
mx, my float64
}
//AddVisual adds a visual received from the chronicle
func (c *Core) AddVisual(vis *mrclean.Visual, reply *int) error {
c.Lock.Lock()
defer c.Lock.Unlock()
//c.Visuals[vis.Name] = vis
*reply = 0
log.Printf("Received visual %+v\n", vis)
//adding visual to the display
rvis := mrclean.Visual{
//Origin: make([]float64, 2),
//Size: make([]float64, 2),
}
err := client.Call("Display.AddVisual", vis, &rvis)
if err != nil {
log.Println(err)
//maye just return nil? chronicle cannot do much a this point
*reply = -1
return err
}
//the rpc result has the missing data so we update the visual and put it in the map
vis.Origin = rvis.Origin
vis.Size = rvis.Size
vis.ID = rvis.ID
c.Visuals[vis.Name] = vis
log.Printf("Added visual %+v\n", vis)
log.Println("len(Visuals) ", len(c.Visuals))
c.mx = math.Max(c.mx, vis.Size[0])
c.my = math.Max(c.my, rvis.Size[1])
return nil
}
//Sort handle the gestures received from ths users to sort the visuals
func (c *Core) Sort(layersorder string, reply *int) error {
c.Lock.Lock()
defer c.Lock.Unlock()
err := c.updatemetadata(layersorder)
if err != nil {
return err
}
//need to copy the map in a slice to sort it
//maybe this should change
visuals := make([]*mrclean.Visual, 0, len(c.Visuals))
for _, v := range c.Visuals {
visuals = append(visuals, v)
}
log.Printf("sorting: len(visuals) = %v\n", len(visuals))
//SORT
By(metaf).Sort(visuals)
//loop to put the visuals on screen
// evenly spaced and sorted
dx := c.mx + 0.05 //5 cm
dy := c.my + 0.05 //5 cm
var (
row float64 = 1.0
lastpx = -c.DispW*0.5 + dx*0.5
lastpy = c.DispH*0.5 - dy*0.5*row
origins *mrclean.VisualOrigins = mrclean.NewVisualOrigins()
)
//log.Printf("dx: %f, dy: %f\n", dx, dy)
//log.Printf(" lastpx: %f lastpy: %f \n", lastpx, lastpy)
for i := range visuals {
visuals[i].Origin[0], visuals[i].Origin[1] = lastpx, lastpy
//keep track of the position locally
c.Visuals[visuals[i].Name].Origin = visuals[i].Origin
//log.Println("Origin: ", visuals[i].Origin)
//fmt.Println("before: ", v.rect, v.rect.Center())
//fmt.Println("after: ", v.rect, v.rect.Center())
origins.Vids = append(origins.Vids, visuals[i].ID)
origins.Origins = append(origins.Origins, visuals[i].Origin)
lastpx += dx
//log.Printf(" lastpx+dx: %f c.DispW*0.5: %f \n", lastpx+dx, c.DispW*0.5)
if lastpx+dx > c.DispW*0.5 {
lastpx = -c.DispW*0.5 + dx*0.5
row += 1
lastpy -= dy //c.DispH*0.5 - dy*row //c.DispH - row*dy
//log.Printf("New ROW: %f ", row)
}
//log.Printf(" lastpx: %f lastpy: %f \n", lastpx, lastpy)
}
//CALL
var repl int = 0
//log.Printf("calling Display.SetVisualsOrigin %v\n", origins)
err = client.Call("Display.SetVisualsOrigin", origins, &repl)
if err != nil {
*reply = -1
//log.Println("Display error setting Visuals orgin: ", err)
return err
}
if repl == 0 {
reply = &repl
} else {
log.Println("Something happened during Display.SetVisualsOrigin ", repl)
*reply = -1
}
//here we set the position of th evisuals locally
//for _, v := range visuals{
// c.Visuals[v.Name]
return nil
}
func (c *Core) Group(layer string, reply *int) error {
c.Lock.Lock()
defer c.Lock.Unlock()
err := c.updatemetadata(layer)
if err != nil {
return err
}
//map af array of visuals, the key is the metadata/layer
groups := make(map[string][]*mrclean.Visual, len(c.Visuals))
//map of the maximum size of each group
rowsize := make(map[string]float64, len(c.Visuals))
for _, v := range c.Visuals {
groups[v.Meta[0]] = append(groups[v.Meta[0]], v)
rowsize[v.Meta[0]] = math.Max(v.Size[0], rowsize[v.Meta[0]])
}
dx := c.mx + 0.05 //5 cm
dy := 0.05 //c.my + 0.05 //5 cm
var origins *mrclean.VisualOrigins = mrclean.NewVisualOrigins()
for k, row := range groups {
lastpx := -c.DispW*0.5 + dx*0.5
lastpy := c.DispH*0.5 - dy - rowsize[k]*0.5 + 0.05
//keep track of the height
dy += rowsize[k] + 0.05
//sort the row the first part of teh metadaa is the same
//for all the element s in the row, the user should define the
//order of the rest of the metadata variables
By(metaf).Sort(row)
//put row by row on screen here
for _, v := range row {
v.Origin[0], v.Origin[1] = lastpx, lastpy
//keep track of the position locally
c.Visuals[v.Name].Origin = v.Origin
origins.Vids = append(origins.Vids, v.ID)
origins.Origins = append(origins.Origins, v.Origin)
lastpx += dx
}
// back to the left
//lastpx = -c.DispW*0.5 + dx*0.5
// next row
//lastpy -= dy
}
//CALL
var repl int = 0
//log.Printf("calling Display.SetVisualsOrigin %v\n", origins)
err = client.Call("Display.SetVisualsOrigin", origins, &repl)
if err != nil {
*reply = -1
//log.Println("Display error setting Visuals orgin: ", err)
return err
}
if repl == 0 {
reply = &repl
} else {
log.Println("Something happened during Display.SetVisualsOrigin ", repl)
*reply = -1
}
return nil
}
func (c *Core) Pan(dir []float64, reply *int) error {
c.Lock.Lock()
defer c.Lock.Unlock()
if len(dir) != 2 {
*reply = -1
return fmt.Errorf("Need a two dimansion vector to pan")
}
origins := mrclean.NewVisualOrigins()
for _, v := range c.Visuals {
v.Origin[0] += dir[0]
v.Origin[1] += dir[1]
origins.Vids = append(origins.Vids, v.ID)
origins.Origins = append(origins.Origins, v.Origin)
}
var repl int = 0
//log.Printf("calling Display.SetVisualsOrigin %v\n", origins)
err := client.Call("Display.SetVisualsOrigin", origins, &repl)
if err != nil {
*reply = -1
//log.Println("Display error setting Visuals orgin: ", err)
return err
}
if repl == 0 {
reply = &repl
} else {
log.Println("Something happened during Display.SetVisualsOrigin ", repl)
*reply = -1
}
return nil
}
//update the metadata in the visuals to the recived order
func (c *Core) updatemetadata(layersorder string) error {
//TODO add check in case we are already up to date
//get the the metadata
layersconf := config["layers"].(string)
//split metdata in to layers
layers := strings.Split(layersconf, string(os.PathSeparator))
//split requested layer order
order := strings.Split(layersorder, string(os.PathSeparator))
//chek we are doing things correctly
if len(layers) != len(order) {
log.Printf("order layer and configured layer mismatch:\n%+v\n%+v\n", order, layers)
//*reply = -1
return fmt.Errorf("sorting layers number differs from configuraion")
}
// we get the map of the position of each layer in
// the sorting order
ordermap := make(map[string]int, len(layers))
for i, s := range order {
ordermap[s] = i
}
//now check we don't have wrong layers
for _, s := range layers {
_, ok := ordermap[s]
if !ok {
log.Printf("order layer and configured layer mismatch:\n%+v\n%+v\n", order, layers)
//*reply = -1
return fmt.Errorf("sorting layers elements differ from configuraion")
}
}
//maps layer position to order position
swap := make(map[int]int, len(layers))
for i, l := range layers {
swap[i] = ordermap[l]
}
// var (
// // maximum sixe of the images, basically the imeges will be in a grid
// // the grid size is the size of teh biggest image plus 5cm, see later
// dx, dy float64
// )
//get the visuals in an array with the correct meta-data
//visuals := make([]mrclean.Visual, 0, len(c.Visuals))
for _, v := range c.Visuals {
//strings for metadata
metastrings := make([]string, len(layers))
// layers in the name
//log.Printf("splitting for metadata: %s", v.Name)
sn := strings.Split(v.Name, string(os.PathSeparator))
if len(sn) != len(metastrings) {
log.Printf("WARNING: metadata and path of images are of different length, path is %d and shuld be %d\n",
len(sn), len(metastrings))
}
//name layer map
//nlm := make(map[string]int, len(layers))
//for i, s := range sn {
// nlm[s] = i
//}
log.Printf(" len(sn) = %d len(metastrings) = %d", len(sn), len(metastrings))
//assemble meta-data swapping position
//according to the swap map
for l, o := range swap {
log.Printf("swapping metastrings[%v] = sn[%v]\n", o, l)
metastrings[o] = sn[l]
}
//v.Meta = strings.Join(metastrings, "/")
v.Meta = metastrings
//visuals = append(visuals, *v)
//get the bigger visual to use as placeholder
//for the displaying
//dx = math.Max(dx, v.Size[0])
//dy = math.Max(dy, v.Size[1])
}
return nil
}
//runs the given struct as an RPC service using JSON as encoding
func srunService(core *Core) {
rpc.Register(core)
l, e := net.Listen("tcp", rpcserver)
if e != nil {
log.Fatal("listen error:", e)
}
for {
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
go rpc.DefaultServer.ServeCodec(jsonrpc.NewServerCodec(conn))
}
}
func ReadConfig(fname string) (map[string]interface{}, error) {
file, err := os.Open(fname)
if err != nil {
return nil, err
}
buff, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
var config map[string]interface{}
err = json.Unmarshal(buff, &config)
if err != nil {
return nil, err
}
log.Printf("Got configuration: %+v\n", config)
return config, nil
}