Class Pump<T extends PumpConfig>

java.lang.Object
com.tccc.kos.ext.dispense.Pump<T>
All Implemented Interfaces:
com.tccc.kos.commons.core.service.config.ConfigAware<T>, com.tccc.kos.commons.core.service.handle.HandleAware, com.tccc.kos.commons.util.Abortable, com.tccc.kos.commons.util.Cancelable, com.tccc.kos.commons.util.Terminable, PourAvailability

public abstract class Pump<T extends PumpConfig> extends Object implements com.tccc.kos.commons.core.service.config.ConfigAware<T>, PourAvailability, com.tccc.kos.commons.util.Terminable
Basic abstraction of a pump or valve. One or more pumps are generally created in the constructor of a Board class. A pump is then assigned to a Holder instance, which provides the ingredient information for the pump. Finally, a pump is also assigned to a Nozzle so that it can logically pour.

kOS is very flexible in configuring pumps/holders/nozzles. For example, a holder may have multiple pumps assigned, allowing multiple options for pouring a single ingredient. It is also possible to define multiple nozzles, which can offer symmetric functionality (both pouring brands) or they can differ, such as one nozzle for brands and another for just water.

A pump can perform two fundamentally different types of pours:

  • Ingredient : An ingredient pour tends to be a utility pour operation used to prime, flush, or calibrate a pump.
  • Beverage : A beverage pour is meant for consumption and typically requires running multiple pumps concurrently to mix a final beverage in the nozzle.
In some cases these two types of pours are implemented differently in hardware, and in other cases the two are conceptually almost identical. Regardless, kOS makes a distinction between the two and defines availability of the pump to operate in these two modes.

The actual process of pouring is implemented using a NozzlePipeline implementation attached to a Nozzle. There are several available pipelines available in kOS, including a brandset-based beverage pipeline and a utility ingredient pipeline.

The utility pipeline supports abstraction of pouring using user-defined "intents". An intent defines a sequence of operations for a pump to complete as an atomic unit, such as "pour, delay, pour". Intents can be bound to pumps by the handle path of the pump, pump type, or a user-defined category associated with the pump. For more information about utility pouring, see PumpNozzlePipeline.

A pump is generally backed by real hardware which is controlled by some hardware running some command protocol. This protocol may control individual pumps or collections of pumps, but the pump abstraction in kOS treats each pump as individually controllable. Within kOS, hardware integration is performed through a Board object. The board connects to the hardware (typically through a native adapter) and abstracts the native hardware protocol for use in Java. Pumps defined by the board typically forward their individual pump operations to the board to be sent to the underlying hardware for execution.

Pump availability, the understanding of whether a pump can be be used for an ingredient or beverage pour, is managed as follows. Anything that can contribute to pump availability must provide an AvailabilityLock, typically via the PourAvailability interface, and the locks are registered with the pump by adding them to the beverageLocks or ingredientLocks lists. If any of these locks are then linked to an AvailabilityTrouble then the pump will be blocked for that type of pour. For example, a pump is associated with an AvailabilityBoard. If the board goes offline the pump should be blocked. This is accomplished by adding the ingredient and beverage locks from the board to the pump lock lists. If a trouble then links to the AvailabilityLock of the board, the pump will be blocked because that lock will also be found in the pump list. Another way to describe this is that anything that may need to block a pump (a board, nozzle, etc...) should implement PourAvailability and the associated locks then registered with the corresponding pumps. Any of these locks can block the pump so when a trouble is created against some thing like a board, if it should impact pump availability, only the lock from the board needs to be linked to a trouble to impact the pump. There is no need to track down all the pumps from the board and update their availability. Similarly, when the trouble is removed, the linked lock will no longer impact pumps.

Every pumps is automatically linked to the availability locks of:

  • The board the pump is managed by.
  • The nozzle the pump is attached to.
  • The holder the pump is connected to.
  • The container providing the ingredient to the pump.
The AvailabilityLock from any of these objects can be linked to a trouble an impact the availability of a pump to perform pours. In most cases, base troubles will automatically implement this policy.
Since:
1.0
Version:
2023-01-16
  • Constructor Details

    • Pump

      public Pump(PumpBoard board, String name, String category)
      Creates a pump with the specified name and optional pump category for use with pump intents. The name is used to construct the unique handle to the pump, and is not necessarily intended for display purposes. Typically, the pump name is used as part of a localization key within the UUI to display the name of the pump to users.

      A pump is backed by real hardware, which requires sending a command of some sort to actually turn the pump on and off. Typically, a Board handles the control of all the pumps associated with the board, and the pump implementation simply forwards commands to the board, which has a connection to the hardware.

      Parameters:
      board - the board the pump is connected to
      name - the name of the pump
      category - the category of the pump for pump intents
  • Method Details

    • getNominalRate

      public double getNominalRate()
      Return the nominal flow rate of the pump. This is used when the pump is run without an explicit rate. The default implementation will use the nominalRate specified in the config for the pump. It this is zero then this method with throw an exception as an effective rate of zero makes no sense.

      Subclasses are free to override this if nominal rate is computed in some other way. This will also be used to compute estimated time for various pump operations that don't define a rate.

    • getNozzle

      public Nozzle getNozzle()
      Returns the nozzle the pump is connected to. A pump is assigned to a nozzle as part of an Assembly installation, which is used to install hardware into kOS. Once assigned to a nozzle, the pump can be used by the various nozzle pipelines installed in the nozzle to pour ingredients or beverages.
    • getHolder

      public Holder getHolder()
      Returns the holder the pump is connected to. A Holder connects the pump to an ingredient via a Container abstraction. This allows for abstractions like having an ingredient assigned, but not currently installed (as can happen when using cartridge ingredients when the cartridge is removed). It is also possible to have more than one pump connected to a holder that connects a single ingredient to more than one nozzle.
    • isInserted

      public boolean isInserted()
      True when the container is fully inserted and ready for use. There are many reasons why a container may not be fully inserted, such as the ingredient is expired or the pump must be primed. Once all insertion processes and validations are completed successfully, this flag is set. When a container is removed, the flag is set to false.

      While typical BiB hardware doesn't have a concept of being physically inserted and removed, these system still go through the logical insertion process, which allows for the enforcement of brand change-out rules and other functions that might otherwise be difficult to enforce.

    • isBeveragePour

      public boolean isBeveragePour()
      True if the pump can pour the inserted ingredient as part of a beverage pour. There are many reasons why a pump can pour ingredients but not beverages. For example, the line may need to be primed, which requires an ingredient pour, but until the line is ready the ingredient cannot be used for beverages.
    • isIngredientPour

      public boolean isIngredientPour()
      True if the pump can pour the inserted ingredient as an ingredient pour operation such as priming, calibrating, and so on. This flag does not indicate that the ingredient can be used for beverages. There are many reasons why an ingredient may not be used in an ingredient pour, such as the ingredient is expired and pouring it would contaminate the line.
    • getUnavailableReasons

      public List<String> getUnavailableReasons()
      If the pump is not available for ingredient or recipe pouring, this will typically contain information about why the pump is blocked. This exists primarily for debugging and analytic data feeds, and should not be used for production code as the list of reasons is not necessarily well-defined, and are not stable over time. Most commonly a reason will be the type of trouble that is blocking the pump.

      This will only contain the first available reason for a pump block, not every reason in the event there are multiple blocks.

    • getLastChangedTime

      public long getLastChangedTime()
      The time the pump availability last changed state. Useful for clients that want to evaluate whether to use the data in the pump based on lastChangedTime comparisons.
    • getIngredientId

      public String getIngredientId()
      Returns the currently attached ingredientId .
    • getPrevIngredientId

      public String getPrevIngredientId()
      The most recently poured ingredientId from this holder. This can be used to determine what ingredient should be put back in the holder when the container is removed. This is not an indication that the ingredient is usable, just that it's been poured and therefore is at least partially in the lines to at least one pump.
    • getEffectiveIngredientId

      public String getEffectiveIngredientId()
      Gets the effective ingredient. This is either the currently inserted ingredient or the previous ingredient. This is considered the effective ingredient because, even if an ingredient is not currently inserted, the expectation is that the previous ingredient will be inserted again.
    • setPrevIngredientId

      public void setPrevIngredientId(String ingredientId)
      Sets the lastPouredIngredientId .
    • record

      public void record(double volume)
      Record pump usage. This should be called to record the volume of ingredient the pump has poured. Ideally this is called while the pump is running to record incremental volumes vs. one total volume at the end of the pour. This data is fed into NozzlePourMetrics which can use actions to trigger pour related logic based on volume of ingredients poured.

      This should be used if the pump provides accurate volume data while pouring. If not, recordRate() can be used to simulate this data.

      Parameters:
      volume - the volume of ingredient poured
    • recordRate

      public void recordRate(double rate)
      Used to generate volumetric data from a pump that doesn't otherwise generate this data from a direct hardware feed. While volumetric data is entirely optional, it feeds NozzleMetrics which can be used for many interesting use cases.

      To use this feature, call recordRate() with the flow rate of the pump at the time the pump is turned on. This rate will be used to periodically compute volume data which is passed to record() . It is possible to adjust the rate on the fly if the pump has a flow meter that reports real flow rates. When the pump is finished, this must be called with a rate of 0 to turn off the simulated volumetric data feed. Ideally this is called before the future that manages the pour is complete as the completion of the future can trigger reporting that uses the final volume data.

    • tpour

      public abstract com.tccc.kos.commons.util.concurrent.future.FutureWork tpour(int duration, double rate)
      Performs a timed pour for the specified time at the specified rate. This is called from performOp() , which replaces a zero rate with the nominal rate from the PumpConfig .

      Pump implementations are responsible for validating rate and time arguments if the underlying hardware has upper or lower limits.

      Returns a future to start and monitor the pour. The caller is responsible for starting the future. The pump is responsible for completing the future using the following logic:

      • success : The pour completed as expected.
      • failed : The pour did not complete as expected.
      • aborted : (optional) The pour did not fail, but some related internal process required the pour to be ended without running to completion. This is the result if the code in the Future throws an exception.
      The pump is also responsible for properly handling the Future being cancelled. This should stop the pour and cleanup any internal state, such that another pour operation can then be performed.

      While cancel and abort are similar, cancel is generally top-down, often originating from the user stopping a process, whereas abort is generally bottom-up, where a process cannot complete and thus has to turn off the pump.

      It is common for a pump to never use abort, as most processes are above the pump. It exists in the event that there is a need to semantically distinguish between an operation that was ended by a user or as a side effect of a failed internal process.

      Standard kOS nozzle pipelines set and clear the errorHandler when the pump is about to be used. This allows pumps report errors up the error handler chain instead of embedding error handling logic in the pump class. The various standard pipelines provide mechanisms to attach handlers as part of standard pour operations, allowing a high degree of error handling policy to be implemented external to the pour engine and pump implementation.

      Parameters:
      duration - the duration of the pour in ms
      rate - the rate of the pour in ml/s
      Returns:
      a future for the pour
    • vpour

      public abstract com.tccc.kos.commons.util.concurrent.future.FutureWork vpour(double volume, double rate)
      Performs a volume pour for the specified volume at the specified rate. This is called from performOp() which replaces a zero rate with the nominal rate from the PumpConfig .

      Pump implementations are responsible for validating rate and volume arguments if the underlying hardware has upper or lower limits.

      For pumps with no flow meter, this can be implemented internally using tpour() where time = (volume * 1000) / rate , which results a time in units of milliseconds. When a flow meter is present, this should monitor the actual flow rate to pour the correct total volume. This implies that if the flow rate is lower than expected, then this will pour longer until the target volume is reached, notwithstanding any errors that the pump may generate due to low flow rate.

      Returns a Future to start and monitor the pour. The caller is responsible for starting the Future . The pump is responsible for completing the Future using the following logic:

      • success : The pour completed as expected.
      • failed : The pour did not complete as expected.
      • aborted : (optional) The pour did not fail, but some related internal process required the pour to be ended without running to completion. This will be the result if the code in the future throws an exception.
      The pump is also responsible for properly handling the future being cancelled. This should stop the pour and cleanup any internal state such that another pour operation can be performed.

      While cancel and abort are similar, cancel is generally top-down, often originating from the user stopping a process, whereas abort is generally bottom-up, where a process cannot complete and thus has to turn off the pump. It is common for a pump to never use abort as most processes are above the pump. It exists in the event that there is a need to semantically distinguish between an operation that was ended by a user or as a side effect of a failed internal process.

      Standard kOS nozzle pipelines set and clear the errorHandler when the pump is about to be used. This allows pumps report errors up the error handler chain instead of embedding error handling logic in the pump class. The various standard pipelines provide mechanisms to attach handlers as part of standard pour operations, allowing a high degree of error handling policy to be implemented external to the pour engine and pump implementation.

      Parameters:
      volume - the volume to pour in ml
      rate - the rate of the pour in ml/s
      Returns:
      a future for the pour
    • getType

      public abstract String getType()
      Returns the type of the pump. Typically used by UI code to determine operations that can be performed on this particular pump. This is also used by the "pump intents" infrastructure to match pump intents to pumps.
    • getCategory

      public String getCategory()
      Returns the category of the pump. This is used by the pump pipeline to identify "pump intents" for this pump. For example, some Freestyle dispensers use the same pump type for plain water, carbonated water, and HFCS. By assigning a category of "hfcs" to the HFCS pump, it is very easy to define intents that differ from water given HFCS has different characteristics and flow rates.
    • getIntent

      public PumpIntent getIntent(Pump<?> pump, String intentType)
      Returns the PumpIntent definition for this pump. This only works if an IngredientNozzlePipeline has been installed for the associated nozzle. This is a convenience method to quickly access the pipeline from the pump.
      Parameters:
      intentType - the type of intent to return
    • pour

      public com.tccc.kos.commons.util.concurrent.future.FutureWork pour(String intentType)
      Returns a future that will pour the specified intent when run. This only works if an IngredientNozzlePipeline has been installed for the associated nozzle. This is a convenience method to quickly access the pipeline from the pump.
      Parameters:
      intentType - the type of intent to perform
    • preStart

      public void preStart()
      Called in conjunction with PumpEventService when the pump is about to be started, but before any start events are generated for the listeners.
    • postStop

      public void postStop()
      Called in conjunction with PumpEventService when the pump has just stopped, but before any stop events are generated for the listeners.
    • cancel

      public void cancel(String reason)
      Forward cancel request to the associated pour object.
      Specified by:
      cancel in interface com.tccc.kos.commons.util.Cancelable
    • abort

      public void abort(String reason)
      Forward abort request to the associated pour object.
      Specified by:
      abort in interface com.tccc.kos.commons.util.Abortable
    • sinceLastStarted

      public long sinceLastStarted()
      Return the time since the pump last started.
    • getHandle

      public com.tccc.kos.commons.core.service.handle.Handle getHandle()
      Specified by:
      getHandle in interface com.tccc.kos.commons.core.service.handle.HandleAware
    • getBoard

      public PumpBoard getBoard()
    • getLastStartTime

      public long getLastStartTime()
    • setLastStartTime

      public void setLastStartTime(long lastStartTime)
    • getConfig

      public T getConfig()
      Specified by:
      getConfig in interface com.tccc.kos.commons.core.service.config.ConfigAware<T extends PumpConfig>
    • setConfig

      public void setConfig(T config)
      Specified by:
      setConfig in interface com.tccc.kos.commons.core.service.config.ConfigAware<T extends PumpConfig>
    • setUnavailableReasons

      public void setUnavailableReasons(List<String> unavailableReasons)
    • getBeverageAvailabilityLock

      public AvailabilityLock getBeverageAvailabilityLock()
      Description copied from interface: PourAvailability
      Return the object add to a trouble impact list to block beverage pouring.
      Specified by:
      getBeverageAvailabilityLock in interface PourAvailability
    • getIngredientAvailabilityLock

      public AvailabilityLock getIngredientAvailabilityLock()
      Description copied from interface: PourAvailability
      Return the object add to a trouble impact list to block all pouring.
      Specified by:
      getIngredientAvailabilityLock in interface PourAvailability
    • getBeverageLocks

      public Set<AvailabilityLock> getBeverageLocks()
    • getIngredientLocks

      public Set<AvailabilityLock> getIngredientLocks()
    • getPour

      public Pour getPour()
    • setPour

      public void setPour(Pour pour)