diff --git a/Simpy_Tutorial/environment.py b/Simpy_Tutorial/environment.py
index 78147453a4bef38af57b870656b09ffdc3885bd8..b94a844174eb4e5821b6664c55e8c6f26c5ef20d 100644
--- a/Simpy_Tutorial/environment.py
+++ b/Simpy_Tutorial/environment.py
@@ -2,6 +2,7 @@ import gymnasium as gym
import numpy as np
import processmodel as model
import simpy
+import eventlog
"""
Environment for the RL agent
@@ -40,6 +41,8 @@ class BusinessProcessEnv(gym.Env):
self.reward = 0
+ self.bigeventlog = []
+
def get_current_state(self, caseid):
process, case, event = model.get_current_state(self.process, caseid)
@@ -94,6 +97,7 @@ class BusinessProcessEnv(gym.Env):
def reset(self, seed=None, options=None):
+
# Reset the environment to the initial state
# Implement a function which extracts the current state from an event log / simulation model
super().reset(seed=seed)
diff --git a/Simpy_Tutorial/eventlog.py b/Simpy_Tutorial/eventlog.py
index 27e541abb4b1feccb8a93c035a884f9ab9816c7d..8a31eaf99ca99fbcee7db494ea8598148373147e 100644
--- a/Simpy_Tutorial/eventlog.py
+++ b/Simpy_Tutorial/eventlog.py
@@ -11,21 +11,28 @@ def add_start_event(process, event_id, case_id, activity, start_timestamp):
process.event_log[event_id] = {
'CaseID': case_id,
'Activity': activity,
- 'StartTimestamp': start_timestamp,
+ 'StartTimestamp': float(start_timestamp),
+ 'EndTimestamp': None
}
def add_end_event(process, event_id, end_timestamp):
- if event_id in process.event_log:
- event = process.event_log[event_id]
- event['EndTimestamp'] = end_timestamp
- process.event_log.append(event)
- del process.event_log[event_id]
+ # if event_id in process.event_log:
+ event = process.event_log[event_id]
+ event['EndTimestamp'] = end_timestamp
+ # process.event_log.append(event)
+ # del process.event_log[event_id]
# add functions for adding events with their attributes to the log
+<<<<<<<< HEAD:simpy_tutorial/eventlog.py
+def export_to_csv(env, file_path):
+ event_log_df = pd.DataFrame.from_dict(env.bigeventlog)
+ event_log_df.to_csv(file_path)
+========
def export_to_csv(process, file_path):
event_log_df = pd.DataFrame.from_dict(process.event_log)
event_log_df.to_csv(file_path, index=False)
+>>>>>>>> 575b59827b5f928da4165070a4a1d51a85eed774:Simpy_Tutorial/eventlog.py
def export_to_xes(process, file_path):
# Use appropriate code to export to XES format
diff --git a/Simpy_Tutorial/processmodel.py b/Simpy_Tutorial/processmodel.py
index ea2fae666eee0a941d7ddad1394cb39ebbbe9b9f..1f158e087ba8c572f4b48a5f84c5634609e0a24a 100644
--- a/Simpy_Tutorial/processmodel.py
+++ b/Simpy_Tutorial/processmodel.py
@@ -78,12 +78,13 @@ def execute_case(env, case, process):
case_obj.state[0] += 1
case_obj.current = 1
process.event_log.append(process.event_counter)
- eventlog.add_start_event(process, process.event_counter, case, "a", env.now)
event_counter = process.event_counter
+ eventlog.add_start_event(process, event_counter, case, "a", env.now)
process.event_counter += 1
print(f"Case {case}: started activity a at {env.now:.2f}")
yield env.process(process.a(case))
- eventlog.add_end_event(process, event_counter, env.now)
+ end = int(env.now)
+ eventlog.add_end_event(process, event_counter, end)
print(f"Case {case}: finished activity a at {env.now:.2f}")
if case_obj.agent: process.flag = False
@@ -250,8 +251,15 @@ def main():
print(state['event'])
print(business_env.flatten_observation(state))
rl.q_learning(space, activities)
+<<<<<<<< HEAD:Simpy_Tutorial/processmodel.py
path = r"C:\Users\nourm\OneDrive\Desktop\Nour\optis_app\out.csv"
eventlog.export_to_csv(business_env.process, path)
+========
+
+ el = business_env.bigeventlog
+
+ # eventlog.export_to_csv(business_env, r'D:\test\optis.csv')
+>>>>>>>> origin/aleks2:simpy_tutorial/processmodel.py
diff --git a/Simpy_Tutorial/rlalgorithm.py b/Simpy_Tutorial/rlalgorithm.py
index ac44e92e4a1ebc892cfcf8bd610fb5a5d2db550a..ed3981a8307566359e6e4d46dbdbbdd9a4a60e44 100644
--- a/Simpy_Tutorial/rlalgorithm.py
+++ b/Simpy_Tutorial/rlalgorithm.py
@@ -3,6 +3,7 @@ import numpy as np
import environment
import processmodel as model
from gymnasium.wrappers import FlattenObservation
+import eventlog
def q_learning(space, activities):
# Define the business process environment
@@ -29,7 +30,7 @@ def q_learning(space, activities):
epsilon = 0.1 # exploration rate
# Train the agent using Q-learning
- num_episodes = 10
+ num_episodes = 2
for episode in range(num_episodes):
state, _ = env.reset()
state = env.flatten_observation(state)
@@ -52,4 +53,7 @@ def q_learning(space, activities):
# Transition to the next state
state = next_state
+ env.bigeventlog += env.process.event_log
+ eventlog.export_to_csv(env, r'D:\test\optis.csv')
+
print(f"Episode {episode} ended.")
\ No newline at end of file
diff --git a/__pycache__/environment.cpython-311.pyc b/__pycache__/environment.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d4a8542792b100289cbe8d758a23e2a2279a6cc1
Binary files /dev/null and b/__pycache__/environment.cpython-311.pyc differ
diff --git a/__pycache__/eventlog.cpython-311.pyc b/__pycache__/eventlog.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..726340a5c7d89836ad99cf2cc6f04f840138e0d4
Binary files /dev/null and b/__pycache__/eventlog.cpython-311.pyc differ
diff --git a/__pycache__/processmodel.cpython-311.pyc b/__pycache__/processmodel.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6285a50268e9a8c80f8bd8a91f005bfc3d523237
Binary files /dev/null and b/__pycache__/processmodel.cpython-311.pyc differ
diff --git a/__pycache__/rlalgorithm.cpython-311.pyc b/__pycache__/rlalgorithm.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..23d7225e816b44e1fc9cfb2a8718f19fcf3f889b
Binary files /dev/null and b/__pycache__/rlalgorithm.cpython-311.pyc differ
diff --git a/backend/__pycache__/agent.cpython-311.pyc b/backend/__pycache__/agent.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..87f701a04a31d900d91fd18b6bc837516be90163
Binary files /dev/null and b/backend/__pycache__/agent.cpython-311.pyc differ
diff --git a/backend/__pycache__/environment.cpython-311.pyc b/backend/__pycache__/environment.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..51daa198751b4836531ffe9cc564ed462aed29b8
Binary files /dev/null and b/backend/__pycache__/environment.cpython-311.pyc differ
diff --git a/backend/__pycache__/simplesimmodel.cpython-311.pyc b/backend/__pycache__/simplesimmodel.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7f8f062a2173a3f3440055bc14475b66274869c3
Binary files /dev/null and b/backend/__pycache__/simplesimmodel.cpython-311.pyc differ
diff --git a/backend/__pycache__/simulationmodel.cpython-311.pyc b/backend/__pycache__/simulationmodel.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..faf0672706b339b6fd1e7ee5c06392cb3993a759
Binary files /dev/null and b/backend/__pycache__/simulationmodel.cpython-311.pyc differ
diff --git a/backend/agent.py b/backend/agent.py
new file mode 100644
index 0000000000000000000000000000000000000000..111df067c5097be66021c5d64734dcc9f66f6117
--- /dev/null
+++ b/backend/agent.py
@@ -0,0 +1,69 @@
+import gymnasium as gym
+import numpy as np
+import environment
+from gymnasium.wrappers import FlattenObservation
+
+"""
+RL agent
+"""
+
+def q_learning(space, activities):
+ # Define the business process environment
+ env = environment.BusinessProcessEnv(space, activities)
+
+ # Define the Q-table
+ num_states = 1
+
+ process_space = env.observation_space['process'].nvec
+ case_space = env.observation_space['case'].nvec
+ event_space = env.observation_space['event'].n
+
+ for i in process_space: num_states *= i
+ for i in case_space: num_states *= i
+ num_states *= event_space
+
+ num_actions = env.action_space.n
+
+ Q = np.zeros((num_states, num_actions), dtype=np.int16)
+
+ # Set the hyperparameters
+ alpha = 0.1 # learning rate
+ gamma = 0.99 # discount factor
+ epsilon = 0.1 # exploration rate
+
+ mean_time = 0
+
+ # Train the agent using Q-learning
+ num_episodes = 100
+ for episode in range(num_episodes):
+ state, _ = env.reset()
+ state = env.flatten_observation(state)
+ done = False
+ start = env.process.env.now
+ while not done:
+ # Choose an action based on the epsilon-greedy policy
+ if np.random.uniform(0, 1) < epsilon:
+ action = env.action_space.sample()
+ else:
+ action = np.argmax(Q[state])
+
+
+ # Execute the action and observe the next state and reward
+ next_state, reward, done, _ = env.step(action)
+
+ # Update the Q-value for the current state-action pair
+ Q[state][action] = Q[state][action] + alpha * (reward + gamma * np.max(Q[next_state]) - Q[state][action])
+
+ # Transition to the next state
+ state = next_state
+
+ time = env.process.env.now - start
+ mean_time += time
+
+ """
+ if (episode % 20 == 19):
+ mean_time /= 20
+ print(f"Episode {episode-19} to episode {episode}: mean time = {mean_time}")
+ """
+
+ print(f"Episode {episode}: time = {time}")
\ No newline at end of file
diff --git a/backend/environment.py b/backend/environment.py
new file mode 100644
index 0000000000000000000000000000000000000000..b96b6425d7216e12269ab8b8a50577c25a72ae05
--- /dev/null
+++ b/backend/environment.py
@@ -0,0 +1,128 @@
+import gymnasium as gym
+import numpy as np
+import simpy
+import simplesimmodel as model
+
+"""
+Environment for the RL agent
+"""
+
+
+class BusinessProcessEnv(gym.Env):
+
+ def __init__(self, space, activities):
+ self.ressources = space[0]
+ self.case = space[1]
+ self.activities = activities
+
+ self.observation_space = gym.spaces.Dict(
+ {
+ 'process': gym.spaces.MultiDiscrete(self.ressources),
+ 'case': gym.spaces.MultiDiscrete(self.case),
+ 'event': gym.spaces.Discrete(self.activities)
+ }
+ )
+
+ self.action_space = gym.spaces.Discrete(self.activities)
+
+ self.current_state = {
+ 'process': np.array(self.ressources),
+ 'case': np.zeros(len(self.case), dtype=int),
+ 'event': 0
+ }
+
+ self.model_env = simpy.Environment()
+ self.process = model.BusinessProcess(self.model_env, self.ressources)
+ self.model_env.process(model.run_process(self.model_env, self.process))
+ # self.done_cases = set([])
+
+ self.reward = 0
+
+
+ def get_current_state(self, caseid):
+ process, case, event = model.get_current_state(self.process, caseid)
+ state = {
+ 'process': process,
+ 'case': case,
+ 'event': event
+ }
+ return state
+
+ def step(self, action):
+
+ self.process.next = action
+
+ if self.process.case_id in self.process.done_cases:
+ self.process.case_id = np.random.choice(self.process.active_cases)
+
+ case_obj = self.process.case_objects[self.process.case_id]
+ self.current_state = self.get_current_state(case_obj)
+
+ # print(f"{self.current_state['event']}")
+
+ start = self.process.env.now
+
+ self.process.flag = True
+
+ if self.process.is_valid(self.current_state['event'], action, case_obj):
+
+ while(self.process.flag):
+ self.model_env.step()
+
+ stop = self.process.env.now
+
+ case_obj = self.process.case_objects[self.process.case_id]
+
+ print(f"Agent did case {self.process.case_id} activity {action}.")
+
+ next_state = self.get_current_state(case_obj)
+ self.current_state = next_state
+ next_state = self.flatten_observation(next_state)
+
+ self.reward += -(stop - start)
+ done = True if (len(self.process.done_cases) == 5 or len(self.process.active_cases) == 0) else False
+ return next_state, self.reward, done, None
+
+ else:
+ self.reward += -100
+ next_state = self.flatten_observation(self.current_state)
+ done = False
+ return next_state, self.reward, done, None
+
+
+ def reset(self, seed=None, options=None):
+ # Reset the environment to the initial state
+ # Implement a function which extracts the current state from an event log / simulation model
+ super().reset(seed=seed)
+
+ self.current_state = {
+ 'process': np.array(self.ressources),
+ 'case': np.zeros(len(self.case), dtype=int),
+ 'event': 0
+ }
+ self.current_step = 0
+
+ self.model_env = simpy.Environment()
+ self.process = model.BusinessProcess(self.model_env, self.ressources)
+ self.model_env.process(model.run_process(self.model_env, self.process))
+ self.process.done_cases = set([])
+
+ self.reward = 0
+
+ return self.current_state, None
+
+
+ def render(self, mode='human'):
+ # Render the current state of the environment
+ pass
+
+
+ def flatten_observation(self, observation):
+ flattened = []
+ for i in observation['process']: flattened.append(i)
+ for j in observation['case']: flattened.append(j)
+ flattened.append(observation['event'])
+
+ return flattened
+
+
diff --git a/backend/eventlog.py b/backend/eventlog.py
new file mode 100644
index 0000000000000000000000000000000000000000..12c771efaaad9a950ead2e04aeba2be9f8c4cbb0
--- /dev/null
+++ b/backend/eventlog.py
@@ -0,0 +1,39 @@
+import pandas as pd
+import simplesimmodel as model
+
+"""
+Event log generator for our simulation model:
+- generate an event log
+- update an event log (adding new events)
+"""
+
+def add_start_event(process, event_id, case_id, activity, start_timestamp):
+ process.event_log[event_id] = {
+ 'CaseID': case_id,
+ 'Activity': activity,
+ 'StartTimestamp': float(start_timestamp),
+ 'EndTimestamp': None
+ }
+
+def add_end_event(process, event_id, end_timestamp):
+ # if event_id in process.event_log:
+ event = process.event_log[event_id]
+ event['EndTimestamp'] = end_timestamp
+ # process.event_log.append(event)
+ # del process.event_log[event_id]
+
+# add functions for adding events with their attributes to the log
+
+<<<<<<<< HEAD:Simpy_Tutorial/eventlog.py
+def export_to_csv(process, file_path):
+ event_log_df = pd.DataFrame.from_dict(process.event_log)
+ event_log_df.to_csv(file_path, index=False)
+========
+def export_to_csv(env, file_path):
+ event_log_df = pd.DataFrame.from_dict(env.bigeventlog)
+ event_log_df.to_csv(file_path)
+>>>>>>>> origin/aleks2:backend/eventlog.py
+
+def export_to_xes(process, file_path):
+ # Use appropriate code to export to XES format
+ pass
\ No newline at end of file
diff --git a/backend/simplesimmodel.py b/backend/simplesimmodel.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe23eb4b077fbfcf15c62daa4a7e844ac1e875e0
--- /dev/null
+++ b/backend/simplesimmodel.py
@@ -0,0 +1,519 @@
+import simpy
+import random
+import numpy as np
+import environment
+import agent
+
+"""
+Simulation model for a simpler business process, including:
+- a class representing the process
+- a function representing the process flow
+- a function to run the process
+"""
+
+
+"""
+This class represents the process with its:
+- ressources: system, managers, stock handlers (a, b and c), manufacturers (a and b), delivery services (a, b and c)
+- activities
+"""
+class BusinessProcess(object):
+ """ Initialize the process with the number of the different types of ressources """
+ def __init__(self, env, ressources):
+
+ # initialize simulation environment
+ self.env = env
+
+ # initialize ressources
+ self.system = simpy.Resource(env, ressources[0])
+ self.order_taker = simpy.Resource(env, ressources[1])
+ self.stock_handler_a = simpy.Resource(env, ressources[2])
+ self.stock_handler_b = simpy.Resource(env, ressources[3])
+ self.stock_handler_c = simpy.Resource(env, ressources[4])
+ self.manufacturer_a = simpy.Resource(env, ressources[5])
+ self.manufacturer_b = simpy.Resource(env, ressources[6])
+ self.packer_a = simpy.Resource(env, ressources[7])
+ self.packer_b = simpy.Resource(env, ressources[8])
+ self.packer_c = simpy.Resource(env, ressources[9])
+ # capacity of each delivery service instead of numbers of workers
+ self.delivery_service_a = simpy.Resource(env, ressources[10])
+ self.delivery_service_b = simpy.Resource(env, ressources[11])
+ self.delivery_service_c = simpy.Resource(env, ressources[12])
+
+ # initialize lists with active cases and all case objects
+ self.active_cases = [0, 1, 2]
+ case_0 = Case(0)
+ case_1 = Case(1)
+ case_2 = Case(2)
+ self.case_objects = [case_0, case_1, case_2]
+
+ # flag indicates whether the process is currently controlled by the agent and we set it on false every time the agent does an activity
+ self.flag = True
+
+ self.next = 0
+
+ self.case_id = 0
+
+ self.done_cases = set([])
+
+ random.seed(1)
+
+ def place_order(self, case):
+ yield self.env.timeout(0)
+
+ def arrange_standard_order(self, case):
+ yield self.env.timeout(random.randint(10, 15))
+
+ def arrange_custom_order(self, case):
+ yield self.env.timeout(random.randint(20, 30))
+
+ def pick_from_stock_a(self, case):
+ yield self.env.timeout(random.randint(10, 40))
+
+ def pick_from_stock_b(self, case):
+ yield self.env.timeout(random.randint(20, 60))
+
+ def pick_from_stock_c(self, case):
+ yield self.env.timeout(random.randint(30, 80))
+
+ def manufacture_a(self, case):
+ yield self.env.timeout(random.randint(240, 360))
+
+ def manufacture_b(self, case):
+ yield self.env.timeout(random.randint(360, 480))
+
+ def pack_a(self, case):
+ yield self.env.timeout(random.randint(10, 20))
+
+ def pack_b(self, case):
+ yield self.env.timeout(random.randint(15, 30))
+
+ def pack_c(self, case):
+ yield self.env.timeout(random.randint(25, 50))
+
+ def attempt_delivery_a(self, case):
+ yield self.env.timeout(random.randint(720, 1440))
+
+ def attempt_delivery_b(self, case):
+ yield self.env.timeout(random.randint(1440, 2160))
+
+ def attempt_delivery_c(self, case):
+ yield self.env.timeout(random.randint(1440, 2880))
+
+ def order_completed(self, case):
+ yield self.env.timeout(1)
+
+ def is_valid(self, event, action, case_obj):
+ if event == 0 and action == 1:
+ return True
+ elif event == 1 and (action == 2 or action == 3):
+ return True
+ elif event == 2 and case_obj.standard_order and (action == 4 or action == 5 or action == 6):
+ return True
+ elif event == 3 and (not case_obj.standard_order) and (action == 7 or action == 8):
+ return True
+ elif (4 <= event <= 8) and (action == 9 or action == 10 or action == 11):
+ return True
+ elif (9 <= event <= 11) and (12 <= action <=14):
+ return True
+ elif (12 <= event <=14) and action == 15:
+ return True
+ else:
+ return False
+
+"""
+A class representing a single case from the process. Every case has an (unique) case id,
+state showing which activities have been already done, current activity being executed,
+agent flag showing if the agent is currently controlling the case and
+a flag whether the order is standard
+"""
+class Case(object):
+ def __init__(self, case):
+ self.case_id = case
+ self.state = np.zeros(15, dtype = int)
+ self.current = 0
+ self.agent = False
+ self.standard_order = True
+
+"""
+A function, which represents the process model with it's control flow and ressource allocation.
+A customer places an order at the system and then is redirected to the order takers, where it's arranged
+what type of order the customer wants - standard or custom. After that depending on whether the order
+is standard or custom, respectively a stock or manufacturer is chosen.
+Afterwards one of three packing options is chosen and executed.
+Following the packing one of the three delivery services deliver the order.
+With that the order is completed.
+"""
+def execute_case(env, case, process):
+ # create a case object to keep track of case attributes if not already existing
+ if len(process.case_objects) <= case:
+ case_obj = Case(case)
+
+ # add the case to the process's active cases list
+ process.case_objects.append(case_obj)
+ process.active_cases.append(case)
+
+ # if the case is one of the first three choose it from the list
+ if len(process.case_objects) <= 3:
+ case_obj = process.case_objects[case]
+
+ # if case chosen by agent set agent flag to true
+ if process.case_id == case:
+ case_obj.agent = True
+
+ # place order
+ with process.system.request() as request:
+ yield request
+ case_obj.state[0] += 1
+ case_obj.current = 1
+ yield env.process(process.place_order(case))
+ if case_obj.agent: print(f"Case {case}: 'placed order' at {env.now:.2f}")
+
+
+ # if the last action was made from the agent set the process flag to be able to return to the environment's step function
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+
+ # before a new action is executed check if the agent is controlling the case and set the flag to true if yes
+ if process.case_id == case: case_obj.agent = True
+
+ # standard order XOR custom order
+ choice = random.randint(2,3) if not case_obj.agent else process.next
+ if choice == 2:
+ case_obj.standard_order = True
+ with process.order_taker.request() as request:
+ yield request
+ case_obj.state[1] += 1
+ case_obj.current = 2
+ if case_obj.agent: print(f"Case {case}: started 'arrange standard order' at {env.now:.2f}")
+ yield env.process(process.arrange_standard_order(case))
+ if case_obj.agent: print(f"Case {case}: finished 'arrange standard order' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ else:
+ case_obj.standard_order = False
+ with process.order_taker.request() as request:
+ yield request
+ case_obj.state[2] += 1
+ case_obj.current = 3
+ if case_obj.agent: print(f"Case {case}: started 'arrange custom order' at {env.now:.2f}")
+ yield env.process(process.arrange_custom_order(case))
+ if case_obj.agent: print(f"Case {case}: finished 'arrange custom order' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+
+
+ if process.case_id == case: case_obj.agent = True
+
+ if case_obj.standard_order and (not case_obj.agent):
+ choice = random.randint(4,6)
+ elif (not case_obj.standard_order) and (not case_obj.agent):
+ choice = random.randint(7,8)
+ else:
+ choice = process.next
+
+ # choose stock or manufacturer
+ if choice == 4:
+ with process.stock_handler_a.request() as request:
+ yield request
+ case_obj.state[3] += 1
+ case_obj.current = 4
+ if case_obj.agent: print(f"Case {case}: started 'pick from stock A' at {env.now:.2f}")
+ yield env.process(process.pick_from_stock_a(case))
+ if case_obj.agent: print(f"Case {case}: finished 'pick from stock A' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ elif choice == 5:
+ with process.stock_handler_b.request() as request:
+ yield request
+ case_obj.state[4] += 1
+ case_obj.current = 5
+ if case_obj.agent: print(f"Case {case}: started 'pick from stock B' at {env.now:.2f}")
+ yield env.process(process.pick_from_stock_b(case))
+ if case_obj.agent: print(f"Case {case}: finished 'pick from stock B' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ elif choice == 6:
+ with process.stock_handler_c.request() as request:
+ yield request
+ case_obj.state[5] += 1
+ case_obj.current = 6
+ if case_obj.agent: print(f"Case {case}: started 'pick from stock C' at {env.now:.2f}")
+ yield env.process(process.pick_from_stock_c(case))
+ if case_obj.agent: print(f"Case {case}: finished 'pick from stock C' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ elif choice == 7:
+ with process.manufacturer_a.request() as request:
+ yield request
+ case_obj.state[6] += 1
+ case_obj.current = 7
+ if case_obj.agent: print(f"Case {case}: started 'manufacture A' at {env.now:.2f}")
+ yield env.process(process.manufacture_a(case))
+ if case_obj.agent: print(f"Case {case}: finished 'manufacture A' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ else:
+ with process.manufacturer_b.request() as request:
+ yield request
+ case_obj.state[7] += 1
+ case_obj.current = 8
+ if case_obj.agent: print(f"Case {case}: started 'manufacture B' at {env.now:.2f}")
+ yield env.process(process.manufacture_b(case))
+ if case_obj.agent: print(f"Case {case}: finished 'manufacture B' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+
+
+ if process.case_id == case: case_obj.agent = True
+
+ choice = random.randint(9,11) if not case_obj.agent else process.next
+
+ if choice == 9:
+ with process.packer_a.request() as request:
+ yield request
+ case_obj.state[8] += 1
+ case_obj.current = 9
+ if case_obj.agent: print(f"Case {case}: started 'pack A' at {env.now:.2f}")
+ yield env.process(process.pack_a(case))
+ if case_obj.agent: print(f"Case {case}: finished 'pack A' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ elif choice == 10:
+ with process.packer_b.request() as request:
+ yield request
+ case_obj.state[9] += 1
+ case_obj.current = 10
+ if case_obj.agent: print(f"Case {case}: started 'pack B' at {env.now:.2f}")
+ yield env.process(process.pack_b(case))
+ if case_obj.agent: print(f"Case {case}: finished 'pack B' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ else:
+ with process.packer_c.request() as request:
+ yield request
+ case_obj.state[10] += 1
+ case_obj.current = 11
+ if case_obj.agent: print(f"Case {case}: started 'pack C' at {env.now:.2f}")
+ yield env.process(process.pack_c(case))
+ if case_obj.agent: print(f"Case {case}: finished 'pack C' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+
+ if process.case_id == case: case_obj.agent = True
+
+ # choose delivery
+ choice = random.randint(12,14) if not case_obj.agent else process.next
+ if choice == 12:
+ with process.delivery_service_a.request() as request:
+ yield request
+ case_obj.state[11] += 1
+ case_obj.current = 12
+ if case_obj.agent: print(f"Case {case}: started 'attempt delivery A' at {env.now:.2f}")
+ yield env.process(process.attempt_delivery_a(case))
+ if case_obj.agent: print(f"Case {case}: finished 'attempt delivery A' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ elif choice == 13:
+ with process.delivery_service_b.request() as request:
+ yield request
+ case_obj.state[12] += 1
+ case_obj.current = 13
+ if case_obj.agent: print(f"Case {case}: started 'attempt delivery B' at {env.now:.2f}")
+ yield env.process(process.attempt_delivery_b(case))
+ if case_obj.agent: print(f"Case {case}: finished 'attempt delivery B' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ else:
+ with process.delivery_service_c.request() as request:
+ yield request
+ case_obj.state[13] += 1
+ case_obj.current = 14
+ if case_obj.agent: print(f"Case {case}: started 'attempt delivery C' at {env.now:.2f}")
+ yield env.process(process.attempt_delivery_c(case))
+ if case_obj.agent: print(f"Case {case}: finished 'attempt delivery C' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+
+
+ if process.case_id == case: case_obj.agent = True
+
+ # case completed
+ with process.system.request() as request:
+ yield request
+ case_obj.state[14] += 1
+ case_obj.current = 15
+ yield env.process(process.order_completed(case))
+ if case_obj.agent: print(f"Case {case}: 'completed' at {env.now:.2f}")
+
+
+ if case in process.active_cases:
+ process.active_cases.remove(case)
+
+ if case_obj.agent:
+ for i in process.case_objects:
+ if (i.current == 15) and (i.case_id in process.active_cases) and (i.case_id != case):
+ process.active_cases.remove(i.case_id)
+ process.done_cases.add(process.case_id)
+ process.flag = False
+
+def get_current_state(process, case):
+ process_state = []
+
+ num_system = process.system.capacity - process.system.count
+ process_state.append(num_system)
+
+ num_order_taker = process.order_taker.capacity - process.order_taker.count
+ process_state.append(num_order_taker)
+
+ num_stock_handler_a = process.stock_handler_a.capacity - process.stock_handler_a.count
+ process_state.append(num_stock_handler_a)
+
+ num_stock_handler_b = process.stock_handler_b.capacity - process.stock_handler_b.count
+ process_state.append(num_stock_handler_b)
+
+ num_stock_handler_c = process.stock_handler_c.capacity - process.stock_handler_c.count
+ process_state.append(num_stock_handler_c)
+
+ num_manufacturer_a = process.manufacturer_a.capacity - process.manufacturer_a.count
+ process_state.append(num_manufacturer_a)
+
+ num_manufacturer_b = process.manufacturer_b.capacity - process.manufacturer_b.count
+ process_state.append(num_manufacturer_b)
+
+ num_packer_a = process.packer_a.capacity - process.packer_a.count
+ process_state.append(num_packer_a)
+
+ num_packer_b = process.packer_b.capacity - process.packer_b.count
+ process_state.append(num_packer_b)
+
+ num_packer_c = process.packer_c.capacity - process.packer_c.count
+ process_state.append(num_packer_c)
+
+ num_delivery_service_a = process.delivery_service_a.capacity - process.delivery_service_a.count
+ process_state.append(num_delivery_service_a)
+
+ num_delivery_service_b = process.delivery_service_b.capacity - process.delivery_service_b.count
+ process_state.append(num_delivery_service_b)
+
+ num_delivery_service_c = process.delivery_service_c.capacity - process.delivery_service_c.count
+ process_state.append(num_delivery_service_c)
+
+ cur_case = case.state
+
+ event = case.current
+
+ return process_state, cur_case, event
+
+def run_process(env, process):
+ # process = Process(env, num_ot, num_m, num_sh_a, num_sh_b, num_sh_c, num_m_a, num_m_b, num_ds_a, num_ds_b, num_ds_c)
+
+ # the waiting orders
+ for case in range(3):
+ env.process(execute_case(env, case, process))
+
+ # the new incoming orders
+ while case < 1000:
+ waittime = random.randint(10,15)
+ yield env.timeout(waittime) # Wait a bit before generating a new case
+
+ case += 1
+ # process.active_cases.append(case)
+ env.process(execute_case(env, case, process))
+
+def main():
+ # Setup
+ # we can use a random seed if we want to generate the same results every time (maybe useful later for the training)
+ # random.seed(42)
+ # initialize the number of resources
+
+ process = []
+ num_s = 1
+ process.append(num_s)
+ num_ot = 5
+ process.append(num_ot)
+ num_sh_a = 3
+ process.append(num_sh_a)
+ num_sh_b = 3
+ process.append(num_sh_b)
+ num_sh_c = 3
+ process.append(num_sh_c)
+ num_m_a = 3
+ process.append(num_m_a)
+ num_m_b = 2
+ process.append(num_m_b)
+ num_p_a = 4
+ process.append(num_p_a)
+ num_p_b = 5
+ process.append(num_p_b)
+ num_p_c = 4
+ process.append(num_p_c)
+ num_ds_a = 8
+ process.append(num_ds_a)
+ num_ds_b = 8
+ process.append(num_ds_b)
+ num_ds_c = 8
+ process.append(num_ds_c)
+
+ case = []
+ for i in range(15):
+ case.append(1)
+
+ space = [process, case]
+ activities = 16
+
+ business_env = environment.BusinessProcessEnv(space, activities)
+ print(business_env.observation_space.shape)
+ print(business_env.observation_space.sample())
+
+ state, _ = business_env.reset()
+ print(state)
+ print(business_env.current_state)
+ print(state['event'])
+ print(business_env.flatten_observation(state))
+ agent.q_learning(space, activities)
+
+if __name__ == "__main__":
+ main()