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

AMC–BLDC: Add the copier for codegen #327

Merged
merged 7 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions emBODY/eBcode/arch-arm/board/amcbldc/utils/directories.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"subdirectories_to_copy": [
{
"source_directory": "_sharedutils",
"source_directory_parent": "ert",
"target_directory": "sharedutils",
"files": [
"*.cpp",
"*.h"
]
},
{
"source_directory": "AMC_BLDC_ert_rtw",
"target_directory": "amc-bldc",
"files": [
"AMC*.cpp",
"AMC*.h"
]
},
{
"source_directory": "can_decoder",
"source_directory_parent": "ert",
"target_directory": "can-decoder",
"files": [
"*.cpp",
"*.h"
]
},
{
"source_directory": "can_encoder",
"source_directory_parent": "ert",
"target_directory": "can-encoder",
"files": [
"*.cpp",
"*.h"
]
},
{
"source_directory": "control_foc",
"source_directory_parent": "ert",
"target_directory": "control-foc",
"files": [
"*.cpp",
"*.h"
]
},
{
"source_directory": "control_outer",
"source_directory_parent": "ert",
"target_directory": "control-outer",
"files": [
"*.cpp",
"*.h"
]
},
{
"source_directory": "estimation_velocity",
"source_directory_parent": "ert",
"target_directory": "estimator",
"files": [
"*.cpp",
"*.h"
]
},
{
"source_directory": "filter_current",
"source_directory_parent": "ert",
"target_directory": "filter-current",
"files": [
"*.cpp",
"*.h"
]
},
{
"source_directory": "SupervisorFSM_RX",
"source_directory_parent": "ert",
"target_directory": "supervisor-rx",
"files": [
"*.cpp",
"*.h"
]
},
{
"source_directory": "SupervisorFSM_TX",
"source_directory_parent": "ert",
"target_directory": "supervisor-tx",
"files": [
"*.cpp",
"*.h"
]
}
]
}
101 changes: 101 additions & 0 deletions emBODY/eBcode/arch-arm/mbd/utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
## How to use the Copier script

To use the Copier script, run the following command:

```bash
Python3 copier.py <source> <destination> <path_to_directories_json>.
```

**Notes:**
- _source_: It's the directory where the generated code by Simulink resides. This should point to the `codegen` directory of the Model repository.

- _destinantion_: It's the directory where to copy the code. This should point to the `src/model-based-design` directory of `icub-firmware` repo of the respective board.

- _path_to_directories_json_: It's the path to the `directories.json` file. This file should be placed, and properly configured, for each board that includes parts of `mbd` code.

- ⚠️ The program is intended to work on **Windows** machines only.

## `directories.json`

The json file is verified before running the script. If something is wrong, you will most likely get an error showing the problem.

The structure of the json goes as follows:


It's the directory where to paste the code. This should point to the `src/model-based-design` directory of the respective `icub-firmware` repo.

You can input the location by pieces in an array. The code will join the strings together.

#### Subdirectories

```json
"subdirectories_to_copy": [
{"..."},
]
```

This holds the information of the subdirectories that will be copied from source to target.

Each entry in the `subdirectories_to_copy` should be another dictionary following this structure:

```json
{
"source_directory": "...",
"source_directory_parent": "...",
"target_directory": "...",
"files": [
"...",
]
}
```

`source_directory`: name of the subdirectory in the source. This subdirectory can be anywhere in the file structure of the source. The copier will search for it through the entire file hierarchy.

`source_directory_parent` [optional]: if there are several subdirectories with the same name, you can specify the parent to select which one to choose.

For example, if inside the source there is a directory called `src/files` and `lib/files`, using `files` as source and `src` in the parent field will select the former of the two.

`target_directory`: name of the directory in which to paste the files. This directory must exist already in the target. If you are copying into a new directory first create the empty target and then run the script.

`files`: selectors for the files. The script will only copy files whose names match the given selectors.

For example, if you only want `.cpp` and `.h` files that begin with `helloworld` you could do the following:

```json
"files": [
"helloworld*.cpp",
"helloworld*.h"
]
```

Any file that doesn't follow this naming will be excluded from being copied.


### Example of `directories.json`

```json
{
"subdirectories_to_copy": [
{
"source_directory": "can_decoder",
"source_directory_parent": "ert",
"target_directory": "can-decoder",
"files": [
"*.cpp",
"*.h"
]
},
{
"source_directory": "can_encoder",
"source_directory_parent": "ert",
"target_directory": "can-encoder",
"files": [
"*.cpp",
"*.h"
]
}
]
}
```

[1]: ../../board/amcbldc/utils/directories.json
109 changes: 109 additions & 0 deletions emBODY/eBcode/arch-arm/mbd/utils/copier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""
Copyright (C) 2022 iCub Tech - Istituto Italiano di Tecnologia
Author: Simone Girardi
email: simone.girardi@iit.it
"""

import os.path
import os
import glob
import shutil
import json
import argparse

SECTION="-------------------"
BIG_SECTION="*******************"

# Checking if the directory is empty or not
def is_empty(dir):
try:
if not os.listdir(dir):
print(dir + " is an empty directory.")
print("Aborting...")
return True
else:
return False
except FileNotFoundError:
print(dir + " directory does not exist.")
print("Aborting...")
return True

def find_subdirectory(main_directory, subdirectory, parent = None):
for root, dirs, files in os.walk(main_directory):
for dir in dirs:
if dir == subdirectory:
dir_parent = root.split('\\')[-1]
if parent is None or dir_parent == parent:
return os.path.join(root, subdirectory)
return ""

def check_dictionary_type(dictionary, key, type):
if not key in dictionary:
raise Exception(f"No {key} found")
if not isinstance(dictionary[key], type):
raise Exception(f"Invalid {key}")

def parse_instructions(dictionary, source_directory, target_directory):
check_dictionary_type(dictionary, "subdirectories_to_copy", list)
subdirectories_to_copy = dictionary["subdirectories_to_copy"]

# loop over the subdirectories
for subdirectory in subdirectories_to_copy:
parent = subdirectory["source_directory_parent"] if "source_directory_parent" in subdirectory else None
source_subdir = find_subdirectory(source_directory, subdirectory["source_directory"], parent)
target_subdir = find_subdirectory(target_directory, subdirectory["target_directory"])

copy_files(source_subdir, target_subdir, subdirectory["files"], True)
print(BIG_SECTION)

def copy_files(source_dir, target_dir, file_selectors, overwrite):
if not (os.path.isdir(source_dir) and os.path.isdir(target_dir)):
raise Exception(f"""Invalid path given (Source: {source_dir}, Target: {target_dir}).
If target is empty, consider creating the empty directory in the desired destination before running the copier.""")
print(BIG_SECTION)
print(f"Copying files from {source_dir} to {target_dir}")
print(SECTION)
files_to_copy = []
for file_selector in file_selectors:
selected_files = glob.glob(os.path.join(source_dir, file_selector))
print(f"Selector {file_selector} matched with the following files: {selected_files}")
files_to_copy = files_to_copy + selected_files
files_to_copy = set(files_to_copy)
print(SECTION)
for source_file in files_to_copy:
file_basename = os.path.basename(source_file)
target_file = os.path.join(target_dir, file_basename)
if overwrite or (not os.path.exists(target_file)):
shutil.copy(source_file, target_file)
print(f"Copying {file_basename} from {source_dir} to {target_dir}")
else:
print(f"Skipping {file_basename} from {source_dir} since overwriting is not allowed")
print("\n")

def main():
parser = argparse.ArgumentParser(prog='The Copier',
description="The copier has the purpose to copy the code generated from Simulink to the proper board folder in icub-firmware ",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('source', help="Absolute path to the codegen directory within icub-firmware-models")
parser.add_argument('destination', help="Absolute path to the model-based-design directory within icub-firmware (for a specific board)")
parser.add_argument('path_to_directories_json', help="Absolute path to the directory containing the directories.json file")

args = parser.parse_args()
config = vars(args)

path_to_json = os.path.join(config['path_to_directories_json'], "directories.json")
path_to_src = config['source']
path_to_dst = config['destination']

# check if source and destination directories exist and are not empty
if not (is_empty(path_to_src) or is_empty(path_to_dst)):

# try to open the json configuration file
with open(path_to_json) as file:
json_instructions = json.load(file)

# start to parse and (eventually) to copy
parse_instructions(json_instructions, path_to_src, path_to_dst)

if __name__ == "__main__":
main()