commit
81e8dd1cd9
@ -0,0 +1,22 @@ |
||||
nodes: |
||||
- name: node1 |
||||
program: ./programs/prak3.hal |
||||
- name: node2 |
||||
program: ./programs/prak3.hal |
||||
- name: node3 |
||||
program: ./programs/prak3.hal |
||||
- name: node4 |
||||
program: ./programs/prak3.hal |
||||
connections: |
||||
- source_node: node1 |
||||
source_port: 3 |
||||
dest_node: node2 |
||||
dest_port: 2 |
||||
- source_node: node2 |
||||
source_port: 3 |
||||
dest_node: node3 |
||||
dest_port: 2 |
||||
- source_node: node3 |
||||
source_port: 3 |
||||
dest_node: node4 |
||||
dest_port: 2 |
@ -0,0 +1,54 @@ |
||||
package cmd |
||||
|
||||
import ( |
||||
"context" |
||||
"git.jfdev.de/JonasFranzDEV/hal/hal" |
||||
"git.jfdev.de/JonasFranzDEV/hal/parser" |
||||
"github.com/spf13/cobra" |
||||
"gopkg.in/yaml.v2" |
||||
"io/ioutil" |
||||
"os" |
||||
) |
||||
|
||||
var clusterCmd = &cobra.Command{ |
||||
Args: cobra.ExactArgs(1), |
||||
Use: "cluster [config file]", |
||||
RunE: runClusterCommand, |
||||
} |
||||
|
||||
func init() { |
||||
rootCommand.AddCommand(clusterCmd) |
||||
} |
||||
|
||||
func runClusterCommand(cmd *cobra.Command, args []string) error { |
||||
configFile := args[0] |
||||
|
||||
// Parse config
|
||||
if stats, err := os.Stat(configFile); err != nil || stats.IsDir() { |
||||
return err |
||||
} |
||||
|
||||
file, err := ioutil.ReadFile(configFile) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
config := &hal.ClusterConfig{} |
||||
if err := yaml.Unmarshal(file, config); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// Parse modules
|
||||
modules, err := parser.GetModulesFromConfig(config, debug) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
// Create the cluster
|
||||
cluster, err := config.BuildCluster(modules) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
// Run
|
||||
return cluster.Run(context.Background()) |
||||
} |
@ -0,0 +1,59 @@ |
||||
package hal |
||||
|
||||
import ( |
||||
"context" |
||||
"golang.org/x/sync/errgroup" |
||||
) |
||||
|
||||
type Cluster struct { |
||||
modules []*Module |
||||
} |
||||
|
||||
func (c *Cluster) Run(ctx context.Context) error { |
||||
errs, _ := errgroup.WithContext(ctx) |
||||
for _, module := range c.modules { |
||||
errs.Go(module.Run) |
||||
} |
||||
return errs.Wait() |
||||
} |
||||
|
||||
func (config *ClusterConfig) BuildCluster(modules map[string]*Module) (*Cluster, error) { |
||||
// We assume all modules in the connection config exist
|
||||
for _, c := range config.Connections { |
||||
conn := NewMultiThreadedIO() |
||||
modules[c.SourceNode].IO[c.SourcePort] = conn |
||||
modules[c.DestNode].IO[c.DestPort] = conn |
||||
} |
||||
|
||||
// for host, client := range mapping {
|
||||
// modules[host.Module] = struct{}{}
|
||||
// modules[client.Module] = struct{}{}
|
||||
// host.Module.IO[host.IOPort] = NewMultiThreadedIO(&client)
|
||||
// }
|
||||
|
||||
cluster := &Cluster{modules: make([]*Module, len(modules), len(modules))} |
||||
var index int64 |
||||
for _, module := range modules { |
||||
cluster.modules[index] = module |
||||
index++ |
||||
} |
||||
return cluster, nil |
||||
} |
||||
|
||||
type ClusterConnection struct { |
||||
SourcePort int64 `yaml:"source_port"` |
||||
SourceNode string `yaml:"source_node"` |
||||
DestPort int64 `yaml:"dest_port"` |
||||
DestNode string `yaml:"dest_node"` |
||||
} |
||||
|
||||
type ClusterNode struct { |
||||
Name string `yaml:"name"` |
||||
// Path to the hal program
|
||||
Program string `yaml:"program"` |
||||
} |
||||
|
||||
type ClusterConfig struct { |
||||
Nodes []*ClusterNode `yaml:"nodes"` |
||||
Connections []*ClusterConnection `yaml:"connections"` |
||||
} |
@ -0,0 +1,149 @@ |
||||
package hal |
||||
|
||||
import ( |
||||
"github.com/stretchr/testify/assert" |
||||
"testing" |
||||
) |
||||
|
||||
// func TestCluster_Run(t *testing.T) {
|
||||
// var mapping ModuleMapping
|
||||
//
|
||||
// firstModule := &Module{
|
||||
// programStorage: map[int64]*ProgrammedInstruction{
|
||||
// 1: {
|
||||
// Instruction: InstructionStart,
|
||||
// },
|
||||
// },
|
||||
// register: []float64{0},
|
||||
// IO: map[int64]IO{},
|
||||
// }
|
||||
// secondModule := &Module{
|
||||
// programStorage: map[int64]*ProgrammedInstruction{
|
||||
// 1: {
|
||||
// Instruction: InstructionStop,
|
||||
// },
|
||||
// },
|
||||
// register: []float64{0},
|
||||
// IO: map[int64]IO{},
|
||||
// }
|
||||
//
|
||||
// mapping = map[MultiThreadedIOPort]MultiThreadedIOPort{
|
||||
// {
|
||||
// Module: firstModule,
|
||||
// IOPort: 0,
|
||||
// }: {
|
||||
// Module: secondModule,
|
||||
// IOPort: 0,
|
||||
// },
|
||||
// }
|
||||
// assert.NoError(t, mapping.BuildCluster().Run(context.Background()))
|
||||
// }
|
||||
|
||||
func TestModuleMapping_BuildCluster(t *testing.T) { |
||||
config := &ClusterConfig{ |
||||
Nodes: []*ClusterNode{ |
||||
{ |
||||
Name: "node1", |
||||
}, |
||||
{ |
||||
Name: "node2", |
||||
}, |
||||
{ |
||||
Name: "node3", |
||||
}, |
||||
}, |
||||
Connections: []*ClusterConnection{ |
||||
{ |
||||
SourceNode: "node1", |
||||
SourcePort: 1, |
||||
DestNode: "node2", |
||||
DestPort: 1, |
||||
}, |
||||
{ |
||||
SourceNode: "node2", |
||||
SourcePort: 1, |
||||
DestNode: "node1", |
||||
DestPort: 1, |
||||
}, |
||||
{ |
||||
SourceNode: "node1", |
||||
SourcePort: 2, |
||||
DestNode: "node2", |
||||
DestPort: 2, |
||||
}, |
||||
{ |
||||
SourceNode: "node2", |
||||
SourcePort: 2, |
||||
DestNode: "node1", |
||||
DestPort: 2, |
||||
}, |
||||
{ |
||||
SourceNode: "node3", |
||||
SourcePort: 2, |
||||
DestNode: "node2", |
||||
DestPort: 3, |
||||
}, |
||||
{ |
||||
SourceNode: "node2", |
||||
SourcePort: 3, |
||||
DestNode: "node3", |
||||
DestPort: 2, |
||||
}, |
||||
{ |
||||
SourceNode: "node3", |
||||
SourcePort: 3, |
||||
DestNode: "node1", |
||||
DestPort: 3, |
||||
}, |
||||
{ |
||||
SourceNode: "node1", |
||||
SourcePort: 3, |
||||
DestNode: "node3", |
||||
DestPort: 3, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
modules := make(map[string]*Module) |
||||
modules = map[string]*Module{ |
||||
"node1": { |
||||
programStorage: map[int64]*ProgrammedInstruction{ |
||||
1: {Instruction: InstructionStart}, |
||||
2: {Instruction: InstructionStop}, |
||||
}, |
||||
register: []float64{0}, |
||||
IO: map[int64]IO{}, |
||||
}, |
||||
"node2": { |
||||
programStorage: map[int64]*ProgrammedInstruction{ |
||||
1: {Instruction: InstructionStart}, |
||||
2: {Instruction: InstructionStop}, |
||||
}, |
||||
register: []float64{0}, |
||||
IO: map[int64]IO{}, |
||||
}, |
||||
"node3": { |
||||
programStorage: map[int64]*ProgrammedInstruction{ |
||||
1: {Instruction: InstructionStart}, |
||||
2: {Instruction: InstructionStop}, |
||||
}, |
||||
register: []float64{0}, |
||||
IO: map[int64]IO{}, |
||||
}, |
||||
} |
||||
|
||||
cluster, err := config.BuildCluster(modules) |
||||
assert.NoError(t, err) |
||||
// First connection config
|
||||
assert.NotNil(t, cluster.modules[0].IO[1]) |
||||
assert.NotNil(t, cluster.modules[1].IO[1]) |
||||
// Second
|
||||
assert.NotNil(t, cluster.modules[0].IO[2]) |
||||
assert.NotNil(t, cluster.modules[1].IO[2]) |
||||
// Third
|
||||
assert.NotNil(t, cluster.modules[1].IO[3]) |
||||
assert.NotNil(t, cluster.modules[2].IO[2]) |
||||
// Fourth
|
||||
assert.NotNil(t, cluster.modules[0].IO[3]) |
||||
assert.NotNil(t, cluster.modules[2].IO[3]) |
||||
} |
@ -0,0 +1,21 @@ |
||||
package hal |
||||
|
||||
type MultiThreadedIO struct { |
||||
channel chan float64 |
||||
} |
||||
|
||||
func NewMultiThreadedIO() *MultiThreadedIO { |
||||
return &MultiThreadedIO{ |
||||
channel: make(chan float64), |
||||
} |
||||
} |
||||
|
||||
func (io *MultiThreadedIO) Read() (float64, error) { |
||||
result := <-io.channel |
||||
return result, nil |
||||
} |
||||
|
||||
func (io *MultiThreadedIO) Write(content float64) error { |
||||
io.channel <- content |
||||
return nil |
||||
} |
@ -0,0 +1,23 @@ |
||||
package parser |
||||
|
||||
import ( |
||||
"git.jfdev.de/JonasFranzDEV/hal/hal" |
||||
) |
||||
|
||||
func GetModulesFromConfig(config *hal.ClusterConfig, debug bool) (modules map[string]*hal.Module, err error) { |
||||
// all modules with their name as key
|
||||
modules = make(map[string]*hal.Module) |
||||
|
||||
for _, node := range config.Nodes { |
||||
program, err := ParseFile(node.Program) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
// By default, all IO is the standard IO, we'll override it with channels where needed.
|
||||
modules[node.Name], err = hal.NewNamedHALModule(program, 256, 4, debug, node.Name) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,2 @@ |
||||
01 START |
||||
02 STOP |
@ -0,0 +1,15 @@ |
||||
01 START |
||||
|
||||
02 IN 2 |
||||
03 STORE 1 |
||||
04 JUMPNEG 30 |
||||
|
||||
10 MULNUM 2 |
||||
11 ADDNUM 1 |
||||
12 OUT 1 |
||||
|
||||
30 OUT 3 |
||||
31 LOAD 1 |
||||
32 JUMPPOS 02 |
||||
|
||||
99 STOP |
Loading…
Reference in new issue