fix: Update protocol clients to fix security policy mismatch and register address issues
- OPC UA client: Explicitly set security policy to 'None' to match server - OPC UA client: Fix connection handling to check if connection succeeded - Modbus client: Update register addresses to match server configuration - Modbus client: Add proper connection success checking
This commit is contained in:
parent
7cf7ed928b
commit
ac89d72aa9
|
|
@ -24,6 +24,12 @@ class OPCUAClient:
|
||||||
try:
|
try:
|
||||||
from asyncua import Client
|
from asyncua import Client
|
||||||
self._client = Client(url=self.endpoint)
|
self._client = Client(url=self.endpoint)
|
||||||
|
|
||||||
|
# Explicitly set security policy to match server configuration
|
||||||
|
# The server supports both Basic256Sha256 and None security policies
|
||||||
|
# Use None for development/testing
|
||||||
|
self._client.set_security_string("http://opcfoundation.org/UA/SecurityPolicy#None")
|
||||||
|
|
||||||
# Set timeout for connection
|
# Set timeout for connection
|
||||||
await asyncio.wait_for(self._client.connect(), timeout=5.0)
|
await asyncio.wait_for(self._client.connect(), timeout=5.0)
|
||||||
logger.info("opcua_client_connected", endpoint=self.endpoint)
|
logger.info("opcua_client_connected", endpoint=self.endpoint)
|
||||||
|
|
@ -46,7 +52,9 @@ class OPCUAClient:
|
||||||
"""Read value from OPC UA node."""
|
"""Read value from OPC UA node."""
|
||||||
try:
|
try:
|
||||||
if not self._client:
|
if not self._client:
|
||||||
await self.connect()
|
connected = await self.connect()
|
||||||
|
if not connected:
|
||||||
|
return None
|
||||||
|
|
||||||
node = self._client.get_node(node_id)
|
node = self._client.get_node(node_id)
|
||||||
value = await asyncio.wait_for(node.read_value(), timeout=3.0)
|
value = await asyncio.wait_for(node.read_value(), timeout=3.0)
|
||||||
|
|
@ -62,7 +70,9 @@ class OPCUAClient:
|
||||||
"""Get all data for a specific pump."""
|
"""Get all data for a specific pump."""
|
||||||
try:
|
try:
|
||||||
if not self._client:
|
if not self._client:
|
||||||
await self.connect()
|
connected = await self.connect()
|
||||||
|
if not connected:
|
||||||
|
return {}
|
||||||
|
|
||||||
# Define node IDs for this pump
|
# Define node IDs for this pump
|
||||||
nodes = {
|
nodes = {
|
||||||
|
|
@ -120,7 +130,9 @@ class ModbusClient:
|
||||||
"""Read holding register(s)."""
|
"""Read holding register(s)."""
|
||||||
try:
|
try:
|
||||||
if not self._client or not self._client.is_socket_open():
|
if not self._client or not self._client.is_socket_open():
|
||||||
self.connect()
|
connected = self.connect()
|
||||||
|
if not connected:
|
||||||
|
return None
|
||||||
|
|
||||||
# Set timeout for the read operation
|
# Set timeout for the read operation
|
||||||
if hasattr(self._client, 'timeout'):
|
if hasattr(self._client, 'timeout'):
|
||||||
|
|
@ -146,7 +158,9 @@ class ModbusClient:
|
||||||
"""Read input register(s)."""
|
"""Read input register(s)."""
|
||||||
try:
|
try:
|
||||||
if not self._client or not self._client.is_socket_open():
|
if not self._client or not self._client.is_socket_open():
|
||||||
self.connect()
|
connected = self.connect()
|
||||||
|
if not connected:
|
||||||
|
return None
|
||||||
|
|
||||||
# Set timeout for the read operation
|
# Set timeout for the read operation
|
||||||
if hasattr(self._client, 'timeout'):
|
if hasattr(self._client, 'timeout'):
|
||||||
|
|
@ -171,22 +185,32 @@ class ModbusClient:
|
||||||
def get_pump_registers(self, pump_num: int) -> Dict[str, Any]:
|
def get_pump_registers(self, pump_num: int) -> Dict[str, Any]:
|
||||||
"""Get all register data for a specific pump."""
|
"""Get all register data for a specific pump."""
|
||||||
try:
|
try:
|
||||||
# Calculate base register for this pump
|
# Calculate register addresses based on server configuration
|
||||||
base_register = 40000 + (pump_num * 10)
|
# Server uses: SETPOINT_BASE=0, STATUS_BASE=100, SAFETY_BASE=200, PERFORMANCE_BASE=400
|
||||||
|
# Each pump gets 10 registers in each block
|
||||||
|
pump_offset = (pump_num - 1) * 10
|
||||||
|
|
||||||
# Read holding registers (setpoints)
|
# Read holding registers (setpoints) - base address 0
|
||||||
setpoint = self.read_holding_register(base_register + 1, 1)
|
setpoint = self.read_holding_register(pump_offset, 1)
|
||||||
|
|
||||||
# Read input registers (status values)
|
# Read input registers (status values) - base address 100
|
||||||
actual_speed = self.read_input_register(base_register + 2, 1)
|
actual_speed = self.read_input_register(100 + pump_offset, 1)
|
||||||
power = self.read_input_register(base_register + 3, 1)
|
power = self.read_input_register(100 + pump_offset + 1, 1)
|
||||||
temperature = self.read_input_register(base_register + 4, 1)
|
flow_rate = self.read_input_register(100 + pump_offset + 2, 1)
|
||||||
|
|
||||||
|
# Read safety status - base address 200
|
||||||
|
safety_status = self.read_input_register(200 + pump_offset, 1)
|
||||||
|
|
||||||
|
# Read performance metrics - base address 400
|
||||||
|
efficiency = self.read_input_register(400 + pump_offset, 1)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"setpoint": setpoint[0] if setpoint else None,
|
"setpoint": setpoint[0] if setpoint else None,
|
||||||
"actual_speed": actual_speed[0] if actual_speed else None,
|
"actual_speed": actual_speed[0] if actual_speed else None,
|
||||||
"power": power[0] if power else None,
|
"power": power[0] if power else None,
|
||||||
"temperature": temperature[0] if temperature else None
|
"flow_rate": flow_rate[0] if flow_rate else None,
|
||||||
|
"safety_status": safety_status[0] if safety_status else None,
|
||||||
|
"efficiency": efficiency[0] if efficiency else None
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("failed_to_get_pump_registers", pump_num=pump_num, error=str(e))
|
logger.error("failed_to_get_pump_registers", pump_num=pump_num, error=str(e))
|
||||||
|
|
@ -279,13 +303,18 @@ class ProtocolDataCollector:
|
||||||
setpoint = modbus_data.get('setpoint', 0) or 0
|
setpoint = modbus_data.get('setpoint', 0) or 0
|
||||||
actual_speed = modbus_data.get('actual_speed', 0) or 0
|
actual_speed = modbus_data.get('actual_speed', 0) or 0
|
||||||
power = modbus_data.get('power', 0) or 0
|
power = modbus_data.get('power', 0) or 0
|
||||||
temperature = modbus_data.get('temperature', 0) or 0
|
flow_rate = modbus_data.get('flow_rate', 0) or 0
|
||||||
|
safety_status = modbus_data.get('safety_status', 0) or 0
|
||||||
|
efficiency = modbus_data.get('efficiency', 0) or 0
|
||||||
|
|
||||||
|
# Calculate pump offset for address display
|
||||||
|
pump_offset = (pump_num - 1) * 10
|
||||||
|
|
||||||
signals.extend([
|
signals.extend([
|
||||||
{
|
{
|
||||||
"name": f"Station_{station_id}_Pump_{pump_id}_Setpoint",
|
"name": f"Station_{station_id}_Pump_{pump_id}_Setpoint",
|
||||||
"protocol": "modbus",
|
"protocol": "modbus",
|
||||||
"address": f"{40000 + (pump_num * 10) + 1}",
|
"address": f"Holding Register {pump_offset}",
|
||||||
"data_type": "Integer",
|
"data_type": "Integer",
|
||||||
"current_value": f"{setpoint} Hz (x10)",
|
"current_value": f"{setpoint} Hz (x10)",
|
||||||
"quality": "Good",
|
"quality": "Good",
|
||||||
|
|
@ -294,7 +323,7 @@ class ProtocolDataCollector:
|
||||||
{
|
{
|
||||||
"name": f"Station_{station_id}_Pump_{pump_id}_ActualSpeed",
|
"name": f"Station_{station_id}_Pump_{pump_id}_ActualSpeed",
|
||||||
"protocol": "modbus",
|
"protocol": "modbus",
|
||||||
"address": f"{40000 + (pump_num * 10) + 2}",
|
"address": f"Input Register {100 + pump_offset}",
|
||||||
"data_type": "Integer",
|
"data_type": "Integer",
|
||||||
"current_value": f"{actual_speed} Hz (x10)",
|
"current_value": f"{actual_speed} Hz (x10)",
|
||||||
"quality": "Good",
|
"quality": "Good",
|
||||||
|
|
@ -303,18 +332,36 @@ class ProtocolDataCollector:
|
||||||
{
|
{
|
||||||
"name": f"Station_{station_id}_Pump_{pump_id}_Power",
|
"name": f"Station_{station_id}_Pump_{pump_id}_Power",
|
||||||
"protocol": "modbus",
|
"protocol": "modbus",
|
||||||
"address": f"{40000 + (pump_num * 10) + 3}",
|
"address": f"Input Register {100 + pump_offset + 1}",
|
||||||
"data_type": "Integer",
|
"data_type": "Integer",
|
||||||
"current_value": f"{power} kW (x10)",
|
"current_value": f"{power} kW (x10)",
|
||||||
"quality": "Good",
|
"quality": "Good",
|
||||||
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": f"Station_{station_id}_Pump_{pump_id}_Temperature",
|
"name": f"Station_{station_id}_Pump_{pump_id}_FlowRate",
|
||||||
"protocol": "modbus",
|
"protocol": "modbus",
|
||||||
"address": f"{40000 + (pump_num * 10) + 4}",
|
"address": f"Input Register {100 + pump_offset + 2}",
|
||||||
"data_type": "Integer",
|
"data_type": "Integer",
|
||||||
"current_value": f"{temperature} °C",
|
"current_value": f"{flow_rate} m³/h",
|
||||||
|
"quality": "Good",
|
||||||
|
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": f"Station_{station_id}_Pump_{pump_id}_SafetyStatus",
|
||||||
|
"protocol": "modbus",
|
||||||
|
"address": f"Input Register {200 + pump_offset}",
|
||||||
|
"data_type": "Integer",
|
||||||
|
"current_value": f"{safety_status}",
|
||||||
|
"quality": "Good",
|
||||||
|
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": f"Station_{station_id}_Pump_{pump_id}_Efficiency",
|
||||||
|
"protocol": "modbus",
|
||||||
|
"address": f"Input Register {400 + pump_offset}",
|
||||||
|
"data_type": "Integer",
|
||||||
|
"current_value": f"{efficiency} %",
|
||||||
"quality": "Good",
|
"quality": "Good",
|
||||||
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue