diff --git a/diploma_thesis/agents/base/agent.py b/diploma_thesis/agents/base/agent.py index 3a658c2..329b6f3 100644 --- a/diploma_thesis/agents/base/agent.py +++ b/diploma_thesis/agents/base/agent.py @@ -1,14 +1,15 @@ + +import logging from abc import ABCMeta, abstractmethod -from typing import TypeVar, Generic import torch -from agents.utils import Phase, EvaluationPhase +from agents.utils import Phase, EvaluationPhase, Loggable from .encoder import Encoder as StateEncoder, Input, State from .model import Model, Action, Result -class Agent(metaclass=ABCMeta): +class Agent(Loggable, metaclass=ABCMeta): def __init__(self, model: Model[Input, State, Action, Result], @@ -18,6 +19,16 @@ def __init__(self, self.model = model self.memory = memory self.phase = EvaluationPhase() + super().__init__() + + def with_logger(self, logger: logging.Logger): + super().with_logger(logger) + + for module in [self.memory, self.model, self.state_encoder]: + if isinstance(module, Loggable): + module.with_logger(logger) + + return self def update(self, phase: Phase): self.phase = phase diff --git a/diploma_thesis/agents/base/encoder.py b/diploma_thesis/agents/base/encoder.py index 26e48a6..514485d 100644 --- a/diploma_thesis/agents/base/encoder.py +++ b/diploma_thesis/agents/base/encoder.py @@ -1,12 +1,13 @@ from abc import abstractmethod - from typing import TypeVar, Generic +from agents.utils import Loggable + Input = TypeVar('Input') State = TypeVar('State') -class Encoder(Generic[Input, State]): +class Encoder(Loggable, Generic[Input, State]): @abstractmethod def encode(self, parameters: Input) -> State: diff --git a/diploma_thesis/agents/base/model.py b/diploma_thesis/agents/base/model.py index 2a7b7ca..9d679b9 100644 --- a/diploma_thesis/agents/base/model.py +++ b/diploma_thesis/agents/base/model.py @@ -1,6 +1,8 @@ + from abc import ABCMeta, abstractmethod from dataclasses import dataclass from typing import TypeVar, Generic +from agents.utils import Loggable State = TypeVar('State') Input = TypeVar('Input') @@ -8,7 +10,7 @@ Result = TypeVar('Result') -class Model(Generic[Input, State, Action, Result], metaclass=ABCMeta): +class Model(Loggable, Generic[Input, State, Action, Result], metaclass=ABCMeta): @dataclass class Record: diff --git a/diploma_thesis/agents/machine/__init__.py b/diploma_thesis/agents/machine/__init__.py index 77abd52..e798957 100644 --- a/diploma_thesis/agents/machine/__init__.py +++ b/diploma_thesis/agents/machine/__init__.py @@ -1,3 +1,4 @@ +import logging from .utils import Input as MachineInput from .machine import Machine diff --git a/diploma_thesis/agents/machine/machine.py b/diploma_thesis/agents/machine/machine.py index 0ec4298..392c5c0 100644 --- a/diploma_thesis/agents/machine/machine.py +++ b/diploma_thesis/agents/machine/machine.py @@ -1,9 +1,7 @@ - from abc import ABCMeta from agents.base.agent import Agent class Machine(Agent, metaclass=ABCMeta): - pass diff --git a/diploma_thesis/agents/machine/model/__init__.py b/diploma_thesis/agents/machine/model/__init__.py index dac8934..07a0274 100644 --- a/diploma_thesis/agents/machine/model/__init__.py +++ b/diploma_thesis/agents/machine/model/__init__.py @@ -1,3 +1,4 @@ +import logging from .model import MachineModel from .static import StaticModel as StaticMachineModel diff --git a/diploma_thesis/agents/machine/model/static.py b/diploma_thesis/agents/machine/model/static.py index e1c336d..3213387 100644 --- a/diploma_thesis/agents/machine/model/static.py +++ b/diploma_thesis/agents/machine/model/static.py @@ -1,3 +1,4 @@ +import logging from .model import MachineModel from agents.machine.state import PlainEncoder @@ -11,6 +12,7 @@ class StaticModel(MachineModel[PlainEncoder.State, None]): def __init__(self, rule: SchedulingRule): self.rule = rule + super().__init__() def __call__(self, state: State, parameters: MachineModel.Input) -> MachineModel.Record: return MachineModel.Record( diff --git a/diploma_thesis/agents/machine/state/__init__.py b/diploma_thesis/agents/machine/state/__init__.py index 6d888b1..b5ac61d 100644 --- a/diploma_thesis/agents/machine/state/__init__.py +++ b/diploma_thesis/agents/machine/state/__init__.py @@ -1,14 +1,20 @@ +import logging + +from typing import Dict from .encoder import StateEncoder from .plain import PlainEncoder - +from .deep_marl_direct import DEEPMARLDirectStateEncoder +from .deep_marl_indirect import DEEPMARLIndirectStateEncoder key_to_class = { - "plain": PlainEncoder + "plain": PlainEncoder, + "deep_marl_direct": DEEPMARLDirectStateEncoder, + "deep_marl_indirect": DEEPMARLIndirectStateEncoder } -def from_cli(parameters) -> StateEncoder: +def from_cli(parameters: Dict) -> StateEncoder: cls = key_to_class[parameters['kind']] return cls.from_cli(parameters.get('parameters', {})) diff --git a/diploma_thesis/agents/machine/state/deep_marl_direct.py b/diploma_thesis/agents/machine/state/deep_marl_direct.py new file mode 100644 index 0000000..90a7115 --- /dev/null +++ b/diploma_thesis/agents/machine/state/deep_marl_direct.py @@ -0,0 +1,131 @@ +import logging +from dataclasses import dataclass +from typing import Dict + +import torch + +from environment import JobReductionStrategy, Job, Machine +from .encoder import StateEncoder + + +class DEEPMARLDirectStateEncoder(StateEncoder): + """ + Encoded state is a tensor of dimension (5, 5) where: + 1. First 4 rows contain the following information: + 1. Current operation processing time on machine + 2. Next remaining processing time + 3. Slack upon moment + 4. Average waiting in next queue + 5. Work center index + Depending on the number of jobs in queue, the state is represented in the following way: + 1. If there are 0 jobs, then the state is a tensor of zeros + 2. If there is 1 job, then the state is a tensor of shape (4, 5) where the first row repeated + 3. If there are more than 1 job, then the information of job minimum values of first 4 criterias are stored + 2. Arriving job info represents information of the job that is about to arrive at the machine + """ + + @dataclass + class State: + state: torch.FloatTensor + job_idx: torch.LongTensor + + def __init__(self, strategy: JobReductionStrategy = JobReductionStrategy.mean): + super().__init__() + + self.reduction_strategy = strategy + + def encode(self, parameters: StateEncoder.Input) -> State: + state, job_idx = self.__make_initial_state(parameters) + arriving_job_state, _ = self.__make_arriving_job_state__(parameters.machine, parameters.now) + + state = torch.vstack([state, arriving_job_state]) + + return self.State(state, job_idx) + + def __make_initial_state(self, parameters: StateEncoder.Input) -> torch.FloatTensor: + machine = parameters.machine + queue_size = machine.queue_size + state = None + + match queue_size: + case 0: + return torch.zeros((4, 5)), torch.zeros(4) + case 1: + job = machine.queue[0] + state = self.__make_machine_state_for_single_job__(job, machine, parameters.now) + state = state.repeat(4, 1) + case _: + candidates = torch.vstack([ + self.__make_machine_state_for_single_job__(job, machine, parameters.now) + for job in machine.queue + ]) + pool = candidates.clone() + + state = [] + + for i in range(4): + is_no_candidates = pool.numel() == 0 + store = candidates if is_no_candidates else pool + + idx = torch.argmin(store[:, i]) + + state += [store[idx]] + + if not is_no_candidates: + pool = pool[torch.arange(pool.size(0)) != idx] + + state = torch.vstack(state) + + return state[:, 1:], state[:, 0] + + def __make_arriving_job_state__(self, machine: Machine, now: float) -> torch.FloatTensor: + arriving_jobs = [ + job for job in machine.shop_floor.in_system_jobs + if job.next_work_center_idx == machine.work_center_idx and job.release_moment_on_machine is not None + ] + + average_waiting_time = machine.work_center.average_waiting_time + + if len(arriving_jobs) == 0: + state = torch.FloatTensor([0, 0, 0, average_waiting_time, 0]) + + return state, None + + job = min(arriving_jobs, key=lambda job: job.release_moment_on_machine) + + wait_time = job.release_moment_on_machine - now + + if wait_time < -1: + self.log_error(f"Arriving job release moment is in the past: {wait_time}") + + wait_time = max(wait_time, 0) + + state = [ + job.next_operation_processing_time(self.reduction_strategy), + job.next_remaining_processing_time(self.reduction_strategy), + job.slack_upon_moment(now, self.reduction_strategy), + average_waiting_time, + wait_time + ] + + state = self.__to_list_of_tensors__(state) + + return torch.hstack(state), job.id + + def __make_machine_state_for_single_job__(self, job: Job, machine: Machine, now) -> torch.FloatTensor: + state = [ + job.id, + job.current_operation_processing_time_on_machine, + job.next_remaining_processing_time(self.reduction_strategy), + job.slack_upon_moment(now, self.reduction_strategy), + machine.shop_floor.average_waiting_in_next_queue(job), + machine.work_center_idx + ] + + state = self.__to_list_of_tensors__(state) + + return torch.hstack(state) + + @staticmethod + def from_cli(parameters: Dict): + return DEEPMARLDirectStateEncoder() diff --git a/diploma_thesis/agents/machine/state/deep_marl_indirect.py b/diploma_thesis/agents/machine/state/deep_marl_indirect.py new file mode 100644 index 0000000..4b119b7 --- /dev/null +++ b/diploma_thesis/agents/machine/state/deep_marl_indirect.py @@ -0,0 +1,18 @@ +from dataclasses import dataclass +from typing import Dict + +from .encoder import StateEncoder + + +class DEEPMARLIndirectStateEncoder(StateEncoder): + + @dataclass + class State: + pass + + def encode(self, parameters: StateEncoder.Input) -> State: + return self.State() + + @staticmethod + def from_cli(parameters: Dict): + return DEEPMARLIndirectStateEncoder() diff --git a/diploma_thesis/agents/machine/state/encoder.py b/diploma_thesis/agents/machine/state/encoder.py index 1215ec4..5e64800 100644 --- a/diploma_thesis/agents/machine/state/encoder.py +++ b/diploma_thesis/agents/machine/state/encoder.py @@ -1,7 +1,10 @@ - +import logging from abc import ABCMeta +from typing import List, TypeVar + +import torch + from agents.base import Encoder -from typing import TypeVar from agents.machine import MachineInput State = TypeVar('State') @@ -10,3 +13,7 @@ class StateEncoder(Encoder[MachineInput, State], metaclass=ABCMeta): Input = MachineInput + + @staticmethod + def __to_list_of_tensors__(parameters: List) -> List[torch.FloatTensor]: + return [parameter if torch.is_tensor(parameter) else torch.tensor(parameter) for parameter in parameters] diff --git a/diploma_thesis/agents/machine/state/plain.py b/diploma_thesis/agents/machine/state/plain.py index 5d665e4..7f5dc72 100644 --- a/diploma_thesis/agents/machine/state/plain.py +++ b/diploma_thesis/agents/machine/state/plain.py @@ -1,8 +1,8 @@ - -from .encoder import StateEncoder from dataclasses import dataclass from typing import Dict +from .encoder import StateEncoder + class PlainEncoder(StateEncoder): diff --git a/diploma_thesis/agents/machine/static.py b/diploma_thesis/agents/machine/static.py index b45cd3b..3911fee 100644 --- a/diploma_thesis/agents/machine/static.py +++ b/diploma_thesis/agents/machine/static.py @@ -1,9 +1,8 @@ - +from typing import Dict from .machine import Machine from .model import MachineModel, from_cli as model_from_cli from .state import StateEncoder, from_cli as state_encoder_from_cli -from typing import Dict class StaticMachine(Machine): @@ -23,4 +22,4 @@ def from_cli(parameters: Dict): model = model_from_cli(parameters['model']) encoder = state_encoder_from_cli(parameters['encoder']) - return StaticMachine(model=model, state_encoder=encoder) \ No newline at end of file + return StaticMachine(model=model, state_encoder=encoder) diff --git a/diploma_thesis/agents/utils/__init__.py b/diploma_thesis/agents/utils/__init__.py index 0a7962e..9c933a3 100644 --- a/diploma_thesis/agents/utils/__init__.py +++ b/diploma_thesis/agents/utils/__init__.py @@ -1,2 +1,3 @@ -from .phase import WarmUpPhase, TrainingPhase, EvaluationPhase, Phase \ No newline at end of file +from .phase import WarmUpPhase, TrainingPhase, EvaluationPhase, Phase +from .loggable import Loggable \ No newline at end of file diff --git a/diploma_thesis/agents/utils/loggable.py b/diploma_thesis/agents/utils/loggable.py new file mode 100644 index 0000000..301bfc7 --- /dev/null +++ b/diploma_thesis/agents/utils/loggable.py @@ -0,0 +1,31 @@ + +import logging + + +class Loggable: + + def __init__(self): + self.logger = None + + def with_logger(self, logger: logging.Logger): + self.logger = logger.getChild(self.__class__.__name__) + + return self + + def log(self, message, level: str = 'info'): + if hasattr(self, 'logger'): + self.logger.log(level, message) + else: + print(message) + + def log_debug(self, message): + self.log(message, logging.DEBUG) + + def log_info(self, message): + self.log(message, logging.INFO) + + def log_warning(self, message): + self.log(message, logging.WARNING) + + def log_error(self, message): + self.log(message, logging.ERROR) diff --git a/diploma_thesis/agents/workcenter/__init__.py b/diploma_thesis/agents/workcenter/__init__.py index b2277ec..0570706 100644 --- a/diploma_thesis/agents/workcenter/__init__.py +++ b/diploma_thesis/agents/workcenter/__init__.py @@ -1,3 +1,4 @@ +import logging from .utils import Input as WorkCenterInput from .work_center import WorkCenter diff --git a/diploma_thesis/agents/workcenter/model/__init__.py b/diploma_thesis/agents/workcenter/model/__init__.py index 26beb10..d67b9e4 100644 --- a/diploma_thesis/agents/workcenter/model/__init__.py +++ b/diploma_thesis/agents/workcenter/model/__init__.py @@ -1,3 +1,5 @@ +import logging + from .model import WorkCenterModel from .static import StaticModel as StaticWorkCenterModel from .rule import RoutingRule diff --git a/diploma_thesis/agents/workcenter/model/static.py b/diploma_thesis/agents/workcenter/model/static.py index 7a4640d..85289a8 100644 --- a/diploma_thesis/agents/workcenter/model/static.py +++ b/diploma_thesis/agents/workcenter/model/static.py @@ -1,8 +1,8 @@ +from typing import Dict -from .model import WorkCenterModel from agents.machine.state import PlainEncoder +from .model import WorkCenterModel from .rule import RoutingRule, ALL_ROUTING_RULES -from typing import Dict class StaticModel(WorkCenterModel[PlainEncoder.State, None]): @@ -11,6 +11,7 @@ class StaticModel(WorkCenterModel[PlainEncoder.State, None]): def __init__(self, rule: RoutingRule): self.rule = rule + super().__init__() def __call__(self, state: State, parameters: WorkCenterModel.Input) -> WorkCenterModel.Record: return WorkCenterModel.Record( diff --git a/diploma_thesis/agents/workcenter/state/__init__.py b/diploma_thesis/agents/workcenter/state/__init__.py index 59f8077..c74f2e2 100644 --- a/diploma_thesis/agents/workcenter/state/__init__.py +++ b/diploma_thesis/agents/workcenter/state/__init__.py @@ -1,3 +1,5 @@ +import logging + from .encoder import StateEncoder from .plain import PlainEncoder diff --git a/diploma_thesis/agents/workcenter/state/plain.py b/diploma_thesis/agents/workcenter/state/plain.py index 5d665e4..9bec4e1 100644 --- a/diploma_thesis/agents/workcenter/state/plain.py +++ b/diploma_thesis/agents/workcenter/state/plain.py @@ -1,3 +1,4 @@ +import logging from .encoder import StateEncoder from dataclasses import dataclass diff --git a/diploma_thesis/agents/workcenter/static.py b/diploma_thesis/agents/workcenter/static.py index fe0c25e..6dc8ef2 100644 --- a/diploma_thesis/agents/workcenter/static.py +++ b/diploma_thesis/agents/workcenter/static.py @@ -1,4 +1,4 @@ - +import logging from .work_center import WorkCenter from .model import StaticWorkCenterModel, from_cli as model_from_cli diff --git a/diploma_thesis/configuration/simulation.yml b/diploma_thesis/configuration/simulation.yml index af291c8..652759a 100644 --- a/diploma_thesis/configuration/simulation.yml +++ b/diploma_thesis/configuration/simulation.yml @@ -29,7 +29,7 @@ simulation: &simulation sampler: kind: 'uniform' parameters: - uniform: [ 1, 5 ] + uniform: [ 1, 100 ] breakdown: kind: 'dynamic' @@ -56,7 +56,7 @@ task: parameters: rule: 'random' encoder: - kind: 'plain' + kind: 'deep_marl_direct' work_center_agent: kind: 'static' diff --git a/diploma_thesis/environment/job.py b/diploma_thesis/environment/job.py index c727686..9da8ff3 100644 --- a/diploma_thesis/environment/job.py +++ b/diploma_thesis/environment/job.py @@ -76,11 +76,11 @@ class History: arrived_machine_idx: torch.LongTensor = field(default_factory=torch.LongTensor) def configure(self, step_idx: torch.LongTensor): - self.started_at = torch.zeros_like(step_idx) - self.finished_at = torch.zeros_like(step_idx) - self.arrived_at_work_center = torch.zeros_like(step_idx) - self.arrived_at_machine = torch.zeros_like(step_idx) - self.arrived_machine_idx = torch.zeros_like(step_idx) + self.started_at = torch.zeros_like(step_idx) - 1 + self.finished_at = torch.zeros_like(step_idx) - 1 + self.arrived_at_work_center = torch.zeros_like(step_idx) - 1 + self.arrived_at_machine = torch.zeros_like(step_idx) - 1 + self.arrived_machine_idx = torch.zeros_like(step_idx) - 1 def with_event(self, event: JobEvent, step_idx: torch.LongTensor): def get_work_center_idx(): @@ -214,6 +214,28 @@ def total_processing_time(self, strategy: ReductionStrategy = ReductionStrategy. """ return reduce(self.processing_times.float(), strategy).sum() + @property + def release_moment_on_machine(self): + """ + Returns: Release time of job from machine or None if the job isn't processed at this moment + """ + if not self.is_being_processed_on_machine: + return None + + started_at = self.history.started_at[self.current_step_idx] + + return started_at + self.current_operation_processing_time_on_machine + + @property + def is_being_processed_on_machine(self): + """ + Returns: True if the job is currently being processed on machine + """ + return (self.current_machine_idx >= 0 + and not self.is_completed + and self.is_dispatched + and self.history.started_at[self.current_step_idx] > 0) + @property def is_completed(self): return self.history.completed_at > 0 @@ -345,6 +367,7 @@ def with_event(self, event: Event): self.current_step_idx = 0 case JobEvent.Kind.forward: self.current_step_idx += 1 + self.current_machine_idx = -1 case JobEvent.Kind.arrival_on_machine: self.current_machine_idx = event.machine_idx case _: diff --git a/diploma_thesis/environment/machine.py b/diploma_thesis/environment/machine.py index a93da03..8e91b49 100644 --- a/diploma_thesis/environment/machine.py +++ b/diploma_thesis/environment/machine.py @@ -323,6 +323,10 @@ def queue(self) -> List[environment.Job]: def work_center_idx(self) -> int: return self.state.work_center_idx + @property + def work_center(self) -> 'environment.WorkCenter': + return self.shop_floor.work_center(self.work_center_idx) + @property def machine_idx(self) -> int: return self.state.machine_idx diff --git a/diploma_thesis/environment/shop_floor.py b/diploma_thesis/environment/shop_floor.py index b90f435..5e5fad5 100644 --- a/diploma_thesis/environment/shop_floor.py +++ b/diploma_thesis/environment/shop_floor.py @@ -7,6 +7,7 @@ import environment from .utils import ShopFloorFactory +from typing import Set @dataclass @@ -15,7 +16,8 @@ class State: job_id: int = 0 - number_of_jobs_in_system: int = 0 + in_system_job_ids: Set[int] = field(default_factory=set) + @property def dispatched_job_count(self): @@ -29,13 +31,13 @@ def with_new_job_id(self): return self - def with_new_job_in_system(self): - self.number_of_jobs_in_system += 1 + def with_new_job_in_system(self, job_id: int = 0): + self.in_system_job_ids.add(job_id) return self - def with_job_completed(self): - self.number_of_jobs_in_system -= 1 + def with_job_completed(self, job_id: int): + self.in_system_job_ids.remove(job_id) return self @@ -147,6 +149,16 @@ def work_centers(self) -> List['environment.WorkCenter']: def machines(self) -> List['environment.Machine']: return self._machines + @property + def in_system_jobs(self) -> List['environment.Job']: + return [self.history.job(job_id) for job_id in self.state.in_system_job_ids] + + def work_center(self, idx: int) -> 'environment.WorkCenter': + return self.work_centers[idx] + + def machine(self, work_center_idx: int, machine_idx: int) -> 'environment.Machine': + return self.work_centers[work_center_idx].machines[machine_idx] + def work_in_next_queue(self, job: environment.Job) -> float: work_center_idx = job.next_work_center_idx @@ -163,7 +175,6 @@ def average_waiting_in_next_queue(self, job: environment.Job) -> float: return self.work_centers[work_center_idx].average_waiting_time - # Navigation def forward(self, job: environment.Job, from_: environment.Machine): @@ -186,13 +197,13 @@ def forward(self, job: environment.Job, from_: environment.Machine): ) ) - self.state.with_job_completed() + self.state.with_job_completed(job.id) self.did_complete(job) self.logger.info( f"Job {job.id} {job.current_step_idx} has been {'completed' if job.is_completed else 'produced'} " f"on machine {from_.state.machine_idx} in work-center {from_.state.work_center_idx}. " - f"Jobs in system {self.state.number_of_jobs_in_system}" + f"Jobs in system {len(self.state.in_system_job_ids)}" ) # Decision methods @@ -228,10 +239,10 @@ def did_complete(self, job: environment.Job): self.delegate.did_complete(self.state.idx, job) self.__test_if_finished__() - def did_breakdown(self, machine: environment.Machine, repair_time: float): + def did_breakdown(self, machine: environment.Machine, repair_time: torch.FloatTensor): self.logger.info( f'Machine {machine.state.machine_idx} in work-center {machine.state.work_center_idx} ' - f'has broken down. Repair time {repair_time}' + f'has broken down. Repair time {repair_time.item()}' ) self.delegate.did_breakdown(self.state.idx, machine, repair_time) @@ -292,7 +303,7 @@ def __sample_job__(self, initial_work_center_idx: int = None, created_at: torch. def __test_if_finished__(self): has_dispatched_all_jobs = not self.__should_dispatch__() - is_no_jobs_in_system = self.state.number_of_jobs_in_system == 0 + is_no_jobs_in_system = len(self.state.in_system_job_ids) == 0 if not self.did_finish_simulation_event.triggered and has_dispatched_all_jobs and is_no_jobs_in_system: self.delegate.did_finish_simulation(self.state.idx) @@ -310,6 +321,6 @@ def __should_dispatch__(self): return timespan < self.configuration.problem.timespan def __dispatch__(self, job: environment.Job, work_center: environment.WorkCenter): - self.state.with_new_job_in_system() + self.state.with_new_job_in_system(job.id) work_center.receive(job) diff --git a/diploma_thesis/environment/statistics.py b/diploma_thesis/environment/statistics.py index 98b7c6f..94b70ab 100644 --- a/diploma_thesis/environment/statistics.py +++ b/diploma_thesis/environment/statistics.py @@ -69,6 +69,7 @@ def jobs(self, predicate: Predicate = Predicate()) -> List[environment.Job]: logs = self.__filter__(predicate) job_ids = logs['job_id'].unique() + job_ids = job_ids[job_ids >= 0] unfinished_job = [self.shop_floor_history.jobs[job_id] for job_id in job_ids] return unfinished_job diff --git a/diploma_thesis/simulator/configuration.py b/diploma_thesis/simulator/configuration.py index c276ab8..84f3a86 100644 --- a/diploma_thesis/simulator/configuration.py +++ b/diploma_thesis/simulator/configuration.py @@ -48,7 +48,7 @@ def from_cli(cls, logger: Logger, parameters: Dict): machine_train_schedule=cls.TrainSchedule.from_cli(parameters['machine_train_schedule']), work_center_train_schedule=cls.TrainSchedule.from_cli(parameters['work_center_train_schedule']), n_workers=parameters['n_workers'], - simulations=from_cli_list(logger=logger, parameters=parameters['simulations']) + simulations=from_cli_list(parameters=parameters['simulations'], logger=logger) ) @@ -61,5 +61,5 @@ class EvaluateConfiguration: def from_cli(cls, logger: Logger, parameters: Dict): return cls( n_workers=parameters['n_workers'], - simulations=from_cli_list(logger=logger, parameters=parameters['simulations']) + simulations=from_cli_list(parameters=parameters['simulations'], logger=logger) ) diff --git a/diploma_thesis/simulator/simulation/__init__.py b/diploma_thesis/simulator/simulation/__init__.py index 1bd17c9..d6f7c11 100644 --- a/diploma_thesis/simulator/simulation/__init__.py +++ b/diploma_thesis/simulator/simulation/__init__.py @@ -9,13 +9,13 @@ } -def from_cli(logger: Logger, parameters: Dict): +def from_cli(parameters: Dict, logger: Logger): cls = key_to_class[parameters['kind']] - return cls.from_cli(name=parameters.get('name', ''), logger=logger, parameters=parameters['parameters']) + return cls.from_cli(name=parameters.get('name', ''), parameters=parameters['parameters'], logger=logger) -def from_cli_list(logger: Logger, parameters: List[Dict]): +def from_cli_list(parameters: List[Dict], logger: Logger): simulation_names = [parameter.get('name', '') for parameter in parameters] names = dict() @@ -29,4 +29,4 @@ def from_cli_list(logger: Logger, parameters: List[Dict]): parameters[idx]['name'] = key - return [from_cli(logger, parameter) for parameter in parameters] + return [from_cli(parameter, logger) for parameter in parameters] diff --git a/diploma_thesis/simulator/simulation/simulation.py b/diploma_thesis/simulator/simulation/simulation.py index 1a7af79..8f8f721 100644 --- a/diploma_thesis/simulator/simulation/simulation.py +++ b/diploma_thesis/simulator/simulation/simulation.py @@ -9,7 +9,7 @@ class Simulation(metaclass=ABCMeta): def __init__(self, name: str, logger: Logger): self.name = name - self.logger = logger.getChild(f'simulation {name}') + self.logger = logger.getChild(f'Simulation {name}') self.shop_floor: ShopFloor = None @property diff --git a/diploma_thesis/workflow/candidates/__init__.py b/diploma_thesis/workflow/candidates/__init__.py index 28b4fa6..2f3dfcb 100644 --- a/diploma_thesis/workflow/candidates/__init__.py +++ b/diploma_thesis/workflow/candidates/__init__.py @@ -1,3 +1,4 @@ +import logging from .template import Template, Candidate from .agent import Agent diff --git a/diploma_thesis/workflow/candidates/agent.py b/diploma_thesis/workflow/candidates/agent.py index d798d99..6651ba1 100644 --- a/diploma_thesis/workflow/candidates/agent.py +++ b/diploma_thesis/workflow/candidates/agent.py @@ -1,4 +1,6 @@ +import logging +from typing import Dict from agents import machine_from_cli, work_center_from_cli from .template import Template, Candidate @@ -6,7 +8,7 @@ class Agent(Template): @classmethod - def from_cli(cls, parameters: dict): + def from_cli(cls, parameters: Dict): name = parameters['name'] machine = machine_from_cli(parameters['machine_agent']) work_center = work_center_from_cli(parameters['work_center_agent']) diff --git a/diploma_thesis/workflow/candidates/static.py b/diploma_thesis/workflow/candidates/static.py index cbb0fff..12d13f0 100644 --- a/diploma_thesis/workflow/candidates/static.py +++ b/diploma_thesis/workflow/candidates/static.py @@ -1,3 +1,4 @@ +import logging from .template import Template, Candidate diff --git a/diploma_thesis/workflow/candidates/template.py b/diploma_thesis/workflow/candidates/template.py index d69f8e1..96ad771 100644 --- a/diploma_thesis/workflow/candidates/template.py +++ b/diploma_thesis/workflow/candidates/template.py @@ -1,3 +1,4 @@ +import logging from abc import ABCMeta, abstractmethod from dataclasses import dataclass diff --git a/diploma_thesis/workflow/simulation.py b/diploma_thesis/workflow/simulation.py index 9a6e13f..db6bef5 100644 --- a/diploma_thesis/workflow/simulation.py +++ b/diploma_thesis/workflow/simulation.py @@ -17,12 +17,12 @@ def __init__(self, parameters: Dict): self.parameters = parameters def run(self): - machine = machine_from_cli(parameters=self.parameters['machine_agent']) - work_center = work_center_from_cli(parameters=self.parameters['work_center_agent']) environment = simpy.Environment() - logger = self.__make_logger__(name='', environment=environment, log_stdout=True) + machine = machine_from_cli(parameters=self.parameters['machine_agent']).with_logger(logger) + work_center = work_center_from_cli(parameters=self.parameters['work_center_agent']).with_logger(logger) + simulator_logger = logger.getChild('Simulator') run_logger = logger.getChild('Run') diff --git a/diploma_thesis/workflow/tournament.py b/diploma_thesis/workflow/tournament.py index 29e94d5..9db7373 100644 --- a/diploma_thesis/workflow/tournament.py +++ b/diploma_thesis/workflow/tournament.py @@ -56,10 +56,15 @@ def __make_criteria__(self): def __evaluate__(self, candidate: Candidate, criteria: List[Criterion]): environment = simpy.Environment() + logger = self.__make_logger__(name=candidate.name, environment=environment, log_stdout=True) logger.setLevel(logging.INFO) + configuration = EvaluateConfiguration.from_cli(logger, self.parameters['simulator']) + candidate.machine.with_logger(logger) + candidate.work_center.with_logger(logger) + simulator = EpisodicSimulator( machine=candidate.machine, work_center=candidate.work_center,