succd: Split scope lock into multiple blocks
All checks were successful
/ test (push) Successful in 11s
/ test (pull_request) Successful in 10s

We noticed huge load spikes with the latest changes.  This was caused
by the modbus goroutine blocking the entire daemon for long periods of
time while doing its data transfer.

Fix this by only holding the lock while performing data accesses.
This commit is contained in:
hmelder 2024-11-10 05:45:32 +01:00 committed by Rahix
parent e7fd2dd7d7
commit d8a467a0c4

View file

@ -40,28 +40,27 @@ func (d *daemon) modbusConnect() error {
// The first one (slave 1) is a temperature/humidity sensor. // The first one (slave 1) is a temperature/humidity sensor.
// The second one (slave 2) is a PTA8D08 transmitter // The second one (slave 2) is a PTA8D08 transmitter
func (d *daemon) modbusUpdate() error { func (d *daemon) modbusUpdate() error {
d.mu.Lock()
defer d.mu.Unlock()
var err error var err error
var registers []uint16 // temperature, humidity
// Switch to slave 1 // Switch to slave 1 (BTA1)
d.modbusClient.SetUnitId(1) d.modbusClient.SetUnitId(1)
// Read temperature and humidity // Read temperature and humidity
registers, err = d.modbusClient.ReadRegisters(1, 2, modbus.INPUT_REGISTER) var registersBTA1 []uint16 // temperature, humidity
registersBTA1, err = d.modbusClient.ReadRegisters(1, 2, modbus.INPUT_REGISTER)
if err != nil { if err != nil {
return err return err
} }
if len(registers) != 2 { if len(registersBTA1) != 2 {
msg := fmt.Sprintf("Expected two registers from modbus slave 1, but got %d", len(registers)) msg := fmt.Sprintf("Expected two registers from modbus slave 1, but got %d", len(registersBTA1))
return errors.New(msg) return errors.New(msg)
} }
d.daemonState.tempSEM = modbusValuesToFloat(registers[0]) d.mu.Lock()
d.daemonState.humiditySEM = modbusValuesToFloat(registers[1]) d.daemonState.tempSEM = modbusValuesToFloat(registersBTA1[0])
d.daemonState.humiditySEM = modbusValuesToFloat(registersBTA1[1])
d.mu.Unlock()
// Switch to slave 2 (KEC2) // Switch to slave 2 (KEC2)
d.modbusClient.SetUnitId(2) d.modbusClient.SetUnitId(2)
@ -70,18 +69,21 @@ func (d *daemon) modbusUpdate() error {
// Channel 0: Cable -WGA6, Sensor "dp bottom" // Channel 0: Cable -WGA6, Sensor "dp bottom"
// Channel 1: Cable -WGA8, Sensor "dp inlet" // Channel 1: Cable -WGA8, Sensor "dp inlet"
// Channel 2: Cable WGA7, Sensor "dp top" // Channel 2: Cable WGA7, Sensor "dp top"
registers, err = d.modbusClient.ReadRegisters(0, 3, modbus.HOLDING_REGISTER) var registersKEC2 []uint16 // temperatures dp
registersKEC2, err = d.modbusClient.ReadRegisters(0, 3, modbus.HOLDING_REGISTER)
if err != nil { if err != nil {
return err return err
} }
if len(registers) != 3 { if len(registersKEC2) != 3 {
return fmt.Errorf("expected three registers from modbus slave 2, but got %d", len(registers)) return fmt.Errorf("expected three registers from modbus slave 2, but got %d", len(registersKEC2))
} }
d.daemonState.tempDPBottom = modbusValuesToFloat(registers[0]) d.mu.Lock()
d.daemonState.tempDPInlet = modbusValuesToFloat(registers[1]) d.daemonState.tempDPBottom = modbusValuesToFloat(registersKEC2[0])
d.daemonState.tempDPTop = modbusValuesToFloat(registers[2]) d.daemonState.tempDPInlet = modbusValuesToFloat(registersKEC2[1])
d.daemonState.tempDPTop = modbusValuesToFloat(registersKEC2[2])
d.mu.Unlock()
// Switch to slave 3 (KEC1) // Switch to slave 3 (KEC1)
d.modbusClient.SetUnitId(3) d.modbusClient.SetUnitId(3)
@ -106,7 +108,7 @@ func (d *daemon) modbusUpdate() error {
// KFA1-KFA8 // KFA1-KFA8
var relayState [8]bool var relayState [8]bool
d.mu.Lock()
// -KFA1 Roughing Pump (normally closed contact) // -KFA1 Roughing Pump (normally closed contact)
relayState[0] = !d.daemonState.rpOn relayState[0] = !d.daemonState.rpOn
// -KFA2 Diffusion Pump // -KFA2 Diffusion Pump
@ -119,23 +121,24 @@ func (d *daemon) modbusUpdate() error {
relayState[5] = !d.aboveRough.output relayState[5] = !d.aboveRough.output
// -KFA7 Fake-Pirani High (normally closed contact) // -KFA7 Fake-Pirani High (normally closed contact)
relayState[6] = !d.aboveHigh.output relayState[6] = !d.aboveHigh.output
d.mu.Unlock()
// The KEC1 module uses a non-standard MODBUS interface // The KEC1 module uses a non-standard MODBUS interface
// instead of coils // instead of coils
// 0x0100 is the open command // 0x0100 is the open command
// 0x0200 is the close command // 0x0200 is the close command
// We write 8 words (16-bit) to address 0x01 to update the relays // We write 8 words (16-bit) to address 0x01 to update the relays
var registerValues [8]uint16 var registerValuesKEC1 [8]uint16
// Convert the boolean values to the commands // Convert the boolean values to the commands
for idx, state := range relayState { for idx, state := range relayState {
if state { if state {
registerValues[idx] = 0x0100 registerValuesKEC1[idx] = 0x0100
} else { } else {
registerValues[idx] = 0x0200 registerValuesKEC1[idx] = 0x0200
} }
} }
err = d.modbusClient.WriteRegisters(0x01, registerValues[:]) err = d.modbusClient.WriteRegisters(0x01, registerValuesKEC1[:])
if err != nil { if err != nil {
return err return err
} }