# SPDX-FileCopyrightText: 2022-present Artur Drogunow <artur.drogunow@zf.com>
#
# SPDX-License-Identifier: MIT
import contextlib
import logging
import platform
import re
import winreg
from ctypes.util import find_library
from enum import IntEnum
from pathlib import Path
from typing import Any, List, Optional, Tuple, Union
import psutil
LOG = logging.getLogger(__name__)
[docs]
class CANapeVersion(IntEnum):
CANAPE_17 = 17
CANAPE_18 = 18
CANAPE_19 = 19
CANAPE_20 = 20
CANAPE_21 = 21
CANAPE_22 = 22
CANAPE_23 = 23
[docs]
class CANapeError(Exception):
def __init__(self, error_code: int, error_string: str, function: str) -> None:
#: The error code according to :class:`~pycanape.cnp_api.cnp_constants.ErrorCodes`
self.error_code = error_code
super().__init__(f"{function} failed ({error_string})")
# keep reference to args for pickling
self._args = error_code, error_string, function
def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
return CANapeError, self._args, {}
def _kill_canape_processes() -> None:
# search for open CANape processes and kill them
for proc in psutil.process_iter():
try:
proc_name = proc.name()
except psutil.AccessDenied:
pass
else:
if proc_name.lower() in ("canape.exe", "canape64.exe"):
proc.kill()
[docs]
def get_canape_versions() -> List[CANapeVersion]:
"""Return a list of all CANape versions, that can be found in Windows Registry."""
versions: List[CANapeVersion] = []
with contextlib.suppress(FileNotFoundError), winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\VECTOR\\CANape"
) as key:
_sub_key_count, value_count, _last_modified = winreg.QueryInfoKey(key)
for idx in range(value_count):
name, _data, _type = winreg.EnumValue(key, idx)
if not re.match(r"Path\d{3}", name):
continue
try:
version_number = re.sub(pattern=r"Path", repl="", string=name)
versions.append(CANapeVersion(int(version_number[:2])))
except ValueError:
continue
return versions
[docs]
def get_canape_path(version: Optional[CANapeVersion] = None) -> Path:
"""Return the path to the CANape installation from Windows registry.
:param version:
Select the CANape version that shall be found. If ``None``,
it will usually return the version, that was installed last.
:return:
Path to the CANape installation.
"""
name = f"Path{version.value}0" if version else "Path"
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\VECTOR\\CANape") as key:
try:
return Path(winreg.QueryValueEx(key, name)[0])
except FileNotFoundError:
err_msg = "CANape path not found in Windows registry."
raise FileNotFoundError(err_msg) from None
[docs]
def get_canape_data_path(version: Optional[CANapeVersion] = None) -> Path:
"""Return the path to the CANape data folder from Windows registry.
:param version:
Select the CANape version that shall be found. If ``None``,
it will usually return the version, that was installed last.
:return:
Path to the CANape data folder.
"""
name = f"DataPath{version.value}0" if version else "DataPath"
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\VECTOR\\CANape") as key:
try:
return Path(winreg.QueryValueEx(key, name)[0])
except FileNotFoundError:
err_msg = "CANape data path not found in Windows registry."
raise FileNotFoundError(err_msg) from None
[docs]
def get_canape_dll_path(version: Optional[CANapeVersion] = None) -> Path:
"""Return the path to the CANapAPI.dll from Windows registry or PATH.
:param version:
Select the CANape version that shall be found. If ``None``,
it will usually return the version, that was installed last.
:return:
Path to the CANapAPI.dll.
"""
dll_name = "CANapAPI64" if platform.architecture()[0] == "64bit" else "CANapAPI"
# try to find dll via registry entry
with contextlib.suppress(FileNotFoundError):
canape_path = get_canape_path(version)
dll_path = canape_path / "CANapeAPI" / (dll_name + ".dll")
if dll_path.exists():
return dll_path
# try to find dll via PATH environment variable
if dll_path_string := find_library(dll_name):
return Path(dll_path_string)
err_msg = "CANape DLL not found."
raise FileNotFoundError(err_msg)