diff --git a/dfssd/gui/application.qrc b/dfssd/gui/application.qrc index 65c5cfcd87581ab614c456eab60d777c2a797f3e..d51b0b6b2e107929e33a5c00953e4d60e0223745 100644 --- a/dfssd/gui/application.qrc +++ b/dfssd/gui/application.qrc @@ -1,5 +1,13 @@ widget.ui + images/server_key.png + images/server_connect.png + images/node_magnifier.png + images/control_play.png + images/control_play_blue.png + images/control_pause.png + images/control_pause_blue.png + images/control_rewind_blue.png diff --git a/dfssd/gui/colors.go b/dfssd/gui/colors.go new file mode 100644 index 0000000000000000000000000000000000000000..384c1e9ee4cc3fb6bd91a048d3ecc5a87caac5da --- /dev/null +++ b/dfssd/gui/colors.go @@ -0,0 +1,18 @@ +package gui + +// This file stores useful colors + +import "github.com/visualfc/goqt/ui" + +var colors = map[string]uint32{ + "red": 0x00ff0000, + "green": 0x0000aa00, + "blue": 0x000000ff, + "black": 0x00000000, +} + +var pen_black = ui.NewPenWithColor(ui.NewColorWithGlobalcolor(ui.Qt_black)) +var pen_gray = ui.NewPenWithColor(ui.NewColorWithGlobalcolor(ui.Qt_gray)) + +var brush_none = ui.NewBrushWithGlobalcolorBrushstyle(ui.Qt_transparent, ui.Qt_SolidPattern) +var brush_black = ui.NewBrushWithGlobalcolorBrushstyle(ui.Qt_black, ui.Qt_SolidPattern) diff --git a/dfssd/gui/events.go b/dfssd/gui/events.go new file mode 100644 index 0000000000000000000000000000000000000000..e03d81d7888c705b7b09afac98acb8dddef922c2 --- /dev/null +++ b/dfssd/gui/events.go @@ -0,0 +1,153 @@ +package gui + +// This file handles event timers and imports. + +import ( + "fmt" + "math" + "time" + + "dfss/dfssd/api" + "github.com/visualfc/goqt/ui" +) + +// TEMPORARY +const quantum = 100 // discretization argument for events (ns) +const speed = 1000 // duration of a quantum (ms) + +// AddEvent interprets an incoming event into a graphic one. +// Expected format: +// +// Timestamp: unix nano timestamp +// Identifier: either "platform", "ttp" or "" +// Log: one of the following +// "sent promise to " +// "sent signature to " +// +// Other messages are currently ignored. +func (w *Window) AddEvent(e *api.Log) { + event := Event{ + Sender: w.scene.identifierToIndex(e.Identifier), + Date: time.Unix(0, e.Timestamp), + } + + w.Log(fmt.Sprint(e.Identifier, " ", e.Log)) + + var receiver string + if n, _ := fmt.Sscanf(e.Log, "sent promise to %s", &receiver); n > 0 { + event.Type = PROMISE + event.Receiver = w.scene.identifierToIndex(receiver) + } else if n, _ := fmt.Sscanf(e.Log, "sent signature to %s", &receiver); n > 0 { + event.Type = SIGNATURE + event.Receiver = w.scene.identifierToIndex(receiver) + } + + if receiver != "" { + w.scene.Events = append(w.scene.Events, event) + } +} + +// DrawEvent triggers the appropriate draw action for a spectific event. +func (w *Window) DrawEvent(e *Event) { + xa, ya := w.GetClientPosition(e.Sender) + xb, yb := w.GetClientPosition(e.Receiver) + + var color string + switch e.Type { + case PROMISE: + color = "blue" + case SIGNATURE: + color = "green" + default: + color = "black" + } + + w.DrawArrow(xa, ya, xb, yb, colors[color]) +} + +// PrintQuantumInformation triggers the update of the "x / y" quantum information. +func (w *Window) PrintQuantumInformation() { + if len(w.scene.Events) == 0 { + w.progress.SetText("No event") + return + } + + beginning := w.scene.Events[0].Date.UnixNano() + totalDuration := w.scene.Events[len(w.scene.Events)-1].Date.UnixNano() - beginning + nbQuantum := math.Max(1, math.Ceil(float64(totalDuration)/quantum)) + durationFromBeginning := w.scene.currentTime.UnixNano() - beginning + currentQuantum := math.Ceil(float64(durationFromBeginning)/quantum) + 1 + + if w.scene.currentEvent == 0 { + currentQuantum = 0 + } + w.progress.SetText(fmt.Sprint(currentQuantum, " / ", nbQuantum)) +} + +// initTimer is called during window initialization. It initializes the timeout signal called for each refresh. +func (w *Window) initTimer() { + w.timer = ui.NewTimerWithParent(w) + + lastNbOfClients := len(w.scene.Clients) + + w.timer.OnTimeout(func() { + nbEvents := len(w.scene.Events) + if w.scene.currentEvent >= nbEvents { + w.replayButton.Click() + return + } + + // Remove arrows from last tick + w.RemoveArrows() + + // Check that we have a least one event to read + if nbEvents == 0 { + return + } + + // Check if need to redraw everything + if lastNbOfClients != len(w.scene.Clients) { + w.initScene() + } + + // Init first time + if w.scene.currentEvent == 0 { + w.scene.currentTime = w.scene.Events[0].Date + } + + endOfQuantum := w.scene.currentTime.Add(quantum * time.Nanosecond) + + for i := w.scene.currentEvent; i < nbEvents; i++ { + e := w.scene.Events[i] + + if e.Date.After(endOfQuantum) || e.Date.Equal(endOfQuantum) { + break + } + + w.DrawEvent(&e) + w.scene.currentEvent++ + } + + w.PrintQuantumInformation() + w.scene.currentTime = endOfQuantum + }) +} + +// identifierToIndex is used to retrieve a client index from its name, inserting a new client if needed. +func (s *Scene) identifierToIndex(identifier string) int { + if identifier == "platform" { + return -1 + } + if identifier == "ttp" { + return -2 + } + + for i, c := range s.Clients { + if c.Name == identifier { + return i + } + } + + s.Clients = append(s.Clients, Client{Name: identifier}) + return len(s.Clients) - 1 +} diff --git a/dfssd/gui/graphics.go b/dfssd/gui/graphics.go new file mode 100644 index 0000000000000000000000000000000000000000..d83d8bd91bf9ce77a0ccbc1179ce118ec95ff3d2 --- /dev/null +++ b/dfssd/gui/graphics.go @@ -0,0 +1,115 @@ +package gui + +// This file handles complex graphic primitives for the demonstrator. + +import ( + "math" + + "github.com/visualfc/goqt/ui" +) + +// These two constants are used to configure arrows +const ARROW_T = math.Pi / 6 // angle +const ARROW_L = 15 // side length + +// DrawClients draws the different clients in a circle. +func (w *Window) DrawClients() { + scene := w.graphics.Scene() + for i, c := range w.scene.Clients { + x, y := w.GetClientPosition(i) + + // Add ellipse + scene.AddEllipseFWithXYWidthHeightPenBrush(x-10, y-10, 20, 20, pen_black, brush_black) + + // Add text + t := scene.AddSimpleText(c.Name) + r := t.BoundingRect() + t.SetX(x - r.Width()/2) + t.SetY(y + 10) + } +} + +// GetClientPosition translates a client index into its cartesian coordinates. +func (w *Window) GetClientPosition(i int) (x, y float64) { + if i < 0 { + return w.GetServerPosition(i == -1) + } + + nbClients := float64(len(w.scene.Clients)) + angle := 2 * math.Pi * float64(i) / nbClients + return math.Cos(angle) * (w.circleSize / 2), math.Sin(angle) * (w.circleSize / 2) +} + +// GetServerPosition translates a server into its cartesian coordinates. +func (w *Window) GetServerPosition(platform bool) (x, y float64) { + x = w.circleSize/2 + 150 + y = 0 + if !platform { + x *= -1 + } + return +} + +// DrawServers draws the DFSS main servers (ttp and platform) +func (w *Window) DrawServers() { + scene := w.graphics.Scene() + + ttp := scene.AddPixmap(w.pixmaps["ttp"]) + x, y := w.GetServerPosition(false) + ttp.SetPosFWithXY(x-32, y-16) // we are shifting here a bit for better arrow display + ttp.SetToolTip("TTP") + + platform := scene.AddPixmap(w.pixmaps["platform"]) + x, y = w.GetServerPosition(true) + platform.SetPosFWithXY(x, y-16) + platform.SetToolTip("Platform") +} + +// DrawArrow is the graphic primitive for drawing an arrow between A and B points +func (w *Window) DrawArrow(xa, ya, xb, yb float64, rgb uint32) { + scene := w.graphics.Scene() + + path := ui.NewPainterPath() + path.MoveToFWithXY(xa, ya) + path.LineToFWithXY(xb, yb) + + v := ui.NewVector2DWithXposYpos(xa-xb, ya-yb) + l := v.Length() + + // from http://math.stackexchange.com/a/1314050 + xc := xb + ARROW_L/l*(v.X()*math.Cos(ARROW_T)+v.Y()*math.Sin(ARROW_T)) + yc := yb + ARROW_L/l*(v.Y()*math.Cos(ARROW_T)-v.X()*math.Sin(ARROW_T)) + xd := xb + ARROW_L/l*(v.X()*math.Cos(ARROW_T)-v.Y()*math.Sin(ARROW_T)) + yd := yb + ARROW_L/l*(v.Y()*math.Cos(ARROW_T)+v.X()*math.Sin(ARROW_T)) + + path.LineToFWithXY(xc, yc) + path.LineToFWithXY(xd, yd) + path.LineToFWithXY(xb, yb) + path.SetFillRule(ui.Qt_WindingFill) + + color := ui.NewColorWithRgb(rgb) + color.SetAlpha(200) + + pen := ui.NewPenWithColor(color) + pen.SetWidth(3) + pen.SetJoinStyle(ui.Qt_RoundJoin) + + brush := ui.NewBrush() + brush.SetColor(color) + brush.SetStyle(ui.Qt_SolidPattern) + + arrow := scene.AddPathWithPathPenBrush(path, pen, brush) + w.currentArrows = append(w.currentArrows, arrow) +} + +// RemoveArrows remove every arrow present in the graphic area, and delete them for better memory management. +func (w *Window) RemoveArrows() { + scene := w.graphics.Scene() + + for _, arrow := range w.currentArrows { + scene.RemoveItem(&arrow.QGraphicsItem) + defer arrow.Delete() + } + + w.currentArrows = nil +} diff --git a/dfssd/gui/images/control_pause.png b/dfssd/gui/images/control_pause.png new file mode 100644 index 0000000000000000000000000000000000000000..186f361f08b9d67858da54d9decd084f25072f2e Binary files /dev/null and b/dfssd/gui/images/control_pause.png differ diff --git a/dfssd/gui/images/control_pause_blue.png b/dfssd/gui/images/control_pause_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..3ab8b69db2782806559e5ba9cb057bdf899ea869 Binary files /dev/null and b/dfssd/gui/images/control_pause_blue.png differ diff --git a/dfssd/gui/images/control_play.png b/dfssd/gui/images/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..3b64790b971704537cdf56306d98c15f0f7d1c16 Binary files /dev/null and b/dfssd/gui/images/control_play.png differ diff --git a/dfssd/gui/images/control_play_blue.png b/dfssd/gui/images/control_play_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..a1f7345f3713a730b613faf82026caaa0adb375c Binary files /dev/null and b/dfssd/gui/images/control_play_blue.png differ diff --git a/dfssd/gui/images/control_rewind_blue.png b/dfssd/gui/images/control_rewind_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..de9fc07b247950c8cb283a10fae0316d2954f35b Binary files /dev/null and b/dfssd/gui/images/control_rewind_blue.png differ diff --git a/dfssd/gui/images/node_magnifier.png b/dfssd/gui/images/node_magnifier.png new file mode 100644 index 0000000000000000000000000000000000000000..b19d01b580acfa7344e6e94c937f0c0b96f20186 Binary files /dev/null and b/dfssd/gui/images/node_magnifier.png differ diff --git a/dfssd/gui/images/server_connect.png b/dfssd/gui/images/server_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..faca1bc1c184be44a70bf00431066fb0400375f7 Binary files /dev/null and b/dfssd/gui/images/server_connect.png differ diff --git a/dfssd/gui/images/server_key.png b/dfssd/gui/images/server_key.png new file mode 100644 index 0000000000000000000000000000000000000000..a4ae3f38830844ac864a31b2eb6eb53cd5752a12 Binary files /dev/null and b/dfssd/gui/images/server_key.png differ diff --git a/dfssd/gui/save.go b/dfssd/gui/save.go new file mode 100644 index 0000000000000000000000000000000000000000..d230c4971f797388eb5f4805a982eccfef7198a2 --- /dev/null +++ b/dfssd/gui/save.go @@ -0,0 +1,43 @@ +package gui + +// This file handles open/save feature. + +import ( + "encoding/json" + "io/ioutil" + "strconv" +) + +func (w *Window) Save(filename string) { + data, err := json.Marshal(w.scene) + if err != nil { + w.StatusBar().ShowMessage(err.Error()) + return + } + + err = ioutil.WriteFile(filename, data, 0600) + if err != nil { + w.StatusBar().ShowMessage(err.Error()) + return + } + w.StatusBar().ShowMessage("Saved file as " + filename) +} + +func (w *Window) Open(filename string) { + data, err := ioutil.ReadFile(filename) + if err != nil { + w.StatusBar().ShowMessage(err.Error()) + return + } + + newScene := &Scene{} + err = json.Unmarshal(data, newScene) + if err != nil { + w.StatusBar().ShowMessage(err.Error()) + return + } + + w.scene = newScene + w.StatusBar().ShowMessage("Imported file from " + filename + " (" + strconv.Itoa(len(w.scene.Events)) + " events)") + w.initScene() +} diff --git a/dfssd/gui/structures.go b/dfssd/gui/structures.go new file mode 100644 index 0000000000000000000000000000000000000000..209fd856356eda31cc8c219b3e772fbce1e2acdd --- /dev/null +++ b/dfssd/gui/structures.go @@ -0,0 +1,60 @@ +package gui + +// This file stores strucutures used in GUI for fast documentation. + +import ( + "time" + + "github.com/visualfc/goqt/ui" +) + +// Window contains all information used to make the demonstrator works. +// It extends QMainWindow and cache several graphic informations. +// Do not attempt to instantiante it directly, use `NewWindow` function instead. +type Window struct { + *ui.QMainWindow + + logField *ui.QTextEdit + graphics *ui.QGraphicsView + progress *ui.QLabel + playButton *ui.QPushButton + stopButton *ui.QPushButton + replayButton *ui.QPushButton + scene *Scene + circleSize float64 + pixmaps map[string]*ui.QPixmap + + currentArrows []*ui.QGraphicsPathItem + timer *ui.QTimer +} + +// Client represents a DFSSC instance +type Client struct { + Name string +} + +// EventType is used as an enum for event types, to differenciate promises, signatures... +type EventType int + +const ( + PROMISE EventType = iota + SIGNATURE + OTHER +) + +// Event represents a single signature event +type Event struct { + Type EventType + Sender int + Receiver int + Date time.Time +} + +// Scene holds the global scene for registered clients and signature events +type Scene struct { + Clients []Client + Events []Event + + currentTime time.Time + currentEvent int +} diff --git a/dfssd/gui/widget.ui b/dfssd/gui/widget.ui index 207beb568fed08a8a802062dd6ca8d661e5b5d13..7e1c26393ee66374ccfed1b814926ecc38f85ba6 100644 --- a/dfssd/gui/widget.ui +++ b/dfssd/gui/widget.ui @@ -1,7 +1,7 @@ - CalculatorForm - + Demonstrator + true @@ -39,6 +39,15 @@ 300 + + + + 255 + 255 + 255 + + + @@ -57,10 +66,32 @@ 0 - - Play + + + 40 + 40 + + + + Qt::NoFocus + + + true + + + + + + + + 40 + 40 + - + + Qt::NoFocus + + true @@ -73,8 +104,17 @@ 0 - - Replay + + + 40 + 40 + + + + Qt::NoFocus + + + true @@ -99,6 +139,9 @@ 0 + + Qt::TabFocus + us @@ -132,11 +175,14 @@ - + 0 0 + + Qt::TabFocus + Qt::Horizontal @@ -148,6 +194,22 @@ + + + + + 80 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + diff --git a/dfssd/gui/window.go b/dfssd/gui/window.go index a4aa9aef09f945dad9f18e5102442c1b07e91574..8ea71a917f63484e277b2d6739c14d4b7a33037b 100644 --- a/dfssd/gui/window.go +++ b/dfssd/gui/window.go @@ -1,16 +1,18 @@ +// Package gui is the graphic part of the dfssd program. package gui +// This file is the entry point of the gui package. +// It handles window instantiation and basic operations on it. + import ( + "math" + "time" + "dfss" "github.com/visualfc/goqt/ui" ) -type Window struct { - *ui.QMainWindow - - logField *ui.QTextEdit -} - +// NewWindow creates and initialiaze a new dfssd main window. func NewWindow() *Window { file := ui.NewFileWithName(":/widget.ui") loader := ui.NewUiLoader() @@ -23,32 +25,138 @@ func NewWindow() *Window { w := &Window{ QMainWindow: window, + scene: &Scene{}, } + w.InstallEventFilter(w) // Load dynamic elements from driver w.logField = ui.NewTextEditFromDriver(widget.FindChild("logField")) + w.graphics = ui.NewGraphicsViewFromDriver(widget.FindChild("graphicsView")) + w.progress = ui.NewLabelFromDriver(widget.FindChild("progressLabel")) + + w.playButton = ui.NewPushButtonFromDriver(widget.FindChild("playButton")) + w.stopButton = ui.NewPushButtonFromDriver(widget.FindChild("stopButton")) + w.replayButton = ui.NewPushButtonFromDriver(widget.FindChild("replayButton")) + + // Load pixmaps + w.pixmaps = map[string]*ui.QPixmap{ + "ttp": ui.NewPixmapWithFilenameFormatFlags(":/images/server_key.png", "", ui.Qt_AutoColor), + "platform": ui.NewPixmapWithFilenameFormatFlags(":/images/server_connect.png", "", ui.Qt_AutoColor), + } + + // Load icons + w.addIcons() // Add actions w.addActions() + w.initScene() + w.initTimer() w.StatusBar().ShowMessage("Ready") + w.PrintQuantumInformation() return w } +// OnResizeEvent is called by Qt each time an user tries to resize the window. +// We have to redraw the whole scene to adapt. +func (w *Window) OnResizeEvent(ev *ui.QResizeEvent) bool { + w.initScene() + return true +} + +// Log is used to print a new line in the log area of the window. +// It should be thread-safe. func (w *Window) Log(str string) { + str = time.Now().Format("[15:04:05.000] ") + str w.logField.Append(str) w.logField.EnsureCursorVisible() } +// addIcons adds icons to control buttons, since we cannot add them directly in QtCreator. +func (w *Window) addIcons() { + w.SetWindowIcon(ui.NewIconWithFilename(":/images/node_magnifier.png")) + + var i *ui.QIcon + i = ui.NewIconWithFilename(":/images/control_play_blue.png") + i.AddFileWithFilenameSizeModeState(":/images/control_play.png", ui.NewSizeWithWidthHeight(32, 32), ui.QIcon_Disabled, ui.QIcon_Off) + w.playButton.SetIcon(i) + + i = ui.NewIconWithFilename(":/images/control_pause_blue.png") + i.AddFileWithFilenameSizeModeState(":/images/control_pause.png", ui.NewSizeWithWidthHeight(32, 32), ui.QIcon_Disabled, ui.QIcon_Off) + w.stopButton.SetIcon(i) + + i = ui.NewIconWithFilename(":/images/control_rewind_blue.png") + w.replayButton.SetIcon(i) +} + +// addActions adds action listenners to interactive parts of the window. func (w *Window) addActions() { + // MENU BAR openAct := ui.NewActionWithTextParent("&Open", w) openAct.SetShortcuts(ui.QKeySequence_Open) openAct.SetStatusTip("Open a demonstration file") + openAct.OnTriggered(func() { + filename := ui.QFileDialogGetOpenFileName() + if filename != "" { + w.Open(filename) + } + }) saveAct := ui.NewActionWithTextParent("&Save", w) saveAct.SetShortcuts(ui.QKeySequence_Save) saveAct.SetStatusTip("Save a demonstration file") + saveAct.OnTriggered(func() { + filename := ui.QFileDialogGetSaveFileName() + if filename != "" { + w.Save(filename) + } + }) w.MenuBar().AddAction(openAct) w.MenuBar().AddAction(saveAct) + + // SIMULATION CONTROL + w.playButton.OnClicked(func() { + w.playButton.SetDisabled(true) + w.stopButton.SetDisabled(false) + w.timer.StartWithMsec(speed) + }) + + w.stopButton.OnClicked(func() { + w.playButton.SetDisabled(false) + w.stopButton.SetDisabled(true) + w.timer.Stop() + }) + w.stopButton.SetDisabled(true) + + w.replayButton.OnClicked(func() { + w.RemoveArrows() + w.scene.currentEvent = 0 + w.PrintQuantumInformation() + }) +} + +// initScene creates the Qt graphic scene associated to our custom scene. +// It draws the base circle, clients and servers, and do some memory management for us. +func (w *Window) initScene() { + // Save old scene + oldScene := w.graphics.Scene() + + scene := ui.NewGraphicsScene() + w.graphics.SetScene(scene) + + // Draw base circle + w.circleSize = math.Min(float64(w.graphics.Width()), float64(w.graphics.Height())) - 50 + r := w.circleSize / 2 + scene.AddEllipseFWithXYWidthHeightPenBrush(-r, -r, w.circleSize, w.circleSize, pen_gray, brush_none) + + // Draw clients + w.DrawClients() + w.DrawServers() + + // Purge + if oldScene != nil { + w.RemoveArrows() + defer oldScene.Delete() + } } diff --git a/dfssd/main.go b/dfssd/main.go index 6be4fd8864356e8922d66042b64f76a018a189d6..fc2ede4eddb35074f830002ac2ab6ea9d78a39c9 100644 --- a/dfssd/main.go +++ b/dfssd/main.go @@ -8,8 +8,9 @@ import ( "strconv" "dfss" - "dfss/dfssd/server" + "dfss/dfssd/api" "dfss/dfssd/gui" + "dfss/dfssd/server" "github.com/visualfc/goqt/ui" ) @@ -51,10 +52,10 @@ func main() { case "version": fmt.Println("v"+dfss.Version, runtime.GOOS, runtime.GOARCH) case "nogui": - lfn := func(str string) { - fmt.Println(str) + fn := func(v *api.Log) { + fmt.Printf("[%d] %s: %s\n", v.Timestamp, v.Identifier, v.Log) } - err := server.Listen("0.0.0.0:" + strconv.Itoa(port), lfn) + err := server.Listen("0.0.0.0:"+strconv.Itoa(port), fn) if err != nil { os.Exit(1) } @@ -62,7 +63,7 @@ func main() { ui.Run(func() { window := gui.NewWindow() go func() { - err := server.Listen("0.0.0.0:" + strconv.Itoa(port), window.Log) + err := server.Listen("0.0.0.0:"+strconv.Itoa(port), window.AddEvent) if err != nil { window.Log("!! " + err.Error()) } diff --git a/dfssd/server/server.go b/dfssd/server/server.go index cf0a4841de5c62bb2ae692fc1ea2996986eaf6c1..02f2bdcef0970b566d00bfa61e65ce37378eece7 100644 --- a/dfssd/server/server.go +++ b/dfssd/server/server.go @@ -20,17 +20,16 @@ func (s *Server) SendLog(ctx context.Context, in *api.Log) (*api.Ack, error) { } // Listen with gRPG service -func Listen(addrPort string, lfn func(string)) error { +func Listen(addrPort string, fn func(*api.Log)) error { // open tcp socket lis, err := net.Listen("tcp", addrPort) if err != nil { grpclog.Fatalf("Failed to open tcp socket: %v", err) return err } - lfn("Server listening on " + addrPort) // log display manager - go displayHandler(lfn) + go displayHandler(fn) // bootstrap gRPC service ! grpcServer := grpc.NewServer() diff --git a/dfssd/server/storage.go b/dfssd/server/storage.go index b2e4e0c5da45ee901a89eb44ebfd35124ce93f69..d11211fbb3b3499f5211b777e82c009f2595230b 100644 --- a/dfssd/server/storage.go +++ b/dfssd/server/storage.go @@ -1,7 +1,6 @@ package server import ( - "fmt" "sort" "sync" "time" @@ -23,7 +22,7 @@ func addMessage(msg *api.Log) { } // display logs that are more than since (ms) old -func display(since int64, lfn func(string)) { +func display(since int64, fn func(*api.Log)) { var out []*api.Log // sorted messages to display var recycled []*api.Log // messages too recent to be displayed @@ -45,15 +44,16 @@ func display(since int64, lfn func(string)) { sort.Sort(ByTimestamp(out)) for _, v := range out { - lfn(fmt.Sprintf("[%d] %s:: %s", v.Timestamp, v.Identifier, v.Log)) + //lfn() + fn(v) } } // refresh every second -func displayHandler(lfn func(string)) { +func displayHandler(fn func(*api.Log)) { ticker := time.NewTicker(time.Second) for range ticker.C { - display(1000, lfn) + display(1000, fn) } }