Introduction
The Pallet Racking Abstract located in cm/abstract/materialHandling provides a convenient framework for customizations to create their own pallet racking system. This abstract offers several key features that facilitate the customization process, including:
- Behaviors – Classes that define a specific functionality of a snapper.
- Child Snappers – Snappers that exist within another snapper.
- Snapper shapes – Proxy classes that also contain geometry information.
- Collision Primitives – Primitives used for snapping and collision detection.
- Spawners – Classes that handle the instantiation of snappers and what behaviors they can have.
- Engine – Manages the above points within the system, handles calculations, and keeps track of the state of snappers.
By leveraging these features, customizations can effectively build, and tailor their own pallet racking systems based on the Pallet Racking Abstract, providing flexibility and control over the design and behavior of the system.
Behaviors
In this abstract, a snapper object (MhSnapper) is defined by a collection of behaviors. Depending on how the behaviors are defined, they could be reused by different snappers. For instance, many frame accessories could potentially share the same snap behavior. Therefore, only a single FrameSnapBehavior needs to be defined and shared among various frame accessories.
The use of behaviors also offers enhanced flexibility and modularity. Developers working on custom extensions have the freedom to choose which behaviors they want to include or exclude from the snapper's list of behaviors as required. These behaviors could be from the abstract or defined by the developers themselves. In general, most behaviors in the framework are stateless behaviors. A special type of behavior present in the abstract are EngineBehaviors. These behaviors will notify the engine of actions like child addition/modification/removal, manipulate props during run, etc.
Child Snappers
A pallet rack is built with several components where each component is a snapper. Each component has a relationship with another component using the concept of child snappers. The main components in a pallet racking system are rows, frames, bays, and levels. The following diagram describes the general relationship between these components:
Besides that, there are also accessories that can be snapped to these components, resulting in them being children to those snappers. For example, if a name plate snapper is snapped to a frame, it will become a child to the frame.
Snapper Shape
A SnapperShape (MhSnapperShape) class describes the shape of a snapper by containing information of a snapper's dimensions and geometries. Geometries are used during collision detection. A shape class also serves as a proxy class that contains the logic for the snapper. For example, instead of a snapper calling its own updateConnectors() method, it will instead call shape.updateConnectors(). It will also hold other information such as the snapper's properties and connectors.
One reason shapes are used in the abstract is to avoid deep inheritance for custom extensions. For example, if a common base class needs to be created in a custom extension that all extension snappers can extend from. If shapes were not used, it would not be possible to have just one base snapper class as the abstract will contain multiple different snapper classes with different hierarchies. Developers will end up having to create multiple base classes where each class will contain duplicate code.
// Not sharing common base class CustomRowSnapper extends MhRowSnapper CustomBaySnapper extends MhBaySnapper CustomFrameSnapper extends MhFrameSnapper
However, with the use of shape classes in the abstract, you will get the following scenario instead:
MhSnapper -- Contains shape field |---CustomSnapper -- Common base class with extension specific logic | |---CustomRowSnapper -- shape=CustomRowShape | |---CustomBaySnapper -- shape=CustomBayShape | |---CustomFrameSnapper -- shape=CustomFrameShape CustomRowShape extends MhRowShape CustomBayShape extends MhBayShape CustomFrameShape extends MhFrameShape
This way, the snappers will contain all the necessary logic in the shape class, while also inheriting common customization specific functionality from a common base class.
Shape classes also allow for more flexibility. Developers can define their own shape classes without having to extend from the existing shape classes in abstract if those do not meet their requirements. This will result in less class dependencies.
Collision Primitives
This abstract makes use of collision primitives to handle snapping within a pallet racking system. Each snapper in a rack will generate their own collision primitive. When a snapper attempts to snap to another snapper, a comparison will be made between the snapper’s collision primitives to ensure that the snappers won’t collide with each other. These collision primitives are generated from the geometry information stored in the snapper's shape class.
In the image above, the beam's collision primitive is prevented from going beyond the collision primitive of the unit load.
Some uses of collision primitives in the framework:
- Populating bays and levels
- Respect clearances between unit loads and levels.
- Automatic arrangements of levels in a bay.
- Spreading objects within the rack via rows/columns/levels
Spawners
SnapperSpawners are commonly used in libraries to spawn snappers when a button in the library is clicked. In this abstract, its functionality is extended to further define how a snapper should be instantiated. Each snapper in the framework will have its own spawner class. These spawners will define its shape, predefined set of behaviors, classification, initialize its properties, and more.
For example, a bay spawner would define:
- Snapper to spawn – CustomBaySnapper
- Shape – CustomBayShape
- Behaviors:
- bayGfxBehavior
- bayAnimationBehavior
- bayStretchBehavior
- levelPopulateBehavior
- …
- Init props:
- w=2800mm
- d=1200mm
- h=8000mm
- bottomStorageLevel=500mm
- …
Engine
An engine is a collection of function libraries that process data on an abstract layer. In the material handling framework, it is the brain that manages a pallet racking system. The engine would iterate through all objects and run the relevant behaviors and functions based on a particular event and resolve collisions between collision primitives.
The advantage of using the engine is it is extensible and flexible and reduces the need to keep subclassing to handle new data. The engine allows us to keep track of the state of snappers before big changes in the system.
In the engine, snappers are not involved during calculations. Instead, the engine works with an abstract representation of snappers. When a change to a snapper should affect the system, the engine is notified and will import the relevant snappers to get some basic information such as their position, rotation, and shape and convert them into engine entries. These engine entries will be used in engine functions to perform specific tasks such as populating a row or arranging a level. After those tasks are done, the modified entry will be exported back to the assigned snapper. It is also possible to spawn new snappers based on the modified engine entry.
The following diagram is a simplified representation of the engine’s workflow:
For example, if a row’s depth has been increased, the other rows in the system would need to be repositioned to maintain the row-to-row distance. The engine would then collect all affected snappers and import them as engine entries, then an engine function that would calculate their new positions is executed. Then, using the exported entries, the snappers would have their positions updated.
Comments
1 comment
Well described, thank you!
Please sign in to leave a comment.