psr-factory 4.1.0b1__py3-none-win_amd64.whl → 4.1.0b3__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- psr/apps/__init__.py +7 -0
- psr/apps/apps.py +232 -0
- psr/apps/version.py +5 -0
- psr/cloud/__init__.py +7 -0
- psr/cloud/cloud.py +1041 -0
- psr/cloud/data.py +105 -0
- psr/cloud/desktop.py +82 -0
- psr/cloud/log.py +40 -0
- psr/cloud/status.py +81 -0
- psr/cloud/tempfile.py +117 -0
- psr/cloud/version.py +5 -0
- psr/cloud/xml.py +55 -0
- psr/factory/__init__.py +1 -1
- psr/factory/api.py +4 -0
- psr/factory/factory.dll +0 -0
- psr/factory/factory.pmd +54 -7
- psr/factory/factory.pmk +162 -24
- psr/factory/factorylib.py +5 -2
- psr/factory/libcurl-x64.dll +0 -0
- psr/psrfcommon/__init__.py +6 -0
- psr/psrfcommon/psrfcommon.py +54 -0
- psr/psrfcommon/tempfile.py +118 -0
- psr/runner/__init__.py +7 -0
- psr/runner/runner.py +649 -0
- psr/runner/version.py +5 -0
- psr_factory-4.1.0b3.dist-info/METADATA +108 -0
- psr_factory-4.1.0b3.dist-info/RECORD +31 -0
- psr_factory-4.1.0b3.dist-info/licenses/LICENSE.txt +21 -0
- psr_factory-4.1.0b1.dist-info/METADATA +0 -75
- psr_factory-4.1.0b1.dist-info/RECORD +0 -11
- {psr_factory-4.1.0b1.dist-info → psr_factory-4.1.0b3.dist-info}/WHEEL +0 -0
- {psr_factory-4.1.0b1.dist-info → psr_factory-4.1.0b3.dist-info}/top_level.txt +0 -0
psr/cloud/data.py
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# PSR Cloud. Copyright (C) PSR, Inc - All Rights Reserved
|
2
|
+
# Unauthorized copying of this file, via any medium is strictly prohibited
|
3
|
+
# Proprietary and confidential
|
4
|
+
|
5
|
+
import os
|
6
|
+
from datetime import datetime
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import List, Optional, Tuple, Union
|
9
|
+
|
10
|
+
|
11
|
+
class CloudInputError(ValueError):
|
12
|
+
"""Raised when invalid input is provided."""
|
13
|
+
|
14
|
+
pass
|
15
|
+
|
16
|
+
|
17
|
+
class CloudError(RuntimeError):
|
18
|
+
"""Raised when case remote execution fails."""
|
19
|
+
|
20
|
+
pass
|
21
|
+
|
22
|
+
|
23
|
+
class Case:
|
24
|
+
def __init__(
|
25
|
+
self,
|
26
|
+
name: str,
|
27
|
+
data_path: Optional[Union[str, Path]],
|
28
|
+
program: str,
|
29
|
+
program_version: Union[str, int],
|
30
|
+
execution_type: Union[str, int],
|
31
|
+
price_optimized: bool,
|
32
|
+
number_of_processes: int,
|
33
|
+
memory_per_process_ratio: str,
|
34
|
+
**kwargs,
|
35
|
+
) -> None:
|
36
|
+
self.name: str = name
|
37
|
+
self._validate_type(self.name, str, "Case name must be a string")
|
38
|
+
|
39
|
+
self.data_path: str = str(data_path)
|
40
|
+
if data_path and not os.path.isabs(data_path):
|
41
|
+
self.data_path = os.path.abspath(data_path)
|
42
|
+
if data_path and not Path(data_path).exists():
|
43
|
+
raise CloudInputError("Data path does not exist")
|
44
|
+
|
45
|
+
self.program: str = program
|
46
|
+
self._validate_type(self.program, str, "Program must be a string")
|
47
|
+
|
48
|
+
self.program_version: Union[str, int] = program_version
|
49
|
+
self._validate_type(
|
50
|
+
self.program_version,
|
51
|
+
(int, str),
|
52
|
+
"Program version must be an integer or string (id or name)",
|
53
|
+
)
|
54
|
+
|
55
|
+
self.execution_type: Union[str, int] = execution_type
|
56
|
+
self._validate_type(
|
57
|
+
self.execution_type,
|
58
|
+
(int, str),
|
59
|
+
"Execution type must be an integer or string (id or name)",
|
60
|
+
)
|
61
|
+
|
62
|
+
self.price_optimized: bool = price_optimized
|
63
|
+
self._validate_type(
|
64
|
+
self.price_optimized, bool, "price_optimized must be a boolean"
|
65
|
+
)
|
66
|
+
|
67
|
+
self.number_of_processes: int = number_of_processes
|
68
|
+
self._validate_type(
|
69
|
+
self.number_of_processes, int, "Number of processes must be an integer"
|
70
|
+
)
|
71
|
+
|
72
|
+
self.memory_per_process_ratio: str = memory_per_process_ratio
|
73
|
+
self._validate_type(
|
74
|
+
self.memory_per_process_ratio,
|
75
|
+
str,
|
76
|
+
"Memory per process ratio must be a string",
|
77
|
+
)
|
78
|
+
|
79
|
+
self.repository_duration: Optional[Union[str, int]] = kwargs.get("repository_duration", 2)
|
80
|
+
self._validate_type(
|
81
|
+
self.repository_duration,
|
82
|
+
(int, str),
|
83
|
+
"Repository duration must be an integer or string (id or name)",
|
84
|
+
)
|
85
|
+
|
86
|
+
self.id: Optional[int] = kwargs.get("id", None)
|
87
|
+
self.user: Optional[str] = kwargs.get("user", None)
|
88
|
+
self.parent_case_id: Optional[int] = kwargs.get("parent_case_id", None)
|
89
|
+
self.execution_date: Optional[datetime] = kwargs.get("execution_date", None)
|
90
|
+
self.budget: Optional[str] = kwargs.get("budget", None)
|
91
|
+
if self.budget is not None:
|
92
|
+
self.budget = self.budget.strip()
|
93
|
+
|
94
|
+
# Save In Cloud
|
95
|
+
self.upload_only = kwargs.get("upload_only", False)
|
96
|
+
|
97
|
+
@staticmethod
|
98
|
+
def _validate_type(
|
99
|
+
value, expected_type: Union[List[type], Tuple[type], type], error_message: str
|
100
|
+
):
|
101
|
+
if not isinstance(value, expected_type):
|
102
|
+
raise CloudInputError(error_message)
|
103
|
+
|
104
|
+
def __str__(self):
|
105
|
+
return str(self.__dict__)
|
psr/cloud/desktop.py
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# PSR Cloud. Copyright (C) PSR, Inc - All Rights Reserved
|
2
|
+
# Unauthorized copying of this file, via any medium is strictly prohibited
|
3
|
+
# Proprietary and confidential
|
4
|
+
|
5
|
+
import os
|
6
|
+
import xml.etree.ElementTree as ET
|
7
|
+
from datetime import datetime
|
8
|
+
from typing import Optional
|
9
|
+
|
10
|
+
from .data import Case
|
11
|
+
from .xml import create_desktop_xml
|
12
|
+
|
13
|
+
|
14
|
+
def import_case(case: Case, console_cluster: str, instance_type_id: int) -> None:
|
15
|
+
case_counter = _get_last_case_id()
|
16
|
+
filepath = os.path.expandvars(
|
17
|
+
os.path.join(r"%appdata%\PSR\PSRCloud\Dados", f"Caso{case_counter}.xml")
|
18
|
+
)
|
19
|
+
now = datetime.now()
|
20
|
+
case_datetime = now.strftime("%d/%m/%Y %H:%M")
|
21
|
+
case_date = now.strftime("%d/%m/%Y")
|
22
|
+
case_lifetime = ""
|
23
|
+
case_budget = "" if case.budget is None else case.budget
|
24
|
+
repositorio_template = (
|
25
|
+
case.parent_case_id
|
26
|
+
if case.parent_case_id is not None and case.parent_case_id != 0
|
27
|
+
else ""
|
28
|
+
)
|
29
|
+
|
30
|
+
parameters = {
|
31
|
+
"Id": str(case_counter),
|
32
|
+
"IdRepositorio": str(case.id),
|
33
|
+
"IdFila": str(case.id),
|
34
|
+
"Modelo": case.program.upper(),
|
35
|
+
"RepositorioTemplate": repositorio_template,
|
36
|
+
"DirDados": case.data_path,
|
37
|
+
"TipoExecucao": str(case.execution_type),
|
38
|
+
"InstanciaTipo": str(instance_type_id),
|
39
|
+
"Cluster": console_cluster,
|
40
|
+
"ClusterSrv": console_cluster,
|
41
|
+
"DtX": case_datetime,
|
42
|
+
"NProc": str(case.number_of_processes),
|
43
|
+
"Versao": str(case.program_version),
|
44
|
+
"Status": "2",
|
45
|
+
"StatusX": "Executando",
|
46
|
+
"Budget": case_budget,
|
47
|
+
"PodeListarFila": "True",
|
48
|
+
"IdX": case.name,
|
49
|
+
"Nom": case.name,
|
50
|
+
"Tag": f"PyCloud\\{case_date.replace('/', '-')}",
|
51
|
+
"DuracaoRepositorio": case_lifetime,
|
52
|
+
"FlagMaqNormalS": "False",
|
53
|
+
}
|
54
|
+
|
55
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
56
|
+
xml_contents = create_desktop_xml(parameters)
|
57
|
+
f.write(xml_contents)
|
58
|
+
|
59
|
+
|
60
|
+
def _get_last_case_id() -> Optional[int]:
|
61
|
+
config_path = os.path.expandvars(r"%appdata%\PSR\PSRCloud\ePSRConfig.xml")
|
62
|
+
data_path = os.path.expandvars(r"%appdata%\PSR\PSRCloud\Dados")
|
63
|
+
|
64
|
+
if os.path.isfile(config_path):
|
65
|
+
xml = ET.parse(config_path, parser=ET.XMLParser(encoding="utf-16"))
|
66
|
+
root = xml.getroot()
|
67
|
+
last_case_id = None
|
68
|
+
for child in root.iter("Aplicacao"):
|
69
|
+
last_case_id = int(child.get("idUltimoCaso"))
|
70
|
+
break
|
71
|
+
if last_case_id is not None:
|
72
|
+
# Check for existing files with the same
|
73
|
+
# last_case_id. Increment it if necessary.
|
74
|
+
last_case_id = int(last_case_id)
|
75
|
+
files = os.listdir(data_path)
|
76
|
+
while f"Caso{last_case_id}.xml" in files:
|
77
|
+
last_case_id += 1
|
78
|
+
return last_case_id
|
79
|
+
# No case has been run yet
|
80
|
+
return None
|
81
|
+
else:
|
82
|
+
raise Exception("ERROR: PSR Cloud Desktop is not installed.")
|
psr/cloud/log.py
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# PSR Cloud. Copyright (C) PSR, Inc - All Rights Reserved
|
2
|
+
# Unauthorized copying of this file, via any medium is strictly prohibited
|
3
|
+
# Proprietary and confidential
|
4
|
+
|
5
|
+
import logging
|
6
|
+
import os
|
7
|
+
|
8
|
+
|
9
|
+
def get_logger(
|
10
|
+
id: int, quiet: bool, debug_mode: bool, log_dir: str = os.getcwd()
|
11
|
+
) -> logging.Logger:
|
12
|
+
logger = logging.getLogger(str(id))
|
13
|
+
logger.setLevel(logging.DEBUG)
|
14
|
+
formatter = logging.Formatter("%(asctime)s - %(message)s")
|
15
|
+
|
16
|
+
if debug_mode:
|
17
|
+
os.makedirs(log_dir, exist_ok=True)
|
18
|
+
file_handler = logging.FileHandler(f"{log_dir}/psr_cloud_console_{id}.log")
|
19
|
+
file_handler.setLevel(logging.DEBUG)
|
20
|
+
file_handler.setFormatter(formatter)
|
21
|
+
logger.addHandler(file_handler)
|
22
|
+
|
23
|
+
if not quiet:
|
24
|
+
console_handler = logging.StreamHandler()
|
25
|
+
if debug_mode:
|
26
|
+
console_handler.setLevel(logging.DEBUG)
|
27
|
+
else:
|
28
|
+
console_handler.setLevel(logging.INFO)
|
29
|
+
console_handler.setFormatter(formatter)
|
30
|
+
logger.addHandler(console_handler)
|
31
|
+
|
32
|
+
return logger
|
33
|
+
|
34
|
+
|
35
|
+
def enable_log_timestamp(logger: logging.Logger, enable: bool) -> None:
|
36
|
+
for handler in logger.handlers:
|
37
|
+
if enable:
|
38
|
+
handler.setFormatter(logging.Formatter("%(asctime)s - %(message)s"))
|
39
|
+
else:
|
40
|
+
handler.setFormatter(logging.Formatter("%(message)s"))
|
psr/cloud/status.py
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# PSR Cloud. Copyright (C) PSR, Inc - All Rights Reserved
|
2
|
+
# Unauthorized copying of this file, via any medium is strictly prohibited
|
3
|
+
# Proprietary and confidential
|
4
|
+
|
5
|
+
from enum import Enum
|
6
|
+
|
7
|
+
|
8
|
+
class ExecutionStatus(Enum):
|
9
|
+
QUEUE = 0
|
10
|
+
PENDING = 1
|
11
|
+
RUNNING = 2
|
12
|
+
SUCCESS = 3
|
13
|
+
ERROR = 4
|
14
|
+
SYNC_ERROR = 5
|
15
|
+
WAITING_CANCEL = 6
|
16
|
+
CANCELLED = 7
|
17
|
+
NOT_IN_QUEUE = 8
|
18
|
+
CANCELLED_PENDING = 9
|
19
|
+
WAITING_MACHINE = 10
|
20
|
+
WAITING_SPOT = 11
|
21
|
+
SPOT_ACTIVE = 12
|
22
|
+
SPOT_CANCELLED = 13
|
23
|
+
SPOT_CANCELLED_PENDING = 14
|
24
|
+
SERVER_CANCELLED = 15
|
25
|
+
ABORTED = 16
|
26
|
+
WAITING_STOP = 17
|
27
|
+
STOPPED = 18
|
28
|
+
CANCELLED_IDLE = 19
|
29
|
+
WAITING_START = 20
|
30
|
+
WAITING_MACHINE_ON = 21
|
31
|
+
WAITING_ARCHITECTURE = 22
|
32
|
+
WARNING = 23
|
33
|
+
PARENT_CASE_FAILED = 24
|
34
|
+
RESTART_INTERRUPTED = 27
|
35
|
+
SPECIAL_WAITING = 30
|
36
|
+
WAITING_UPLOAD_QUEUE = 31
|
37
|
+
UPLOADED = 32
|
38
|
+
|
39
|
+
|
40
|
+
FAULTY_TERMINATION_STATUS = [
|
41
|
+
ExecutionStatus.ERROR,
|
42
|
+
ExecutionStatus.SYNC_ERROR,
|
43
|
+
ExecutionStatus.CANCELLED,
|
44
|
+
ExecutionStatus.NOT_IN_QUEUE,
|
45
|
+
ExecutionStatus.ABORTED,
|
46
|
+
ExecutionStatus.CANCELLED_IDLE,
|
47
|
+
ExecutionStatus.RESTART_INTERRUPTED,
|
48
|
+
]
|
49
|
+
|
50
|
+
FINISHED_STATUS = FAULTY_TERMINATION_STATUS + [ExecutionStatus.SUCCESS]
|
51
|
+
|
52
|
+
STATUS_MAP_TEXT = {
|
53
|
+
ExecutionStatus.QUEUE: "Item in the queue without machine allocations",
|
54
|
+
ExecutionStatus.PENDING: "Waiting to turn machines on",
|
55
|
+
ExecutionStatus.RUNNING: "Running",
|
56
|
+
ExecutionStatus.SUCCESS: "Finished successfully",
|
57
|
+
ExecutionStatus.ERROR: "Finished with errors",
|
58
|
+
ExecutionStatus.SYNC_ERROR: "Finished due to lack of update request in a synchronous execution",
|
59
|
+
ExecutionStatus.WAITING_CANCEL: "The cancellation was requested, but there is still no response from the queue processor",
|
60
|
+
ExecutionStatus.CANCELLED: "Cancelled process",
|
61
|
+
ExecutionStatus.NOT_IN_QUEUE: "Process is not in the queue",
|
62
|
+
ExecutionStatus.CANCELLED_PENDING: "The cancellation process has already been called, but there is still no response from the Process Handler to complete the cancellation",
|
63
|
+
ExecutionStatus.WAITING_MACHINE: "State where the queue is waiting for the machines to be connected to move to PENDING state",
|
64
|
+
ExecutionStatus.WAITING_SPOT: "Waiting for the spot offer to be made",
|
65
|
+
ExecutionStatus.SPOT_ACTIVE: "The bid is in active mode",
|
66
|
+
ExecutionStatus.SPOT_CANCELLED: "The platform requested cancellation, but there is still no response from the Queue Processor",
|
67
|
+
ExecutionStatus.SPOT_CANCELLED_PENDING: "The cancellation process has already been called by the server, but there is still no response from the Process Handler to complete the cancellation",
|
68
|
+
ExecutionStatus.SERVER_CANCELLED: "Cancelled process by the server",
|
69
|
+
ExecutionStatus.ABORTED: "The model runs more aborts due to lack of input files",
|
70
|
+
ExecutionStatus.WAITING_STOP: "Waiting for STOP by the queue processor",
|
71
|
+
ExecutionStatus.STOPPED: "STOP machines in the round are frozen",
|
72
|
+
ExecutionStatus.CANCELLED_IDLE: "Cancelled after being idle",
|
73
|
+
ExecutionStatus.WAITING_START: "Waiting for START by the queue processor",
|
74
|
+
ExecutionStatus.WAITING_MACHINE_ON: "Waiting for machine to turn on",
|
75
|
+
ExecutionStatus.WAITING_ARCHITECTURE: "Waiting for architecture change",
|
76
|
+
ExecutionStatus.WARNING: "Finished with warnings",
|
77
|
+
ExecutionStatus.PARENT_CASE_FAILED: "Parent case failed",
|
78
|
+
ExecutionStatus.SPECIAL_WAITING: "Special Waiting",
|
79
|
+
ExecutionStatus.WAITING_UPLOAD_QUEUE: "Waiting for uploading case",
|
80
|
+
ExecutionStatus.UPLOADED: "Uploaded and Saved in Cloud",
|
81
|
+
}
|
psr/cloud/tempfile.py
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# PSR Cloud. Copyright (C) PSR, Inc - All Rights Reserved
|
2
|
+
# Unauthorized copying of this file, via any medium is strictly prohibited
|
3
|
+
# Proprietary and confidential
|
4
|
+
|
5
|
+
import errno
|
6
|
+
import io
|
7
|
+
import os
|
8
|
+
from random import Random
|
9
|
+
|
10
|
+
|
11
|
+
class _RandomNameSequence:
|
12
|
+
"""An instance of _RandomNameSequence generates an endless
|
13
|
+
sequence of unpredictable strings which can safely be incorporated
|
14
|
+
into file names. Each string is eight characters long. Multiple
|
15
|
+
threads can safely use the same instance at the same time.
|
16
|
+
|
17
|
+
_RandomNameSequence is an iterator."""
|
18
|
+
|
19
|
+
# Method extracted from tempfile Python's module.
|
20
|
+
|
21
|
+
characters = "abcdefghijklmnopqrstuvwxyz0123456789_"
|
22
|
+
|
23
|
+
@property
|
24
|
+
def rng(self):
|
25
|
+
cur_pid = os.getpid()
|
26
|
+
if cur_pid != getattr(self, "_rng_pid", None):
|
27
|
+
self._rng = Random() # nosec
|
28
|
+
self._rng_pid = cur_pid
|
29
|
+
return self._rng
|
30
|
+
|
31
|
+
def __iter__(self):
|
32
|
+
return self
|
33
|
+
|
34
|
+
def __next__(self):
|
35
|
+
c = self.characters
|
36
|
+
choose = self.rng.choice
|
37
|
+
letters = [choose(c) for dummy in range(8)]
|
38
|
+
return "".join(letters)
|
39
|
+
|
40
|
+
|
41
|
+
def _get_tempfile_name(base_path: str, prefix: str):
|
42
|
+
"""Calculate the default directory to use for temporary files.
|
43
|
+
This routine should be called exactly once.
|
44
|
+
|
45
|
+
We determine whether a candidate temp dir is usable by
|
46
|
+
trying to create and write to a file in that directory. If this
|
47
|
+
is successful, the test file is deleted. To prevent denial of
|
48
|
+
service, the name of the test file must be randomized."""
|
49
|
+
# Method extracted from tempfile Python's module.
|
50
|
+
|
51
|
+
_text_openflags = os.O_RDWR | os.O_CREAT | os.O_EXCL
|
52
|
+
if hasattr(os, "O_NOFOLLOW"):
|
53
|
+
_text_openflags |= os.O_NOFOLLOW
|
54
|
+
|
55
|
+
_bin_openflags = _text_openflags
|
56
|
+
if hasattr(os, "O_BINARY"):
|
57
|
+
_bin_openflags |= os.O_BINARY
|
58
|
+
|
59
|
+
namer = _RandomNameSequence()
|
60
|
+
|
61
|
+
if base_path != os.curdir:
|
62
|
+
base_path = os.path.abspath(base_path)
|
63
|
+
# Try only a few names per directory.
|
64
|
+
for seq in range(100):
|
65
|
+
name = next(namer)
|
66
|
+
filename = os.path.join(base_path, prefix + name)
|
67
|
+
try:
|
68
|
+
fd = os.open(filename, _bin_openflags, 0o600)
|
69
|
+
try:
|
70
|
+
try:
|
71
|
+
with io.open(fd, "wb", closefd=False) as fp:
|
72
|
+
fp.write(b"blat")
|
73
|
+
finally:
|
74
|
+
os.close(fd)
|
75
|
+
finally:
|
76
|
+
os.unlink(filename)
|
77
|
+
return filename
|
78
|
+
except FileExistsError:
|
79
|
+
pass
|
80
|
+
except PermissionError:
|
81
|
+
# This exception is thrown when a directory with the chosen name
|
82
|
+
# already exists on windows.
|
83
|
+
if (
|
84
|
+
os.name == "nt"
|
85
|
+
and os.path.isdir(base_path)
|
86
|
+
and os.access(base_path, os.W_OK)
|
87
|
+
):
|
88
|
+
continue
|
89
|
+
break # no point trying more names in this directory
|
90
|
+
except OSError:
|
91
|
+
break # no point trying more names in this directory
|
92
|
+
raise FileNotFoundError(
|
93
|
+
errno.ENOENT, "No usable temporary file found in " % base_path
|
94
|
+
)
|
95
|
+
|
96
|
+
|
97
|
+
class CreateTempFile:
|
98
|
+
def __init__(
|
99
|
+
self,
|
100
|
+
base_path: str,
|
101
|
+
prefix: str,
|
102
|
+
xml_content: str,
|
103
|
+
delete_temp_xml: bool = True,
|
104
|
+
):
|
105
|
+
self.delete_temp_xml = delete_temp_xml
|
106
|
+
# get temp file name
|
107
|
+
self.xml_file_name = _get_tempfile_name(base_path, prefix) + ".xml"
|
108
|
+
self.xml_content = xml_content
|
109
|
+
|
110
|
+
def __enter__(self):
|
111
|
+
with open(self.xml_file_name, "w", encoding="utf-8-sig") as xml_file:
|
112
|
+
xml_file.write(self.xml_content)
|
113
|
+
return xml_file
|
114
|
+
|
115
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
116
|
+
if self.delete_temp_xml:
|
117
|
+
os.remove(self.xml_file_name)
|
psr/cloud/version.py
ADDED
psr/cloud/xml.py
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# PSR Cloud. Copyright (C) PSR, Inc - All Rights Reserved
|
2
|
+
# Unauthorized copying of this file, via any medium is strictly prohibited
|
3
|
+
# Proprietary and confidential
|
4
|
+
|
5
|
+
from typing import Any, Dict
|
6
|
+
from xml.etree import ElementTree as ET
|
7
|
+
|
8
|
+
|
9
|
+
def create_case_xml(parameters: Dict[str, Any]) -> str:
|
10
|
+
root = ET.Element("ColecaoParametro")
|
11
|
+
for name, value in parameters.items():
|
12
|
+
value = _handle_invalid_xml_chars(value)
|
13
|
+
parameter = ET.SubElement(root, "Parametro", nome=name, tipo="System.String")
|
14
|
+
parameter.text = value
|
15
|
+
ET.indent(root, " ")
|
16
|
+
return ET.tostring(root, encoding="unicode", method="xml")
|
17
|
+
|
18
|
+
|
19
|
+
def create_desktop_xml(parameters: Dict[str, Any]) -> str:
|
20
|
+
# use element tree to write the file contents instead
|
21
|
+
node = ET.Element("Repositorio")
|
22
|
+
case_node = ET.SubElement(node, "CasoOperacao")
|
23
|
+
for key, value in parameters.items():
|
24
|
+
value_escaped = _handle_invalid_xml_chars(value)
|
25
|
+
case_node.set(key, value_escaped)
|
26
|
+
tree = ET.ElementTree(node)
|
27
|
+
return ET.tostring(
|
28
|
+
tree.getroot(), encoding="unicode", method="xml", xml_declaration=False
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
def _return_invalid_xml_chars(xml_content: str) -> str:
|
33
|
+
special_chars = {
|
34
|
+
"&": "&",
|
35
|
+
"<": "<",
|
36
|
+
">": ">",
|
37
|
+
""": '"',
|
38
|
+
"'": "'",
|
39
|
+
}
|
40
|
+
for char, replacement in special_chars.items():
|
41
|
+
xml_content = xml_content.replace(char, replacement)
|
42
|
+
return xml_content
|
43
|
+
|
44
|
+
|
45
|
+
def _handle_invalid_xml_chars(xml_content: str) -> str:
|
46
|
+
special_chars = {
|
47
|
+
"&": "&",
|
48
|
+
"<": "<",
|
49
|
+
">": ">",
|
50
|
+
'"': """,
|
51
|
+
"'": "'",
|
52
|
+
}
|
53
|
+
for char, replacement in special_chars.items():
|
54
|
+
xml_content = str(xml_content).replace(char, replacement)
|
55
|
+
return xml_content
|
psr/factory/__init__.py
CHANGED
psr/factory/api.py
CHANGED
@@ -2174,6 +2174,10 @@ def _initialize():
|
|
2174
2174
|
"INDEX_STARTS_AT_ZERO": True,
|
2175
2175
|
"NAME": "Python",
|
2176
2176
|
"VERSION": f"{sys.version}",
|
2177
|
+
"EXE": f"{sys.executable}",
|
2178
|
+
"LIB": f"{factorylib.get_lib_path()}",
|
2179
|
+
"BASE_PREFIX": f"{sys.base_prefix}",
|
2180
|
+
"REAL_PREFIX": f"{sys.prefix}",
|
2177
2181
|
}
|
2178
2182
|
for prop, value in map_prop_values.items():
|
2179
2183
|
_value = Value()
|
psr/factory/factory.dll
CHANGED
Binary file
|
psr/factory/factory.pmd
CHANGED
@@ -2914,6 +2914,8 @@ DEFINE_MODEL MODL:Optmain_Options
|
|
2914
2914
|
PARM INTEGER RFBF
|
2915
2915
|
PARM INTEGER ASNM
|
2916
2916
|
PARM INTEGER ASDT
|
2917
|
+
PARM INTEGER TUNE
|
2918
|
+
PARM INTEGER NCVR
|
2917
2919
|
END_MODEL
|
2918
2920
|
|
2919
2921
|
//--------------------------------------------------------------------------------------------------
|
@@ -4103,6 +4105,7 @@ DEFINE_MODEL MODL:SDDP_V10.2_Hidro
|
|
4103
4105
|
VETOR DATE Data @addyear_modification
|
4104
4106
|
VETOR INTEGER Existing INDEX Data
|
4105
4107
|
VETOR INTEGER Unidades INDEX Data
|
4108
|
+
VETOR REAL MinGen INDEX Data
|
4106
4109
|
VETOR REAL PotInst INDEX Data
|
4107
4110
|
VETOR REAL FPMed INDEX Data
|
4108
4111
|
VETOR REAL Qmin INDEX Data
|
@@ -4245,6 +4248,10 @@ DEFINE_MODEL MODL:SDDP_V10.2_Hidro
|
|
4245
4248
|
PARM INTEGER SingleReserveInfo
|
4246
4249
|
PARM INTEGER SingleReserveUnit
|
4247
4250
|
|
4251
|
+
PARM REAL MinTurbiningPenalty
|
4252
|
+
VETOR DATE DataMinTurbining @chronological @addyear_chronological
|
4253
|
+
VETOR REAL MinTurbining INDEX DataMinTurbining
|
4254
|
+
|
4248
4255
|
PARM REAL MaxTurbiningPenalty
|
4249
4256
|
VETOR DATE DataMaxTurbining @chronological @addyear_chronological
|
4250
4257
|
VETOR REAL MaxTurbining INDEX DataMaxTurbining
|
@@ -4998,7 +5005,7 @@ DEFINE_MODEL MODL:SDDP_WaterWay
|
|
4998
5005
|
PARM REAL FlowMax
|
4999
5006
|
VETOR DATE DataFlowMaximum
|
5000
5007
|
VETOR REAL FlowMaximum INDEX DataFlowMaximum
|
5001
|
-
PARM
|
5008
|
+
PARM REAL TravelTime
|
5002
5009
|
PARM INTEGER Disabled
|
5003
5010
|
END_MODEL
|
5004
5011
|
|
@@ -5238,6 +5245,38 @@ DEFINE_MODEL MODL:SDDP_Execution_Options
|
|
5238
5245
|
PARM INTEGER HIRS
|
5239
5246
|
PARM INTEGER FINJ
|
5240
5247
|
PARM INTEGER ETYP
|
5248
|
+
PARM INTEGER RFCF
|
5249
|
+
PARM INTEGER PFCF
|
5250
|
+
PARM INTEGER PENL
|
5251
|
+
PARM INTEGER LSFI
|
5252
|
+
PARM INTEGER FIXL
|
5253
|
+
PARM INTEGER HCMT
|
5254
|
+
PARM REAL MXFT
|
5255
|
+
PARM INTEGER HLFR
|
5256
|
+
PARM INTEGER LSUP
|
5257
|
+
PARM INTEGER PVNC
|
5258
|
+
PARM INTEGER HMEM
|
5259
|
+
PARM INTEGER HTPP
|
5260
|
+
PARM INTEGER FSCN
|
5261
|
+
PARM INTEGER SKPS
|
5262
|
+
PARM REAL MMPP
|
5263
|
+
PARM INTEGER FMIP
|
5264
|
+
PARM INTEGER MUPR
|
5265
|
+
PARM INTEGER MXBD
|
5266
|
+
PARM REAL TOLR
|
5267
|
+
PARM INTEGER IGMR
|
5268
|
+
PARM REAL GCTE
|
5269
|
+
PARM INTEGER LCIR
|
5270
|
+
PARM INTEGER VSEC
|
5271
|
+
PARM INTEGER SRPD
|
5272
|
+
PARM INTEGER LSST
|
5273
|
+
PARM REAL LLSF
|
5274
|
+
PARM INTEGER GLLS
|
5275
|
+
PARM INTEGER LSAL
|
5276
|
+
PARM INTEGER PTOU
|
5277
|
+
PARM INTEGER SHOT
|
5278
|
+
PARM INTEGER FCFO
|
5279
|
+
PARM INTEGER HYRM
|
5241
5280
|
|
5242
5281
|
PARM REAL NCPL_MIPR
|
5243
5282
|
PARM REAL NCPL_LTOL
|
@@ -5281,10 +5320,13 @@ END_MODEL
|
|
5281
5320
|
// Generic variable
|
5282
5321
|
//--------------------------------------------------------------------------------------------------
|
5283
5322
|
DEFINE_MODEL PSRGenericVariable
|
5284
|
-
|
5285
|
-
PARM
|
5323
|
+
PARM STRING unit
|
5324
|
+
PARM INTEGER resol
|
5286
5325
|
PARM INTEGER type // 0 continuous, 1 binary
|
5326
|
+
PARM INTEGER UseCostAndBounds //determines if following values are used
|
5287
5327
|
PARM REAL cost
|
5328
|
+
PARM REAL UpperBound
|
5329
|
+
PARM REAL LowerBound
|
5288
5330
|
END_MODEL
|
5289
5331
|
|
5290
5332
|
//--------------------------------------------------------------------------------------------------
|
@@ -5295,6 +5337,7 @@ DEFINE_MODEL PSRHydroUnit
|
|
5295
5337
|
VETOR INTEGER Nunits INDEX Data
|
5296
5338
|
VETOR REAL MinTurb INDEX Data
|
5297
5339
|
VETOR REAL MaxTurb INDEX Data
|
5340
|
+
VETOR REAL MinGen INDEX Data
|
5298
5341
|
VETOR REAL MaxGen INDEX Data
|
5299
5342
|
VETOR INTEGER Existing INDEX Data
|
5300
5343
|
END_MODEL
|
@@ -5329,10 +5372,10 @@ END_MODEL
|
|
5329
5372
|
DEFINE_MODEL MODL:SDDP_Area
|
5330
5373
|
DIMENSION block
|
5331
5374
|
|
5332
|
-
|
5333
|
-
VETOR DATE
|
5334
|
-
VETOR REAL
|
5335
|
-
VETOR REAL
|
5375
|
+
VETOR DATE DateMinTransfer @addyear_modification
|
5376
|
+
VETOR DATE DateMaxTransfer @addyear_modification
|
5377
|
+
VETOR REAL MinTransfer DIM(block) INDEX DateMinTransfer
|
5378
|
+
VETOR REAL MaxTransfer DIM(block) INDEX DateMaxTransfer
|
5336
5379
|
END_MODEL
|
5337
5380
|
|
5338
5381
|
//----------------------------------------------------------------------
|
@@ -5694,6 +5737,7 @@ DEFINE_MODEL MODL:SDDP_FlowController
|
|
5694
5737
|
VETOR REAL Xmax INDEX Data
|
5695
5738
|
VETOR REAL Rn INDEX Data
|
5696
5739
|
VETOR REAL Re INDEX Data
|
5740
|
+
VETOR REAL MaxPower INDEX Data
|
5697
5741
|
|
5698
5742
|
PARM INTEGER Location
|
5699
5743
|
PARM INTEGER FlowControlType
|
@@ -5949,6 +5993,7 @@ DEFINE_CLASS PSRGenerationOfferHydroPlant
|
|
5949
5993
|
VECTOR DATE Date @addyear_modification
|
5950
5994
|
VECTOR REAL Price INDEX Date
|
5951
5995
|
VECTOR REAL Quantity INDEX Date
|
5996
|
+
PARM REFERENCE Plant PSRHydroPlant
|
5952
5997
|
END_CLASS
|
5953
5998
|
|
5954
5999
|
DEFINE_CLASS PSRGenerationOfferThermalPlant
|
@@ -5958,6 +6003,7 @@ DEFINE_CLASS PSRGenerationOfferThermalPlant
|
|
5958
6003
|
VECTOR DATE Date @addyear_modification
|
5959
6004
|
VECTOR REAL Price INDEX Date
|
5960
6005
|
VECTOR REAL Quantity INDEX Date
|
6006
|
+
PARM REFERENCE Plant PSRThermalPlant
|
5961
6007
|
END_CLASS
|
5962
6008
|
|
5963
6009
|
DEFINE_CLASS PSRGenerationOfferRenewablePlant
|
@@ -5967,6 +6013,7 @@ DEFINE_CLASS PSRGenerationOfferRenewablePlant
|
|
5967
6013
|
VECTOR DATE Date @addyear_modification
|
5968
6014
|
VECTOR REAL Price INDEX Date
|
5969
6015
|
VECTOR REAL Quantity INDEX Date
|
6016
|
+
PARM REFERENCE Plant PSRGndPlant
|
5970
6017
|
END_CLASS
|
5971
6018
|
|
5972
6019
|
//----------------------------------------------------------------------
|