diff --git a/ingestor/proto/__init__.py b/ingestor/proto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ingestor/proto/opc_gateway.proto b/ingestor/proto/opc_gateway.proto new file mode 100644 index 0000000..6def8d3 --- /dev/null +++ b/ingestor/proto/opc_gateway.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package opcgateway; + +service OpcGateway { + rpc ReadTag(ReadTagRequest) returns (ReadTagResponse); +} +message ReadTagRequest { + string server_id = 1; + string addr = 2; + string data_type = 3; +} +message ReadTagResponse { + string value = 1; + string opc_status_code = 2; + string source_timestamp = 3; + string server_timestamp = 4; + bool persisted = 5; +} diff --git a/ingestor/proto/opc_gateway_pb2.py b/ingestor/proto/opc_gateway_pb2.py new file mode 100644 index 0000000..0a5f08c --- /dev/null +++ b/ingestor/proto/opc_gateway_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: opc_gateway.proto +# Protobuf Python Version: 7.35.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 7, + 35, + 0, + '', + 'opc_gateway.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11opc_gateway.proto\x12\nopcgateway\"D\n\x0eReadTagRequest\x12\x11\n\tserver_id\x18\x01 \x01(\t\x12\x0c\n\x04\x61\x64\x64r\x18\x02 \x01(\t\x12\x11\n\tdata_type\x18\x03 \x01(\t\"\x80\x01\n\x0fReadTagResponse\x12\r\n\x05value\x18\x01 \x01(\t\x12\x17\n\x0fopc_status_code\x18\x02 \x01(\t\x12\x18\n\x10source_timestamp\x18\x03 \x01(\t\x12\x18\n\x10server_timestamp\x18\x04 \x01(\t\x12\x11\n\tpersisted\x18\x05 \x01(\x08\x32P\n\nOpcGateway\x12\x42\n\x07ReadTag\x12\x1a.opcgateway.ReadTagRequest\x1a\x1b.opcgateway.ReadTagResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opc_gateway_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_READTAGREQUEST']._serialized_start=33 + _globals['_READTAGREQUEST']._serialized_end=101 + _globals['_READTAGRESPONSE']._serialized_start=104 + _globals['_READTAGRESPONSE']._serialized_end=232 + _globals['_OPCGATEWAY']._serialized_start=234 + _globals['_OPCGATEWAY']._serialized_end=314 +# @@protoc_insertion_point(module_scope) diff --git a/ingestor/proto/opc_gateway_pb2_grpc.py b/ingestor/proto/opc_gateway_pb2_grpc.py new file mode 100644 index 0000000..1f8ac2d --- /dev/null +++ b/ingestor/proto/opc_gateway_pb2_grpc.py @@ -0,0 +1,97 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +from . import opc_gateway_pb2 as opc__gateway__pb2 + +GRPC_GENERATED_VERSION = '1.82.1' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in opc_gateway_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class OpcGatewayStub: + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.ReadTag = channel.unary_unary( + '/opcgateway.OpcGateway/ReadTag', + request_serializer=opc__gateway__pb2.ReadTagRequest.SerializeToString, + response_deserializer=opc__gateway__pb2.ReadTagResponse.FromString, + _registered_method=True) + + +class OpcGatewayServicer: + """Missing associated documentation comment in .proto file.""" + + def ReadTag(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_OpcGatewayServicer_to_server(servicer, server): + rpc_method_handlers = { + 'ReadTag': grpc.unary_unary_rpc_method_handler( + servicer.ReadTag, + request_deserializer=opc__gateway__pb2.ReadTagRequest.FromString, + response_serializer=opc__gateway__pb2.ReadTagResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'opcgateway.OpcGateway', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('opcgateway.OpcGateway', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class OpcGateway: + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def ReadTag(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/opcgateway.OpcGateway/ReadTag', + opc__gateway__pb2.ReadTagRequest.SerializeToString, + opc__gateway__pb2.ReadTagResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/pyproject.toml b/pyproject.toml index e4841ad..163b85f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ exclude = [ "*.pyc", ".pytest_cache", "htmlcov", + "ingestor/proto/*_pb2*.py", ] [tool.ruff.lint] diff --git a/tests/unit/proto/__init__.py b/tests/unit/proto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/proto/test_proto.py b/tests/unit/proto/test_proto.py new file mode 100644 index 0000000..de8a319 --- /dev/null +++ b/tests/unit/proto/test_proto.py @@ -0,0 +1,36 @@ +from ingestor.proto import opc_gateway_pb2, opc_gateway_pb2_grpc + + +def test_read_tag_request_roundtrip(): + req = opc_gateway_pb2.ReadTagRequest(server_id='s1', addr='ns=2;s=Tag1', data_type='Float') + + data = req.SerializeToString() + parsed = opc_gateway_pb2.ReadTagRequest() + parsed.ParseFromString(data) + + assert parsed.server_id == 's1' + assert parsed.addr == 'ns=2;s=Tag1' + assert parsed.data_type == 'Float' + + +def test_read_tag_response_roundtrip(): + resp = opc_gateway_pb2.ReadTagResponse( + value='42.0', + opc_status_code='Good', + source_timestamp='2026-07-14T00:00:00Z', + server_timestamp='2026-07-14T00:00:01Z', + persisted=True, + ) + + data = resp.SerializeToString() + parsed = opc_gateway_pb2.ReadTagResponse() + parsed.ParseFromString(data) + + assert parsed.value == '42.0' + assert parsed.opc_status_code == 'Good' + assert parsed.persisted is True + + +def test_grpc_stub_module_loads(): + assert hasattr(opc_gateway_pb2_grpc, 'OpcGatewayStub') + assert hasattr(opc_gateway_pb2_grpc, 'OpcGatewayServicer')