Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added method for returning full plan object #34

Closed
wants to merge 10 commits into from

Conversation

adrianLIrobotics
Copy link

I added another method for returning the whole plan for later parsing in the AC/AS.

Many thanks!

@adrianLIrobotics adrianLIrobotics added the enhancement New feature or request label May 12, 2023
Copy link
Contributor

@Kapim Kapim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gateway_get_full_plan is almost identical to the gateway_get_plan (the only difference is that the latter only returns one ActionPlanID). Can you rewrite the gateway_get_plan method to utilize the gateway_get_full_plan?

@@ -254,6 +254,35 @@ def gateway_get_plan(self, taskid: str, resource_lock: bool, robot_id: str) -> s
except KeyError as e:
raise FailedToConnect(f"Could not get the plan: {e}")


def gateway_get_full_plan(self, taskid: str, resource_lock: bool, robot_id: str) -> str:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method does not return a string but a response (a dict probably?). Moreover, should it return the whole response? I think it would be better if we could create a data class that will describe the plan and return it (similar to MiddlewareInfo in https://github.com/5G-ERA/era-5g-client/blob/main/era_5g_client/dataclasses.py).

@adrianLIrobotics
Copy link
Author

Hello Michal, Bartosz, can you check the dataclasses file and the parser_middleware_plan_info in client.py

The idea is that the response from the middleware will be translated into a dataclass object defined with the middleware response. Please do not merge as i still want to do proper testing :)

Copy link
Member

@Artonus Artonus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adrianLIrobotics, the logic looks good to me, but please correct the changes mentioned in the comments. On top of this, please make sure to address the changes that @Kapim has requested.
Thanks!

# Is currently been used by the robot
enabled: bool

def build_api_endpoint(self, path: str) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adrianLIrobotics is this function needed here? I don't see how the topic needs to build an API endpoint :)

# Onboarded time for network application.
onboardedTime: str

def build_api_endpoint(self, path: str) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as in the previous class

# Services of the action
services : list[MiddlewareServiceInfo]

def build_api_endpoint(self, path: str) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

# List of actions in the task
actionSequence: list[MiddlewareActionInfo]

def build_api_endpoint(self, path: str) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, this function does not need to be duplicated this many times

topicPubData = service["rosTopicsPub"]
topicSubData = service["rosTopicsSub"]
for z in range(0,len(topicPubData)):
rosTopicsPub.append(MiddlewareRosTopicModel(topicPubData["name"],topicPubData["type"],topicPubData["description"],topicPubData["enabled"] ))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can a constructor be created in the dataclass to accept the dictionary? Then in this constructor, we could extract all the necessary data. It would make this section of the code much cleaner and easy to read.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep the constructor as it is but either implement some "from_dict" static method which will instantiate the object from the dictionary or use, e.g., the dacite.from_dict method to create the instance rosTopicPub.append(dacite.from_dict(data_class=MiddlewareRosTopicModel, data=topicPubData)). @ZdenekM, what would be the best way to instantiate the dataclass model here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I definitely agree with @Artonus that we should make this section more cleaner

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kapim If you need from_dict method for dataclasses, takes a look here: https://github.com/s-knibbs/dataclasses-jsonschema/ - pretty cool, I used it in arcor2.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And because properties in a dictionary are named in the camelCase convention and properties in dataclasses will be in snake_case, we will need to convert the dictionary between those with this: https://github.com/nficano/humps.

Copy link
Contributor

@ZdenekM ZdenekM May 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I looked better, and it seems there is a mix of camelCase and PascalCase (ServiceType).

# The name of the task
name: str
# Block semantic planning, use predefined action seq.
ReplanActionPlannerLocked: bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adrianLIrobotics I believe that these names don't follow the correct python name format, and this is why you can't get the CI build passing.
@Kapim can you confirm it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you should follow the PEP 8 style guide, i.e., the variable names should be snake_case formatted (lowercase with words separated by underscores). Only classes should use the PascalCase format.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are more problems :) But most of them can be solved by using autoformatting (./pants fmt ::). After passing this stage of check, there will be type checking (mypy) which will (possibly) reveal another set of problems.

Snímek obrazovky pořízený 2023-05-19 08-40-34

Copy link
Contributor

@Kapim Kapim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a few more comments

def parser_middleware_plan_info(self, response: dict) -> MiddlewarePlanInfo:
actionSequenceData = response['ActionSequence']
action_list = []
for x in range(0,len(actionSequenceData)):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is more "Pythonic" to iterate through iterable like this for action in actionSequenceData:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, action_list = [] should have a type annotation.

action = actionSequenceData[x]
service_list = []
serviceData = action["services"]
for y in range(0,len(serviceData)):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above, please use for service in serviceData:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add type annotation to service_list = [].

rosTopicsSub = []
topicPubData = service["rosTopicsPub"]
topicSubData = service["rosTopicsSub"]
for z in range(0,len(topicPubData)):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. Moreover, is the topicPubData a list or something? Because inside the cycle, you are not iterating through it and only extracting the data from it.

topicPubData = service["rosTopicsPub"]
topicSubData = service["rosTopicsSub"]
for z in range(0,len(topicPubData)):
rosTopicsPub.append(MiddlewareRosTopicModel(topicPubData["name"],topicPubData["type"],topicPubData["description"],topicPubData["enabled"] ))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep the constructor as it is but either implement some "from_dict" static method which will instantiate the object from the dictionary or use, e.g., the dacite.from_dict method to create the instance rosTopicPub.append(dacite.from_dict(data_class=MiddlewareRosTopicModel, data=topicPubData)). @ZdenekM, what would be the best way to instantiate the dataclass model here?

topicPubData = service["rosTopicsPub"]
topicSubData = service["rosTopicsSub"]
for z in range(0,len(topicPubData)):
rosTopicsPub.append(MiddlewareRosTopicModel(topicPubData["name"],topicPubData["type"],topicPubData["description"],topicPubData["enabled"] ))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I definitely agree with @Artonus that we should make this section more cleaner

# The name of the task
name: str
# Block semantic planning, use predefined action seq.
ReplanActionPlannerLocked: bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you should follow the PEP 8 style guide, i.e., the variable names should be snake_case formatted (lowercase with words separated by underscores). Only classes should use the PascalCase format.

@ZdenekM
Copy link
Contributor

ZdenekM commented May 19, 2023

@adrianLIrobotics Before making a commit, please run ./pants fmt :: (it will sort imports and format the code), then ./pants lint :: (it will run flake8 and black tools and report possible problems), and finally ./pants check :: (runs mypy tool, which performs static analysis of the code and types). Thanks!

@ZdenekM
Copy link
Contributor

ZdenekM commented May 19, 2023

@adrianLIrobotics Also, please merge commits into one so we have a clear and comprehensible history. Many thanks!

@Kapim
Copy link
Contributor

Kapim commented Sep 20, 2023

@adrianLIrobotics do you plan to work on this in the near future?

@ZdenekM
Copy link
Contributor

ZdenekM commented Apr 23, 2024

Closing for no activity.

@ZdenekM ZdenekM closed this Apr 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Extra function for gateway_get_plan returning dictionary with all data
4 participants