Rahix
152290f5a3
The accesses to -KEC1 always time out on the attempt to update the output values. We noticed that this is related to the timing between the reading of the inputs and the following write to the outputs. Fix -KEC1 accesses by waiting before sending the next request to the board after receiving the reply for the previous one.
159 lines
4.3 KiB
Go
159 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/simonvetter/modbus"
|
|
"k8s.io/klog"
|
|
)
|
|
|
|
func modbusValuesToFloat(v uint16) float32 {
|
|
return float32(v) / 10.0
|
|
}
|
|
|
|
func (d *daemon) modbusConnect() error {
|
|
var err error
|
|
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
// Setup modbus client
|
|
d.modbusClient, err = modbus.NewClient(&modbus.ClientConfiguration{
|
|
URL: "tcp://10.250.241.20:8887",
|
|
Timeout: 5 * time.Second,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Connect to modbus client
|
|
err = d.modbusClient.Open()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// There are currently two devices connected to the modbus.
|
|
// The first one (slave 1) is a temperature/humidity sensor.
|
|
// The second one (slave 2) is a PTA8D08 transmitter
|
|
func (d *daemon) modbusUpdate() {
|
|
var err error
|
|
|
|
// Switch to slave 1 (BTA1)
|
|
d.modbusClient.SetUnitId(1)
|
|
|
|
// Read temperature and humidity
|
|
var registersBTA1 []uint16 // temperature, humidity
|
|
registersBTA1, err = d.modbusClient.ReadRegisters(1, 2, modbus.INPUT_REGISTER)
|
|
if err != nil {
|
|
klog.Warningf("error while reading registers from BTA1 %v", err)
|
|
} else if len(registersBTA1) != 2 {
|
|
klog.Warningf("expected two registers from modbus slave 1, but got %d", len(registersBTA1))
|
|
} else {
|
|
d.mu.Lock()
|
|
d.daemonState.tempSEM = modbusValuesToFloat(registersBTA1[0])
|
|
d.daemonState.humiditySEM = modbusValuesToFloat(registersBTA1[1])
|
|
d.mu.Unlock()
|
|
}
|
|
|
|
// Switch to slave 2 (KEC2)
|
|
d.modbusClient.SetUnitId(2)
|
|
|
|
// PT100 mapping
|
|
// Channel 0: Cable -WGA6, Sensor "dp bottom"
|
|
// Channel 1: Cable -WGA8, Sensor "dp inlet"
|
|
// Channel 2: Cable WGA7, Sensor "dp top"
|
|
var registersKEC2 []uint16 // temperatures dp
|
|
registersKEC2, err = d.modbusClient.ReadRegisters(0, 3, modbus.HOLDING_REGISTER)
|
|
if err != nil {
|
|
klog.Warningf("error while reading registers from KEC2 %v", err)
|
|
} else if len(registersKEC2) != 3 {
|
|
klog.Warningf("expected three registers from modbus slave 2, but got %d", len(registersKEC2))
|
|
} else {
|
|
d.mu.Lock()
|
|
d.daemonState.tempDPBottom = modbusValuesToFloat(registersKEC2[0])
|
|
d.daemonState.tempDPInlet = modbusValuesToFloat(registersKEC2[1])
|
|
d.daemonState.tempDPTop = modbusValuesToFloat(registersKEC2[2])
|
|
d.mu.Unlock()
|
|
}
|
|
|
|
// Switch to slave 3 (KEC1)
|
|
d.modbusClient.SetUnitId(3)
|
|
|
|
// Do a read first to avoid side-effects from the subsequent write to the relay states
|
|
var digitalInputs [8]bool
|
|
var digitalInputRegisters []uint16
|
|
|
|
digitalInputRegisters, err = d.modbusClient.ReadRegisters(0x81, 8, modbus.HOLDING_REGISTER)
|
|
if err != nil {
|
|
klog.Warningf("error while reading digital inputs from KEC1 %v", err)
|
|
} else {
|
|
// Convert MODBUS words into bools
|
|
for idx, value := range digitalInputRegisters {
|
|
if value != 0 {
|
|
digitalInputs[idx] = true
|
|
} else {
|
|
digitalInputs[idx] = false
|
|
}
|
|
}
|
|
// TODO: Input mapping goes here
|
|
}
|
|
|
|
// We must wait between reading and writing to the -KEC1 relay board
|
|
// because otherwise it chokes and times out the write registers
|
|
// command.
|
|
time.Sleep(time.Millisecond * 10)
|
|
|
|
// KFA1-KFA8
|
|
var relayState [8]bool
|
|
d.mu.Lock()
|
|
// -KFA1 Roughing Pump (normally closed contact)
|
|
relayState[0] = !d.daemonState.rpOn
|
|
// -KFA2 Diffusion Pump
|
|
relayState[1] = d.daemonState.dpOn
|
|
// -KFA4 Button Vent
|
|
relayState[3] = d.daemonState.vent.output
|
|
// -KFA5 Button Pump-Down
|
|
relayState[4] = d.daemonState.pumpdown.output
|
|
// -KFA6 Fake-Pirani Rough (normally closed contact)
|
|
relayState[5] = !d.aboveRough.output
|
|
// -KFA7 Fake-Pirani High (normally closed contact)
|
|
relayState[6] = !d.aboveHigh.output
|
|
d.mu.Unlock()
|
|
|
|
// The KEC1 module uses a non-standard MODBUS interface
|
|
// instead of coils
|
|
// 0x0100 is the open command
|
|
// 0x0200 is the close command
|
|
// We write 8 words (16-bit) to address 0x01 to update the relays
|
|
var registerValuesKEC1 [8]uint16
|
|
// Convert the boolean values to the commands
|
|
for idx, state := range relayState {
|
|
if state {
|
|
registerValuesKEC1[idx] = 0x0100
|
|
} else {
|
|
registerValuesKEC1[idx] = 0x0200
|
|
}
|
|
}
|
|
|
|
err = d.modbusClient.WriteRegisters(0x01, registerValuesKEC1[:])
|
|
if err != nil {
|
|
klog.Warningf("error while updating registers %v", err)
|
|
}
|
|
}
|
|
|
|
// Call modbusUpdate every 100 milliseconds
|
|
func (d *daemon) modbusProcess(ctx context.Context) {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
d.modbusUpdate()
|
|
time.Sleep(time.Millisecond * 100)
|
|
}
|
|
}
|
|
}
|