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

Remove all instances of MessageToDict #3405

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

AmyKawa
Copy link

@AmyKawa AmyKawa commented Nov 9, 2024

Please fill out the following before requesting review on this PR

Description

  • Removed MessageToDict import, as well as all instances of MessageToDict
  • Replaced variables accessing a dictionary into accessing values from .GetOptions()

Testing Done

n/a

Resolved Issues

#3341

Review Checklist

It is the reviewers responsibility to also make sure every item here has been covered

  • Function & Class comments: All function definitions (usually in the .h file) should have a javadoc style comment at the start of them. For examples, see the functions defined in thunderbots/software/geom. Similarly, all classes should have an associated Javadoc comment explaining the purpose of the class.
  • Remove all commented out code
  • Remove extra print statements: for example, those just used for testing
  • Resolve all TODO's: All TODO (or similar) statements should either be completed or associated with a github issue

@AmyKawa AmyKawa changed the title 1 Remove all instances of MessageToDict Nov 9, 2024
"""
tactic_assignments = play_info_dict["robotTacticAssignment"]
tactic_assignments = play_info.robotTacticAssignment()
Copy link
Contributor

Choose a reason for hiding this comment

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

this doesn't quite work. The correct way access the robot tactic assignment field is: play_info.robot_tactic_assignment().

This will return a MessageMapContainer which has this API: https://googleapis.dev/python/protobuf/3.17.0/google/protobuf/internal/containers.html#google.protobuf.internal.containers.MessageMap

The function call will match the field from the definition of the proto parameters:

map<uint32, Tactic> robot_tactic_assignment = 1;

@@ -38,20 +36,17 @@ def __init__(self, name: str, buffer_size: int = 5) -> None:
def refresh_graphics(self) -> None:
"""Update graphics in this layer"""
self.cached_world = self.world_buffer.get(block=False)
play_info = self.play_info_buffer.get(block=False)
play_info_dict = MessageToDict(play_info)
play_info = self.play_info_buffer.get(block=False).GetOptions()
Copy link
Contributor

Choose a reason for hiding this comment

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

you don't need to call GetOptions() here. I believe you'll only need this if you define specific bounds for the proto definitions (like if you defined this proto:

required double ball_is_kicked_m_per_s_threshold = 1
and you needed to access the "bounds" values). We don't use this pattern in most of other protos, including this one.

playinfo = self.playinfo_buffer.get(block=False, return_cached=False)
playinfo = self.playinfo_buffer.get(
block=False, return_cached=False
).GetOptions()
Copy link
Contributor

Choose a reason for hiding this comment

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

you don't need to call GetOptions here

robot_ids = []
tactic_fsm_states = []
tactic_names = []
play_name = []

if "robotTacticAssignment" not in play_info_dict:
if "robotTacticAssignment" not in playinfo:
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe you can do if not playinfo.robot_tactic_assignment()

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe try if not playinfo.robot_tactic_assignment if that doesn't work. I may be wrong, but I feel like robot_tactic_assignment would return a type of dict which is not callable?

Comment on lines +56 to +72
len(playinfo.robotTacticAssignment()),
len(playinfo.play.playState()),
)

# setting table size dynamically
self.play_table.setRowCount(num_rows)

for state in play_info_dict["play"]["playState"]:
for state in playinfo.play().playstate():
play_name.append(state)

for robot_id in sorted(play_info_dict["robotTacticAssignment"]):
for robot_id in sorted(playinfo.robotTacticAssignment()):
robot_ids.append(robot_id)
tactic_fsm_states.append(
play_info_dict["robotTacticAssignment"][robot_id]["tacticFsmState"]
playinfo.robotTacticAssignment().robot_id().tacticFsmState()
)
tactic_names.append(
play_info_dict["robotTacticAssignment"][robot_id]["tacticName"]
playinfo.robotTacticAssignment().robot_id().tacticName()
Copy link
Contributor

Choose a reason for hiding this comment

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

these function calls should be updated. The field names should match the proto field names.

You should take a look at the proto definition:

message PlayInfo

And take a look at the Python generated code reference guide: https://protobuf.dev/reference/python/python-generated/

Copy link
Contributor

Choose a reason for hiding this comment

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

for your reference, it should be something like this:

for robot_id in sorted(playinfo.robot_tactic_assignment):
          robot_ids.append(robot_id)
          tactic_fsm_states.append(
              playinfo.robot_tactic_assignment[robot_id].tactic_fsm_state
          )

          tactic_names.append(
              playinfo.robot_tactic_assignment[robot_id].tactic_name
          )

playinfo has type is a google protobuf type. playinfo.robot_tactic_assignment returns a dictionary. So to access this we need an id like robot_id. playinfo.robot_tactic_assignment[robot_id] is this type:

message Tactic
{
string tactic_name = 1;
string tactic_fsm_state = 2;
}

To get the tactic_name we do playinfo.robot_tactic_assignment[robot_id].tactic_name

@@ -36,15 +35,13 @@ def __init__(self, buffer_size: int = 1) -> None:

def refresh(self) -> None:
"""Update the referee info widget with new referee information"""
referee = self.referee_buffer.get(block=False, return_cached=False)
referee = self.referee_buffer.get(block=False, return_cached=False).GetOptions()
Copy link
Contributor

Choose a reason for hiding this comment

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

you don't need to call GetOptions here

Comment on lines -46 to 45

if not referee_msg_dict:
if not referee:
return
Copy link
Contributor

Choose a reason for hiding this comment

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

this check is duplicated above, you can delete this now

referee_msg_dict = MessageToDict(referee)

if not referee_msg_dict:
if not referee:
return

stage_time_left_s = (
Copy link
Contributor

Choose a reason for hiding this comment

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

you need to update:
referee_msg_dict["stageTimeLeft"]
referee_msg_dict["packetTimestamp"]

blue.append(referee_msg_dict["blue"][info])
yellow.append(referee_msg_dict["yellow"][info])
blue.append(referee.blue().info())
yellow.append(referee.yellow().info())

set_table_data(
{
Copy link
Contributor

Choose a reason for hiding this comment

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

you also need to update the implementation of parse_yellow_card_times I believe for the team_info object

Copy link
Contributor

@itsarune itsarune left a comment

Choose a reason for hiding this comment

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

Good start. I recommend running ./tbots.py run thunderscope_main --enable_autoref and check the console to see if errors are being spammed. If everything is working properly, you should see the "Play Info" and "Referee Info" widget constantly update.

return

num_rows = max(
len(play_info_dict["robotTacticAssignment"]),
len(play_info_dict["play"]["playState"]),
len(playinfo.robotTacticAssignment()),
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 you meant len(playinfo.robot_tactic_assignment)

len(play_info_dict["robotTacticAssignment"]),
len(play_info_dict["play"]["playState"]),
len(playinfo.robotTacticAssignment()),
len(playinfo.play.playState()),
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 this should be len(playinfo.play.play_state), you should check these two comments.

@@ -54,27 +51,27 @@ def refresh(self) -> None:
f"Packet Timestamp: {round(float(referee_msg_dict['packetTimestamp']) * SECONDS_PER_MICROSECOND, 3)}\n"
+ f"Stage Time Left: {int(stage_time_left_s / SECONDS_PER_MINUTE):02d}"
+ f":{int(stage_time_left_s % SECONDS_PER_MINUTE):02d}\n"
+ f"Stage: {referee_msg_dict['stage']}\n"
+ f"Stage: {referee.stage()}\n"
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you need a (), so just referee.stage

+ "Command: "
+ referee_msg_dict["command"]
+ referee.command()
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

+ "\n"
+ f"Blue Team on Positive Half: {referee_msg_dict['blueTeamOnPositiveHalf']}\n"
+ f"Blue Team on Positive Half: {referee.blueTeamOnPositiveHalf()}\n"
Copy link
Contributor

Choose a reason for hiding this comment

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

here as well

Comment on lines 86 to +97
if info == "yellowCardTimes":
blue.append(self.parse_yellow_card_times(referee_msg_dict["blue"]))
yellow.append(self.parse_yellow_card_times(referee_msg_dict["yellow"]))
blue.append(self.parse_yellow_card_times(referee.blue()))
yellow.append(self.parse_yellow_card_times(referee.yellow()))
elif info == "remainingTimeouts":
blue.append(referee_msg_dict["blue"]["timeouts"])
yellow.append(referee_msg_dict["yellow"]["timeouts"])
blue.append(referee.blue().timeouts())
yellow.append(referee.yellow().timeouts())
elif info == "goalkeeperID":
blue.append(referee_msg_dict["blue"]["goalkeeper"])
yellow.append(referee_msg_dict["yellow"]["goalkeeper"])
blue.append(referee.blue().goalkeepers())
yellow.append(referee.yellow().goalkeeper())
else:
blue.append(referee_msg_dict["blue"][info])
yellow.append(referee_msg_dict["yellow"][info])
blue.append(referee.blue().info())
yellow.append(referee.yellow().info())
Copy link
Contributor

Choose a reason for hiding this comment

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

you actually don't need the () here.

from thefuzz import fuzz
from proto.import_all_protos import *


def __create_int_parameter_writable(key, value, descriptor):
Copy link
Contributor

Choose a reason for hiding this comment

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

it would be nice to add type annotation

def __create_int_parameter_writable(key, value, descriptor): to def __create_int_parameter_writable(key:str, value:int, descriptor:FieldDescriptor):

Also, FieldDescriptor could be found here: from google.protobuf.descriptor import FieldDescriptor

@Mr-Anyone
Copy link
Contributor

looking good. just need some final touches.

@Mr-Anyone
Copy link
Contributor

FYI:

Say we have the following protobuf file person.proto:

message Person{
  required string first_name = 1;
  required string last_name = 2;
}

We use a protobuf compiler, called protoc, to generate the python file. This would then be turned into some python file under some black magic process.

To set the field, we do something like this;

person = Person()
person.first_name =  "Someone" 
person.last_name = "Someone's Lastname" 

To access the field, we do something like this:

print(f"their first name  is {person.first_name}")
print(f"their first name  is {person.last_name}")

Since from the .proto above, we know that .first_name is a string and .last_name is a string. We cannot do something like the following:

person.first_name()

This would imply that person.first_name is callable, which it is not.

see here for the entire code:

https://github.com/Mr-Anyone/Thunderbot_Software/tree/proto_example/src/proto_example

You might have to do:

git remote add vincent [email protected]:Mr-Anyone/Thunderbot_Software.git 
git switch -c proto_example vincent/proto_example
./tbots.py run main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants