-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #53 from MelbourneHighSchoolRobotics/batched_commands
Batched commands
- Loading branch information
Showing
13 changed files
with
211 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
Batched Commands | ||
================ | ||
|
||
Running a batched command | ||
------------------------- | ||
|
||
In the previous document, we run the simulator and attach script logic in two separate terminals. | ||
|
||
.. code-block:: bash | ||
ev3sim bot.yaml | ||
ev3attach demo.py Robot-0 | ||
When testing robot code, as well as competitions, many of these commands will be the same however, and it would be much easier if the entire simulation, with code running, could be invoked by a single command. | ||
In fact, the simulator allows for this! | ||
|
||
To run the simulator with two bots both running the demo code, execute the following command: | ||
|
||
.. code-block:: bash | ||
ev3sim -b soccer_competition.yaml | ||
This ``-b`` or ``--batch`` flag specifies to use the file ``soccer_competition.yaml`` as the information for the simulator, as well as attaching code to bots. | ||
|
||
Defining batched commands | ||
------------------------- | ||
|
||
You can write your own batched commands, just as you can write your own bot definitions and bot code. You can find the source for ``soccer_competition.yaml`` `here`_. | ||
|
||
.. _here: https://github.com/MelbourneHighSchoolRobotics/ev3sim/tree/main/ev3sim/batched_commands/soccer_competition.yaml | ||
|
||
The batched command file looks like the following: | ||
|
||
.. code-block:: yaml | ||
preset_file: soccer.yaml | ||
bots: | ||
- name: bot.yaml | ||
scripts: | ||
- demo.py | ||
- name: bot.yaml | ||
scripts: | ||
- demo.py | ||
The ``preset_file`` points to the preset to load (usually specified with the ``-p`` flag in ``ev3sim``, but defaults to ``soccer.yaml``). | ||
After this you can specify any bots to load, as well as scripts to attach to them. | ||
|
||
Batched command problems | ||
------------------------ | ||
|
||
If your computer is not powerful enough to run the number of bots specified with scripts attached, the command may just fail or hang. | ||
This method of loading robots is only supplied for ease of use, and has its problems. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
preset_file: soccer.yaml | ||
bots: | ||
- name: bot.yaml | ||
scripts: | ||
- communication_server.py | ||
- name: bot.yaml | ||
scripts: | ||
- communication_client.py | ||
- name: bot.yaml | ||
scripts: | ||
- communication_client.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
preset_file: soccer.yaml | ||
bots: | ||
- name: bot.yaml | ||
scripts: | ||
- demo.py | ||
- name: bot.yaml | ||
scripts: | ||
- demo.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import sys | ||
import yaml | ||
from ev3sim.file_helper import find_abs | ||
from multiprocessing import Process | ||
|
||
def batched_run(batch_file): | ||
from ev3sim.single_run import single_run as sim | ||
from ev3sim.attach import main as attach | ||
|
||
batch_path = find_abs(batch_file, allowed_areas=['local', 'local/batched_commands/', 'package', 'package/batched_commands/']) | ||
with open(batch_path, 'r') as f: | ||
config = yaml.safe_load(f) | ||
|
||
bot_paths = [x['name'] for x in config['bots']] | ||
|
||
sim_process = Process(target=sim, args=[config['preset_file'], bot_paths]) | ||
script_processes = [] | ||
for i, bot in enumerate(config['bots']): | ||
for script in bot.get('scripts', []): | ||
script_processes.append(Process(target=attach, kwargs={'passed_args': ['Useless', script, f"Robot-{i}"]})) | ||
|
||
sim_process.start() | ||
for p in script_processes: | ||
p.start() | ||
|
||
# At the moment, just wait for the simulator to finish then kill all attach processes. | ||
# If any attach threads error out, then the stack trace is printed anyways so this is fine. | ||
sim_process.join() | ||
for p in script_processes: | ||
p.terminate() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,26 @@ | ||
def main(): | ||
|
||
import argparse | ||
import sys | ||
from collections import deque | ||
from queue import Queue | ||
import time | ||
from ev3sim.file_helper import find_abs | ||
|
||
parser = argparse.ArgumentParser(description='Run the simulation, include some robots.') | ||
parser.add_argument('--preset', type=str, help="Path of preset file to load. (You shouldn't need to change this, by default it is presets/soccer.yaml)", default='soccer.yaml', dest='preset') | ||
parser.add_argument('robots', nargs='+', help='Path of robots to load. Separate each robot path by a space.') | ||
|
||
args = parser.parse_args(sys.argv[1:]) | ||
|
||
import yaml | ||
from ev3sim.simulation.loader import runFromConfig | ||
|
||
preset_file = find_abs(args.preset, allowed_areas=['local', 'local/presets/', 'package', 'package/presets/']) | ||
with open(preset_file, 'r') as f: | ||
config = yaml.safe_load(f) | ||
|
||
config['robots'] = config.get('robots', []) + args.robots | ||
|
||
shared_data = { | ||
'tick': 0, # Current tick | ||
'write_stack': deque(), # All write actions are processed through this | ||
'data_queue': {}, # Simulation data for each bot | ||
'active_count': {}, # Keeps track of which code connection each bot has. | ||
'bot_locks': {}, # Threading Locks and Conditions for each bot to wait for connection actions | ||
'bot_communications_data': {}, # Buffers and information for all bot communications | ||
'tick_updates': {}, # Simply a dictionary where the simulation tick will push static data, so the other methods are aware of when the simulation has exited. | ||
} | ||
|
||
result_bucket = Queue(maxsize=1) | ||
|
||
from threading import Thread | ||
from ev3sim.simulation.communication import start_server_with_shared_data | ||
|
||
def run(shared_data, result): | ||
try: | ||
runFromConfig(config, shared_data) | ||
except Exception as e: | ||
result.put(('Simulation', e)) | ||
return | ||
result.put(True) | ||
|
||
comm_thread = Thread(target=start_server_with_shared_data, args=(shared_data, result_bucket), daemon=True) | ||
sim_thread = Thread(target=run, args=(shared_data, result_bucket), daemon=True) | ||
|
||
comm_thread.start() | ||
sim_thread.start() | ||
|
||
try: | ||
with result_bucket.not_empty: | ||
while not result_bucket._qsize(): | ||
result_bucket.not_empty.wait(0.1) | ||
r = result_bucket.get() | ||
if r is not True: | ||
print(f"An error occured in the {r[0]} thread. Raising an error now...") | ||
time.sleep(1) | ||
raise r[1] | ||
except KeyboardInterrupt: | ||
pass | ||
import argparse | ||
import sys | ||
import time | ||
from ev3sim.file_helper import find_abs | ||
|
||
parser = argparse.ArgumentParser(description='Run the simulation, include some robots.') | ||
parser.add_argument('--preset', '-p', type=str, help="Path of preset file to load. (You shouldn't need to change this, by default it is presets/soccer.yaml)", default='soccer.yaml', dest='preset') | ||
parser.add_argument('robots', nargs='+', help='Path of robots to load. Separate each robot path by a space.') | ||
parser.add_argument('--batch', '-b', action='store_true', help='Whether to use a batched command to run this simulation.', dest='batched') | ||
|
||
def main(passed_args = None): | ||
if passed_args is None: | ||
passed_args = sys.argv | ||
|
||
args = parser.parse_args(passed_args[1:]) | ||
|
||
if args.batched: | ||
from ev3sim.batched_run import batched_run | ||
assert len(args.robots) == 1, "Exactly one batched command file should be provided." | ||
batched_run(args.robots[0]) | ||
else: | ||
from ev3sim.single_run import single_run | ||
single_run(args.preset, args.robots) | ||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import argparse | ||
import sys | ||
from collections import deque | ||
from queue import Queue | ||
import time | ||
from ev3sim.file_helper import find_abs | ||
import yaml | ||
from ev3sim.simulation.loader import runFromConfig | ||
|
||
def single_run(preset_filename, robots): | ||
|
||
preset_file = find_abs(preset_filename, allowed_areas=['local', 'local/presets/', 'package', 'package/presets/']) | ||
with open(preset_file, 'r') as f: | ||
config = yaml.safe_load(f) | ||
|
||
config['robots'] = config.get('robots', []) + robots | ||
|
||
shared_data = { | ||
'tick': 0, # Current tick | ||
'write_stack': deque(), # All write actions are processed through this | ||
'data_queue': {}, # Simulation data for each bot | ||
'active_count': {}, # Keeps track of which code connection each bot has. | ||
'bot_locks': {}, # Threading Locks and Conditions for each bot to wait for connection actions | ||
'bot_communications_data': {}, # Buffers and information for all bot communications | ||
'tick_updates': {}, # Simply a dictionary where the simulation tick will push static data, so the other methods are aware of when the simulation has exited. | ||
} | ||
|
||
result_bucket = Queue(maxsize=1) | ||
|
||
from threading import Thread | ||
from ev3sim.simulation.communication import start_server_with_shared_data | ||
|
||
def run(shared_data, result): | ||
try: | ||
runFromConfig(config, shared_data) | ||
except Exception as e: | ||
result.put(('Simulation', e)) | ||
return | ||
result.put(True) | ||
|
||
comm_thread = Thread(target=start_server_with_shared_data, args=(shared_data, result_bucket), daemon=True) | ||
sim_thread = Thread(target=run, args=(shared_data, result_bucket), daemon=True) | ||
|
||
comm_thread.start() | ||
sim_thread.start() | ||
|
||
try: | ||
with result_bucket.not_empty: | ||
while not result_bucket._qsize(): | ||
result_bucket.not_empty.wait(0.1) | ||
r = result_bucket.get() | ||
if r is not True: | ||
print(f"An error occured in the {r[0]} thread. Raising an error now...") | ||
time.sleep(1) | ||
raise r[1] | ||
except KeyboardInterrupt: | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters