func sendStartupData(s *seed.Seed, socket socket, runningState string) { messages := []executor.MonitorMessage{} // _service block content messages = append(messages, executor.MonitorMessage{ Block: "_service", Data: fmt.Sprintf("<code>%s</code>", s.String()[1:]), // skip the beginning newline in the string }) // _graph block content dot, err := dot.ToDot(s, s.Name) if err != nil { panic(err) } graphviz := exec.Command("dot", "-Tsvg") graphviz.Stdin = bytes.NewBuffer(dot) graph, err := graphviz.Output() if err != nil { panic(err) } messages = append(messages, executor.MonitorMessage{ Block: "_graph", Data: string(graph), // skip the beginning newline in the string }) // list of collections for input control collections := "" for name, _ := range s.Collections { collections += fmt.Sprintf("<option value=\"%s\">%s</option>", name, name) } messages = append(messages, executor.MonitorMessage{ Block: "_collections", Data: collections, }) // runningState messages = append(messages, executor.MonitorMessage{ Block: "_command", Data: runningState, }) for _, message := range messages { data, err := json.Marshal(message) if err != nil { panic(err) } socket.Write(data) } }
// Transform uses graphs to add network interfaces to Seeds func Transform(orig *seed.Seed) (*seed.Seed, error) { // build graph g := seedGraph.SeedAsGraph(orig) orig.Name = strings.Title(orig.Name) + "Server" for inputName, input := range orig.Collections { if input.Type != seed.CollectionInput { continue } input.Key = append([]string{"@address"}, input.Key...) start, ok := g.NodeFor(inputName) if !ok { panic("could not find node for " + inputName) } for outputName, output := range orig.Collections { if output.Type != seed.CollectionOutput { continue } goal, ok := g.NodeFor(outputName) if !ok { panic("could not find node for " + outputName) } path, cost, _ := graph.AStar(start, goal, g, cost, nil) if math.IsInf(cost, 0) { // there is no path (that does not go through a table) continue } outputAddress := outputName + "_addr" previousCollection := inputName for _, node := range path[:len(path)] { // unbox graph.internalNode; node = g.GetNode(node.ID()) switch node := node.(type) { case seedGraph.CollectionNode: previousCollection = node.Name switch node.Collection.Type { case seed.CollectionInput, seed.CollectionScratch: node.Collection.Key = prependIfNotExists(node.Collection.Key, outputAddress) case seed.CollectionOutput: node.Collection.Key = prependIfNotExists(node.Collection.Key, "@"+outputAddress) case seed.CollectionChannel, seed.CollectionTable: panic("should not encounter these collection types") default: panic(fmt.Sprintf("unhandled type: %d", node.Collection.Type)) } case seedGraph.RuleNode: exists := false for _, expression := range node.Rule.Intension { switch expression := expression.(type) { case seed.QualifiedColumn: if expression.Column == outputAddress { // if a reference to the outputAddress already exists (i.e., from being added by another flow) // then add a constraint to make the rows match up node.Rule.Predicate = append(node.Rule.Predicate, seed.Constraint{ Left: expression, Right: seed.QualifiedColumn{ Collection: previousCollection, Column: outputAddress, }, }) exists = true } case seed.MapFunction, seed.ReduceFunction: continue default: panic(fmt.Sprintf("unhandled type: %v", reflect.TypeOf(expression).String())) } } if !exists { // add to the projection node.Rule.Intension = append([]seed.Expression{seed.QualifiedColumn{ Collection: previousCollection, Column: outputAddress, }}, node.Rule.Intension...) } default: panic(fmt.Sprintf("unhandled type: %v", reflect.TypeOf(node).String())) } } } } // change inputs and outputs to channels for _, collection := range orig.Collections { switch collection.Type { case seed.CollectionInput, seed.CollectionOutput: collection.Type = seed.CollectionChannel case seed.CollectionScratch, seed.CollectionTable, seed.CollectionChannel: // no-op default: panic(collection.Type) } } // rules supplying channels must be asynchronous for _, rule := range orig.Rules { collectionName := rule.Supplies collection, ok := orig.Collections[collectionName] if !ok { // should never happen panic(collectionName) } if collection.Type == seed.CollectionChannel { rule.Operation = "<~" } } return orig, nil }