SIENTIAPDE-1811
Enhance OPC UA communication and metrics tracking - Updated README.md to include new OPC UA Communication section and detailed metrics for session and write diagnostics. - Added new metrics in laborious/metrics.py for tracking OPC UA session states and write attempts. - Refactored OPC activity in laborious/activities/opc.py to handle session errors and improve error reporting. - Updated e2e tests to cover new scenarios for OPC session/channel errors and reconnect handling. - Modified .gitignore to include relatorio files and mlruns directory. - Added ipykernel to requirements-dev.txt for Jupyter notebook support.
This commit is contained in:
@@ -5,7 +5,15 @@ from pandas import DataFrame
|
||||
from pytest import mark
|
||||
from sientia_do.notifications.models import NotificationLevel
|
||||
|
||||
from laborious.activities.opc import OPC
|
||||
from laborious.activities.opc import (
|
||||
OPC,
|
||||
OPC_COMMENT_SEPARATOR,
|
||||
OPC_RECONNECT_IN_PROGRESS_COMMENT,
|
||||
OPC_SESSION_BAD_COMMENT_PREFIX,
|
||||
OPC_SESSION_BAD_CONFIDENCE,
|
||||
OPC_WRITTING_ERROR_CONFIDENCE,
|
||||
OPC_WRITTING_ERROR_MESSAGE,
|
||||
)
|
||||
|
||||
metadata = {
|
||||
'metadata': {
|
||||
@@ -206,7 +214,7 @@ WRITE_DATA_CASES = [
|
||||
async def test_write_data_success(opc, tag, data_type, data):
|
||||
opc.opc_repository['server1'].write_data.return_value = (True, {'response_time': 0.1})
|
||||
|
||||
result = await opc.write_data(
|
||||
response_time, error_info = await opc.write_data(
|
||||
server_id='server1',
|
||||
tag=tag,
|
||||
data=data,
|
||||
@@ -214,10 +222,9 @@ async def test_write_data_success(opc, tag, data_type, data):
|
||||
tag_type='prediction',
|
||||
metadata=metadata,
|
||||
)
|
||||
assert result == 0.1
|
||||
opc.opc_repository['server1'].write_data.assert_called_once_with(
|
||||
tag, data, data_type, opc.logger, metadata
|
||||
)
|
||||
assert response_time == 0.1
|
||||
assert error_info is None
|
||||
opc.opc_repository['server1'].write_data.assert_called_once_with(tag, data, data_type, metadata)
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
@@ -233,7 +240,7 @@ async def test_write_data_failed(opc):
|
||||
},
|
||||
)
|
||||
|
||||
result = await opc.write_data(
|
||||
response_time, error_info = await opc.write_data(
|
||||
server_id='server1',
|
||||
tag='tag1',
|
||||
data=50,
|
||||
@@ -241,7 +248,8 @@ async def test_write_data_failed(opc):
|
||||
tag_type='prediction',
|
||||
metadata=metadata,
|
||||
)
|
||||
assert result is None
|
||||
assert response_time is None
|
||||
assert error_info is not None
|
||||
|
||||
opc.send_notification_async.assert_called_once_with(
|
||||
metadata=metadata,
|
||||
@@ -283,7 +291,7 @@ async def test_write_data_exception(opc):
|
||||
|
||||
@mark.asyncio
|
||||
async def test_manage_output_tags_success(opc):
|
||||
opc.write_data = AsyncMock(return_value=0.1)
|
||||
opc.write_data = AsyncMock(return_value=(0.1, None))
|
||||
|
||||
data = DataFrame({'prediction': [0.75], 'prediction_confidence': [0.95]})
|
||||
config = {
|
||||
@@ -291,7 +299,7 @@ async def test_manage_output_tags_success(opc):
|
||||
'confidence_tags': {'tag2': {'data_type': 'float'}},
|
||||
}
|
||||
|
||||
output_data, opc_metrics = await opc.manage_output_tags(
|
||||
output_data, opc_metrics, _, _, _ = await opc.manage_output_tags(
|
||||
server_id='server1',
|
||||
config=config,
|
||||
data=data,
|
||||
@@ -323,7 +331,13 @@ async def test_manage_output_tags_success(opc):
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
@mark.parametrize('side_effect', [[0.1, None], [None, 0.2]])
|
||||
@mark.parametrize(
|
||||
'side_effect',
|
||||
[
|
||||
[(0.1, None), (None, {})],
|
||||
[(None, {}), (0.2, None)],
|
||||
],
|
||||
)
|
||||
async def test_manage_output_tags_failed(opc, side_effect):
|
||||
opc.write_data = AsyncMock(side_effect=side_effect)
|
||||
data = DataFrame({'prediction': [0.75], 'prediction_confidence': [0.95]})
|
||||
@@ -331,14 +345,14 @@ async def test_manage_output_tags_failed(opc, side_effect):
|
||||
'prediction_tags': {'tag1': {'data_type': 'float'}},
|
||||
'confidence_tags': {'tag2': {'data_type': 'float'}},
|
||||
}
|
||||
output_data, opc_metrics = await opc.manage_output_tags(
|
||||
output_data, opc_metrics, _, _, _ = await opc.manage_output_tags(
|
||||
server_id='server1',
|
||||
config=config,
|
||||
data=data,
|
||||
metadata=metadata['metadata'],
|
||||
)
|
||||
assert output_data is False
|
||||
assert opc_metrics == {'tag1': side_effect[0], 'tag2': side_effect[1]}
|
||||
assert opc_metrics == {'tag1': side_effect[0][0], 'tag2': side_effect[1][0]}
|
||||
opc.write_data.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
@@ -363,12 +377,12 @@ async def test_manage_output_tags_failed(opc, side_effect):
|
||||
|
||||
@mark.asyncio
|
||||
async def test_manage_output_tags_do_nothing(opc):
|
||||
opc.write_data = AsyncMock(return_value=0.1)
|
||||
opc.write_data = AsyncMock(return_value=(0.1, None))
|
||||
data = DataFrame({'prediction': [0.75], 'prediction_confidence': [0.95]})
|
||||
config = {
|
||||
'_invalid_key': {'tag1': {'data_type': 'float'}},
|
||||
}
|
||||
output_data, opc_metrics = await opc.manage_output_tags(
|
||||
output_data, opc_metrics, _, _, _ = await opc.manage_output_tags(
|
||||
server_id='server1',
|
||||
config=config,
|
||||
data=data,
|
||||
@@ -395,7 +409,9 @@ async def test_write_opc_data_success(mock_dataframe, opc):
|
||||
}
|
||||
|
||||
# Act
|
||||
opc.manage_output_tags = AsyncMock(return_value=(True, {'tag1': 0.1, 'tag2': 0.2}))
|
||||
opc.manage_output_tags = AsyncMock(
|
||||
return_value=(True, {'tag1': 0.1, 'tag2': 0.2}, False, None, False)
|
||||
)
|
||||
|
||||
opc.process_confidence = MagicMock(return_value={'data': 'data'})
|
||||
output_data, opc_metrics = await opc.write_opc_data(input_data)
|
||||
@@ -413,6 +429,9 @@ async def test_write_opc_data_success(mock_dataframe, opc):
|
||||
mock_dataframe.return_value,
|
||||
True,
|
||||
metadata['metadata'],
|
||||
session_bad=False,
|
||||
opc_status=None,
|
||||
reconnect_in_progress=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -462,13 +481,130 @@ async def test_write_opc_data_no_validate_server(opc):
|
||||
],
|
||||
)
|
||||
def test_process_confidence(opc, data, success, expected):
|
||||
# Act
|
||||
result = opc.process_confidence(data, success, metadata)
|
||||
|
||||
# Assert
|
||||
result = opc.process_confidence(data, success, metadata['metadata'])
|
||||
assert result['prediction_confidence'][0] == expected
|
||||
|
||||
|
||||
def test_process_confidence_session_bad(opc):
|
||||
data = DataFrame({'prediction_confidence': [0.9]})
|
||||
result = opc.process_confidence(
|
||||
data,
|
||||
False,
|
||||
metadata['metadata'],
|
||||
session_bad=True,
|
||||
opc_status='BadSessionIdInvalid',
|
||||
)
|
||||
assert result['prediction_confidence'][0] == OPC_SESSION_BAD_CONFIDENCE
|
||||
assert result['comments'][0].startswith(OPC_SESSION_BAD_COMMENT_PREFIX)
|
||||
assert 'BadSessionIdInvalid' in result['comments'][0]
|
||||
|
||||
|
||||
def test_process_confidence_generic_failure(opc):
|
||||
data = DataFrame({'prediction_confidence': [0.9]})
|
||||
result = opc.process_confidence(data, False, metadata['metadata'])
|
||||
assert result['prediction_confidence'][0] == OPC_WRITTING_ERROR_CONFIDENCE
|
||||
assert result['comments'][0] == OPC_WRITTING_ERROR_MESSAGE
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_manage_output_tags_session_bad(opc):
|
||||
opc.write_data = AsyncMock(
|
||||
side_effect=[
|
||||
(
|
||||
None,
|
||||
{
|
||||
'opc_error_kind': 'session_bad',
|
||||
'opc_status': 'BadSessionIdInvalid',
|
||||
'notification_id': 'OPC_WRITE_DATA_ERROR_1',
|
||||
'message': 'bad',
|
||||
'block': 'opc_repository',
|
||||
'level': NotificationLevel.ERROR,
|
||||
},
|
||||
),
|
||||
(0.2, None),
|
||||
]
|
||||
)
|
||||
data = DataFrame({'prediction': [0.75], 'prediction_confidence': [0.95]})
|
||||
config = {
|
||||
'prediction_tags': {'tag1': {'data_type': 'float'}},
|
||||
'confidence_tags': {'tag2': {'data_type': 'float'}},
|
||||
}
|
||||
(
|
||||
success,
|
||||
metrics,
|
||||
session_bad_seen,
|
||||
opc_status,
|
||||
reconnect_in_progress,
|
||||
) = await opc.manage_output_tags('server1', config, data, metadata['metadata'])
|
||||
assert success is False
|
||||
assert session_bad_seen is True
|
||||
assert reconnect_in_progress is False
|
||||
assert opc_status == 'BadSessionIdInvalid'
|
||||
assert metrics['tag1'] is None
|
||||
assert metrics['tag2'] == 0.2
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_manage_output_tags_reconnect_in_progress(opc):
|
||||
opc.write_data = AsyncMock(
|
||||
return_value=(
|
||||
None,
|
||||
{
|
||||
'opc_error_kind': 'reconnect_in_progress',
|
||||
'notification_id': 'OPC_WRITE_RECONNECT_IN_PROGRESS_1',
|
||||
'message': 'skipped',
|
||||
'block': 'opc_repository',
|
||||
'level': NotificationLevel.WARNING,
|
||||
},
|
||||
)
|
||||
)
|
||||
data = DataFrame({'prediction': [0.75], 'prediction_confidence': [0.95]})
|
||||
config = {'prediction_tags': {'tag1': {'data_type': 'float'}}}
|
||||
(
|
||||
success,
|
||||
metrics,
|
||||
session_bad_seen,
|
||||
opc_status,
|
||||
reconnect_in_progress,
|
||||
) = await opc.manage_output_tags('server1', config, data, metadata['metadata'])
|
||||
assert success is False
|
||||
assert session_bad_seen is False
|
||||
assert reconnect_in_progress is True
|
||||
assert opc_status is None
|
||||
assert metrics['tag1'] is None
|
||||
|
||||
|
||||
def test_process_confidence_reconnect_in_progress(opc):
|
||||
data = DataFrame({'prediction_confidence': [0.9]})
|
||||
result = opc.process_confidence(
|
||||
data,
|
||||
False,
|
||||
metadata['metadata'],
|
||||
reconnect_in_progress=True,
|
||||
)
|
||||
assert result['prediction_confidence'][0] == OPC_SESSION_BAD_CONFIDENCE
|
||||
assert result['comments'][0] == OPC_RECONNECT_IN_PROGRESS_COMMENT
|
||||
|
||||
|
||||
def test_process_confidence_concatenates_multiple_comments(opc):
|
||||
data = DataFrame({'prediction_confidence': [0.9]})
|
||||
session_comment = f'{OPC_SESSION_BAD_COMMENT_PREFIX} BadSessionIdInvalid'
|
||||
|
||||
result = opc.process_confidence(
|
||||
data,
|
||||
False,
|
||||
metadata['metadata'],
|
||||
session_bad=True,
|
||||
opc_status='BadSessionIdInvalid',
|
||||
reconnect_in_progress=True,
|
||||
)
|
||||
|
||||
assert result['prediction_confidence'][0] == OPC_SESSION_BAD_CONFIDENCE
|
||||
assert result['comments'][0] == OPC_COMMENT_SEPARATOR.join(
|
||||
[session_comment, OPC_RECONNECT_IN_PROGRESS_COMMENT]
|
||||
)
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_validate_server(opc):
|
||||
assert await opc.validate_server('server1', metadata) is True
|
||||
|
||||
Reference in New Issue
Block a user