Combiner in Batch Mode: How to Dynamically Change Target Quantity Based on Queue Count?

Combiner in Batch Mode: How to Dynamically Change Target Quantity Based on Queue Count?

tugba_aslanturk
Contributor Contributor
1,783 Views
18 Replies
Message 1 of 19

Combiner in Batch Mode: How to Dynamically Change Target Quantity Based on Queue Count?

tugba_aslanturk
Contributor
Contributor

Hi everyone,

I'm working with a Combiner in Batch mode, and I want it to dynamically adjust the Target Quantity based on how many items are currently in the input queue.

Here’s exactly what I want to achieve:

  • If there are 20 or more items, it should batch 20.

  • If there are between 10 and 19 items, it should batch the current amount.

  • If there are fewer than 10, it should wait (not batch anything).

  • No label usage, no schedule/time condition — just simple queue count-based logic.

  • Ideally, I’d like to manage this through the On Entry Trigger.

I’ve tried using setnodenum(node("variables/componentlist/From Input Port 2/Target Quantity", current), 10);, and while it works sometimes, it’s not reliable and causes issues in larger models.

Is there a clean and safe way to dynamically control the Target Quantity at runtime for a Combiner in Batch mode, based purely on input buffer count?

Thanks in advance!


 
0 Likes
Accepted solutions (1)
1,784 Views
18 Replies
Replies (18)
Message 2 of 19

moehlmann_fe
Advocate
Advocate

The target quantity of the combiner should only be changed while it is empty or when the container/first item enters through port 1 (as part of the OnEntry trigger).

I would keep the input of the combiner closed until at least 10 items have accumulated in the queue. Then open the input, set the target quantity in the OnEntry trigger when the first item enters and close the output upon exit if there are less than 10 items in the queue at that time.

0 Likes
Message 3 of 19

tugba_aslanturk
Contributor
Contributor

Hi again, thank you for your previous reply — it helped clarify a lot.

Actually, here's what I'm really trying to do:

I'm modeling a cargo dispatch system based on cutoff times. Think of the Combiner as a vehicle that should only depart (batch out) based on either a full load or a time-based cutoff.

There are two cutoff time slots during the day:

  • The first slot is at 22:00 (3600×22 seconds)

  • The second slot is at 01:00 (3600×25 seconds)
    (I'm considering simulation start time as 00:00, so 01:00 is on the next day)

My goal is:

  • At those two cutoff times, if there are at least 10 items in the queue, the Combiner should batch and send them out (even if it's not full).

  • At all other times, the Combiner should only batch when exactly 20 items are available.

I already have an OnEntry trigger that sets the target quantity and sends a message to trigger the output — it works well.
But I need to incorporate this time-based cutoff logic as well.

Any suggestions on how best to implement that?

Thanks again!

0 Likes
Message 4 of 19

moehlmann_fe
Advocate
Advocate

I'd use a small Process Flow for that. The queue is set to not release items by itself. When an item enters a token is created. Those tokens are batched up to a qty of 20. But there is also a maximum wait time that gets set to elapse at the next cutoff time. For this I use a global table with the times to lookup the next one. If they follow a regular interval, you can of course also determine the next time based on the current model time.

When a batch gets released (either full or at cutoff time) a check is made whether there are at least 10 tokens/items in the batch (see label aggregation in the Batch activity properties). If so the combiner target qty is to the batch size and the items get released to it. If not, the tokens loop back to the Batch activity.

For testing purposes I added a cutoff time at 100s.

0 Likes
Message 5 of 19

tugba_aslanturk
Contributor
Contributor

Since I have a large number of combiners in my model (one for each route), setting up individual Process Flows for each one would be inefficient. That’s why I thought it would be easier to manage everything through a script written in the OnEntry trigger of the combiner instead.

However, the code I wrote doesn’t work properly — it either throws errors or doesn’t behave as expected, likely because it’s not fully aligned with FlexSim’s scripting syntax. I’d really appreciate any help with fixing it.

 

0 Likes
Message 6 of 19

moehlmann_fe
Advocate
Advocate

You don't need a Process Flow for each combiner. As long as the port-structure stays the same (combiner receives pallet from source and boxes from one connected queue) an Object Process Flow can be used to run all combiners in a parallel in a single flow. 

0 Likes
Message 7 of 19

SchreyerNicholasB
Contributor
Contributor

I have created similar code but I have it set up using an OnMessage trigger on the combiner. The line(s) of code in the OnMessage trigger can be an if statement to check for the correct message parameters (if you are sending multiple messages this will help verify it is receiving the message to release) followed by an if statement checking for the content of the combiner. If the correct message parameters are received and the combiners content meets your release requirements (Ex. 10 or more items in the combiner), the line of code:  releaseitem(current.subnodes[1],1); will release the current combiner contents. This will avoid the need to change any target quantities or target component sum values. Then in your process flow, just have a scheduled source to create a token whenever your desired times are and use a custom code feature to send a message to all your combiners. I could be mistake, but I think you can send a message to an entire group of items, if your combiners are in a group. @moehlmann_fe would know if that will work or not. 

0 Likes
Message 8 of 19

SchreyerNicholasB
Contributor
Contributor

Attached are snips of the code I used.

0 Likes
Message 9 of 19

moehlmann_fe
Advocate
Advocate

As far as I know you can only send one message at a time. Sending a message to a group does not mean sending a message to each object within it. But you can run a for-loop over the contents of the group easily enough.

Group group = Group("Group1");
for(int i = 1; i <= group.length; i++) {
	sendmessage(group[i], group[i]);
}

I would like to point something out though:

Manually releasing the pallet only works as long as the next pallet is guaranteed to enter the combiner before another item is ready at the second input port. Releasing the pallet in this way does not properly reset the combiner. The second input stays open. So if another box arrives before the pallet, it would enter the combiner as the container item.

0 Likes
Message 10 of 19

SchreyerNicholasB
Contributor
Contributor

Could that be fixed by adding a close input for the second port within the OnMessage trigger on the combiner, right above the line to release the pallet?

0 Likes
Message 11 of 19

tugba_aslanturk
Contributor
Contributor

I’ve written a script in the OnEntry trigger of Queue1 to control the behavior of a downstream Combiner based on the current queue length and simulation time.

However, the logic seems to work intermittently — sometimes it behaves correctly, and other times it either fails silently or causes unexpected bugs.

Could you please help me review the code below and let me know what might be going wrong?

0 Likes
Message 12 of 19

SchreyerNicholasB
Contributor
Contributor

Along with setting the target quantity, try setting the "targetcomponentsum" node as well. 

0 Likes
Message 13 of 19

tugba_aslanturk
Contributor
Contributor

Thanks for the suggestion!

I'm not very familiar with the exact structure of FlexSim's internal nodes and scripting conventions, so I'm not sure how to properly include the targetcomponentsum setting alongside targetquantity.

Could you kindly help by editing the code below directly to show me how and where to include that?

Here’s the code I currently have in the OnEntry trigger of Queue1:

/**Custom Code*/

Object current = ownerobject(c);

Object item = param(1);

int port = param(2);

 

int current_val = current.stats.input.value - current.stats.output.value;

int max_val = 20;

int min_val = 10;

 

double simTime = time();

 

print("current", current_val);

if ((simTime >= 6 && simTime <= 8 && current_val >= min_val) || current_val == max_val){

print(current_val);

if (current_val == max_val){

setnodenum(node("MODEL:/Combiner1>variables/componentlist/From Input Port 2/Target Quantity", current), max_val);

}

else {

setnodenum(node("MODEL:/Combiner1>variables/componentlist/From Input Port 2/Target Quantity",current), current_val);

}

print(node("MODEL:/Combiner1>variables/componentlist/From Input Port 2/Target Quantity"));

current.output.open();

}

else{

current.output.close();

}

0 Likes
Message 14 of 19

saisriharireddy57
Community Visitor
Community Visitor

You can use a control logic or script block to monitor the queue count and update the target quantity parameter dynamically during batch execution.https://erusuconsultants.com/industrial-design/ 

0 Likes
Message 15 of 19

SchreyerNicholasB
Contributor
Contributor

See if this will work for you: 

/**Custom Code*/
Object current = ownerobject(c);
Object item = param(1);
int port = param(2);

int current_val = current.stats.input.value - current.stats.output.value;
int max_val = 20;
int min_val = 10;

double simTime = time();

print("current", current_val);

if ((simTime >= 6 && simTime <= 8 && current_val >= min_val) || current_val == max_val){

print(current_val);

if (current_val == max_val){

setnodenum(node("MODEL:/Combiner1>variables/componentlist/From Input Port 2/Target Quantity", current), max_val);
setnodenum(node("MODEL:/Combiner1>variables/targetcomponentsum", current), max_val);
}

else {

setnodenum(node("MODEL:/Combiner1>variables/componentlist/From Input Port 2/Target Quantity",current), current_val);
setnodenum(node("MODEL:/Combiner1>variables/targetcomponentsum",current), current_val);
}

print(node("MODEL:/Combiner1>variables/componentlist/From Input Port 2/Target Quantity"));
print(node("MODEL:/Combiner1>variables/targetcomponentsum"));
current.output.open();

}

else{

current.output.close();

}

0 Likes
Message 16 of 19

tugba_aslanturk
Contributor
Contributor

Thanks so much for adjusting the code!

It mostly works well now — the combiner correctly batches 20 boxes at 50 seconds, which is perfect.

However, there's one issue I'm still facing:

In my model, the cutoff time is set to 7 seconds, and at that moment I have 15 boxes in the queue.
I expect the combiner to batch all 15 boxes at once when the cutoff time hits.

But right now, it only takes 10 boxes, and the remaining 5 stay behind.

Is there a way to make sure the combiner takes everything in the queue (as long as it’s ≥10) at cutoff time — in this case, all 15?

Would really appreciate your help in tweaking the code to make that part work as intended.

Thanks again!

0 Likes
Message 17 of 19

SchreyerNicholasB
Contributor
Contributor

I am not sure what would cause that to happen if we are setting both the target qty and target component sum to the quantity of boxes in the queue. @moehlmann_fe Any input here?

 

0 Likes
Message 18 of 19

moehlmann_fe
Advocate
Advocate
Accepted solution

The output of the queue is opened when the sixth item enters. Entries and exit overlap from that point on and the queue never reaches more than 10 concurrent items (you can check that by looking at its max. content stat.)

What you should do here is introduce a "Breathe" by sending a message with a delay of 0s to the queue and then evaluating its content in the OnMessage trigger. That gives all other items from the same 'batch' time to enter the queue before the output is opened.

To stop items from flowing through the queue before the OnMessage code is executed, keep closing the output every time an item enters. To handle batches larger than 20 items, the second comparison should be changed to "current_val >= max_val" and the output should also be closed when a pallet is starting being processed on the combiner. So that if, for example, 23 items enter the queue, the first 20 are send to the combiner and the last three remain in the queue afterwards.

0 Likes
Message 19 of 19

tugba_aslanturk
Contributor
Contributor

Thank you both so much for your help! @moehlmann_fe @SchreyerNicholasB I was stuck on this for quite a while, and your suggestions made all the difference. The combiner logic works perfectly now thanks to your insights. Really appreciate the time you took to explain things so clearly.

0 Likes