(HC) Queue for multple processes, do first available, but remain in queue?

(HC) Queue for multple processes, do first available, but remain in queue?

cphilpottX22K5
Enthusiast Enthusiast
185 Views
13 Replies
Message 1 of 14

(HC) Queue for multple processes, do first available, but remain in queue?

cphilpottX22K5
Enthusiast
Enthusiast

[ FlexSim 23.1.1 ]

I am completing some model logic and need to find a better solution for something.

Currently, at registration/triage, my patients get a list of the imaging types that they will need. Once they get to the imaging process group they go through a sequence of Decide components that checks whether they need certain imaging processes.

  • 1.1 They check to see if they need an imaging step.
    • a. If they don't need that one, they will proceed to check the next imaging process
    • b If they do need that one they branch off
      • i. If they succeed at acquiring the resource, they go through the process and the imaging tyoe is removed from their list
      • ii. If they fail at acquiring the resource, they bounce back out and move to check for the next process. (2.1, 3.1...)
  • 2. If they do not need any or fail to acquire all imaging, they check to see if they still need any imaging.
    • a. If they do, they go back through this loop
    • b. If they don't (imaging all complete), they exit the loop.

What I need is a way to have a patient who needs an imaging process but fails to acquire it to remain in the queue (but still advance to check for the next imaging. If Xray is backed up but CT is free, we need to have the patient do CT while waiting for xray)

I would think that this is managed in a list for each imaging room... on a failed acquire, the token is pushed to the list. At the same time, all attempts to acquire can only succeed/be made by the person who is last on the list. (It has to be their turn)

I think I could make that logic, but I think it's odd that I can't push to the end of a list with built in components and I wonder if it's a signal that I'm thinking about this wrong.

  • I'd rather say: if patient == list[1]
  • than: if patient == list

      --

      Would a global list be the correct way to manage this? Do I need to create a child token to sit in the queue?

      Thanks!

    Accepted solutions (1)
    186 Views
    13 Replies
    Replies (13)
    Message 2 of 14

    julie_weller
    Not applicable

    Hi @Chandler! Maybe instead on thinking about pushing onto a list in order (which by default I'm not sure that they have an order at all), I would consider pulling based on the age of the token. That label is automatically built into a global list, and you could just query the list based on the age (you can change it to ASC or DESC based on if you want the oldest person on the list or the newest). The trickiest part would be the fact that lists require the token to stay there. You could experiment with using a max wait timer and the leave on list option. Another idea, would be to create a child token like you suggested.

    Another idea would be to use an array and adding the patient to the end. That would be another way to allow you to maintain the order they were added and wouldn't require a child token. However, it would mean you'd need to check if the object is currently busy before popping it off the array, but you'd have to do that with a list too...

    Do you think that would work for you? It's hard to know for sure without being able to experiment on the model 🙂

    Message 3 of 14

    moehlmann_fe
    Explorer
    Explorer
    I have a question that my answer would depend on. In your example of the patient first doing CT while waiting for Xray, could another patient acquire and start the Xray while the first patient is doing CT? Or should the Xray be acquired by the first patient as soon as it becomes available and stay idle until the CT is done?
    Message 4 of 14

    cphilpottX22K5
    Enthusiast
    Enthusiast
    That's an insightful question!


    Yes, I would like for people who are currently engaged in another imaging process to be allowed to be skipped over for another process, exactly as you described, so they don't tie up all of the processes at once.

    My needs in order:

    1. First - to allow queuing and get the oldest request to always acquire and process first.
    2. Then - Allow other patients to always skip over the oldest request if the person who made it is busy.
    3. Finally - Control when a newer request is allowed to skip:
      1. Patient A is in Xray and has the oldest request for CT. We could check some parameters for Patient A (acuity, or the processing time of Xray), and then allow or deny Patient B to skip past them based on those conditions.
    If it's simple enough to address all of these at the same time, then I'm open to that.
    0 Likes
    Message 5 of 14

    cphilpottX22K5
    Enthusiast
    Enthusiast
    Yes I think that would work. (Sorry I haven't shared a model - I wanted to try to get this question out quickly and didn't have time to cull our client info from the model for posting)


    To be clear? Are you describing an SQL order by query? (I'd like to make an attempt and if it's not working out I can save out a model of the relevant content.)

    0 Likes
    Message 6 of 14

    cphilpottX22K5
    Enthusiast
    Enthusiast
    Oh I just realized, if I understand you correctly, using the age of the token wouldn't work unless I do create a child token to push to the list, which seems unnecessary. Perhaps I'm wrong about that though.


    Just some more thoughts...

    The push to list activity has an option to insert at the front of the list ... by default working as a stack. I could also do that by using a label on the imaging resource.

    0 Likes
    Message 7 of 14

    julie_weller
    Not applicable

    Yes but more like a list aided query. If you created a list that had some fields like this:

    1692197342519.png

    You could only pull items that aren't busy using the partition ID and using the label of Busy (it's dynamic so it could change after it was added to the list. Then you would choose the oldest person on the list that's not busy using a Query. Something like this for the Pull from list:

    1692197456794.png

    You could use the create tokens activity to create a token for each activity:

    1692197600073.png

    Or you could make the partition ID NumOfActivity so you're choosing the right activity and use the Query to make sure they're not busy (which will probably be the better method)

    0 Likes
    Message 8 of 14

    julie_weller
    Not applicable
    That could work too if you don't want to deal with age! @Felix Möhlmann might have a really clean way to do it though, my ideas are kind of clunky haha
    Message 9 of 14

    moehlmann_fe
    Explorer
    Explorer
    Accepted solution

    My thoughts on this:

    The third point from your comment is what makes this somewhat tricky. Since a 'better' patient might arrive, a location should only ever get acquired by a patient that can start to use it immediately. With a list, you would have to keep track of the current 'best' candidate, who gets to acquire the location as soon as both him and the location are free.

    Keeping track of that best candidate doesn't really get any easier by using a list. So in the attached model I don't actually use any list.

    A patient token creates a child token for each process the patient has to go through. These run through a subflow where they first check if the respective location is currently available (denoted by a label on the location object). Either because nobody has reserved it yet or because the new patient has a higher "Acuity" value than the one that current is pending to use the location.

    If the location is available the token can continue. It has to enter a zone that only lets a single token per patient enter at a time. Tokens that wait in the "Enter Zone" activity can thus be preempted by newly arriving patients.

    If the location is not available, the token is send to a "Wait for Event" activity (so are tokens that are preempted during their wait to enter the zone). Whenever a patient finishes a process, a message is send to the Process Flow and the tokens that need to acquired that location are released from the "Wait for Event" to try to 'acquire' it again. Another zone orders them by their "StartTime" label (age of the token) and spaces them out using a "Breathe" delay, so any label updates after the check happen before the next token enters the Decide activity.

    flexible-process-order-fm.fsm

    Message 10 of 14

    cphilpottX22K5
    Enthusiast
    Enthusiast

    Wow! ...that was fun.

    I didn't quite understand your approach so I took some time to dissect it. After picking it apart and rebuilding it like this...

    1692400621826.png

    flexible-process-order-fm_7.fsm

    ... what you were doing finally clicked, and I see now it's very elegant. (I'm also immediately adopting the practice of using green color to indicate the presence of custom code)

    I'm still not certain that it's perfect for my use. For example, I can't check where someone is in the queue, as I can in my version. But the more I refine it the closer I get to what you have built.

    I could also see a case in your version where a new token enters the subflow while older tokens are in the wait for event activity, and this happens at the right microsecond, that new token could bypass the queue. (Or is that impossible for a reason I don't know?)

    I will continue to solve this challenge and update here when it's resolved! I haven't decided whether it is important enough for me to have the list that I need to develop it further, or just use something like what you have made.

    ---

    For info, here is what my version does:

    1. The token receives the patient's unique ID on a label, and I assign patient.busy = 0
    2. Patients are not in the queue, so they are added.
      1. token.inQueue = 1
      2. A child token is created and pushed to the list, where there are fields for token.Acuity, pushTime, and patient.busy
      3. The child token is added to the parent's list of tokens in queue.
    3. A parent token pulls the top token on the list (but leaves it there)
      1. ORDER BY busy ASC, acuity ASC, pushTime ASC
    4. Check to see if the pulled token is in the parent's list of queued tokens:
      1. Yes: parent attempts to enter the zone.
      2. No: parent waits for its number to be called.
    5. Once in the zone (max 1 token) the parent pulls its child token from the list and removes it. The child is sunk. token.inQueue = 0.
    6. The parent token's label patient.busy = 1, then it processes, then the label is set back to 0.

    I had been using a 30 sec inter-arrival source in a general process flow to ping the wait for event with the name of the first patient in the queue, but I changed that to be a message sent by the token after completing a process.

    0 Likes
    Message 11 of 14

    moehlmann_fe
    Explorer
    Explorer
    Yes, you are correct. A newly arriving token could potentially skip ahead of the waiting tokens in my model when it enters the subflow in the same instant another token finishes the process.

    However, since the walking times and the process time itself are all non-integer values, the chance of a new arrival happening at that exact time should be practically zero if it is not directly triggered by the process finishing.

    I guess to be on the safe side, you could set the Wait for Event activity to also trigger whenever a new token enters the subflow, so that the Queue Strategy of the Pull Order zone would come into effect.

    0 Likes
    Message 12 of 14

    cphilpottX22K5
    Enthusiast
    Enthusiast
    How would you implement this code if the patients are waiting for one of a group of locations instead of for a specific location? Let's say we have two rooms for processNum = 1 - we can't just set the label to "inUse".


    I am trying to use a global variable and increment/decrement its value while patients are processing, so instead of checking whether the resource is in use, I'm checking whether the number in the variable is less than the length of the group associated with the location resource.


    Is there any way you would recommend instead of that? For example, if global variables are not necessary in this case. I was getting errors when I tried to increment/decrement a value using the set variable activity.

    0 Likes
    Message 13 of 14

    moehlmann_fe
    Explorer
    Explorer

    If the objects are organized in groups anyway, I probably would keep the same logic and just iterate over all objects in the respective group until the token either finds an unused object, an object it has a higher priority for or all objects are found to be unavailable at the moment.

    Using a global variable to keep track of the number of used locations can work. But as I see it, you have to store references to the tokens/patients that are currently in line for each location anyways. So checking each object individually is not actually much overhead.

    The Set Variable activity is used to set Process Flow variables.

    1692686040456.png

    Global variables are a separate thing. You would set those in a simple code snippet.

    varName += 1;
    0 Likes
    Message 14 of 14

    cphilpottX22K5
    Enthusiast
    Enthusiast

    This all makes sense. I have enough info to go on now, though the problem isn't completely solved. I'll return here if I have a relevant question or create a separate post if I get too far off this topic.


    Thanks.