在仿真过程当中,许多 activity 是以函数的形式做为参数传入的。这些函数可能与环境交互,好比now
函数用来提取环境当前的时间,get_capacity
函数用于提取环境中resource
对应的容量,get_n_generated
函数用于获取生成器的状态,或者用 get_mon
函数直接收集的历史监测值。惟一须要注意的是,仿真环境必需要包含在轨迹之中,下面是一个错误示例:html
library(simmer) library(simmer.plot) t <- trajectory() %>% log_(function() as.character(now(env))) env <- simmer() %>% add_generator("dummy", t, function() 1) %>% run(4) #> 1: dummy0: #> Error in now(env): object 'env' not found
由于,env
是全局变量,它没法在运行时执行。仿真执行过程于仿真结果的赋值须要分开。在这个仿真用例中,环境 env
由轨迹 t
生成,能够经过 run()
方法将整个过程分离开来:git
t <- trajectory() %>% log_(function() as.character(now(env))) env <- simmer() %>% add_generator("dummy", t, function() 1) env %>% run(4) %>% invisible #> 1: dummy0: 1 #> 2: dummy1: 2 #> 3: dummy2: 3
咱们获取了预期结果。可是,做为最佳实践的通用规则,仍是建议环境在最初单独初始化,这样能够避免没必要要的错误,也使得代码更具备可读性:github
# 首先,初始化环境 env <- simmer() # 生成轨迹 t <- trajectory() %>% log_(function() as.character(now(env))) # 执行环境模拟过程 env %>% add_generator("dummy", t, function() 1) %>% run(4) %>% invisible #> 1: dummy0: 1 #> 2: dummy1: 2 #> 3: dummy2: 3
当生成器建立一个到达流的时候,它会给轨迹分配一个到达对象。轨迹在这里的定义是由一个到达对象在系统中全生命周期的一系列行为。一旦一个到达对象被分配到轨迹中,它一般会以必定的顺序开始执行轨迹中的预期行为,最后离开系统。好比:编程
patient_traj <- trajectory(name = "patient_trajectory") %>% seize(resource = "doctor", amount = 1) %>% timeout(task = 3) %>% release(resource = "doctor", amount = 1)
这里咱们建立一个病人就医3分钟而后离开的例子。这是一个直截了当的例子,可是大部分轨迹相关的函数都在此基础上演化高级用法,下面会一一介绍。segmentfault
此外, 建议你能够尝试下simmer的插件 simmer.bricks
包,它封装了经常使用的一些轨迹。(见 simmer.bricks入门)微信
log_()
log_(., message, level)
方法用来打印仿真过程当中的信息以辅助debug,经过不一样的 level
能够调整打印的层次:app
t <- trajectory() %>% log_("this is always printed") %>% # level = 0 by default log_("this is printed if `log_level>=1`", level = 1) %>% log_("this is printed if `log_level>=2`", level = 2) simmer() %>% add_generator("dummy", t, at(0)) %>% run() %>% invisible #> 0: dummy0: this is always printed simmer(log_level = 1) %>% add_generator("dummy", t, at(0)) %>% run() %>% invisible #> 0: dummy0: this is always printed #> 0: dummy0: this is printed if `log_level>=1` simmer(log_level = Inf) %>% add_generator("dummy", t, at(0)) %>% run() %>% invisible #> 0: dummy0: this is always printed #> 0: dummy0: this is printed if `log_level>=1` #> 0: dummy0: this is printed if `log_level>=2`
set_attribute(), set_global()
set_attribute(., keys, values)
方法提供了设置到达流属性的方法。keys
和values
能够以向量或者函数的形式返回。可是, values
只可以以数值型表示。异步
patient_traj <- trajectory(name = "patient_trajectory") %>% set_attribute(keys = "my_key", values = 123) %>% timeout(5) %>% set_attribute(keys = "my_key", values = 456) env <- simmer() %>% add_generator("patient", patient_traj, at(0), mon = 2) %>% run() get_mon_attributes(env) #> time name key value replication #> 1 0 patient0 my_key 123 1 #> 2 5 patient0 my_key 456 1
如上,轨迹的到达流在 0 时刻(经过 at 函数实现),仅包含 {my_key
:123} 的属性。add_generator
的 参数 mon = 2
表示对到达流的属性进行持续观察。咱们能够用 get_mon_attributes
方法查看 my_key
对应的值在仿真过程当中的变化。ide
若是你想要设置一个存在依赖链路的属性也是容许的。属性能够经过get_attribute(., keys)
的方式获取。下面是一个实际用例:异步编程
patient_traj <- trajectory(name = "patient_trajectory") %>% set_attribute("my_key", 123) %>% timeout(5) %>% set_attribute("my_key", 1, mod="+") %>% timeout(5) %>% set_attribute("dependent_key", function() ifelse(get_attribute(env, "my_key")<=123, 1, 0)) %>% timeout(5) %>% set_attribute("independent_key", function() runif(1)) env<- simmer() %>% add_generator("patient", patient_traj, at(0), mon = 2) env %>% run() #> simmer environment: anonymous | now: 15 | next: #> { Monitor: in memory } #> { Source: patient | monitored: 2 | n_generated: 1 } get_mon_attributes(env) #> time name key value replication #> 1 0 patient0 my_key 123.0000000 1 #> 2 5 patient0 my_key 124.0000000 1 #> 3 10 patient0 dependent_key 0.0000000 1 #> 4 15 patient0 independent_key 0.5500812 1
对于每一次到达,属性只对于到达者可见,其他人不可见。
writer <- trajectory() %>% set_attribute(keys = "my_key", values = 123) reader <- trajectory() %>% log_(function() paste0(get_attribute(env, "my_key"))) env <- simmer() %>% add_generator("writer", writer, at(0), mon = 2) %>% add_generator("reader", reader, at(1), mon = 2) env %>% run() #> 1: reader0: NA #> simmer environment: anonymous | now: 1 | next: #> { Monitor: in memory } #> { Source: writer | monitored: 2 | n_generated: 1 } #> { Source: reader | monitored: 2 | n_generated: 1 } get_mon_attributes(env) #> time name key value replication #> 1 0 writer0 my_key 123 1
所以,在前例中 reader
获取的返回值是缺失值。不过,属性也能够经过 set_global(., keys, values)
全局变量声明:
writer <- trajectory() %>% set_global(keys = "my_key", values = 123) reader <- trajectory() %>% log_(function() paste0(get_attribute(env, "my_key"), ", ", get_global(env, "my_key"))) env <- simmer() %>% add_generator("writer", writer, at(0), mon = 2) %>% add_generator("reader", reader, at(1), mon = 2) env %>% run() #> 1: reader0: NA, 123 #> simmer environment: anonymous | now: 1 | next: #> { Monitor: in memory } #> { Source: writer | monitored: 2 | n_generated: 1 } #> { Source: reader | monitored: 2 | n_generated: 1 } get_mon_attributes(env) #> time name key value replication #> 1 0 my_key 123 1
如上显示,全局变量经过 get_mon_attributes()
赋值未命名的键值对。
timeout(), timeout_from_attribute()
timeout(., task)
经过给轨迹分配必定的时间来延迟用户的到达行为,回顾以前最简单的病人看病模型,经过赋予 task
参数一个固定值实现超时机制。
patient_traj <- trajectory(name = "patient_trajectory") %>% timeout(task = 3) env <- simmer() %>% add_generator("patient", patient_traj, at(0)) %>% run() get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 patient0 0 3 3 TRUE 1
一般,超时是依赖于一个分布假设或者经过 属性进行设置的,它经过给 task
参数传入一个函数实现。
patient_traj <- trajectory(name = "patient_trajectory") %>% set_attribute("health", function() sample(20:80, 1)) %>% # distribution-based timeout timeout(function() rexp(1, 10)) %>% # attribute-dependent timeout timeout(function() (100 - get_attribute(env, "health")) * 2) env <- simmer() %>% add_generator("patient", patient_traj, at(0), mon = 2) env %>% run() #> simmer environment: anonymous | now: 52.123429586641 | next: #> { Monitor: in memory } #> { Source: patient | monitored: 2 | n_generated: 1 } get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 patient0 0 52.12343 52.12343 TRUE 1 get_mon_attributes(env) #> time name key value replication #> 1 0 patient0 health 74 1
若是想经过 timeout()
方法动态地设置 task
参数,须要经过回调函数的方式操做。好比 timeout(function() rexp(1, 10))
,rexp(1, 10)
将被每次活动超时都执行。可是,若是你不经过回调函数方式操做,它只会以静态值的方式在初始化的时候执行一次,好比 timeout(rexp(1, 10))
。
固然,经过回调函数的方式会使得代码实现复杂功能,好比同时要检查资源的状态,和环境中其余实体交互等等。一样地,对于其余活动类型,也都是能够以泛函的方式操做。
若是你只须要延迟设置属性值那么能够考虑 timeout_from_attribute(., key)
或者 timeout_from_global(., key)
, 所以,下面两个个超时写法是等价的,可是后者的显然简单不少。
traj <- trajectory() %>% set_attribute("delay", 2) %>% timeout(function() get_attribute(env, "delay")) %>% log_("first timeout") %>% timeout_from_attribute("delay") %>% log_("second timeout") env <- simmer() %>% add_generator("dummy", traj, at(0)) env %>% run() %>% invisible #> 2: dummy0: first timeout #> 4: dummy0: second timeout
seize(), release()
seize(., resource, amount)
用于获取指定数量的资源。相反地,release(., resource, amount)
用于释放指定数量的资源。须要注意的是,为了使用这些函数来指定资源,你须要在模拟环境中经过 add_resource
函数来初始化。
patient_traj <- trajectory(name = "patient_trajectory") %>% seize(resource = "doctor", amount = 1) %>% timeout(3) %>% release(resource = "doctor", amount = 1) env <- simmer() %>% add_resource("doctor", capacity=1, mon = 1) %>% add_generator("patient", patient_traj, at(0)) %>% run() get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 doctor 0 1 0 1 Inf 1 Inf 1 #> 2 doctor 3 0 0 1 Inf 0 Inf 1
这里 add_resource()
中的参数 mon=1
表示模拟环境监控资源使用状况。使用 get_mon_resources(env)
能够获取资源在仿真系统中的日志流水。
有时候,资源的获取和释放但愿经过依赖的到达流属性进行动态调整。为了实现这个工恩呢该,你能够在 amount
参数中传入get_attribute(.)
来代替以前的固定值。
patient_traj <- trajectory(name = "patient_trajectory") %>% set_attribute("health", function() sample(20:80, 1)) %>% set_attribute("docs_to_seize", function() ifelse(get_attribute(env, "health")<50, 1, 2)) %>% seize("doctor", function() get_attribute(env, "docs_to_seize")) %>% timeout(3) %>% release("doctor", function() get_attribute(env, "docs_to_seize")) #> Warning in is.na(env[[name]]): is.na() applied to non-(list or vector) of #> type 'closure' #> Warning in is.na(amount): is.na() applied to non-(list or vector) of type #> 'closure' env <- simmer() %>% add_resource("doctor", capacity = 2, mon = 1) %>% add_generator("patient", patient_traj, at(0), mon = 2) env %>% run() #> simmer environment: anonymous | now: 3 | next: #> { Monitor: in memory } #> { Resource: doctor | monitored: 1 | server status: 0(2) | queue status: 0(Inf) } #> { Source: patient | monitored: 2 | n_generated: 1 } get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 doctor 0 2 0 2 Inf 2 Inf 1 #> 2 doctor 3 0 0 2 Inf 0 Inf 1 get_mon_attributes(env) #> time name key value replication #> 1 0 patient0 health 80 1 #> 2 0 patient0 docs_to_seize 2 1
默认状况下,seize()
失败会致使拒绝到达。下面的例子中,第二位病人尝试找仅有的一名正在给另一位病人看病的医生看病,在没有等候区的状况下就会发生拒绝。
patient_traj <- trajectory(name = "patient_trajectory") %>% log_("arriving...") %>% seize("doctor", 1) %>% # the second patient won't reach this point log_("doctor seized") %>% timeout(5) %>% release("doctor", 1) env <- simmer() %>% add_resource("doctor", capacity = 1, queue_size = 0) %>% add_generator("patient", patient_traj, at(0, 1)) %>% run() #> 0: patient0: arriving... #> 0: patient0: doctor seized #> 1: patient1: arriving... get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 patient1 1 1 0 FALSE 1 #> 2 patient0 0 5 5 TRUE 1 get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 doctor 0 1 0 1 0 1 1 1 #> 2 doctor 5 0 0 1 0 0 1 1
有时,你不想拒毫不成功的seize()
,能够提供另一条路径。好比在例子中,咱们改成第二名病人也能够先去看看护士:
patient_traj <- trajectory(name = "patient_trajectory") %>% log_("arriving...") %>% seize("doctor", 1, continue = FALSE, reject = trajectory("rejected patient") %>% log_("rejected!") %>% seize("nurse", 1) %>% log_("nurse seized") %>% timeout(2) %>% release("nurse", 1)) %>% # the second patient won't reach this point log_("doctor seized") %>% timeout(5) %>% release("doctor", 1) env <- simmer() %>% add_resource("doctor", capacity = 1, queue_size = 0) %>% add_resource("nurse", capacity = 10, queue_size = 0) %>% add_generator("patient", patient_traj, at(0, 1)) %>% run() #> 0: patient0: arriving... #> 0: patient0: doctor seized #> 1: patient1: arriving... #> 1: patient1: rejected! #> 1: patient1: nurse seized get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 patient1 1 3 2 TRUE 1 #> 2 patient0 0 5 5 TRUE 1 get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 doctor 0 1 0 1 0 1 1 1 #> 2 nurse 1 1 0 10 0 1 10 1 #> 3 nurse 3 0 0 10 0 0 10 1 #> 4 doctor 5 0 0 1 0 0 1 1
continue
标记意味着不管是否 reject
发生,子轨迹都会紧跟着主轨迹执行。在这个例子中,continue=FALSE
意味着被拒绝的到达流获取护士和释放护士后就完全结束了到达流的生命周期。不然,它将继续在主轨迹中执行行动。
注意第二位病人可能也会持续尝试,若是他执意想看这位医生。
patient_traj <- trajectory(name = "patient_trajectory") %>% log_("arriving...") %>% seize("doctor", 1, continue = FALSE, reject = trajectory("rejected patient") %>% log_("rejected!") %>% # go for a walk and try again timeout(2) %>% log_("retrying...") %>% rollback(amount = 4, times = Inf)) %>% # the second patient will reach this point after a couple of walks log_("doctor seized") %>% timeout(5) %>% release("doctor", 1) %>% log_("leaving") env <- simmer() %>% add_resource("doctor", capacity = 1, queue_size = 0) %>% add_generator("patient", patient_traj, at(0, 1)) %>% run() #> 0: patient0: arriving... #> 0: patient0: doctor seized #> 1: patient1: arriving... #> 1: patient1: rejected! #> 3: patient1: retrying... #> 3: patient1: rejected! #> 5: patient1: retrying... #> 5: patient0: leaving #> 5: patient1: doctor seized #> 10: patient1: leaving get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 patient0 0 5 5 TRUE 1 #> 2 patient1 1 10 9 TRUE 1 get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 doctor 0 1 0 1 0 1 1 1 #> 2 doctor 5 0 0 1 0 0 1 1 #> 3 doctor 5 1 0 1 0 1 1 1 #> 4 doctor 10 0 0 1 0 0 1 1
post.seize
是另外一个可能的子轨迹选项,它在成功执行 seize()
后被执行。
patient_traj <- trajectory(name = "patient_trajectory") %>% log_("arriving...") %>% seize("doctor", 1, continue = c(TRUE, TRUE), post.seize = trajectory("admitted patient") %>% log_("admitted") %>% timeout(5) %>% release("doctor", 1), reject = trajectory("rejected patient") %>% log_("rejected!") %>% seize("nurse", 1) %>% timeout(2) %>% release("nurse", 1)) %>% # both patients will reach this point, as continue = c(TRUE, TRUE) timeout(10) %>% log_("leaving...") env <- simmer() %>% add_resource("doctor", capacity = 1, queue_size = 0) %>% add_resource("nurse", capacity = 10, queue_size = 0) %>% add_generator("patient", patient_traj, at(0, 1)) %>% run() #> 0: patient0: arriving... #> 0: patient0: admitted #> 1: patient1: arriving... #> 1: patient1: rejected! #> 13: patient1: leaving... #> 15: patient0: leaving... get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 patient1 1 13 12 TRUE 1 #> 2 patient0 0 15 15 TRUE 1 get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 doctor 0 1 0 1 0 1 1 1 #> 2 nurse 1 1 0 10 0 1 10 1 #> 3 nurse 3 0 0 10 0 0 10 1 #> 4 doctor 5 0 0 1 0 0 1 1
set_capacity(), set_queue_size()
set_capacity(., resource, value)
能够设置资源容量,set_queue_size(., resource, value)
则能够设置队列长度。注意,在使用这些函数以前,要记得在环境初始化时经过 add_resource
初始化资源,一样这里也支持静态和动态两种类型的赋值模式。
这些行为颇有意思,它引入了动态变化的资源。例如,两个轨迹争取资源的能力:
set.seed(12345) t1 <- trajectory() %>% seize("res1", 1) %>% set_capacity(resource = "res1", value = 1, mod="+") %>% set_capacity(resource = "res2", value = -1, mod="+") %>% timeout(function() rexp(1, 1)) %>% release("res1", 1) t2 <- trajectory() %>% seize("res2", 1) %>% set_capacity(resource = "res2", value = 1, mod="+") %>% set_capacity(resource = "res1", value = -1, mod="+") %>% timeout(function() rexp(1, 1)) %>% release("res2", 1) env <- simmer() %>% add_resource("res1", capacity = 20, queue_size = Inf) %>% add_resource("res2", capacity = 20, queue_size = Inf) %>% add_generator("t1_", t1, function() rexp(1, 1)) %>% add_generator("t2_", t2, function() rexp(1, 1)) %>% run(100) plot(get_mon_resources(env), "usage", c("res1", "res2"), steps = TRUE)
select()
当资源在环境中事先分配时,seize()
, release()
, set_capacity()
和 set_queue_size()
能够顺利使用,但有时候资源也须要经过一些策略动态选择。好比下面的状况,select(., resources, policy, id)
方法提供了选择资源的一种方法,根据特定策略来选择:seize_selected()
, release_selected()
,set_capacity_selected()
,set_queue_size_selected()
。
patient_traj <- trajectory(name = "patient_trajectory") %>% select(resources = c("doctor1", "doctor2", "doctor3"), policy = "round-robin") %>% set_capacity_selected(1) %>% seize_selected(amount = 1) %>% timeout(5) %>% release_selected(amount = 1) env <- simmer() %>% add_resource("doctor1", capacity = 0) %>% add_resource("doctor2", capacity = 0) %>% add_resource("doctor3", capacity = 0) %>% add_generator("patient", patient_traj, at(0, 1, 2)) %>% run() get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 patient0 0 5 5 TRUE 1 #> 2 patient1 1 6 5 TRUE 1 #> 3 patient2 2 7 5 TRUE 1 get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 doctor1 0 0 0 1 Inf 0 Inf 1 #> 2 doctor1 0 1 0 1 Inf 1 Inf 1 #> 3 doctor2 1 0 0 1 Inf 0 Inf 1 #> 4 doctor2 1 1 0 1 Inf 1 Inf 1 #> 5 doctor3 2 0 0 1 Inf 0 Inf 1 #> 6 doctor3 2 1 0 1 Inf 1 Inf 1 #> 7 doctor1 5 0 0 1 Inf 0 Inf 1 #> 8 doctor2 6 0 0 1 Inf 0 Inf 1 #> 9 doctor3 7 0 0 1 Inf 0 Inf 1
若是你提供给 select()
提供一组动态的资源,那么后续能够经过 seize_selected()
调整获取资源的策略。
patient_traj <- trajectory(name = "patient_trajectory") %>% set_attribute("resource", function() sample(1:3, 1)) %>% select(resources = function() paste0("doctor", get_attribute(env, "resource"))) %>% seize_selected(amount = 1) %>% timeout(5) %>% release_selected(amount = 1) env <- simmer() %>% add_resource("doctor1", capacity = 1) %>% add_resource("doctor2", capacity = 1) %>% add_resource("doctor3", capacity = 1) %>% add_generator("patient", patient_traj, at(0, 1, 2), mon = 2) env %>% run() #> simmer environment: anonymous | now: 10 | next: #> { Monitor: in memory } #> { Resource: doctor1 | monitored: TRUE | server status: 0(1) | queue status: 0(Inf) } #> { Resource: doctor2 | monitored: TRUE | server status: 0(1) | queue status: 0(Inf) } #> { Resource: doctor3 | monitored: TRUE | server status: 0(1) | queue status: 0(Inf) } #> { Source: patient | monitored: 2 | n_generated: 3 } get_mon_attributes(env) #> time name key value replication #> 1 0 patient0 resource 3 1 #> 2 1 patient1 resource 3 1 #> 3 2 patient2 resource 2 1 get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 patient0 0 5 5 TRUE 1 #> 2 patient2 2 7 5 TRUE 1 #> 3 patient1 1 10 5 TRUE 1 get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 doctor3 0 1 0 1 Inf 1 Inf 1 #> 2 doctor3 1 1 1 1 Inf 2 Inf 1 #> 3 doctor2 2 1 0 1 Inf 1 Inf 1 #> 4 doctor3 5 1 0 1 Inf 1 Inf 1 #> 5 doctor2 7 0 0 1 Inf 0 Inf 1 #> 6 doctor3 10 0 0 1 Inf 0 Inf 1
activate(), deactivate()
activate(., source)
和deactivate(., source)
方法可以分别按照ID来开始和暂停活动。这个名字能够提供一个字符串或一个函数返回一个字符串。在如下简单的例子中,使用这些方法经过设置固定的时间间隔 1 来体现:
t <- trajectory() %>% deactivate(source = "dummy") %>% timeout(1) %>% activate(source = "dummy") simmer() %>% add_generator("dummy", t, function() 1) %>% run(10) %>% get_mon_arrivals() #> name start_time end_time activity_time finished replication #> 1 dummy0 1 2 1 TRUE 1 #> 2 dummy1 3 4 1 TRUE 1 #> 3 dummy2 5 6 1 TRUE 1 #> 4 dummy3 7 8 1 TRUE 1
set_trajectory(), set_source()
set_trajectory(., source, trajectory)
和 set_source(., source, object)
方法提供了单独地轨迹切换的方法。 source
能够是一个固定的字符串ID也能够经过函数动态生成字符串ID。
在下面的分布中,t2 切换分布到 t1,t2 只有首次到达时被执行。
t1 <- trajectory() %>% timeout(1) t2 <- trajectory() %>% set_source("dummy", function() 1) %>% set_trajectory("dummy", t1) %>% timeout(2) simmer() %>% add_generator("dummy", trajectory = t2, distribution = function() 2) %>% run(10) %>% get_mon_arrivals() #> name start_time end_time activity_time finished replication #> 1 dummy0 2 4 2 TRUE 1 #> 2 dummy1 3 4 1 TRUE 1 #> 3 dummy2 4 5 1 TRUE 1 #> 4 dummy3 5 6 1 TRUE 1 #> 5 dummy4 6 7 1 TRUE 1 #> 6 dummy5 7 8 1 TRUE 1 #> 7 dummy6 8 9 1 TRUE 1
set_prioritization()
add_generator()
经过给到达流赋予优先级的方式控制。set_prioritization(., values)
和 get_prioritization(.)
方法能够在轨迹中的任意一个节点中改变/获取优先级。
set_attribute("priority", 3) %>% # static values set_prioritization(values = c(3, 7, TRUE)) %>% # dynamically with a function set_prioritization(values = function() { prio <- get_prioritization(env) attr <- get_attribute(env, "priority") c(attr, prio[[2]]+1, FALSE) })
branch()
The branch(., option, continue, ...)
提供在轨迹中️以必定几率添加替代路径的方法。下面的例子显示一个到达在轨迹中被随机分叉:
t1 <- trajectory("trajectory with a branch") %>% seize("server", 1) %>% branch(option = function() sample(1:2, 1), continue = c(T, F), trajectory("branch1") %>% timeout(function() 1), trajectory("branch2") %>% timeout(function() rexp(1, 3)) %>% release("server", 1) ) %>% release("server", 1)
当到达流被分叉,第一个参数 option
是用来传后续的具体路径的几率值,所以它必须是可执行的,返回值须要是在1到n条路径之间。第二个参数 continue
表示在选择路径后是否到达必须继续执行活动。上述例子中,只有第一个路径会走到最后的 release()
流程。
有时,你可能须要统计一条肯定轨迹在一个肯定的分支上进入多少次,或者到达流进入那条轨迹花费了多少时间。对于这种场景,处于计数需求,能够资源容量设置为无限,以下举例:
t0 <- trajectory() %>% branch(function() sample(c(1, 2), 1), continue = c(T, T), trajectory() %>% seize("branch1", 1) %>% # do stuff here timeout(function() rexp(1, 1)) %>% release("branch1", 1), trajectory() %>% seize("branch2", 1) %>% # do stuff here timeout(function() rexp(1, 1/2)) %>% release("branch2", 1)) env <- simmer() %>% add_generator("dummy", t0, at(rep(0, 1000))) %>% # Resources with infinite capacity, just for accounting purposes add_resource("branch1", Inf) %>% add_resource("branch2", Inf) %>% run() arrivals <- get_mon_arrivals(env, per_resource = T) # Times that each branch was entered table(arrivals$resource) #> #> branch1 branch2 #> 496 504 # The `activity_time` is the total time inside each branch for each arrival # Let's see the distributions ggplot(arrivals) + geom_histogram(aes(x=activity_time)) + facet_wrap(~resource)
rollback()
rollback(., amount, times, check)
回滚方法容许到达流轨迹回滚若干步,好比一个字符串在超时函数中被打印出来,在第一次执行后,轨迹再回滚3次(所以总共打印 "Hello" 4次)。
t0 <- trajectory() %>% log_("Hello!") %>% timeout(1) %>% rollback(amount = 2, times = 3) simmer() %>% add_generator("hello_sayer", t0, at(0)) %>% run() %>% invisible #> 0: hello_sayer0: Hello! #> 1: hello_sayer0: Hello! #> 2: hello_sayer0: Hello! #> 3: hello_sayer0: Hello!
rollback()
方法也接受一个选项 check
来覆盖默认的基于数值的行为。该方法能够传入一个返回逻辑值的函数。每次一个到达接收到活动,check 都会判断一下是否以指定步长调用 rollback()
回滚:
t0 <- trajectory() %>% set_attribute("happiness", 0) %>% log_(function() { level <- get_attribute(env, "happiness") paste0(">> Happiness level is at: ", level, " -- ", ifelse(level<25,"PETE: I'm feeling crappy...", ifelse(level<50,"PETE: Feelin' a bit moody", ifelse(level<75,"PETE: Just had a good espresso", "PETE: Let's do this! (and stop this loop...)")))) }) %>% set_attribute("happiness", 25, mod="+") %>% rollback(amount = 2, check = function() get_attribute(env, "happiness") < 100) env <- simmer() %>% add_generator("mood_swinger", t0, at(0)) env %>% run() %>% invisible() #> 0: mood_swinger0: >> Happiness level is at: 0 -- PETE: I'm feeling crappy... #> 0: mood_swinger0: >> Happiness level is at: 25 -- PETE: Feelin' a bit moody #> 0: mood_swinger0: >> Happiness level is at: 50 -- PETE: Just had a good espresso #> 0: mood_swinger0: >> Happiness level is at: 75 -- PETE: Let's do this! (and stop this loop...)
leave()
leave(., prob) 容许一个到达以必定几率离开整个轨迹:
patient_traj <- trajectory(name = "patient_trajectory") %>% seize("nurse", 1) %>% timeout(3) %>% release("nurse", 1) %>% log_("before leave") %>% leave(prob = 1) %>% log_("after leave") %>% # patients will never seize the doctor seize("doctor", 1) %>% timeout(3) %>% release("doctor", 1) env <- simmer() %>% add_resource("nurse", capacity=1) %>% add_resource("doctor", capacity=1) %>% add_generator("patient", patient_traj, at(0)) %>% run() #> 3: patient0: before leave get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 nurse 0 1 0 1 Inf 1 Inf 1 #> 2 nurse 3 0 0 1 Inf 0 Inf 1
固然, 几率也能够动态调整:
set.seed(1234) patient_traj <- trajectory(name = "patient_trajectory") %>% seize("nurse", 1) %>% timeout(3) %>% release("nurse", 1) %>% log_("before leave") %>% leave(prob = function() runif(1) < 0.5) %>% log_("after leave") %>% # some patients will seize the doctor seize("doctor", 1) %>% timeout(3) %>% release("doctor", 1) env <- simmer() %>% add_resource("nurse", capacity=1) %>% add_resource("doctor", capacity=1) %>% add_generator("patient", patient_traj, at(0, 1)) %>% run() #> 3: patient0: before leave #> 6: patient1: before leave #> 6: patient1: after leave get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 patient0 0 3 3 FALSE 1 #> 2 patient1 1 9 6 TRUE 1 get_mon_resources(env) #> resource time server queue capacity queue_size system limit replication #> 1 nurse 0 1 0 1 Inf 1 Inf 1 #> 2 nurse 1 1 1 1 Inf 2 Inf 1 #> 3 nurse 3 1 0 1 Inf 1 Inf 1 #> 4 nurse 6 0 0 1 Inf 0 Inf 1 #> 5 doctor 6 1 0 1 Inf 1 Inf 1 #> 6 doctor 9 0 0 1 Inf 0 Inf 1
clone(), synchronize()
clone(., n, ...)
提供复制 n-1 次到达几率的方法来并行处理子轨迹。synchronize(., wait, mon_all)
提供同步的方法来去除副本。默认,synchronize()
等待全部副本到达而且容许最后一个继续执行:
t <- trajectory() %>% clone(n = 3, trajectory("original") %>% timeout(1), trajectory("clone 1") %>% timeout(2), trajectory("clone 2") %>% timeout(3)) %>% synchronize(wait = TRUE) %>% timeout(0.5) env <- simmer(verbose = TRUE) %>% add_generator("arrival", t, at(0)) %>% run() #> 0 | source: arrival | new: arrival0 | 0 #> 0 | arrival: arrival0 | activity: Clone | 3, 3 paths #> 0 | arrival: arrival0 | activity: Timeout | 1 #> 0 | arrival: arrival0 | activity: Timeout | 2 #> 0 | arrival: arrival0 | activity: Timeout | 3 #> 1 | arrival: arrival0 | activity: Synchronize | 1 #> 2 | arrival: arrival0 | activity: Synchronize | 1 #> 3 | arrival: arrival0 | activity: Synchronize | 1 #> 3 | arrival: arrival0 | activity: Timeout | 0.5 get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 arrival0 0 3.5 3.5 TRUE 1
注意,参数 n
也能够是一个函数,若是有子轨迹须要clone,那么重复的子轨迹不须要反复声明。若是子轨迹数量小于 clone 数量,部分clone将直接继续下一个行动:
t <- trajectory() %>% clone(n = 3, trajectory("original") %>% timeout(1), trajectory("clone 1") %>% timeout(2)) %>% synchronize(wait = TRUE) %>% timeout(0.5) env <- simmer(verbose = TRUE) %>% add_generator("arrival", t, at(0)) %>% run() #> 0 | source: arrival | new: arrival0 | 0 #> 0 | arrival: arrival0 | activity: Clone | 3, 2 paths #> 0 | arrival: arrival0 | activity: Timeout | 1 #> 0 | arrival: arrival0 | activity: Timeout | 2 #> 0 | arrival: arrival0 | activity: Synchronize | 1 #> 1 | arrival: arrival0 | activity: Synchronize | 1 #> 2 | arrival: arrival0 | activity: Synchronize | 1 #> 2 | arrival: arrival0 | activity: Timeout | 0.5 get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 arrival0 0 2.5 2.5 TRUE 1
若是预期为弱依赖,但愿最快完成副本任务,那么 synchronize()
能够设置 wait = FALSE:
t <- trajectory() %>% clone(n = 3, trajectory("original") %>% timeout(1), trajectory("clone 1") %>% timeout(2), trajectory("clone 2") %>% timeout(3)) %>% synchronize(wait = FALSE) %>% timeout(0.5) env <- simmer(verbose = TRUE) %>% add_generator("arrival", t, at(0)) %>% run() #> 0 | source: arrival | new: arrival0 | 0 #> 0 | arrival: arrival0 | activity: Clone | 3, 3 paths #> 0 | arrival: arrival0 | activity: Timeout | 1 #> 0 | arrival: arrival0 | activity: Timeout | 2 #> 0 | arrival: arrival0 | activity: Timeout | 3 #> 1 | arrival: arrival0 | activity: Synchronize | 0 #> 1 | arrival: arrival0 | activity: Timeout | 0.5 #> 2 | arrival: arrival0 | activity: Synchronize | 0 #> 3 | arrival: arrival0 | activity: Synchronize | 0 get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 arrival0 0 1.5 1.5 TRUE 1
synchronize()
默认不记录被移除的 clone信息 (mon_all=FALSE),可是若是须要能够经过修改 mon_all=TRUE 来实现:
t <- trajectory() %>% clone(n = 3, trajectory("original") %>% timeout(1), trajectory("clone 1") %>% timeout(2), trajectory("clone 2") %>% timeout(3)) %>% synchronize(wait = FALSE, mon_all = TRUE) %>% timeout(0.5) env <- simmer(verbose = TRUE) %>% add_generator("arrival", t, at(0)) %>% run() #> 0 | source: arrival | new: arrival0 | 0 #> 0 | arrival: arrival0 | activity: Clone | 3, 3 paths #> 0 | arrival: arrival0 | activity: Timeout | 1 #> 0 | arrival: arrival0 | activity: Timeout | 2 #> 0 | arrival: arrival0 | activity: Timeout | 3 #> 1 | arrival: arrival0 | activity: Synchronize | 0 #> 1 | arrival: arrival0 | activity: Timeout | 0.5 #> 2 | arrival: arrival0 | activity: Synchronize | 0 #> 3 | arrival: arrival0 | activity: Synchronize | 0 get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 arrival0 0 1.5 1.5 TRUE 1 #> 2 arrival0 0 2.0 2.0 TRUE 1 #> 3 arrival0 0 3.0 3.0 TRUE 1
batch(), separate()
batch(., n, timeout, permanent, name, rule)
提供以必定几率收集必定数量的到达流后批量处理的方法。而后,经过 separate(.)
方法来分离以前创建的临时批次。这容许咱们实现一个过山车过程,举例:
有一个10人座的过山车,队列是20人排队参与,每次玩过山车持续5分钟,咱们能够将问题按以下方式建模:
set.seed(1234) t <- trajectory() %>% batch(10, timeout = 5, permanent = FALSE) %>% seize("rollercoaster", 1) %>% timeout(5) %>% release("rollercoaster", 1) %>% separate() env <- simmer() %>% # capacity and queue_size are defined in batches of 10 add_resource("rollercoaster", capacity = 1, queue_size = 2) %>% add_generator("person", t, function() rexp(1, 2)) %>% run(15) get_mon_arrivals(env, per_resource = TRUE) #> name start_time end_time activity_time resource replication #> 1 person0 3.800074 8.800074 5 rollercoaster 1 #> 2 person1 3.800074 8.800074 5 rollercoaster 1 #> 3 person2 3.800074 8.800074 5 rollercoaster 1 #> 4 person3 3.800074 8.800074 5 rollercoaster 1 #> 5 person4 3.800074 8.800074 5 rollercoaster 1 #> 6 person5 3.800074 8.800074 5 rollercoaster 1 #> 7 person6 3.800074 8.800074 5 rollercoaster 1 #> 8 person7 3.800074 8.800074 5 rollercoaster 1 #> 9 person8 3.800074 8.800074 5 rollercoaster 1 #> 10 person9 3.800074 8.800074 5 rollercoaster 1 #> 11 person10 8.800074 13.800074 5 rollercoaster 1 #> 12 person11 8.800074 13.800074 5 rollercoaster 1 #> 13 person12 8.800074 13.800074 5 rollercoaster 1 #> 14 person13 8.800074 13.800074 5 rollercoaster 1 #> 15 person14 8.800074 13.800074 5 rollercoaster 1 #> 16 person15 8.800074 13.800074 5 rollercoaster 1
这里建立了 3 个批次,前10我的都是在3.8分钟同时上车的。而后在第一波游玩结束时, 只有6我的在等待,可是 batch()
设置的计时器 timeout=5
已经到时了,另一波游客就能够发动了。由于这个 batch
设置了 (permanent=FALSE),因此能够用 separate()
方法将队列切开。
固然具体的rule
参数也能够用更精细粒度的选择哪些游客须要被组成一个批次。对于每一个特定的到达,默认都是一 rule = TRUE 返回。上面的例子,也能够经过 rule = FALSE,避免和其余乘客同时玩一个过山车。
t_batch <- trajectory() %>% batch(10, timeout = 5, permanent = FALSE, rule = function() FALSE) %>% seize("rollercoaster", 1) %>% timeout(5) %>% release("rollercoaster", 1) %>% separate() t_nobatch <- trajectory() %>% seize("rollercoaster", 1) %>% timeout(5) %>% release("rollercoaster", 1) set.seed(1234) env_batch <- simmer() %>% # capacity and queue_size are defined in batches of 10 add_resource("rollercoaster", capacity = 1, queue_size = 2) %>% add_generator("person", t_batch, function() rexp(1, 2)) %>% run(15) set.seed(1234) env_nobatch <- simmer() %>% # capacity and queue_size are defined in batches of 10 add_resource("rollercoaster", capacity = 1, queue_size = 2) %>% add_generator("person", t_nobatch, function() rexp(1, 2)) %>% run(15) get_mon_arrivals(env_batch, per_resource = TRUE) #> name start_time end_time activity_time resource replication #> 1 person0 1.250879 6.250879 5 rollercoaster 1 #> 2 person1 1.374259 11.250879 5 rollercoaster 1 get_mon_arrivals(env_nobatch, per_resource = TRUE) #> name start_time end_time activity_time resource replication #> 1 person0 1.250879 6.250879 5 rollercoaster 1 #> 2 person1 1.374259 11.250879 5 rollercoaster 1
默认,批次的 name
参数为空,它表示每一个乘客是独立的,可是,有趣的是怎么给不一样轨迹赋予相同批次。好比,咱们能够尝试:
t0 <- trajectory() %>% batch(2) %>% timeout(2) %>% separate() t1 <- trajectory() %>% timeout(1) %>% join(t0) env <- simmer(verbose = TRUE) %>% add_generator("t0_", t0, at(0)) %>% add_generator("t1_", t1, at(0)) %>% run() #> 0 | source: t0_ | new: t0_0 | 0 #> 0 | arrival: t0_0 | activity: Batch | 2, 0, 0, #> 0 | source: t1_ | new: t1_0 | 0 #> 0 | arrival: t1_0 | activity: Timeout | 1 #> 1 | arrival: t1_0 | activity: Batch | 2, 0, 0, get_mon_arrivals(env) #> [1] name start_time end_time activity_time finished #> <0 rows> (or 0-length row.names)
咱们没有得到预期的两个不一样批次结果。t1
紧跟着 t0
到达,则意味着实际状况是下面这样:
t0 <- trajectory() %>% batch(2) %>% timeout(2) %>% separate() t1 <- trajectory() %>% timeout(1) %>% batch(2) %>% timeout(2) %>% separate()
所以到达流紧随着一个不一样轨迹将终止在一个不一样批次上。除非,有一个方法共享 batch()
的行动,如今能够经过 name
参数实现。
t0 <- trajectory() %>% batch(2, name = "mybatch") %>% timeout(2) %>% separate() t1 <- trajectory() %>% timeout(1) %>% batch(2, name = "mybatch") %>% timeout(2) %>% separate() env <- simmer(verbose = TRUE) %>% add_generator("t0_", t0, at(0)) %>% add_generator("t1_", t1, at(0)) %>% run() #> 0 | source: t0_ | new: t0_0 | 0 #> 0 | arrival: t0_0 | activity: Batch | 2, 0, 0, mybatch #> 0 | source: t1_ | new: t1_0 | 0 #> 0 | arrival: t1_0 | activity: Timeout | 1 #> 1 | arrival: t1_0 | activity: Batch | 2, 0, 0, mybatch #> 1 | arrival: batch_mybatch | activity: Timeout | 2 #> 3 | arrival: batch_mybatch | activity: Separate | get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 t0_0 0 3 2 TRUE 1 #> 2 t1_0 0 3 3 TRUE 1 Or, equivalently, t0 <- trajectory() %>% batch(2, name = "mybatch") %>% timeout(2) %>% separate() t1 <- trajectory() %>% timeout(1) %>% join(t0) env <- simmer(verbose = TRUE) %>% add_generator("t0_", t0, at(0)) %>% add_generator("t1_", t1, at(0)) %>% run() #> 0 | source: t0_ | new: t0_0 | 0 #> 0 | arrival: t0_0 | activity: Batch | 2, 0, 0, mybatch #> 0 | source: t1_ | new: t1_0 | 0 #> 0 | arrival: t1_0 | activity: Timeout | 1 #> 1 | arrival: t1_0 | activity: Batch | 2, 0, 0, mybatch #> 1 | arrival: batch_mybatch | activity: Timeout | 2 #> 3 | arrival: batch_mybatch | activity: Separate | get_mon_arrivals(env) #> name start_time end_time activity_time finished replication #> 1 t0_0 0 3 2 TRUE 1 #> 2 t1_0 0 3 3 TRUE 1
send(), trap(), untrap(), wait()
这组行动容许异步编程。经过 send(., signals, delay)
广播一个或者一组信号给到每一个订阅信息的到达流。信号能够当即被触发:
t <- trajectory() %>% send(signals = c("signal1", "signal2")) simmer(verbose = TRUE) %>% add_generator("signaler", t, at(0)) %>% run() %>% invisible #> 0 | source: signaler | new: signaler0 | 0 #> 0 | arrival: signaler0 | activity: Send | [signal1, signal2], 0 #> 0 | task: Broadcast | : |
或者安排在一些延迟以后:
t <- trajectory() %>% send(signals = c("signal1", "signal2"), delay = 3) simmer(verbose = TRUE) %>% add_generator("signaler", t, at(0)) %>% run() %>% invisible #> 0 | source: signaler | new: signaler0 | 0 #> 0 | arrival: signaler0 | activity: Send | [signal1, signal2], 3 #> 3 | task: Broadcast | : |
注意,这两个参数,signals
和delay
,能够是函数,所以他们能够从到达流中获取的属性值。
若是无人监听,广播其实没意义。到达流订阅广播而后能够用 trap(., signals, handler, interruptible)
来赋予一个处理器。在下面的例子中,一个到达流订阅一个信号而且阻塞知道收到 wait(.)
方法。
t_blocked <- trajectory() %>% trap("you shall pass") %>% log_("waiting...") %>% wait() %>% log_("continuing!") t_signaler <- trajectory() %>% log_("you shall pass") %>% send("you shall pass") simmer() %>% add_generator("blocked", t_blocked, at(0)) %>% add_generator("signaler", t_signaler, at(5)) %>% run() %>% invisible #> 0: blocked0: waiting... #> 5: signaler0: you shall pass #> 5: blocked0: continuing!
注意信号能够被忽略,当到达流是在资源队列中等待。相同的操做也能够在批处理中执行:全部在进入批次以前的被订阅信息都将被忽略。所以,下面的批次将被无限阻塞:
t_blocked <- trajectory() %>% trap("you shall pass") %>% log_("waiting inside a batch...") %>% batch(1) %>% wait() %>% log_("continuing!") t_signaler <- trajectory() %>% log_("you shall pass") %>% send("you shall pass") simmer() %>% add_generator("blocked", t_blocked, at(0)) %>% add_generator("signaler", t_signaler, at(5)) %>% run() %>% invisible #> 0: blocked0: waiting inside a batch... #> 5: signaler0: you shall pass #> inf: batch0: continuing!
在接收信号,中止当前活动并执行处理程序提供。而后,执行后返回到活动中断的点:
t_worker <- trajectory() %>% trap("you are free to go", handler = trajectory() %>% log_("ok, I'm packing...") %>% timeout(1) ) %>% log_("performing a looong task...") %>% timeout(100) %>% log_("and I'm leaving!") t_signaler <- trajectory() %>% log_("you are free to go") %>% send("you are free to go") simmer() %>% add_generator("worker", t_worker, at(0)) %>% add_generator("signaler", t_signaler, at(5)) %>% run() %>% invisible #> 0: worker0: performing a looong task... #> 5: signaler0: you are free to go #> 5: worker0: ok, I'm packing... #> 6: worker0: and I'm leaving!
最后,untrap(., signals)
来根据 signals
执行退订:
t_worker <- trajectory() %>% trap("you are free to go", handler = trajectory() %>% log_("ok, I'm packing...") %>% timeout(1) ) %>% log_("performing a looong task...") %>% untrap("you are free to go") %>% timeout(100) %>% log_("and I'm leaving!") t_signaler <- trajectory() %>% log_("you are free to go") %>% send("you are free to go") simmer() %>% add_generator("worker", t_worker, at(0)) %>% add_generator("signaler", t_signaler, at(5)) %>% run() %>% invisible #> 0: worker0: performing a looong task... #> 5: signaler0: you are free to go #> 100: worker0: and I'm leaving!
Signal 处理器默认是能够被打断,这意味着若是有大量频繁的请求信号,处理器会反复重启:
t_worker <- trajectory() %>% trap("you are free to go", handler = trajectory() %>% log_("ok, I'm packing...") %>% timeout(1) ) %>% log_("performing a looong task...") %>% timeout(100) %>% log_("and I'm leaving!") t_signaler <- trajectory() %>% log_("you are free to go") %>% send("you are free to go") simmer() %>% add_generator("worker", t_worker, at(0)) %>% add_generator("signaler", t_signaler, from(5, function() 0.5)) %>% run(10) %>% invisible #> 0: worker0: performing a looong task... #> 5: signaler0: you are free to go #> 5: worker0: ok, I'm packing... #> 5.5: signaler1: you are free to go #> 5.5: worker0: ok, I'm packing... #> 6: signaler2: you are free to go #> 6: worker0: ok, I'm packing... #> 6.5: signaler3: you are free to go #> 6.5: worker0: ok, I'm packing... #> 7: signaler4: you are free to go #> 7: worker0: ok, I'm packing... #> 7.5: signaler5: you are free to go #> 7.5: worker0: ok, I'm packing... #> 8: signaler6: you are free to go #> 8: worker0: ok, I'm packing... #> 8.5: signaler7: you are free to go #> 8.5: worker0: ok, I'm packing... #> 9: signaler8: you are free to go #> 9: worker0: ok, I'm packing... #> 9.5: signaler9: you are free to go #> 9.5: worker0: ok, I'm packing...
若是须要实现一个不能打断的处理器,能够经过设置合适的 flag 实现:
t_worker <- trajectory() %>% trap("you are free to go", handler = trajectory() %>% log_("ok, I'm packing...") %>% timeout(1), interruptible = FALSE # make it uninterruptible ) %>% log_("performing a looong task...") %>% timeout(100) %>% log_("and I'm leaving!") t_signaler <- trajectory() %>% log_("you are free to go") %>% send("you are free to go") simmer() %>% add_generator("worker", t_worker, at(0)) %>% add_generator("signaler", t_signaler, from(5, function() 0.5)) %>% run(10) %>% invisible #> 0: worker0: performing a looong task... #> 5: signaler0: you are free to go #> 5: worker0: ok, I'm packing... #> 5.5: signaler1: you are free to go #> 6: worker0: and I'm leaving! #> 6: signaler2: you are free to go #> 6.5: signaler3: you are free to go #> 7: signaler4: you are free to go #> 7.5: signaler5: you are free to go #> 8: signaler6: you are free to go #> 8.5: signaler7: you are free to go #> 9: signaler8: you are free to go #> 9.5: signaler9: you are free to go
renege_in(), renege_if(), renege_abort()
renege_in(., t, out)
方法提供设置超时时间来出发到达流放弃轨迹的退出机制。中途退出后,到达流能够选择从一个子轨迹中出去。renege_abort(.)
方法提供了一个反悔机制。这些方法容许咱们作一些事情,好比,创建有限病人的模型。下面的例子中,用户 到达银行,只有一个职员处于服务态。 客服在等待5分钟后若是还不能服务能够选择离开。
t <- trajectory(name = "bank") %>% log_("Here I am") %>% # renege in 5 minutes renege_in(5, out = trajectory() %>% log_("Lost my patience. Reneging...") ) %>% seize("clerk", 1) %>% # stay if I'm being attended within 5 minutes renege_abort() %>% log_("I'm being attended") %>% timeout(10) %>% release("clerk", 1) %>% log_("Finished") simmer() %>% add_resource("clerk", 1) %>% add_generator("customer", t, at(0, 1)) %>% run() %>% invisible #> 0: customer0: Here I am #> 0: customer0: I'm being attended #> 1: customer1: Here I am #> 6: customer1: Lost my patience. Reneging... #> 10: customer0: Finished
一样也能够经过 renege_if(., signal, out)
实现,假设 在 t=5时刻, customer0 发送一个消息给 customer1:
t <- trajectory(name = "bank") %>% log_("Here I am") %>% # renege when "renege now" is received renege_if("renege now", out = trajectory() %>% log_("Ok. Reneging...") ) %>% seize("clerk", 1) %>% # stay if I'm being attended within 5 minutes renege_abort() %>% log_("I'm being attended") %>% timeout(5) %>% log_("I say: renege now") %>% send("renege now") %>% timeout(5) %>% release("clerk", 1) %>% log_("Finished") simmer() %>% add_resource("clerk", 1) %>% add_generator("customer", t, at(0, 1)) %>% run() %>% invisible #> 0: customer0: Here I am #> 0: customer0: I'm being attended #> 1: customer1: Here I am #> 5: customer0: I say: renege now #> 5: customer1: Ok. Reneging... #> 10: customer0: Finished
注意,和 trap()
不一样的是, reneg*
是直接被触发的,即便到达流还在队列或者临时批次中。
join()
join(...)
将任意多个轨迹聚合,好比:
t1 <- trajectory() %>% seize("dummy", 1) t2 <- trajectory() %>% timeout(1) t3 <- trajectory() %>% release("dummy", 1) t0 <- join(t1, t2, t3) t0 #> trajectory: anonymous, 3 activities #> { Activity: Seize | resource: dummy, amount: 1 } #> { Activity: Timeout | delay: 1 } #> { Activity: Release | resource: dummy, amount: 1 }
或者,它可能嵌套使用,相似另外一个行为:
t0 <- trajectory() %>% join(t1) %>% timeout(1) %>% join(t3) t0 #> trajectory: anonymous, 3 activities #> { Activity: Seize | resource: dummy, amount: 1 } #> { Activity: Timeout | delay: 1 } #> { Activity: Release | resource: dummy, amount: 1 }
原文做者: Iñaki Ucar, Bart Smeets 译者: Harry Zhu 英文原文地址:
https://r-simmer.org/articles...做为分享主义者(sharism),本人全部互联网发布的图文均听从CC版权,转载请保留做者信息并注明做者 Harry Zhu 的 FinanceR专栏:https://segmentfault.com/blog...,若是涉及源代码请注明GitHub地址:https://github.com/harryprince。微信号: harryzhustudio商业使用请联系做者。