2

我需要向通过 API 创建的 Schedule 对象添加一个 Action。有文档化的界面可以设置几乎所有的选项,除了 Action。动作是如何附加到这些对象上的?

当我尝试以编程方式将一个新事件(从单独的配置文件读取)添加到 Schedule 对象时,我收到错误消息,指出 Schedule 已被初始化,我必须构造一个新对象并手动添加其配置。我可以使用可用的 Schedule API 来完成大部分工作。我可以设置有关计划的所有内容,除了操作代码。

计划用于流程模型。查看 Java 编辑器中的模型,我看到我试图通过 API 复制的代码如下所示:

  @Override
  @AnyLogicInternalCodegenAPI
  public void executeActionOf( EventTimeout _e ) {
    if ( _e == _fuelDeliverySchedule_Action_xjal ) {
      Schedule<Integer> self = this.fuelDeliverySchedule;
      Integer value = fuelDeliverySchedule.getValue();

logger.info("{} received {} pounds of fuel", this.getName(), this.fuelDeliverySchedule.getValue());

this.fuelAvailablePounds += fuelDeliverySchedule.getValue();

;
      _fuelDeliverySchedule_Action_xjal.restartTo( fuelDeliverySchedule.getTimeOfNextValue() );
      return;
    }
    super.executeActionOf( _e );
  }

也许我可以使用这样的东西来创建自己的动作函数,但我不确定如何让 Scheduled 事件使用它。

谢谢,汤姆

4

1 回答 1

2

[编辑(扩展/重写)03.11.2014 在上下文的更多用户详细信息之后。]

你澄清了上下文

当我尝试以编程方式将“发生的事情”(从单独的配置文件中读取)添加到 Schedule 对象时,我收到错误消息,指出 Schedule 已被初始化,我必须构造一个新对象并手动添加其配置。我可以使用可用的 Schedule API 来完成大部分工作。我可以设置有关计划的所有内容,除了操作代码。

(您可能希望将其编辑到问题中......一般来说,解释为什么您尝试做这件事的背景总是很好的。)

我想我现在明白了。我假设您的配置文件包含调度详细信息,并且当您说您试图“添加发生的事情”(错误)时,您的意思是您试图更改Schedule. 所以您的问题是,由于您无法调整预先存在的计划,您必须以编程方式实例化(创建)您自己的,但ScheduleAPI 不允许您设置操作代码(如 GUI 计划元素所示) )。

这是一个相当复杂的解决方案,所以请耐心等待。在深入细节之前,我会给出一个简短的“tl;dr”总结。

概括

您不能以编程方式编写 AnyLogic 操作(针对任何元素),因为这相当于动态创建 Java 类。解决您的问题需要认识到 schedule GUI元素会创建一个Schedule实例一个超时事件 ( EventTimeout) 实例来触发操作。因此,您可以自己显式创建这两个元素(前者是动态的)。诀窍是在替换实例时重置超时事件Schedule(在 new 的下一个“翻转”点触发Schedule)。

[实际上,从您的措辞来看,我怀疑该操作总是相同的,但是,一般来说,如果您的配置文件详细信息可能想要更改操作的性质以及调度模式的性质,我将展示您如何处理它. ]

细节

问题是 GUI 元素(令人困惑)不仅仅是它生成的代码的一个Schedule实例。有一个(与 GUI 元素的名称相同),它只包含计划“模式”,并且与 API 一样,具有确定下一个开/关时间段的方法(对于开/关计划)发生。(所以它是一种花哨的日历功能。)但是 AnyLogic 也会生成一个 超时事件来实际执行操作。如果您进一步查看生成的代码,您会看到类似于下面的内容(假设您的 GUI 计划被调用 fuelSchedule,我添加了 Java 注释):

  // Definition of the timeout event
  @AnyLogicInternalCodegenAPI
  public EventTimeout _fuelSchedule_Action_xjal = new EventTimeout(this);

  // First occurrence time of the event as the next schedule on/off change
  // time
  @Override
  @AnyLogicInternalCodegenAPI
  public double getFirstOccurrenceTime( EventTimeout _e ) {

    if ( _e == _fuelSchedule_Action_xjal ) return fuelSchedule.getTimeOfValue() == time() ? time() : fuelSchedule.getTimeOfNextValue();
    return super.getFirstOccurrenceTime( _e );
  }

  // After your user action code, the event is rescheduled for the next
  // schedule on/off change time
   _fuelSchedule_Action_xjal.restartTo( fuelSchedule.getTimeOfNextValue() );

即,这将创建一个事件,该事件在每次调度“翻转”时触发,并执行 GUI 调度元素中指定的操作。

所以没有对Schedule实例进行更改的操作;它实际上与EventTimeout实例有关。但是,您不能在此处以编程方式更改它(或动态创建一个新的),原因与您无法更改任何AnyLogic 元素的操作相同:这实际上是以编程方式创建 Java 类定义,而不是无需非常专业的 Java 代码即可。(您可以在字符串中创建 Java 源代码并在其上动态运行 Java 编译器以生成一个类。但是,这是非常“高级”的 Java,有很多潜在的陷阱,我绝对不建议走这条路。你还必须为 的用户子类创建源代码EventTimeout,因为您不知道 AnyLogic 专有的正确源代码EventTimeout类,在任何情况下,这可能会在每个版本中发生变化。)

但是您不需要:您的配置文件可以包含一组严格的可能操作。(它们不能是任意的 Java 代码片段,因为它们必须“适应”模拟。)因此,您可以通过以编程方式创建Schedule 但使用您相应调整的 GUI 创建的超时事件来做您想做的事情(假设在这里关闭/打开时间表,并且一次只有一个时间表处于活动状态;显然根据您的需要调整这个框架,我还没有在 AnyLogic 中完全测试过):

1.有一个 AnyLogic 变量activeAction来指定当前的活动动作。(为简单起见,我将其视为int此处,但最好使用enum与 AnyLogic 7 选项列表相同的 Java,并且可以在 AnyLogic 6 中使用原始 Java 创建。)

2.在 GUI 中创建一个变量fuelSchedule,比如名为,类型为 ,Schedule但具有初始值null。在用户控制模式下创建一个单独的超时事件,比如调用fuelScheduleTrigger,操作如下:

// Perform the appropriate action (dependent on activeAction)
doAppropriateScheduleAction();
// Set the event to retrigger at the next schedule on/off switch time
fuelScheduleTrigger.restartTo(fuelSchedule.getTimeOfNextValue());

(在用户控制模式下,这个事件还没有被触发来初始触发,这是我们想要的。)

3.为每个不同的动作选择编写一组函数;假设这里只有 2 个 (fuelAction1fuelAction2) 作为示例。代码 doAppropriateScheduleAction为:

if (activeAction == 1) {
    fuelAction1();
}
else if (activeAction == 2) {
    fuelAction2();
}

4.在您​​的代码中读取配置文件并获取更新的计划信息。(可能从循环超时事件或类似事件中运行),将其替换 fuelSchedule为具有修改后的计划模式的新实例(正如您一直在做的那样),activeAction适当设置,然后将超时事件重置为新fuelSchedule.getTimeOfValue()时间:

[set up fuelSchedule and activeAction]
// Reset schedule action to match revised schedule
fuelScheduleTrigger.restartTo(fuelSchedule.getTimeOfNextValue());

我认为这在边缘情况下可以正常工作,因为Schedule在你设置它时新的下一次“翻转”。(如果您将事件重新启动到当前时间,我认为它会在当前时间安排一个事件 OK,如果当前时间没有其他事件也安排在下一次发生;实际上,如果您正在使用它肯定会发生下一次一个 LIFO 同步时间调度机制——参见我的博客文章。)

替代和 AnyLogic 增强

另一种方法是在 GUI 中创建一个“完整”计划,并使用前面的操作。您的配置文件读取代码可以替换底层Schedule实例,然后重置内部AnyLogic 生成的超时事件。但是,这不太可取,因为您依赖于内部命名的 AnyLogic 事件(这也可能在未来的 AnyLogic 版本中发生变化,从而破坏您的代码)。

AnyLogic 可以通过向ScheduleAPI 添加一个获取相关超时事件的方法来帮助解决这种情况;例如,getActionTriggeringEventTimeout()。然后,您能够“正确”重新启动它,并且ScheduleAPI 会更清楚地表明Schedule始终与EventTimeout触发动作的 an 相关联。

当然,AnyLogic 还可以通过更改 Schedule 来更进一步,以允许动态更改调度细节(如果继续这样设计,则在内部处理超时事件所需的更新),但这需要做更多的工作,可能会有更深层次的技术原因,为什么他们希望在调度初始化后修复调度模式。

有任何 AnyLogic 支持人员阅读吗?

于 2014-10-31T10:08:45.127 回答