Skip to content

Commit

Permalink
Minor cleanups, example for replaying ROS
Browse files Browse the repository at this point in the history
  • Loading branch information
chrizog committed Jun 3, 2024
1 parent ec5bca3 commit 5a36bdf
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 16 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ someipy is based on the specification version of R22-11:
- [SOME/IP Protocol Specification](https://www.autosar.org/fileadmin/standards/R22-11/FO/AUTOSAR_PRS_SOMEIPProtocol.pdf)
- [SOME/IP Service Discovery Protocol Specification](https://www.autosar.org/fileadmin/standards/R22-11/FO/AUTOSAR_PRS_SOMEIPServiceDiscoveryProtocol.pdf)

The library is currently developed and tested under Ubuntu 22.04 and Python 3.12.
The library is currently developed and tested under Ubuntu 22.04 and Python 3.8.

## Typical Use Cases

Expand All @@ -35,7 +35,7 @@ someipy excels in scenarios where a full-scale Autosar (Adaptive or Classic) int
The package can be installed from [PyPi](https://pypi.org/project/someipy/).

```bash
pip install someipy
pip3 install someipy
```

## Example Applications
Expand Down
8 changes: 8 additions & 0 deletions example_apps/rosbags/Pose.msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This is the original ROS message for topic "/turtle1/pose" contained in "test.bag"
# This ROS message is just added for demonstration purposes, but not needed for running replay_rosbags.py
float32 x
float32 y
float32 theta

float32 linear_velocity
float32 angular_velocity
17 changes: 17 additions & 0 deletions example_apps/rosbags/TurtlesimPose.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from someipy.serialization import SomeIpPayload, Float32

# For defining the TurtleSimPose as a SOME/IP message, simply inherit from SomeIpPayload and use
# the provided datatypes such as Float32 for declaring the fields of the message
class TurtlesimPose(SomeIpPayload):
x: Float32
y: Float32
theta: Float32
linear_velocity: Float32
angular_velocity: Float32

def __init__(self):
self.x = Float32()
self.y = Float32()
self.theta = Float32()
self.linear_velocity = Float32()
self.angular_velocity = Float32()
114 changes: 114 additions & 0 deletions example_apps/rosbags/replay_rosbag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import asyncio
import ipaddress
import logging
import rosbag
import TurtlesimPose

from someipy import TransportLayerProtocol, ServiceBuilder, EventGroup, construct_server_service_instance
from someipy.service_discovery import construct_service_discovery
from someipy.logging import set_someipy_log_level
from someipy.serialization import Float32

SD_MULTICAST_GROUP = "224.224.224.245"
SD_PORT = 30490
INTERFACE_IP = "127.0.0.1"

SAMPLE_SERVICE_ID = 0x1234
SAMPLE_INSTANCE_ID = 0x5678
SAMPLE_EVENTGROUP_ID = 0x0321
SAMPLE_EVENT_ID = 0x0123


async def main():
# It's possible to configure the logging level of the someipy library, e.g. logging.INFO, logging.DEBUG, logging.WARN, ..
set_someipy_log_level(logging.DEBUG)

# 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
service_discovery = await construct_service_discovery(
SD_MULTICAST_GROUP, SD_PORT, INTERFACE_IP
)

turtle_eventgroup = EventGroup(
id=SAMPLE_EVENTGROUP_ID, event_ids=[SAMPLE_EVENT_ID]
)
turtle_pose_service = (
ServiceBuilder()
.with_service_id(SAMPLE_SERVICE_ID)
.with_major_version(1)
.with_eventgroup(turtle_eventgroup)
.build()
)

# For sending events use a ServerServiceInstance
service_instance_turtle_pose = await construct_server_service_instance(
turtle_pose_service,
instance_id=SAMPLE_INSTANCE_ID,
endpoint=(
ipaddress.IPv4Address(INTERFACE_IP),
3000,
), # src IP and port of the service
ttl=5,
sd_sender=service_discovery,
cyclic_offer_delay_ms=2000,
protocol=TransportLayerProtocol.UDP
)

# The service instance has to be attached always to the ServiceDiscoveryProtocol object, so that the service instance
# is notified by the ServiceDiscoveryProtocol about e.g. subscriptions from other ECUs
service_discovery.attach(service_instance_turtle_pose)

# ..it's also possible to construct another ServerServiceInstance and attach it to service_discovery as well

# After constructing and attaching ServerServiceInstances to the ServiceDiscoveryProtocol object the
# start_offer method has to be called. This will start an internal timer, which will periodically send
# Offer service entries with a period of "cyclic_offer_delay_ms" which has been passed above
print("Start offering service..")
service_instance_turtle_pose.start_offer()

bag = rosbag.Bag('test.bag')

# Get the timestamp of the first message of /turtle1/pose in order to reproduce the timing of the recording
starting_timestamp = next(bag.read_messages(topics=['/turtle1/pose'])).timestamp

for topic, msg, t in bag.read_messages(topics=['/turtle1/pose']):

# Calculate the time difference between the current message and the message before
time_sleep = (t - starting_timestamp).to_sec()

# Use asyncio.sleep to wait for the time difference between the current message and the message before
print(f"Sleeping for {(t - starting_timestamp).to_sec()} seconds")
await asyncio.sleep(time_sleep)

# Create a SomeIpPayload object and fill it with the values from the rosbag message
someipPose = TurtlesimPose.TurtlesimPose()
someipPose.x = Float32(msg.x)
someipPose.y = Float32(msg.y)
someipPose.theta = Float32(msg.theta)
someipPose.linear_velocity = Float32(msg.linear_velocity)
someipPose.angular_velocity = Float32(msg.angular_velocity)

# Serialize the SomeIpPayload object to a byte array
payload = someipPose.serialize()

print(f"Sending event for message {msg}")
# Send the serialized byte array to all subscribers of the event group
service_instance_turtle_pose.send_event(
SAMPLE_EVENTGROUP_ID, SAMPLE_EVENT_ID, payload
)

starting_timestamp = t

bag.close()

await service_instance_turtle_pose.stop_offer()
print("Service Discovery close..")
service_discovery.close()
print("End main task..")

if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
pass
Binary file added example_apps/rosbags/test.bag
Binary file not shown.
6 changes: 3 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = someipy
version = 0.0.1
version = 0.0.2
author = Christian H.
author_email = someipy.package@gmail.com
description = A Python package implementing the SOME/IP protocol
Expand All @@ -13,13 +13,13 @@ project_urls =
classifiers =
Intended Audience :: Developers
Operating System :: POSIX :: Linux
Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.8

[options]
package_dir =
= src
packages = find:
python_requires = >=3.12
python_requires = >=3.8

[options.packages.find]
where = src
3 changes: 0 additions & 3 deletions src/someipy/_internal/someip_data_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@ def _reset(self):
self._expected_bytes = 0

def process_data(self, new_data: bytes) -> bool:

received_length = len(new_data)

print(" ".join(f"{byte:02X}" for byte in new_data))

# UDP case
if self._datagram_mode:
header = SomeIpHeader.from_buffer(new_data)
Expand Down
6 changes: 3 additions & 3 deletions src/someipy/_internal/someip_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import asyncio
from abc import ABC, abstractmethod
from typing import Callable, Tuple, Any
from typing import Callable, Tuple, Any, Union
from someipy._internal.someip_message import SomeIpMessage
from someipy._internal.tcp_client_manager import (
TcpClientManagerInterface,
Expand Down Expand Up @@ -44,10 +44,10 @@ def set_someip_callback(
def connection_made(self, transport: asyncio.DatagramTransport) -> None:
self._transport = transport

def connection_lost(self, exc: Exception | None) -> None:
def connection_lost(self, exc: Exception) -> None:
pass

def datagram_received(self, data: bytes, addr: Tuple[str | Any | int]) -> None:
def datagram_received(self, data: bytes, addr: Tuple[Union[str, Any, int]]) -> None:
result = self._processor.process_data(data)
if result and self._callback is not None:
self._callback(self._processor.someip_message, addr)
Expand Down
19 changes: 18 additions & 1 deletion src/someipy/_internal/someip_sd_builder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
from .someip_sd_header import *
import ipaddress
from typing import Tuple
from someipy._internal.someip_header import SomeIpHeader
from .someip_sd_header import (
SD_BYTE_LENGTH_IP4ENDPOINT_OPTION,
SD_SINGLE_ENTRY_LENGTH_BYTES,
SdService,
SomeIpSdHeader,
SdEntry,
SdEntryType,
SdServiceEntry,
SdOptionCommon,
SD_IPV4ENDPOINT_OPTION_LENGTH_VALUE,
SdOptionType,
SdIPV4EndpointOption,
SdEventGroupEntry,
)
from someipy._internal.someip_sd_header import TransportLayerProtocol


def build_offer_service_sd_header(
Expand Down
3 changes: 2 additions & 1 deletion src/someipy/_internal/someip_sd_extractors.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .someip_sd_header import *
from typing import List, Tuple
from .someip_sd_header import SomeIpSdHeader, SdService, SdEntryType, SdIPV4EndpointOption, SdEventGroupEntry


def extract_offered_services(someip_sd_header: SomeIpSdHeader) -> List[SdService]:
Expand Down
4 changes: 2 additions & 2 deletions src/someipy/_internal/tcp_client_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ def _build_key(self, ip_addr: str, port: int) -> str:
return f"{ip_addr}-{port}"

def register_client(self, client: TcpClientProtocolInterface) -> None:
print(f"Register new client {client.ip_addr}, {client.port}")
#print(f"Register new client {client.ip_addr}, {client.port}")
self._clients[self._build_key(client.ip_addr, client.port)] = client

def unregister_client(self, client: TcpClientProtocolInterface) -> None:
print(f"Unregister client {client.ip_addr}, {client.port}")
#print(f"Unregister client {client.ip_addr}, {client.port}")
if self._build_key(client.ip_addr, client.port) in self._clients.keys():
del self._clients[self._build_key(client.ip_addr, client.port)]

Expand Down
1 change: 0 additions & 1 deletion src/someipy/client_service_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ async def call_method(self, method_id: int, payload: bytes) -> Tuple[MethodResul
def someip_message_received(
self, someip_message: SomeIpMessage, addr: Tuple[str, int]
) -> None:
print(someip_message.header)
if (
someip_message.header.client_id == 0x00
and someip_message.header.message_type == MessageType.NOTIFICATION.value
Expand Down

0 comments on commit 5a36bdf

Please sign in to comment.