The current operator design in SMTK/CMB is focused on model sessions and meshing procedures.
The simplest way to add new operations is to either extend an existing Session or create/derive a new Session class.
Meshing (and Exporting) operations are treated somewhat specially - Meshing operations are relegated to a special panel or as a something under the file menu (export simulation).
No way to process an operation asynchronously or get ongoing status back
Operations are modeled using SMTK's attributes and tend to need their own attribute system
In client/server applications (like CMB) attribute definitions of the operators (and the resulting instances) are sent between the client and server
There is no way to restrict which operations are needed for a specific task or indicate how they should be displayed.
The base class Operator should be moved out of smtk/model since operators need not only refer to models (export operators, meshing operators, etc..)
We could add an Operator class in smtk/common, and make smtk/model/Operator a derived class of smtk/Common/Operator. (Yumin)
Currently the operator assumes that it is defined by a single attribute definition - Is this sufficient or should it be defined by its own attribute system?
One limitation with one attribute system for operators in a session is that we can't override an operator's spec with a new one, however an operator with its own attribute system seems to be overkill. (Yumin)
Should Sessions know about Operators or should it be the other way around?
If Operators where outside of Sessions, would it make the Sessions simpler/more manageable ?
Should Operators provide their own View Layout (maybe a default one?)?
By default we are using instanced view, and for some polygon operators (create edge, edit edge, split edge), we already are using customized view layout. (Yumin)
You may want an operator to look differently based on the Task at hand.
Operator's State should be one of the following:
Not Ready to Run
Ready to Run
Combining Rob's comment: Not Ready, Queued, Running, Paused(Waiting), Completed. (Yumin)
Operators produce status (is this straight text or a Logger?)
Operators can be synchronous or asynchronous
Operators can "lock" the resources (models, meshes, attribute systems) and resource components (mesh Sets, model entities, attributes) they are referring to
If the resource is not available due to locking then the operator must wait
The lock can indicate if reading is permitted
Resources can be unlocked prior to operator completion (for example a meshing operator may unlock the model was it has been translated into the format needed by the mesher.
Should be able to create Python "glue" operators that call wrapped C++ functions
Operators should able to generate a reproducible state for Undo/Redo from applications. (Yumin)
This singleton is responsible for the execution of Operators as well as providing status back from asynchronous Operators.
Operators would be registered with the manager
This manager would be able to indicate the current status of defined operations
We should be able to cancel operations, and define priorities of operations. (Yumin)
Comments 07-Jul-2016 (john)
I don't have any hands-on with operators, but for what it's worth:
I would vote to make operators independent of sessions. If instead operators require sessions in some way, then we should refactor that functionality into a separate operator context or runtime class.
We should also plan on future operators that use resources from multiple/different modeling kernels at the same time and, by inference, work with multiple sessions concurrently.
As more/future operators will be carried out remotely using REMUS or cumulus, we should plan on separating the operator template into separate functional and platform/endpoint specifications.
Comments 20-Jul-2016 (robert)
The proposed Operator's state of not ready to run I would propose to be removed since it would require you to evaluate all the required lock requests and than return a true / false. But by the time that request has completed the locks could be removed and so you have to re-issue the request. This basically boils down to an atomic read/write issue.
instead you should have the following:
Completed ( Sub state of Valid / Error )
Now when you submit a request to do an operator you either explicitly run the operator if none of the resources are locked, or add it to the queue. The challenge than becomes how to handle the use-case of locked resource being modified / deleted while you are queued. My initial thought is that when you are removed from the queue, we re-validate the requirements and move to an Error state if any of the resources have been deleted.
The "lock" on resources should follow the classic multi consumer - single writer design. The challenge in any lock design is how to handle atomicity of the resource when you have multiple readers and something overwriting the data. Consider launch multiple async tasks that only require read locks, than halfway
through execution a write lock to that resource is requested by another task. You need to stall the write task while all the read tasks are completed, and if by default all locks are write, you cause all your async tasks to become heavily threaded serial tasks.
For this reasons, operators should express what lock types per resource they require, and than have a separate system to acquire and release locks inside subsections of the operator. Additional we should seriously consider that our lock types be:
Consider a meshing operation. It requires a Read lock on the model resource, and an Add lock on the mesh resource. We don't want to force a Modify lock on the mesh resource, since we aren't going to touch any existing items inside the mesh resource, but we do want some atomicity with other operations that are iterating all the Collections inside the mesh resource.