标题:Error|Exception handling in AnyLogic models
链接:https://www.theanylogicmodeler.com/post/error-exception-handling-in-anylogic-models
作者:Yash Mishra
本文为您分享了一篇来自 Yash Mishra 的特邀客座博文。文章简要概述了如何管理 AnyLogic 仿真模型中的错误,展示了 Java 中可用的标准错误处理架构的示例,并以在GIS地图中尝试查找路线为例,说明了如何在 AnyLogic 中实际使用它。他还展示了如何在代码中抛出自己的错误,以创建一个更强大的模型,当事情没有按预期进行时模型会向您发出警告。
注意:这篇文章只是对错误处理的快速概述。
在建立仿真模型时,我们经常会遇到错误。有时在编译模型时会出现编译错误,有时在执行模型时会出现运行错误。当这些错误发生时,我们会查看控制台中的错误详细信息,并努力纠正它们。本文将重点介绍如何处理运行时错误。
技术说明:Errors 和 Exceptions 是 Java 中的两个不同的类,在这篇文章中,为简单起见,我们将交替使用错误和异常。
运行时的错误可能由于各种原因而发生,一旦发生模型执行将立即停止。有一些运行时的错误,如 OutOfMemoryError、VirtualMachineError 等,是不可恢复的。但仍有许多运行错误是可以处理的,例如dividingByZero、indexOutOfBounds 等,以及著名的 NullPointerException。有时我们希望与其在这些错误发生时模型直接运行失败,不如在错误发生时捕获它们,处理它们并控制接下来的步骤。
所有此类错误都可以通过称为异常处理的强大机制来处理。在 Java 中,它是通过 try-and-catch 块完成的。
try { //Do some actions that might cause an error, e.g. divide by zero } catch(Exception e) { //print cannot divide by zero }
将可能引发错误的代码放在 try-and-catch 块中,模型就能够自行处理错误并避免模型执行突然停止。
Java 中的某些函数会强制您应用错误处理。尝试输入下面的代码,看看您得到的编译错误。看看您是否可以使用 try-catch 方法解决它 ;-)
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
Date myDate = dateFormatter.parse("2021-02-01");
一
让我们深入了解这个概念,看看异常处理的实际应用。最近,我正在为一家电子商务公司建立一个模型,在运输部门,有一个要求是找到交付订单的最佳路线。交付订单的首选方式是使用铁路路线(如果有),如果没有,则使用公路。
为了启动模型,我使用空间标记(Space Markup)面板中可用的GIS地图组件。
由于首选路线是铁路,我们在GIS地图组件的参数上将Road type选择为“Rail“
为了确定最佳路线,我们需要获取两个位置之间的距离。在地图上,我们可以使用GIS地图组件提供的 getDistanceByRoute 功能计算起始地点和目标地点之间的距离并获取路线。
map.getDistanceByRoute(latFrom, lonFrom, latTo, lonTo)
对于这样一个简单的样例,可以创建一个按钮控件,并在按钮控件的单击部分中应用上述功能来查找距离,使用纽约作为起始地址 (40.71, 74.00) ,华盛顿特区作为目的地 (38.90, 77.03)。
我们可以看到它根据铁路路线返回铁路距离。棒极了,不是吗?
现在让我们来看另一个例子,我们的起始点是巴尔的摩(Baltimore)郊区的某个地方(39.76,-77.51),目的地是多伦多(Toronto)附近的一个偏远地区(43.95,-81.27)。
它向我展示了一条直线......这看起来可不像一条铁路。事情有些不对......
当我仔细查看地图属性时,我发现如果铁路线路不存在,我需要选择“show error dialog”。
然而,如果选择“show error dialog”,当不存在轨道路线时,它将产生错误,但这无法解决我的问题,因为当弹出错误对话框时,模型将立刻停止。但实际上我们想找到一条替代的公路路线......
因此,除了显示错误对话框(Show error dialog)外,我还需要一些东西来处理GIS地图组件在没有可用铁路路线时产生的错误。为了克服这个问题,我们将使用 try-and-catch 块的错误处理方法。
因此,在我的按钮控件单击部分中,我编写了以下代码来处理错误并保护模型不会突然停止。
try {
message = "The Rail distance is: " +
format(map.getDistanceByRoute(39.76, -77.51, 43.95,
-81.27)/1000) + "KM";
} catch(Error | Exception e) {
message = "couldn't find the route by rail, will deliver by road";
}
P.S.代码“Erro
r | Exception”仅表示“...捕获错误或异常”。您可以添加想要捕获的任意数量的未测试类类型,并简单地用“|”分隔它们。
由于错误处理,我们可以保持模型运行,但如果没有可用的铁路路线,业务需要通过公路交付产品。出于所有实际目的,AnyLogic 中的GIS地图组件可以生成铁路或公路的路线,但不能同时使用两者的路线。
因此,想到的第一个解决方案是再添加另一个GIS地图组件来计算公路距离,如果第一个GIS地图组件由于铁路路线不可用而引发错误,我们将调用第二个GIS地图组件......
AnyLogic 不允许在同一个智能体上添加两个GIS地图组件。
为了克服这个问题,AnyLogic 提供了一个 Route Provider 组件,该组件将使用GIS地图组件来查找距离。
所以我们可以在同一个智能体上拥有两个Route Provider 组件(这是允许的 )。一个Route Provider 寻找铁路路线,另一个寻找公路路线。Route Provider 组件有一个 getRoute() 函数,该函数将GIS地图组件以及起始和目标坐标作为输入,并将路线作为输出返回。
try { traceln("The distance by Rail is :" + routeProviderRail.getRoute(map, gisPointSource, gisPointDestination).length(LENGTH_UNIT_KILOMETER)); } catch(Error | Exception e) { traceln("The distance by Road is :" + routeProviderCar.getRoute(map, gisPointSource, gisPointDestination).length(LENGTH_UNIT_KILOMETER)); }
可以使用空间标记面板中的gisPoint 组件,而不是上面代码中使用的位置坐标。将它们作为标记放置在地图上,会自动从GIS地图组件中获取位置坐标。
一
✱选项A:在一个智能体内创建错误
我们还可以使用 AnyLogic Utilities 类提供的error function 在运行时抛出错误。这将引发由 AnyLogic 引擎处理的错误。当遇到任何其他错误时,如果不在 try-catch 环境中处理,也会突然停止模型。
//generating 1 with 90% and 0 with 10% probability int x = binomial(0.9, 1); //if x is 0 produce an error if(x==0) { error("value of x is 0, which is invalid. Hence, error"); } else { traceln("Happy life - No errors!!!"); }
上面的代码被放置在一个循环事件中——一旦 x 变为 0,模型就会产生错误并停止。而且,当您查看控制台时,您将能够看到 if 块中的错误字符串。
✱选项B:在Java类中创建错误
自然,Utilities 类在 Java 类中不可用,除非您在创建Java类的实例时将某个智能体作为参数传递,因为智能体可以访问 Utilities 类。要在 Java 类中创建错误或异常,您不仅需要创建新的错误类,还需要“抛出”它。
if (fuelLevel < 0) { throw new RuntimeException("Fuellevel can never be negative"); }
有多种类型的错误和异常可用,例如 RuntimeException、ParseError 等
一
总之:能够在仿真中处理和创建运行时错误是一项非常有用的技能。有些函数需要您处理它们可能抛出的错误,而对于其他函数,您可能想要捕获它们,然后执行一些其他操作以让您更深入地了解错误,甚至尝试替代操作。