]> git.giacomolli.uk - onehouse/blob - driver/m31/topology.go
Exchange bounded M31 Modbus TCP requests
[onehouse] / driver / m31 / topology.go
1 package m31
2
3 import (
4 "fmt"
5 "time"
6
7 "onehouse/protocol"
8 )
9
10 type topology struct {
11 inputs []point
12 outputs []point
13 analog []point
14 }
15
16 func newTopology(modules []Module) (topology, error) {
17 var result topology
18
19 if len(modules) == 0 || len(modules) > 16 {
20 return result, fmt.Errorf("topology requires one through sixteen modules")
21 }
22
23 for slot, module := range modules {
24 inputs, outputs, analog := 0, 0, 0
25
26 switch module.Model {
27 case HostModel, MixedModel:
28 inputs, outputs = 8, 8
29 case InputModel:
30 inputs = 16
31 case OutputModel:
32 outputs = 16
33 case AnalogModel:
34 analog = 8
35 default:
36 return topology{}, fmt.Errorf("unsupported module model in slot %d", slot)
37 }
38
39 if (slot == 0) != (module.Model == HostModel) {
40 return topology{}, fmt.Errorf("only slot zero must contain the host")
41 }
42
43 for channel, width := range module.PulseWidths {
44 if channel < 0 || channel >= outputs || width < 50*time.Millisecond || width > 65535*time.Millisecond || width%time.Millisecond != 0 {
45 return topology{}, fmt.Errorf("invalid pulse channel or width in slot %d", slot)
46 }
47 }
48
49 for channel := range inputs {
50 result.inputs = append(result.inputs, point{
51 id: fmt.Sprintf("m%d.di%d", slot, channel),
52 address: uint16(len(result.inputs)),
53 })
54 }
55
56 for channel := range outputs {
57 result.outputs = append(result.outputs, point{
58 id: fmt.Sprintf("m%d.do%d", slot, channel),
59 address: uint16(len(result.outputs)),
60 pulse: uint16(module.PulseWidths[channel] / time.Millisecond),
61 })
62 }
63
64 for channel := range analog {
65 result.analog = append(result.analog, point{
66 id: fmt.Sprintf("m%d.ao%d", slot, channel),
67 address: uint16(len(result.analog)),
68 })
69 }
70 }
71
72 return result, nil
73 }
74
75 func (t topology) descriptor() protocol.DescriptorPayload {
76 result := protocol.DescriptorPayload{
77 Manufacturer: "ebyte",
78 Model: HostModel,
79 Firmware: "unknown",
80 }
81
82 for _, input := range t.inputs {
83 result.Capabilities = append(result.Capabilities, protocol.Capability{
84 ID: input.id,
85 Type: "switch",
86 ReportsState: true,
87 })
88 }
89
90 for _, output := range t.outputs {
91 kind := "switch"
92 if output.pulse != 0 {
93 kind = "pulse"
94 }
95
96 result.Capabilities = append(result.Capabilities, protocol.Capability{
97 ID: output.id,
98 Type: kind,
99 ReportsState: output.pulse == 0,
100 AcceptsCommands: true,
101 })
102 }
103
104 for _, output := range t.analog {
105 result.Capabilities = append(result.Capabilities, protocol.Capability{
106 ID: output.id,
107 Type: "voltage",
108 ReportsState: true,
109 AcceptsCommands: true,
110 })
111 }
112
113 return result
114 }