diff --git a/README.md b/README.md
index 1d0efa7..343b2b6 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ pip3 install someipy
## Example Applications
-In the directory [example_apps](./example_apps/), examples including explanations, can be found for using the someipy library. In [temperature_msg.py](./example_apps/temperature_msg.py), a payload interface "TemperatureMsg" is defined, which can be serialized and deserialized. In [send_events.py](./example_apps/send_events.py), the service discovery and two services are instantiated. The "TemperatureMsg" is serialized and used as the payload for sending events.
+In the directory [example_apps](./example_apps/), examples including explanations, can be found for using the someipy library.
## Supported Features, Limitations and Deviations
@@ -64,7 +64,6 @@ The library is still under development. The current major limitations and deviat
- Configuration and load balancing options in SOME/IP SD messages are not supported.
- TTL of Service Discovery entries is not checked yet.
- The Initial Wait Phase and Repetition Phase of the Service Discovery specification are skipped. For simplification, the Main Phase is directly entered, i.e. SD Offer Entries are immediately sent cyclically.
-- Multiple Service Discovery entries are not packed together in a single SD message, which is sent via UDP.
### De-/Serialization
diff --git a/example_apps/call_method_tcp.py b/example_apps/call_method_tcp.py
index 2042b96..df7b925 100644
--- a/example_apps/call_method_tcp.py
+++ b/example_apps/call_method_tcp.py
@@ -3,9 +3,8 @@
import logging
import sys
-from someipy import TransportLayerProtocol
+from someipy import TransportLayerProtocol, ReturnCode, MessageType
from someipy.client_service_instance import (
- MethodResult,
construct_client_service_instance,
)
from someipy.service import ServiceBuilder
@@ -65,31 +64,42 @@ async def main():
try:
while True:
-
method_parameter = Addends(addend1=1, addend2=2)
- # The call method function returns a tuple with the first element being a MethodResult enum
- method_success, method_result = await client_instance_addition.call_method(
- SAMPLE_METHOD_ID, method_parameter.serialize()
- )
- # Check the result of the method call and handle it accordingly
- if method_success == MethodResult.SUCCESS:
- print(
- f"Received result for method: {' '.join(f'0x{b:02x}' for b in method_result)}"
+ try:
+ # You can query if the service offering the method was already found via SOME/IP service discovery
+ print(f"Service found: {client_instance_addition.service_found()}")
+
+ while not client_instance_addition.service_found():
+ print("Waiting for service..")
+ await asyncio.sleep(0.5)
+
+ # The call_method function can raise an error, e.g. if no TCP connection to the server can be established
+ # In case there is an application specific error in the server, the server still returns a response and the
+ # message_type and return_code are evaluated.
+ method_result = await client_instance_addition.call_method(
+ SAMPLE_METHOD_ID, method_parameter.serialize()
)
- try:
- sum = Sum().deserialize(method_result)
- print(f"Sum: {sum.value.value}")
- except Exception as e:
- print(f"Error during deserialization of method's result: {e}")
- elif method_success == MethodResult.ERROR:
- print("Method call failed..")
- elif method_success == MethodResult.TIMEOUT:
- print("Method call timed out..")
- elif method_success == MethodResult.SERVICE_NOT_FOUND:
- print("Service not yet available..")
-
- await asyncio.sleep(2)
+
+ if method_result.message_type == MessageType.RESPONSE:
+ print(
+ f"Received result for method: {' '.join(f'0x{b:02x}' for b in method_result.payload)}"
+ )
+ if method_result.return_code == ReturnCode.E_OK:
+ sum = Sum().deserialize(method_result.payload)
+ print(f"Sum: {sum.value.value}")
+ else:
+ print(
+ f"Method call returned an error: {method_result.return_code}"
+ )
+ elif method_result.message_type == MessageType.ERROR:
+ print("Server returned an error..")
+ # In case the server includes an error message in the payload, it can be deserialized and printed
+
+ except Exception as e:
+ print(f"Error during method call: {e}")
+
+ await asyncio.sleep(1)
# When the application is canceled by the user, the asyncio.CancelledError is raised
except asyncio.CancelledError:
diff --git a/example_apps/call_method_udp.py b/example_apps/call_method_udp.py
index 1cbdc38..7c20d77 100644
--- a/example_apps/call_method_udp.py
+++ b/example_apps/call_method_udp.py
@@ -4,7 +4,7 @@
import logging
import sys
-from someipy import TransportLayerProtocol
+from someipy import TransportLayerProtocol, MessageType, ReturnCode
from someipy.client_service_instance import (
MethodResult,
construct_client_service_instance,
@@ -36,8 +36,6 @@ async def main():
interface_ip = sys.argv[i + 1]
break
- print(interface_ip)
-
# Since the construction of the class ServiceDiscoveryProtocol is not trivial and would require an async __init__ function
# use the construct_service_discovery function
# The local interface IP address needs to be passed so that the src-address of all SD UDP packets is correctly set
@@ -69,26 +67,42 @@ async def main():
try:
while True:
method_parameter = Addends(addend1=1, addend2=2)
- method_success, method_result = await client_instance_addition.call_method(
- SAMPLE_METHOD_ID, method_parameter.serialize()
- )
- if method_success == MethodResult.SUCCESS:
- print(
- f"Received result for method: {' '.join(f'0x{b:02x}' for b in method_result)}"
+
+ try:
+ # You can query if the service offering the method was already found via SOME/IP service discovery
+ print(f"Service found: {client_instance_addition.service_found()}")
+
+ while not client_instance_addition.service_found():
+ print("Waiting for service..")
+ await asyncio.sleep(0.5)
+
+ # The call_method function can raise an error, e.g. if no TCP connection to the server can be established
+ # In case there is an application specific error in the server, the server still returns a response and the
+ # message_type and return_code are evaluated.
+ method_result = await client_instance_addition.call_method(
+ SAMPLE_METHOD_ID, method_parameter.serialize()
)
- try:
- sum = Sum().deserialize(method_result)
- print(f"Sum: {sum.value.value}")
- except Exception as e:
- print(f"Error during deserialization of method's result: {e}")
- elif method_success == MethodResult.ERROR:
- print("Method call failed..")
- elif method_success == MethodResult.TIMEOUT:
- print("Method call timed out..")
- elif method_success == MethodResult.SERVICE_NOT_FOUND:
- print("Service not yet available..")
+
+ if method_result.message_type == MessageType.RESPONSE:
+ print(
+ f"Received result for method: {' '.join(f'0x{b:02x}' for b in method_result.payload)}"
+ )
+ if method_result.return_code == ReturnCode.E_OK:
+ sum = Sum().deserialize(method_result.payload)
+ print(f"Sum: {sum.value.value}")
+ else:
+ print(
+ f"Method call returned an error: {method_result.return_code}"
+ )
+ elif method_result.message_type == MessageType.ERROR:
+ print("Server returned an error..")
+ # In case the server includes an error message in the payload, it can be deserialized and printed
+
+ except Exception as e:
+ print(f"Error during method call: {e}")
await asyncio.sleep(1)
+
except asyncio.CancelledError:
print("Shutdown..")
finally:
diff --git a/example_apps/offer_method_tcp.py b/example_apps/offer_method_tcp.py
index f1d4856..b07acad 100644
--- a/example_apps/offer_method_tcp.py
+++ b/example_apps/offer_method_tcp.py
@@ -4,7 +4,7 @@
import sys
from typing import Tuple
-from someipy import TransportLayerProtocol
+from someipy import TransportLayerProtocol, MethodResult, MessageType, ReturnCode
from someipy.service import ServiceBuilder, Method
from someipy.service_discovery import construct_service_discovery
from someipy.server_service_instance import construct_server_service_instance
@@ -21,29 +21,34 @@
SAMPLE_METHOD_ID = 0x0123
-def add_method_handler(input_data: bytes, addr: Tuple[str, int]) -> Tuple[bool, bytes]:
- # Process the data and return True/False indicating the success of the operation
- # and the result of the method call in serialized form (bytes object)
- # If False is returned an error message will be sent back to the client. In that case
- # the payload can be an empty bytes-object, e.g. return False, b""
-
+def add_method_handler(input_data: bytes, addr: Tuple[str, int]) -> MethodResult:
print(
f"Received data: {' '.join(f'0x{b:02x}' for b in input_data)} from IP: {addr[0]} Port: {addr[1]}"
)
+ result = MethodResult()
+
try:
# Deserialize the input data
addends = Addends()
addends.deserialize(input_data)
except Exception as e:
print(f"Error during deserialization: {e}")
- return False, b""
+
+ # Set the return code to E_MALFORMED_MESSAGE and return
+ result.message_type = MessageType.RESPONSE
+ result.return_code = ReturnCode.E_MALFORMED_MESSAGE
+ return result
# Perform the addition
sum = Sum()
sum.value = Sint32(addends.addend1.value + addends.addend2.value)
print(f"Send back: {' '.join(f'0x{b:02x}' for b in sum.serialize())}")
- return True, sum.serialize()
+
+ result.message_type = MessageType.RESPONSE
+ result.return_code = ReturnCode.E_OK
+ result.payload = sum.serialize()
+ return result
async def main():
diff --git a/example_apps/offer_method_udp.py b/example_apps/offer_method_udp.py
index 728ff3d..e8a0731 100644
--- a/example_apps/offer_method_udp.py
+++ b/example_apps/offer_method_udp.py
@@ -4,7 +4,7 @@
import sys
from typing import Tuple
-from someipy import TransportLayerProtocol
+from someipy import TransportLayerProtocol, MethodResult, ReturnCode, MessageType
from someipy.service import ServiceBuilder, Method
from someipy.service_discovery import construct_service_discovery
from someipy.server_service_instance import construct_server_service_instance
@@ -21,29 +21,34 @@
SAMPLE_METHOD_ID = 0x0123
-def add_method_handler(input_data: bytes, addr: Tuple[str, int]) -> Tuple[bool, bytes]:
- # Process the data and return True/False indicating the success of the operation
- # and the result of the method call in serialized form (bytes object)
- # If False is returned an error message will be sent back to the client. In that case
- # the payload can be an empty bytes-object, e.g. return False, b""
-
+def add_method_handler(input_data: bytes, addr: Tuple[str, int]) -> MethodResult:
print(
f"Received data: {' '.join(f'0x{b:02x}' for b in input_data)} from IP: {addr[0]} Port: {addr[1]}"
)
+ result = MethodResult()
+
try:
# Deserialize the input data
addends = Addends()
addends.deserialize(input_data)
except Exception as e:
print(f"Error during deserialization: {e}")
- return False, b""
+
+ # Set the return code to E_MALFORMED_MESSAGE and return
+ result.message_type = MessageType.RESPONSE
+ result.return_code = ReturnCode.E_MALFORMED_MESSAGE
+ return result
# Perform the addition
sum = Sum()
sum.value = Sint32(addends.addend1.value + addends.addend2.value)
print(f"Send back: {' '.join(f'0x{b:02x}' for b in sum.serialize())}")
- return True, sum.serialize()
+
+ result.message_type = MessageType.RESPONSE
+ result.return_code = ReturnCode.E_OK
+ result.payload = sum.serialize()
+ return result
async def main():
diff --git a/example_apps/receive_events_udp.py b/example_apps/receive_events_udp.py
index 23588e9..27d09ba 100644
--- a/example_apps/receive_events_udp.py
+++ b/example_apps/receive_events_udp.py
@@ -30,7 +30,9 @@ def temperature_callback(someip_message: SomeIpMessage) -> None:
None: This function does not return anything.
"""
try:
- print(f"Received {len(someip_message.payload)} bytes. Try to deserialize..")
+ print(
+ f"Received {len(someip_message.payload)} bytes for event {someip_message.header.method_id}. Try to deserialize.."
+ )
temperature_msg = TemparatureMsg().deserialize(someip_message.payload)
print(temperature_msg)
except Exception as e:
@@ -69,7 +71,6 @@ async def main():
ServiceBuilder()
.with_service_id(SAMPLE_SERVICE_ID)
.with_major_version(1)
- .with_eventgroup(temperature_eventgroup)
.build()
)
diff --git a/setup.cfg b/setup.cfg
index 74c88c9..95b8a18 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = someipy
-version = 0.0.7
+version = 0.0.8
author = Christian H.
author_email = someipy.package@gmail.com
description = A Python package implementing the SOME/IP protocol
diff --git a/src/someipy/__init__.py b/src/someipy/__init__.py
index 8de8f51..624de8a 100644
--- a/src/someipy/__init__.py
+++ b/src/someipy/__init__.py
@@ -11,3 +11,6 @@
) # noqa: F401
from ._internal.someip_message import SomeIpMessage # noqa: F401
+from ._internal.method_result import MethodResult # noqa: F401
+from ._internal.return_codes import ReturnCode # noqa: F401
+from ._internal.message_types import MessageType # noqa: F401
diff --git a/src/someipy/_internal/message_types.py b/src/someipy/_internal/message_types.py
index 4a0c74a..81fce54 100644
--- a/src/someipy/_internal/message_types.py
+++ b/src/someipy/_internal/message_types.py
@@ -1,20 +1,21 @@
# Copyright (C) 2024 Christian H.
-#
+#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
from enum import Enum
+
class MessageType(Enum):
REQUEST = 0x00
REQUEST_NO_RETURN = 0x01
@@ -24,19 +25,5 @@ class MessageType(Enum):
TP_REQUEST = 0x20
TP_REQUEST_NO_RETURN = 0x21
TP_NOTIFICATION = 0x22
- TP_RESPONSE = 0xa0
- TP_ERROR = 0xa1
-
-# PRS_SOMEIP_00191
-class ReturnCode(Enum):
- E_OK = 0x00 # No error occurred
- E_NOT_OK = 0x01 # An unspecified error occurred
- E_UNKNOWN_SERVICE = 0x02 # The requested Service ID is unknown.
- E_UNKNOWN_METHOD = 0x03 # The requested Method ID is unknown.
- E_NOT_READY = 0x04 # Service ID and Method ID are known. Application not running.
- E_NOT_REACHABLE = 0x05 # System running the service is not reachable (inter-nal error code only).
- E_TIMEOUT = 0x06 # A timeout occurred (internal error code only).
- E_WRONG_PROTOCOL_VERSION = 0x07 # Version of SOME/IP protocol not supported
- E_WRONG_INTERFACE_VERSION = 0x08 # Interface version mismatch
- E_MALFORMED_MESSAGE = 0x09 # Deserialization error, so that payload cannot be de-serialized.
- E_WRONG_MESSAGE_TYPE = 0x0a # An unexpected message type was received (e.g. REQUEST_NO_RETURN for a method defined as REQUEST).
+ TP_RESPONSE = 0xA0
+ TP_ERROR = 0xA1
diff --git a/src/someipy/_internal/method_result.py b/src/someipy/_internal/method_result.py
new file mode 100644
index 0000000..e3b643e
--- /dev/null
+++ b/src/someipy/_internal/method_result.py
@@ -0,0 +1,13 @@
+from .message_types import MessageType
+from .return_codes import ReturnCode
+
+
+class MethodResult:
+ message_type: MessageType
+ return_code: ReturnCode
+ payload: bytes
+
+ def __init__(self):
+ self.message_type = MessageType.RESPONSE
+ self.return_code = ReturnCode.E_OK
+ self.payload = b""
diff --git a/src/someipy/_internal/return_codes.py b/src/someipy/_internal/return_codes.py
new file mode 100644
index 0000000..5ac3ef8
--- /dev/null
+++ b/src/someipy/_internal/return_codes.py
@@ -0,0 +1,35 @@
+# Copyright (C) 2024 Christian H.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from enum import Enum
+
+
+# PRS_SOMEIP_00191
+class ReturnCode(Enum):
+ E_OK = 0x00 # No error occurred
+ E_NOT_OK = 0x01 # An unspecified error occurred
+ E_UNKNOWN_SERVICE = 0x02 # The requested Service ID is unknown.
+ E_UNKNOWN_METHOD = 0x03 # The requested Method ID is unknown.
+ E_NOT_READY = 0x04 # Service ID and Method ID are known. Application not running.
+ E_NOT_REACHABLE = (
+ 0x05 # System running the service is not reachable (internal error code only).
+ )
+ E_TIMEOUT = 0x06 # A timeout occurred (internal error code only).
+ E_WRONG_PROTOCOL_VERSION = 0x07 # Version of SOME/IP protocol not supported
+ E_WRONG_INTERFACE_VERSION = 0x08 # Interface version mismatch
+ E_MALFORMED_MESSAGE = (
+ 0x09 # Deserialization error, so that payload cannot be de-serialized.
+ )
+ E_WRONG_MESSAGE_TYPE = 0x0A # An unexpected message type was received (e.g. REQUEST_NO_RETURN for a method defined as REQUEST).
diff --git a/src/someipy/client_service_instance.py b/src/someipy/client_service_instance.py
index c0195cd..b2dce58 100644
--- a/src/someipy/client_service_instance.py
+++ b/src/someipy/client_service_instance.py
@@ -19,6 +19,7 @@
from typing import Iterable, Tuple, Callable, Set, List
from someipy import Service
+from someipy._internal.method_result import MethodResult
from someipy._internal.someip_data_processor import SomeipDataProcessor
from someipy._internal.someip_sd_header import (
SdService,
@@ -40,6 +41,7 @@
)
from someipy._internal.logging import get_logger
from someipy._internal.message_types import MessageType
+from someipy._internal.return_codes import ReturnCode
from someipy._internal.someip_endpoint import (
SomeipEndpoint,
UDPSomeipEndpoint,
@@ -50,13 +52,6 @@
_logger_name = "client_service_instance"
-class MethodResult(Enum):
- SUCCESS = 0
- TIMEOUT = 1
- SERVICE_NOT_FOUND = 2
- ERROR = 3
-
-
class ExpectedAck:
def __init__(self, eventgroup_id: int) -> None:
self.eventgroup_id = eventgroup_id
@@ -139,10 +134,10 @@ def register_callback(self, callback: Callable[[SomeIpMessage], None]) -> None:
"""
self._callback = callback
- async def call_method(
- self, method_id: int, payload: bytes
- ) -> Tuple[MethodResult, bytes]:
- get_logger(_logger_name).debug(f"Try to call method 0x{method_id:04X}")
+ def service_found(self) -> bool:
+ """
+ Returns whether the service instance represented by the ClientServiceInstance has been offered by a server and was found.
+ """
has_service = False
for s in self._found_services:
if (
@@ -151,12 +146,33 @@ async def call_method(
):
has_service = True
break
+ return has_service
+
+ async def call_method(self, method_id: int, payload: bytes) -> MethodResult:
+ """
+ Calls a method on the service instance represented by the ClientServiceInstance.
+
+ Args:
+ method_id (int): The ID of the method to call.
+ payload (bytes): The payload to send with the method call.
+
+ Returns:
+ MethodResult: The result of the method call which can contain an error or a successfull result including the response payload.
+
+ Raises:
+ RuntimeError: If the TCP connection to the server cannot be established or if the server service has not been found yet.
+ asyncio.TimeoutError: If the method call times out, i.e. the server does not send back a response within one second.
+ """
- if not has_service:
- get_logger(_logger_name).warn(
- f"Do not execute method call. Service 0x{self._service.id:04X} with instance 0x{self._instance_id:04X} not found."
+ get_logger(_logger_name).debug(f"Try to call method 0x{method_id:04X}")
+
+ if not self.service_found():
+ get_logger(_logger_name).warning(
+ f"Method 0x{method_id:04x} called, but service 0x{self._service.id:04X} with instance 0x{self._instance_id:04X} not found yet."
+ )
+ raise RuntimeError(
+ f"Method 0x{method_id:04x} called, but service 0x{self._service.id:04X} with instance 0x{self._instance_id:04X} not found yet."
)
- return MethodResult.SERVICE_NOT_FOUND, b""
header = SomeIpHeader(
service_id=self._service.id,
@@ -195,17 +211,21 @@ async def call_method(
await asyncio.wait_for(self._tcp_connection_established_event.wait(), 2)
except asyncio.TimeoutError:
get_logger(_logger_name).error(
- "Could not establish TCP connection for method call."
+ f"Cannot establish TCP connection to {dst_address}:{dst_port}."
+ )
+ raise RuntimeError(
+ f"Cannot establish TCP connection to {dst_address}:{dst_port}."
)
- return MethodResult.ERROR, b""
if self._tcp_connection.is_open():
self._tcp_connection.writer.write(someip_message.serialize())
else:
get_logger(_logger_name).error(
- "TCP connection for method call is not open."
+ f"TCP connection to {dst_address}:{dst_port} is not opened."
+ )
+ raise RuntimeError(
+ f"TCP connection to {dst_address}:{dst_port} is not opened."
)
- return MethodResult.ERROR, b""
else:
# In case of UDP, just send out the datagram and wait for the response
@@ -221,9 +241,9 @@ async def call_method(
get_logger(_logger_name).error(
f"Waiting on response for method call 0x{method_id:04X} timed out."
)
- return MethodResult.TIMEOUT, b""
+ raise
- return MethodResult.SUCCESS, self._method_call_future.result()
+ return self._method_call_future.result()
def someip_message_received(
self, someip_message: SomeIpMessage, addr: Tuple[str, int]
@@ -231,25 +251,22 @@ def someip_message_received(
if (
someip_message.header.client_id == 0x00
and someip_message.header.message_type == MessageType.NOTIFICATION.value
- and someip_message.header.return_code == 0x00
+ and someip_message.header.return_code == ReturnCode.E_OK.value
):
if self._callback is not None and self._subscription_active:
self._callback(someip_message)
-
- if (
- someip_message.header.message_type == MessageType.RESPONSE.value
- and someip_message.header.return_code == 0x00
- ): # E_OK
- if self._method_call_future is not None:
- self._method_call_future.set_result(someip_message.payload)
return
if (
- someip_message.header.message_type == MessageType.ERROR.value
- and someip_message.header.return_code == 0x01
- ): # E_NOT_OK
+ someip_message.header.message_type == MessageType.RESPONSE.value
+ or someip_message.header.message_type == MessageType.ERROR.value
+ ):
if self._method_call_future is not None:
- self._method_call_future.set_result(b"")
+ result = MethodResult()
+ result.message_type = MessageType(someip_message.header.message_type)
+ result.return_code = ReturnCode(someip_message.header.return_code)
+ result.payload = someip_message.payload
+ self._method_call_future.set_result(result)
return
def subscribe_eventgroup(self, eventgroup_id: int):
diff --git a/src/someipy/serialization.py b/src/someipy/serialization.py
index 96b0515..b244699 100644
--- a/src/someipy/serialization.py
+++ b/src/someipy/serialization.py
@@ -1,15 +1,15 @@
# Copyright (C) 2024 Christian H.
-#
+#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
@@ -440,10 +440,10 @@ def serialize(obj) -> bytes:
"""
Serializes an object into bytes by iterating over its attributes, excluding those starting with double underscores or underscores.
For each attribute, it calls the `serialize` method of the attribute and appends the returned bytes to the output.
-
+
Parameters:
obj (object): The object to be serialized.
-
+
Returns:
bytes: The serialized representation of the object as bytes.
"""
@@ -460,7 +460,7 @@ def serialize(obj) -> bytes:
class SomeIpPayload:
"""
- A base class for defining a custom Some/IP payload ("structs"). It can be recursively nested, i.e. a SomeIpPayload object may contain other SomeIpPayload objects.
+ A base class for defining a custom SOME/IP payload ("structs"). It can be recursively nested, i.e. a SomeIpPayload object may contain other SomeIpPayload objects.
"""
def __len__(self) -> int:
@@ -514,11 +514,13 @@ def deserialize(self, payload: bytes):
pos += type_length
return self
+
T = TypeVar("T")
+
class SomeIpFixedSizeArray(Generic[T]):
"""
- A datatype for a SOME/IP fixed size array. This type shall be used with someipy datatypes that support serialization and deserialization.
+ A datatype for a SOME/IP fixed size array. This type shall be used with someipy datatypes that support serialization and deserialization.
"""
data: List[T]
@@ -583,7 +585,7 @@ def serialize(self) -> bytes:
"""
Serialize the object into bytes by iterating over its attributes, excluding those starting with double underscores or underscores.
For each attribute, it calls the `serialize` method of the attribute and appends the returned bytes to the output.
-
+
Returns:
bytes: The serialized representation of the object as bytes.
"""
diff --git a/src/someipy/server_service_instance.py b/src/someipy/server_service_instance.py
index c67fff1..79b3295 100644
--- a/src/someipy/server_service_instance.py
+++ b/src/someipy/server_service_instance.py
@@ -20,7 +20,8 @@
from someipy.service import Service
from someipy._internal.tcp_client_manager import TcpClientManager, TcpClientProtocol
-from someipy._internal.message_types import MessageType, ReturnCode
+from someipy._internal.message_types import MessageType
+from someipy._internal.return_codes import ReturnCode
from someipy._internal.someip_sd_builder import (
build_stop_offer_service_sd_header,
build_subscribe_eventgroup_ack_entry,
@@ -168,7 +169,7 @@ def send_response():
)
if header.service_id != self._service.id:
- get_logger(_logger_name).warn(
+ get_logger(_logger_name).warning(
f"Unknown service ID received from {addr}: ID 0x{header.service_id:04X}"
)
header_to_return.message_type = MessageType.RESPONSE.value
@@ -177,7 +178,7 @@ def send_response():
return
if header.method_id not in self._service.methods.keys():
- get_logger(_logger_name).warn(
+ get_logger(_logger_name).warning(
f"Unknown method ID received from {addr}: ID 0x{header.method_id:04X}"
)
header_to_return.message_type = MessageType.RESPONSE.value
@@ -192,17 +193,15 @@ def send_response():
and header.return_code == 0x00
):
method_handler = self._service.methods[header.method_id].method_handler
- success, payload_result = method_handler(message.payload, addr)
- if not success:
- header_to_return.message_type = MessageType.ERROR.value
- else:
- header_to_return.message_type = MessageType.RESPONSE.value
- payload_to_return = payload_result
+ result = method_handler(message.payload, addr)
+ header_to_return.message_type = result.message_type.value
+ header_to_return.return_code = result.return_code.value
+ payload_to_return = result.payload
send_response()
else:
- get_logger(_logger_name).warn(
+ get_logger(_logger_name).warning(
f"Unknown message type received from {addr}: Type 0x{header.message_type:04X}"
)
diff --git a/src/someipy/service.py b/src/someipy/service.py
index e7bc79f..58d08c8 100644
--- a/src/someipy/service.py
+++ b/src/someipy/service.py
@@ -1,29 +1,35 @@
# Copyright (C) 2024 Christian H.
-#
+#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
from dataclasses import dataclass
from typing import List, Callable, Tuple, Dict
+from someipy._internal.method_result import MethodResult
+
+
+MethodHandler = Callable[[bytes, Tuple[str, int]], MethodResult]
+
@dataclass
class Method:
"""
Class representing a SOME/IP method with a method id and a method handler.
"""
+
id: int
- method_handler: Callable[[bytes, Tuple[str, int]], Tuple[bool, bytes]]
+ method_handler: MethodHandler
def __eq__(self, __value: object) -> bool:
return self.method_id == __value.method_id
@@ -34,6 +40,7 @@ class EventGroup:
"""
Class representing a SOME/IP eventgroup with an eventgroup id and a list of event ids.
"""
+
id: int
event_ids: List[int]
@@ -43,6 +50,7 @@ class Service:
"""
Class representing a SOME/IP service. A service has an id, major and minor version and 0 or more methods and/or eventgroups.
"""
+
id: int
major_version: int
minor_version: int