v0.1.17
This commit is contained in:
57
robogauge/utils/file_utils.py
Normal file
57
robogauge/utils/file_utils.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : file_utils.py
|
||||
@Time : 2025/12/27 17:14:29
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Common File Utilities
|
||||
'''
|
||||
import tarfile
|
||||
import shutil
|
||||
import os
|
||||
from pathlib import Path
|
||||
import logging
|
||||
|
||||
def log_msg(logger: logging.Logger, msg: str, level: str):
|
||||
if logger:
|
||||
getattr(logger, level)(msg)
|
||||
else:
|
||||
print(msg)
|
||||
|
||||
def compress_directory(source_dir, output_filename=None, delete_original=True, logger: logging.Logger=None):
|
||||
"""
|
||||
Compress a directory into a .tar.xz file using LZMA compression.
|
||||
|
||||
Args:
|
||||
source_dir (str | Path): Path to the directory to compress.
|
||||
output_filename (str | Path, optional): Output path. Defaults to source_dir + ".tar.xz".
|
||||
delete_original (bool): Whether to delete the source directory after successful compression.
|
||||
"""
|
||||
source_dir = Path(source_dir)
|
||||
if not source_dir.exists():
|
||||
log_msg(logger, f"⚠️ Source directory for compression not found: {source_dir}", "warning")
|
||||
return
|
||||
|
||||
if output_filename is None:
|
||||
output_filename = source_dir.with_suffix('.tar.xz')
|
||||
else:
|
||||
output_filename = Path(output_filename)
|
||||
|
||||
log_msg(logger, f"📦 Compressing logs: {source_dir.name} -> {output_filename.name} ...", "info")
|
||||
|
||||
try:
|
||||
with tarfile.open(output_filename, "w:xz") as tar:
|
||||
tar.add(source_dir, arcname=source_dir.name)
|
||||
|
||||
log_msg(logger, f"✅ Compression finished: {output_filename}", "info")
|
||||
|
||||
if delete_original:
|
||||
shutil.rmtree(source_dir)
|
||||
log_msg(logger, f"🗑️ Deleted original directory: {source_dir}", "info")
|
||||
|
||||
except Exception as e:
|
||||
log_msg(logger, f"❌ Failed to compress directory {source_dir}: {e}", "error")
|
||||
# If compression failed, ensure we don't leave a half-baked file
|
||||
if output_filename.exists():
|
||||
os.remove(output_filename)
|
||||
@@ -76,6 +76,7 @@ def parse_args():
|
||||
{"name": "--base-mass", "type": float, "default": 0.0, "help": "Set the base mass of the robot."},
|
||||
{"name": "--friction", "type": float, "default": 1.0, "help": "Set the ground friction coefficient."},
|
||||
{"name": "--level", "type": int, "help": "Set the difficulty level of the environment, range 1-10 (flat is 0)."},
|
||||
{"name": "--goals", "type": str, "nargs": "+", "help": "List of goal names to evaluate."},
|
||||
|
||||
# Multiprocessing parameters, with different seeds
|
||||
{"name": "--multi", "action": "store_true", "default": False, "help": "Enable multiprocessing."},
|
||||
@@ -91,6 +92,8 @@ def parse_args():
|
||||
{"name": "--stress-benchmark", "action": "store_true", "default": False, "help": "Use stress pipeline to benchmark model robustness."},
|
||||
{"name": "--stress-terrain-names", "type": str, "nargs": "+", "default": ["flat", "slope", "wave", "stairs_up", "stairs_down"], "help": "List of terrain names for stress benchmark."},
|
||||
{"name": "--stress-num-processes", "type": int, "default": 2, "help": "Number of parallel processes for stress benchmark."},
|
||||
|
||||
{"name": "--compress-logs", "action": "store_true", "default": False, "help": "Compress and delete logs after run."},
|
||||
]
|
||||
for param in parameters:
|
||||
parser.add_argument(param['name'], **{k: v for k, v in param.items() if k != 'name'})
|
||||
|
||||
@@ -58,7 +58,8 @@ class Logger:
|
||||
experiment_name,
|
||||
run_name,
|
||||
console_output=True, color_output=True,
|
||||
log_level=logging.DEBUG, save_file_mode='a'
|
||||
log_level=logging.DEBUG, save_file_mode='a',
|
||||
parent_log_dir=None
|
||||
):
|
||||
"""
|
||||
Create customed Logger
|
||||
@@ -69,6 +70,7 @@ class Logger:
|
||||
color_output (bool, optional): Whether use color output. Defaults to True.
|
||||
log_level (int, optional): Defaults to logging.DEBUG.
|
||||
save_file_mode (str, optional): The mode of saving to path_log_file
|
||||
parent_log_dir (Path | str, optional): If specified, log_dir will be created under this directory.
|
||||
|
||||
Returns:
|
||||
logging.Logger: logger
|
||||
@@ -102,7 +104,11 @@ class Logger:
|
||||
sh.setFormatter(console_formatter)
|
||||
self.logger.addHandler(sh)
|
||||
|
||||
self.log_dir = Path(ROBOGAUGE_LOGS_DIR) / experiment_name / self.tag
|
||||
if parent_log_dir:
|
||||
baes_dir = Path(parent_log_dir)
|
||||
else:
|
||||
baes_dir = Path(ROBOGAUGE_LOGS_DIR)
|
||||
self.log_dir = baes_dir / experiment_name / self.tag
|
||||
self.log_dir.mkdir(parents=True, exist_ok=True)
|
||||
path_log_file = self.log_dir / "stdout.log"
|
||||
if path_log_file:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'''
|
||||
from robogauge import ROBOGAUGE_ROOT_DIR
|
||||
from robogauge.utils.logger import logger
|
||||
from robogauge.utils.helpers import parse_args, set_seed
|
||||
from robogauge.utils.helpers import parse_args, set_seed, class_to_dict
|
||||
from robogauge.tasks.gauge.gauge_configs.terrain_levels_config import TerrainSearchLevelsConfig
|
||||
|
||||
class TaskRegister():
|
||||
@@ -73,14 +73,23 @@ class TaskRegister():
|
||||
if args.level is not None:
|
||||
gauger_cfg.assets.terrain_level = args.level
|
||||
levels_cfg = TerrainSearchLevelsConfig()
|
||||
cfg = getattr(levels_cfg, gauger_cfg.assets.terrain_name)
|
||||
cfg = getattr(levels_cfg, gauger_cfg.assets.terrain_name, None)
|
||||
assert cfg is not None, f"Level {args.level} configuration not found in TerrainLevelsConfig."
|
||||
assert args.level in cfg.levels, f"Level must be in {cfg.levels}."
|
||||
gauger_cfg.goals.target_pos_velocity.target_pos = cfg.targets[cfg.levels.index(args.level)]
|
||||
if hasattr(cfg, 'targets'):
|
||||
gauger_cfg.goals.target_pos_velocity.target_pos = cfg.targets[cfg.levels.index(args.level)]
|
||||
if hasattr(cfg, 'spawns'):
|
||||
gauger_cfg.assets.terrain_spawn_pos = cfg.spawns[cfg.levels.index(args.level)]
|
||||
xml = gauger_cfg.assets.terrain_xmls[0]
|
||||
xml = xml.rsplit('/', 1)[0] + f"/{gauger_cfg.assets.terrain_name}_{args.level}.xml"
|
||||
gauger_cfg.assets.terrain_xmls[0] = xml
|
||||
if args.goals is not None:
|
||||
keys = class_to_dict(gauger_cfg.goals).keys()
|
||||
enable_count = 0
|
||||
for key in keys:
|
||||
flag = key in args.goals
|
||||
getattr(gauger_cfg.goals, key).enabled = flag
|
||||
enable_count += int(flag)
|
||||
assert enable_count > 0, f"At least one goal must be enabled from '{args.goals}', available goals are {list(keys)}."
|
||||
|
||||
task_register = TaskRegister()
|
||||
|
||||
Reference in New Issue
Block a user