root folder
This folder contains mainly installation files and the main digital twin program.
digital_twin.bat
File to run the digital twin in MSWindows OS. It is installation specific and is modified by the setup.py script (see below)
@echo off
python "C:\Users\blecha\eVectors\03-Digital_twin\digital_twin.py" %*
digital_twin.py
Main python program.
It loads and the input_file.yaml and call the different modules to run the desired analysis.
mkdocs.yaml
Documentation root file used by mkdocs
requirements.txt
Package dependencies for users
# This is the list of packages required to run the digital twin
# to install all the packages at once: pip install -r requirements.txt
numpy >= 1.24.0
pandas >= 2.0.0
matplotlib
scipy >= 1.10.1
pyyaml
tk
pyproj
pytest
pytest-benchmark
requirements-docs.txt
Package dependencies for developpers willing to update the documentation
# Additional required packages to edit the documentation
# to install, run: pip install -r requirements-docs.txt
mkdocs
mkdocs-material
mkdocs-monorepo-plugin
mkdocstrings
mkdocstrings-python
mkdocs-include-markdown-plugin
setup.py
Installation script.
import os
import sys
import winreg
import importlib.util
import ctypes
from ctypes import wintypes
def get_script_dir():
# Absolute path to the directory where setup.py resides
return os.path.abspath(os.path.dirname(__file__))
def create_bat_file(script_dir):
# Path to main.py
main_py = os.path.join(script_dir, 'digital_twin.py')
main_py = os.path.normpath(main_py)
# Path to .bat file (in the same dir)
bat_path = os.path.join(script_dir, 'digital_twin.bat')
bat_content = f'@echo off\npython "{main_py}" %*\n'
with open(bat_path, 'w') as f:
f.write(bat_content)
print(f".bat file created in: {bat_path}\n")
return bat_path
def get_user_path():
# Read the user's PATH variable from the registry
with winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
r'Environment',
0, winreg.KEY_READ
) as key:
try:
value, _ = winreg.QueryValueEx(key, 'Path')
return value
except FileNotFoundError:
return ""
def set_user_path(new_path):
# Set the user's PATH variable in the registry
with winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
r'Environment',
0, winreg.KEY_SET_VALUE
) as key:
winreg.SetValueEx(key, 'Path', 0, winreg.REG_EXPAND_SZ, new_path)
# Broadcast WM_SETTINGCHANGE to notify the system of the change
HWND_BROADCAST = 0xFFFF
WM_SETTINGCHANGE = 0x001A
SMTO_ABORTIFHUNG = 0x0002
result = ctypes.windll.user32.SendMessageTimeoutW(
HWND_BROADCAST,
WM_SETTINGCHANGE,
0,
"Environment",
SMTO_ABORTIFHUNG,
5000, # 5 second timeout
ctypes.byref(wintypes.DWORD())
)
# Update the current process's environment immediately
os.environ['PATH'] = new_path
return result != 0 # Returns True if broadcast was successful
def add_to_user_path(script_dir):
# Add script_dir to user's PATH if not present
path = get_user_path()
path_list = [os.path.normcase(os.path.normpath(p)) for p in path.split(';') if p]
script_dir_norm = os.path.normcase(os.path.normpath(script_dir))
if script_dir_norm not in path_list:
new_path = path + (';' if path and not path.endswith(';') else '') + script_dir
isbroadcasted = set_user_path(new_path)
print(f"Added '{script_dir}' to the user PATH.\n")
if isbroadcasted == True:
print("You may need to restart your terminal.\n")
else:
print("You need to restart your terminal or log out and log in for changes to take effect.\n")
else:
print(f"'{script_dir}' is already in the user PATH.\n")
def parse_requirements(requirements_path):
requirements = []
if not os.path.isfile(requirements_path):
print("No requirements.txt found.\n")
return requirements
with open(requirements_path, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#'):
# Only take the package name (strip version info for import check)
pkg_name = line.split('==')[0].split('>=')[0].split('<=')[0].strip()
requirements.append((pkg_name, line))
return requirements
def check_installed(requirements):
missing = []
for pkg_name, full_req in requirements:
# Try to import using importlib.util.find_spec
# Some packages have different import names!
# Example: 'pillow' is installed as 'Pillow', import as 'PIL'
# You may wish to expand this mapping if needed.
import_name = pkg_name
if pkg_name.lower() == 'pillow':
import_name = 'PIL'
elif pkg_name.lower() == 'pyyaml':
import_name = 'yaml'
elif pkg_name.lower() == 'opencv-python':
import_name = 'cv2'
elif pkg_name.lower() == 'scikit-learn':
import_name = 'sklearn'
elif pkg_name.lower() == 'beautifulsoup4':
import_name = 'bs4'
elif pkg_name.lower() == 'python-dateutil':
import_name = 'dateutil'
elif pkg_name.lower() == 'pyserial':
import_name = 'serial'
elif pkg_name.lower() == 'pyqt5':
import_name = 'PyQt5'
elif pkg_name.lower() == 'tk':
import_name = 'tkinter'
if importlib.util.find_spec(import_name) is None:
missing.append(full_req)
return missing
def check_requirements(script_dir):
requirements_path = os.path.join(script_dir, 'requirements.txt')
requirements = parse_requirements(requirements_path)
if not requirements:
return
missing = check_installed(requirements)
if missing:
print("\nThe following Python packages are required but not installed:")
for pkg in missing:
print(f" {pkg}")
print("\nPlease install them using:")
print(" python -m pip install -r requirements.txt\n")
else:
print("All required Python packages are installed.\n")
def main():
if os.name != 'nt':
print("This script only works on Windows.")
sys.exit(1)
print("\nThis is the setup script for the eVector digital twin\n")
print("This script creates a .bat file and adds it to the user's path\n")
script_dir = get_script_dir()
print(f"Script directory: {script_dir}\n")
bat_path = create_bat_file(script_dir)
add_to_user_path(script_dir)
check_requirements(script_dir)
print("You can know execute eVECTORS digital twin in a consol\n")
print("Start with the GUI: digital_twin.bat -i yes")
print("Start with file argument: digital_twin.bat -f input_file.yaml")
print("Get help: digital_twin.bat -h")
print("\nDigital Twin setup script finished with grace!\n")
print("Enjoy your day!")
if __name__ == '__main__':
main()