within an AnyLogic project, in the 'seize' block I need to make a custom choice of resources from resource sets.
Having in the properties tab of the 'seize' block the field "Resource sets" with the value { {ResourcePool_A, ResourcePool_B} } and the flag "customize resource choice" checked. In the "resource choice condition" code section, I need to make a choice like:
if (unit isfrom ResourcePool_A)
{
if (unit.param_a == value)
do something
....
}
else if (unit isfrom ResourcePool_B)
{
if (unit.param_b == value)
do something
....
}
How can I check if a resource unit is from a given pool or not and then discriminate resources accordingly with their features? Thank you. Best regards.
from your question it seems that you don't need to choose a specific resource, but rather do a specific set of actions on the resource once it has been seized.
which is why i've added two answers.
1.
If you just want to do a specific set of action. You should just copy your code to the "On seize unit" action in the seize object.
2.
If you want to select a specific resource. the easiest way to do that is to create an Anylogic function resource_selector()that returns a boolean.
if(unit isfrom ResourcePool_A && unit.param_foo == agent.param_bar)
...
your own code
...
return true;
else
return false;
and then in the Resource choice condition write:
resource_selector(unit, agent);
I solved the issue writing a an Anylogic function that returns a boolean and I used it in the resource choice condition. I implemented "isfrom" to discriminate from which pools the resource is picked up as shown in following code:
`
// cast pool object to the prorper type
ResourcePool t_pool = (ResourcePool)pool;
// resource selection condition
if ( (t_pool == ResourcePool_A && ((Resource_A)unit).param == agent.param_bar) ||
(t_pool == ResourcePool_B && ((Resource_B)unit).param == agent.param_bar) ) {
return true;
}
else {
return false;
}
`
In the Anylogic documentation is not explained that in the resource choice condition of the seize block you have access also to the pool object (this is bad...).
Related
Agents being pushed through the model have a variable isMorning which is set to false, but is made true if they are injected before a certain time. When the agents enter a seize block, I would like to add a condition that relies on if isMorning is true or not in order to pick a resource set. I would like to know the proper syntax in using conditionals within the Resource sets parameter to choose between different resource sets, or if this must be done in another parameter.
Best option is to create a new function e.g. getResoruceSet(boolean isMorning and then return ResourcePool[][]
You can then use this function to replace the resource set parameter in the seize block
your code inside the function needs to create new arrays for ResorucePool where the first dimension is the number of resources that needs to be seized and the second is the different sets.
For example:
if (isMorning) {
return new ResourcePool[][] {
{ resourcePool, resourcePool }
};
} else {
return new ResourcePool[][] {
{ resourcePool1},
{ resourcePool, resourcePool2, resourcePool2}
};
}
If it is morning you need to seize 2 units of resourcePool. If it is not morning you either need to seize 1 unit from resorucePool1 or 2 unit from resourcePool and 2 from resorucePool2
I'm trying to work with lists in drools. I'm passing in a request which has a purchase list as part of it. I want to do several rules including checking if the size is correct, then if all elements are the same, if all purchases are authorized, ... I have the following code but I'm running into problems working with the list. Is this the right approach? Especially when checking for the size?
import com.rules.Purchase
import com.rules.PurchaseRequest
dialect "mvel"
global Boolean eligibleForRefund
rule "Check for list not equal to two elements" salience 10
when
PurchaseRequest(getPurchases != null, getPurchases.size() != 2)
then
drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end
rule "Check for two purchases" salience 9
when:
$purchaseRequest: PurchaseRequest()
Number(intValue != 2) from accumulate(Purchase(getStatus() == "Approved") from $purchaseRequest.getPurchases(), count(1))
then
drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end
rule "Check for the same purchases" salience 8
when:
$purchaseRequest: PurchaseRequest()
then
firstPurchase = $purchaseRequest.getPurchases().get(0).getCost();
hasAllElements = true;
for (Purchase purchase : $purchaseRequest.getPurchases()) {
if (purchase.getCost() != firstPurchase) {
hasAllElements = false;
}
}
drools.getKieRuntime().setGlobal("eligibleForRefund", hasAllElements);
end
Assuming that your class definition looks like this:
class PurchaseRequest {
private List<Purchase> purchases;
public List<Purchase> getPurchases() { return this.purchases; }
}
You should be pulling references out of the holder instead of constantly interacting with things via the getters. In other projects this helps with keep data consistent especially with shared resources. Recall that if you have a getter whose name matches the format getXyz, you can refer to it simply as xyz and drools will automagically map it to the getter function. This allows us to get the purchases via PurchaseRequest( $purchases: purchases ) since purchases will be mapped to getPurchases(). (Note that if purchases happened to be a public variable, it would have mapped to that first; but since it's private it falls back on the public getter that follows bean naming conventions.)
Second you use an accumulate in a very simple scenario where a collect would probably be more appropriate. Generally you'd use accumulate for more complicated "get things that look like this" sort of situations; but for simple matching, a collect works just as well.
The third rule needs the most work. You do not want to do this kind of business logic on the right hand side of your rule. There's a whole lot of ways you could go about checking that all the elements are the same -- if you've implemented equals/hashCode you could just shove everything into a set and confirm that the length of the set is still the length of the list; you could invert the rule to instead check for at least one item that's different; you could use accumulate or collect; ...
Finally --
Avoid saliences. They're bad design. Your rules should stand alone. You only need saliences here because your third rule sets both true and false. If instead you defaulted to true and then used the rules to override it to false, you could get away with having absolutely no saliences at all.
It's very unusual to use primitives for a global variable. I'm frankly not convinced that this will even work with a primitive. Globals work because the object is passed in by reference, and updated in the rules, and therefore the caller which retains the reference to the object will get the updated value. That doesn't work with primitives.
rule "Check for list not equal to two elements"
salience 1
when
PurchaseRequest($purchases: purchases != null)
List(size != 2) from $purchases
then
drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end
rule "Check for two purchases"
salience 1
when:
PurchaseRequest( $purchases: purchases != null)
List( size != 2 ) from collect( Purchase(status == "Approved") from $purchases)
then
drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end
// I've no idea what data type `getCost()` returns; I'm assuming "String"
rule "Check for the same purchases"
when:
PurchaseRequest($purchases: purchases != null)
// accumulate all of the costs into a set. if all costs are the same, set size = 1
$costs: Set() from accumulate( Purchase( $cost: cost ) from $purchases;
collectSet($cost))
then
drools.getKieRuntime().setGlobal("eligibleForRefund", $costs.size() == 1);
end
I'm having an object as below:
class License{
private field1;
private field2;
private boolean active;
private String activeMessage;
private boolean processed = false;
//Getter and setter methods
}
What I'm trying to do is, based on the values of field1, and field2, I need to set the isActive flag and a corresponding message. However, if either the rule for field1 or field2 is fired, I need to stop the rules processing. That is, I need to execute only 1 successful rule.
I read on a post that doing ksession.fireAllRules(1) will solve this. But the fireAllRules() method is not available in Drools 6. I also tried putting a return; statement at the end of each rule. That didn't help me either.
Finally, I ended up adding an additional field to my object called processed. So whenever I execute any rule, I set the processed flag to true. And if the flag is already set, then I do not execute any rule. This is my rules file:
rule "Check field1"
when
$obj : License(getField1() == "abc" && isProcessed() == false)
then
System.out.println("isProcessed >>>>>> "+$obj.isProcessed());
$obj.setActive(true);
$order.setActiveMessage("...");
$order.setProcessed(true);
end
rule "Check field2"
when
$obj : License(getField2() == "def" && isProcessed() == false)
then
System.out.println("isProcessed >>>>>> "+$obj.isProcessed());
$obj.setActive(true);
$order.setActiveMessage("...");
$order.setProcessed(true);
end
However, I see that even now both my rules are being fired. When I try to print the value of isProcessed(), it says true, even though I enter the rule only if isProcessed() is false.
This is how I'm calling the drools engine:
kieService = KieServices.Factory.get();
kContainer = kieService.getKieClasspathContainer();
kSession = kContainer.newStatelessKieSession();
kSession.execute(licenseObj);
It is not just 2 rules, I have a lot of rules, so controlling the rules execution by changing the order of the rules in the drl file is not an option. What is happening here? How can I solve this problem? I am sort of new to Drools, so I might be missing something here.
Thanks.
Your question contains a number of errors.
It is definitely not true that fireAllRules has disappeared in Drools 6. You might have looked at the javadoc index, to find four (4!) overloaded versions of this method in package org.kie.api.runtime.rule in the interface StatefulRuleSession.
You might easily avoid the problem of firing just one out of two rules by combining the triggering constraint:
rule "Check field1 and field2"
when
$lic: License(getField1() == "abc" || getField2() == "def" )
//...
then
$lic.setXxx(...);
end
You complain that both of your rules fire in spite of setting the processed flag in the fact. Here you are missing a fundamental point (which is covered in the Drools reference manual), i.e., the necessity of notifying the Engine whenever you change fact data. You should have used modify on the right hand side of your rules.
But even that would not have been good enough. Whenever an update is made due to some properties, a constraint should be added to avoid running the update over and over again. You might have written:
rule "Check field1 and field2"
when
$lic: License(getField1() == "abc" || getField2() == "def",
! active )
//...
then
modify( $lic ){ setActive( true ) }
end
You might even write this in two distinct rules, one for each field, and only one of these rules will fire...
I am completely new to Drools and just exploring. So far I had been using a single rule and withing which we use if conditions just like java.
Now I had to use complex rules where I need to use multiple if and else chain but to end it when one in the series of conditions satisfies.
I call the drools from the Apache Camel. The rules should reply back to Camel.
Now How do I do to break the chain of rules and then send response back to the caller which is Camel code when one of if and else condition fails.
1.Can I use multiple end statements to respond back?
2.I can use functions and I know is to write functions in java and import them.
3.Is there any possibility to create functions in Drools and use just like in java?
I am not using the Drools in the way it should be used but so far the rules had not been so complex as the one we are using them now . Any help is is useful for me.
Here is an example which I would like to use please suggest if the below would work or some other alternative like the below.
rule "my rule"
when
#some condition
then
if(){
end
}else if(){
#do something
}
if(){
#do some other logic
}
end
Sample after My second comment
When
object:SomeObject(); // This helps for my camel code to trigger this rule and this rule only
then
if(){
}
else if()
{
return;
}else if() {
}
if(){
}else if(){
return;
}
if(){
}
end
I don't know what you mean by "breaking a chain of rules". Evaluating rules ends all by iteself, when there aren't any more rule activations to be executed,
Answers to 1. - 3.:
No. - There is no such thing I can associate with the term "respond back", but there aren't "multiple end statements [for one rule]".
Yes, you can use static Java function: import the class, call it, as in Java.
Yes. this is explained in the Drools manual, in a section with the title "Function". The example is from the manual:
function String hello(String name) {
return "Hello "+name+"!";
}
Comment on the rule added later to the question
You cannot use end between then and the actual end of the rule. If you want to terminate the consequence part of a rule, use Java's return statement (without an expression, of course).
Reconsider using complex conditional statements in the consequence. Logic decisions should be expressed in the condition parts of rules.
Much later Workaround due to possible Guvnor bug - does not accept return;
boolean skip = false;
if(){
} else
if() {
skip = true; // return;
} else
if() {
}
if( ! skip ){
if(){
} else
if(){
skip = true; // return;
}
}
if( ! skip ){
if (){
}
}
end
I'm writing a custom validator that will validate against multiple other form element values. In my form, I call my custom validator like this:
$textFieldOne = new Zend_Form_Element_Text('textFieldOne');
$textFieldOne->setAllowEmpty(false)
->addValidator('OnlyOneHasValue', false, array(array('textFieldTwo', 'textFieldThree')));
My validator will check that only one of those three fields (textFieldOne, textFieldTwo, textFieldThree) has a value. I want to prevent a future developer from accidentally passing the same field twice.
$textFieldOne->addValidator('OnlyOneHasValue', false, array(array('textFieldOne', 'textFieldTwo', 'textFieldThree')));
So far, my validator works perfectly, except when I pass the same field name as the field that has the valiator set on it.
In my validator, you can see that I am checking that the value (of the element with the validator set on it). I'm also checking the values of the other fields that were passed to the validator.
public function isValid($value, $context = null) {
$this->_setValue($value);
$this->_context = $context;
if ($this->valueIsNotEmpty()) {
if ($this->numberOfFieldsWithAValue() == 0) {
return true;
}
$this->_error(self::MULTIPLE_VALUES);
return false;
}
if ($this->numberOfFieldsWithAValue() == 0) {
$this->_error(self::ALL_EMPTY);
return false;
}
if ($this->numberOfFieldsWithAValue() == 1) {
return true;
}
if ($this->numberOfFieldsWithAValue() > 1) {
$this->_error(self::MULTIPLE_VALUES);
return false;
}
}
private function valueIsNotEmpty() {
return Zend_Validate::is($this->_value, 'NotEmpty');
}
private function numberOfFieldsWithAValue() {
$fieldsWithValue = 0;
foreach ($this->_fieldsToMatch as $fieldName) {
if (isset($this->_context[$fieldName]) && Zend_Validate::is($this->_context[$fieldName], 'NotEmpty')) {
$fieldsWithValue++;
}
}
return $fieldsWithValue;
}
My solution is to either...
A. Let the developer figure out there is a certain way to do it.
B. Ignore $value, forcing you to pass all the elements (which isn't much different than the first option).
or C. (if possible) Find the name of the element that called my validator in the first place and ignore it from the list of $fieldsWithValue.
I don't think there is a way to apply a validator on a form without attaching it to an element, but that would be even better, if it were an option.
How can I solve this problem?
Normaly i'd advise against such things, but, in this case I believe a static member in your class would actually provide a good solution to this problem.
With a static member, you can set it to the value in the first time the isValid is called, and check against it in subsequent calls, thus giving you a mechanism for this.
You may want to set this up to use some array in the configuration options, so that you can namespace and allow multiple instances of the validator to exist happily alongside each other for different sets.
The only problem that you really have to decide how to overcome, is where you wish to display the error, as yes the form itself does not take validators. if you want all the duplicates after the first to display an error, it is not so much of a problem.