A Proposal for a Set of COM Interfaces to Form the Basis of an Interoperability Mechanism for Open Tools
by Crosbie Fitch
(in response to Mark Baker’s proposal for Open Tools in Develop magazine Sep
2003)
DRAFT: 0.08
Exposition (Inverse Encapsulation)
Standard Optimised Data Interfaces
Proprietary Interfaces - Examples
The original inspiration for Open Tools was driven by an observed need for the UK games industry to be able to at least produce interoperable tools. The initial toolset could be formed from a wide variety of Open Source tools, and widely available proprietary tools. A standard interoperability mechanism would then enable in-house tools to exploit the Open Tools, and in some cases allow strategically allied games companies to share their in-house tools with each other.
Although it is not necessarily the prime motivation, it would be good if this standard encouraged companies in the games industry to make their in-house tools more widely available to the rest of the industry. If this encourages their release as Open Source tools so much the better.
For the Open Tools concept to be realised, we need to formulate a mechanism to achieve interoperability.
This document attempts to do just that, i.e. describe a mechanism to enable data processing tools to interoperate. New and existing tools can be made to conform to this mechanism in order that they can portably interoperate with other tools (known or unknown).
The use of the term ‘tool’ in this context is used to indicate a piece of software designed to perform a particular process, typically with minimal if any user interaction. The use of ‘open’ is used to indicate a tool that is compatible with the ‘Open Tools’ interoperability standard, and ideally a tool that may be freely used by the games industry.
There will be a straightforward means of enabling a user to collect tools contained in files and install them (if only by placing in a certain directory or file path) for use by a command line program or GUI based application. Perhaps a file-type suffix of ‘.OTM’ to indicate a DLL containing an Open Tool COM module?
This section to be expanded by the intrepid…
Some simple instructions to start developing an Open Tool in MSVC6:
1) File, New, ATL COM AppWizard
2) Project name = APrefixOpenTool
3) DLL, Finish.
4) Insert New Class, Name=Open Tool, Custom, OK
5) Adjust GUIDs as necessary (to be advised)
This section to be expanded by the intrepid…
Open Tools are COM modules that encapsulate one or more ‘tools’, each able to perform some processing function. Each module is able to enumerate the tools it contains. Each tool is able to describe its purpose, the inputs it requires and outputs it produces. Each of these inputs and outputs is also able to describe its nature and give indication as to the most appropriate user interface (where manual entry may be appropriate).
An Open Tool is a COM object that has been instantiated to perform a specific function or process with the inputs provided to it. It may produce outputs, or operate directly upon the inputs (if required).
Naturally, it is possible and a primary requirement to be able to provide the inputs of one tool from a selection of outputs of other tools.
To some extent an Open Tool can almost be thought of in the same way as a crude data structure: some fields containing input data, and some left blank for output data, it then gets passed to a function which dutifully fills in the blanks, and with luck returns promptly giving some indication that it was successful. With object orientation, the function (or methods) can be tightly bound to this data structure, such that the data is never accessed directly, it’s all constructed, processed and queried via methods. With COM, we are able to abstract things further to some extent such that a caller can find out at run time if an object behaves like a class that was known to the caller (potentially a long time before the COM object was written).
With Open Tools, we take things in a slightly different direction. We still have the sets of input and output data, but we now invert the philosophy of encapsulation and expose these (as interfaces) and hide all the methods (there is a single method implicit in the object as a tool).
Although a tool performs a particular process, no tool knows what any other tool does, but they do know, to varying degrees, how to send and receive data between each other. It is expected that a human operator or other agency, with holistic knowledge of all the tools available, will assemble the tools, making data connections as appropriate to the overall task.
However, some tools that support IOTChain can attempt, when given a particular source tool, to programmatically interrogate it for as many data outputs as it can in order to satisfy its own inputs.
A tool is considered to have a number of IO connectors. There are two kinds of input connector: input only, or input with tap (duplicated input as output). There is one kind of output: output only.
A tool Input is information that is possibly required to be read or modified by a tool.
A tool Output is information that is generated by a tool for purposes of reading or modification by another party.
There are two types of information conveyed as input or output: Static, passive, read-only data, and Interactive, volatile, read/write data.
Static Data may be thought of in the same way as a binary data file. It may be empty, partial, or full. It is also incomplete or complete depending upon whether the tool has finished producing it – an event is generated on completion.
When it is used as an input, Interactive Data may still receive modifications from a tool, though if it does, it should generally be duplicated as an output. Interactive data need not necessarily always be received as an input, a tool could produce its own dynamic/volatile data and make this available as an output – it may even permit modifications.
NB It is therefore possible that a tool may permit upstream data travel, e.g. by taking note of changes made to an interactive data output and making changes in response, to an interactive data input. However, this just demonstrates that strictly speaking, ‘input’ and ‘output’ indicate responsibility for, and ownership of, information sources as opposed to information flow.
Each Open Tool is implemented in a separate CoClass (one CLSID per Open Tool). There may be multiple CoClasses per DLL (multiple Open Tools per Open Tool COM Module).
Each tool supports the interface IOpenTool. Open Tool related interfaces may be prefixed with ‘OT’, e.g. IOTProperName.
Other interfaces that may be supported are:
· IEnumUnknown
· IOTDescribe
· IOTChain
· IConnectionPointContainer
An Open Tool MUST support at least IOpenTool.
A useful tool will also support at least IEnumUnknown.
A helpful tool will support IOTDescribe.
A chainable tool will support IOTChain.
A tool that supports ‘call-backs’ will support IConnectionPointContainer.
Note that IMultiQI may often be automatically implemented and thus provide a convenient mechanism for querying for all these interfaces.
There is no ‘execute’ method.
A tool can commence processing as soon as all necessary inputs are provided, and may finish processing shortly after all inputs have completed.
However, the key issue is not the processing phase of the tool, but when it has completed the production of its outputs. At this point the tool may be said to have finished, though in some cases, where inputs may change, or the tool may change its outputs, the tool may continue processing even though all outputs are complete.
An ‘execute’ method can be simulated by having a dummy input, and marking it complete as soon as it is desired for the tool to commence processing.
Tools are normally expected to process their inputs as soon as possible, and thus complete their outputs as soon as possible. In this way the data is ‘pushed’ through the tool chain. However, there is an alternative ‘pull’ modality. This is where a tool will monitor whether any of its outputs have been ‘AddReffed’, implicitly indicating output is requested, and only commence processing once this has happened. Tools preferring to operate in ‘pull’ mode may also wish to support ‘pull signalling’, e.g. having a special ‘Inhibit’ dummy output that when ‘AddReffed’ (by another tool operating in ‘pull’ mode) inhibits the tool’s processing until it is ‘Released’ (or an Output Ref count exceeds the number of reference counts on the ‘Inhibit’ output).
In general a tool should operate in ‘push’ mode. The ‘pull’ alternative is only mentioned here in case it is appropriate for some tools.
A tool must support IEnumUnknown in order to enumerate its IO connectors (inputs, outputs, and any interactive interfaces). Each of these is recognisable by supporting the IOTConnector interface.
The availability of IO connectors must be preserved for at least the lifetime of the Open Tool. No IO connector may be removed. Each connector is identifiable by its IUnknown address.
IO connectors may appear in different orders or at different positions on successive enumerations.
NB An IO connector may NOT refer to other connectors by index, or an ordinal position, e.g. IO connector 3, or the 5th IO connector that supports a particular interface. This is because the tool may be wrapped inside another during its lifetime and the indexes and ordinal positions may consequently be affected.
The adding of IO connectors during the lifetime of a tool has not been ruled out, but for the time being this should be considered an unsupported, non-standard behaviour, i.e. client tools should not be expected to check for additional IO connectors if they don’t expect them.
It is possible that an Open Tool may also exhibit the same characteristics as an IO connector, i.e. it supports IOTConnector. This would be where the tool had a single or primary connector – input and/or output.
If a tool does not support IEnumUnknown it is implicitly a single connection tool, and all the connector interfaces are obtained from QueryInterface(). If in this case it also supports IEnumUnknown, it must also supply the same connector interface via that.
Even if a tool supports multiple connections via IEnumUnknown it may still provide the interfaces of one of the connectors via QueryInterface().
A helpful tool has a secondary interface IOTDescribe to describe its function (human readable), and classify its function (for ergonomic user selection). A classification of Open Tools will therefore be required (appendix?). Use of COM ‘Component Categories’ is a possible option (possibly utilised in parallel).
Tools can provide information to provide advice on the way they’re best presented to the user in a user interface.
If a tool supports IOTChain, it is demonstrating its ability to automatically attempt to source its inputs from the outputs of one or more other specified tools.
For each chained tool specified via ChainIn(), this tool will satisfy as many of its data inputs as possible from the chained tool’s data outputs. This is typically achieved by querying each of the chained tool’s data outputs for particular IOTConnect<> interfaces, and given matches, establishing compatible IOTDataIs<> and IOTData<> interfaces.
Possibly in conjunction with the use of aggregation, one may easily create new tools by manually assembling or chaining tools together.
The tool may optionally support ‘connection points’, i.e. each input/output is a connection point providing event information concerning changes in each input/output.
A tool may provide additional interfaces, e.g. for certification, licensing, etc. but of course, these are optional.
Each Open Tool has one or more IO interfaces. These can be inspected and connected via the IOTOutput and IOTInput interfaces.
In general, each IO connector provided by an Open Tool should support the interface IOTConnector.
The IOTConnector is provided to let users know whether this connector is potentially useful to them, if not provided, the connector should not be used even if other interfaces are understood.
Other interfaces that may be supported are:
· IOTConnectorDescribe
· IOTConnectorDescribeUI
· IOTConnect<Purpose>
· IOTOutput
· IOTInput
· IConnectionPoint
· IOTDataIs<Meaning>
· IOTData<Shape>
A helpful connector will support IOTConnectorDescribe.
A connector with display advice will support IOTConnectorDescribeUI.
An unambiguous connector will support IOTConnect<>.
A connector intended for data input will support IOTInput.
A connector with data output available will support IOTOutput.
A connector that supports event notification will support IConnectionPoint.
A connector with data output will support IOTData<> to convey the data, and IOTDataIs<> to clarify its meaning and interpretation.
For purposes of determining the difference between ‘inputs’ and ‘outputs’ the rules are as follows:
If an IO connector supports IOTInput, it is an ‘input connector’. If it also supports IOTOutput this is simply a convenience duplication of whatever input it receives.
If an IO connector doesn’t support IOTInput, it must support IOTOutput, and is an ‘output connector’.
Describes the nature of the connector for a human reader.
It has methods to describe the function of the connector (in the contexts of input and/or output).
A connector also has methods to provide advice as to how best the data should be presented for display or editing in a user interface.
Every data connector has a purpose in relation to the tool. In human terms this is described within the name and description information provided by IOTConnectorDescribe. However, there also needs to be a mechanism where one tool can disambiguate a particular data connector by more than just data type and meaning. IOTConnect<Purpose> is provided for precisely this purpose. It can be considered the ‘machine readable’ name for the IO connector.
Purposes are expected to be standardised so that tool connectors can automatically connect. Where they don’t match, it is still possible for the connection to be made manually (or optionally, by data compatibility alone).
It is permissible for more than one connector to have the same purpose, however this should only be where such a situation will not lead to ambiguity, i.e. where multiple connectors of identical purpose will be expected. Whoever defines the purpose must be clear as to whether it must be unique or not.
Where a connector has both input and output, sometimes a purpose can be exposed that may have been defined with this usage in mind. Otherwise it may be necessary either to define a new purpose, or to expose two purposes, i.e. a purpose for the input and a purpose for the output.
IO connectors may support multiple purposes for other reasons, but only where this will not result in confusion. A typical circumstance may be where a new purpose has been created, but is based on, and compatible with, an older purpose. Note that renaming of purpose interface identifiers does not necessarily warrant the creating of a new IID, e.g. adopting a proprietary purpose into the standard.
A tool must NOT document permanent orderings or purposes of particular IO connectors by establishing their indices or ordinal position, e.g. “the 1st input of type X always has this purpose”, or “Output at index 0 always contains Y data”.
All tools must convey purposes of IO connectors via IOTConnect<Purpose>.
This only argument in support of relaxing the requirement for an IOTConnect<Purpose> interface would be where the IO connectors a tool requires and provides can be fully disambiguated solely by IOTDataIs<Meaning> + IOTData<Shape> interfaces. This means that any chained input must not duplicate outputs of a particular meaning+data interface (unless their interchange makes no difference), and that all outputs must also be via unique meaning+data interfaces (unless their interchange makes no difference). If such a tool receives a ChainIn() request to an input tool that has ambiguous outputs the tool must fail (it may not simply pick arbitrary outputs).
It is permissible for a tool to fail ChainIn() requests when supplied with a tool that does not provide IOTConnect<Purpose> interfaces on its IO connectors.
Proprietary purposes may be created and these should be given names of the form I<Proprietary prefix>OTConnect<Purpose>, e.g. IGameDevOTConnectPassword.
For chaining to succeed, each connection can only be made if connectors have at least two IOTConnect<Purpose> interfaces that match, or one side provides IOTConnectAny.
If connections are made manually, the purposes are ignored (although a success return code will indicate a match). The connection will only fail if either IOTDataIs<Meaning> fails to match (where IOTDataIs interfaces are provided by both sides, unless one side provides IOTDataIsInstrinsic), or there is no pair of matching IOTData<Shape> interfaces.
If necessary, if not provided by the tool itself, data adaptors or converters can be produced, thus augmenting the tool’s flexibility.
The IOTOutput interface describes the status of outgoing data on this connector (accessible via IOTData<>), and the IOTInput interface advertises the fact that this connector accepts incoming data.
An input can be connected to an output and vice versa. However, although an output can be connected to several inputs, an input can only be connected to a single output.
Effectively, a connector is an output, the data available via IOTData<> is always the data associated with the output. IOTInput simply provides the means to supply this connector with a data output from another tool. To obtain status information of the input, it is necessary to call Input() to obtain the connector attached to the input, and then query that for IOTOutput.
Represents one of many data input connectors for an Open Tool.
A connector supporting IOTInput may also be queried for IOTOutput for an optional duplication of the input.
If a connector only supports IOTInput and not IOTOutput, there will not be any IOTDataIs<> or IOTData<> interfaces.
If for some reason one needs to determine what interfaces an input is compatible with, one simply connects a dummy output and records all the interfaces it is queried for. However, the key input interfaces required should be documented.
Represents one of many data output connectors for an Open Tool.
It may either represent data originated by the tool, or a data input duplicated from the output of another tool.
Note that the IOTOutput methods are not necessarily synchronised with those of an IOTInput if present on the same connector.
If a connector supports IOTOutput, it should also specify and provide the data via IOTDataIs<> and IOTData<>.
The events reported by the connection point are: Unavailable Û Available, Empty Þ Partial Þ Full, and Incomplete Þ Complete. The arrows indicate the permissible event transitions.
Connection points are optional. If not implemented by the Open Tool they may be provided automatically via an adaptor that supplies the Connection Point events by polling the IO connectors’ Status() methods.
Assuming compatible purpose has been established, a connection is tried between an input connector and an output connector. The input connector will query the output connector for data interfaces that it understands. Hopefully, there is a suitable match and the connection is made (otherwise it fails).
Note that therefore, IOTData and IOTDataIs interfaces accompany, and are associated with, IOTOutput. They have no association with IOTInput.
To see what IOTData/IOTDataIs interfaces an IOTOutput connector supports, one uses QueryUnknown(). To see what IOTData/IOTDataIs interfaces an IOTInput connector supports, one attempts to attach a dummy IOTOutput connector, and then record the various interfaces that are queried for. NB Data interfaces should queried in order of preference, most preferred first.
Data is described by two interfaces: its shape and its meaning. The shape is conveyed via IOTData<Shape> along with various methods for accessing the data. Where it is necessary to specify how the data is to be interpreted, or clarify its meaning (especially where the same data shapes may be used for a wide variety of meanings), a special ‘meaning’ interface is also provided, i.e. IOTDataIs<Meaning>. The meaning interface has no methods.
Data compatibility will always require that both shape and meaning are in agreement (though meaning may be omitted if appropriate).
Aside from the purpose of a connector, there is also the matter of the data that is conveyed via that connector. The pure data is described by an IOTData<Shape> interface, however, how it should be interpreted is conveyed by the IOTDataIs<Meaning> interface.
In addition to a name and description, the data that may be output from an IO connector has a meaning. The meaning IID must not be confused with the data interface IID. Moreover, new data interface IIDs should not be generated for otherwise identical data interfaces solely to imply a different meaning. The meaning is entirely conveyed via the dedicated ‘meaning’ interface.
NB While purposes are attached to connectors, meanings are always attached to data. So whilst in any successful connection there may be two different purposes involved, there is only one set of data+meaning. Another way of looking at it is to realise that a connector’s data always has ‘output’ semantics. The input is simply a means of specifying a data output belonging to another tool.
Proprietary meanings may be created and these should be given names of the form I<Proprietary prefix>OTDataIs<Meaning>, e.g. IGameDevOTDataIsMotionCapture.
Data interfaces are designed to enable physical data flow. This is where we actually start getting down to transporting bits and bytes along the wire. It could be something optimised for conveying a disk file, or something optimised for conveying a stream of vertex data from memory. It could be a single data structure such as a matrix or a set, or a primitive type such as a floating point number, a bit mask, or even just a Boolean value.
Data output from a connector will have an interface to read (and potentially modify) the data values. However, even data broken down from a complex structure into elemental form will still be best conveyed via an interface tailored to its particular shape. One can think of ‘shape’ as meaning the basic data structure, but one that cannot be sensibly broken down further into simpler constituent data elements. For example, a list of indices, could be broken down into as many connectors as there were elements in the list, but they really belong in aggregate form. The general rule is that things should be broken down as far as one might conceivably wish to individually parameterise a particular element.
Tools have inputs and outputs. When they are in possession of all inputs they process them and produce outputs. All outputs must definitely complete after all inputs have completed. It is highly recommended that tools emit interim outputs (meaningful ‘life signs’) during intensive processing prior to completion. Output may commence prior to input completion.
Inputs and outputs are either static or interactive in nature. A static input/output is an immutable, static, and one-way chunk of data or value. An interactive input/output is a somewhat dynamic, potentially volatile, read/write, ‘interactive’ data store.
Inputs and outputs have value semantics (sometimes those of a volatile value). That means, while not necessarily complete, they may be passed around as if simple values such as an integer or a large ‘value’ such as a file.
Static data interfaces are for static, immutable, read-only, non-volatile data values or sources (or ones that are indistinguishable from such). They are used for input and output. However, such data may be partially available, e.g. it may be 10 lines of an N line log file (but never the last 100 lines of a 1000 line log file, with 900 lines omitted) .
Static data has methods to describe the kind of data interface it accepts (data type, the range of values + meanings for each). Sufficient information is provided to enable automatic determination of appropriate GUI data input/output mechanisms.
Static data may be anything from a multi-gigabyte 3D database dump, to a single Boolean value (or even nothing, e.g. mere presence and/or completion).
An input is either a primary data source, a secondary data source, or an option that may be supplied to the tool as a control parameter. There should be one interface per distinct input, i.e. avoid shoe-horning multiple input parameters into one interface.
An input is always optional (even the primary data source), or rather, this is an ‘expected’ as opposed to ‘exceptional’ situation. Informative reasons for non-production of output can be delivered to a diagnostic oriented output, e.g. “Still waiting for input (i/fX and/or i/fY and/or i/fZ)”.
An output may be a primary data output, a secondary one, or a status, progress, or diagnostic indicator for a human operator.
For each output a tool describes, the output must be eventually generated and completed if all inputs have completed. It is nevertheless, acceptable to emit an empty albeit complete output.
Null output is not considered a failure (or exceptional situation). No single tool has the authority to consider that an exceptional situation has arisen (except within its own internal context). Any failure or internal exception should be reported to an appropriate output (it is possible that outputs may be categorised as primary/secondary/status/diagnostic/error/exception). If a tool loses integrity and spins (probably with no meaningful output/progress indicated), it will be up to an external timeout decision made by the system or user as to whether an exceptional situation exists and the particular tool aborted (outputs forcibly marked empty+complete).
Interactive data is explored and modified in situ via interfaces specialised for situations where data is liable to occasional and unpredictable change, and where it is awkward to use the inputðtransformðoutput pattern.
This type of data is also useful for tools offering interactive controls or feedback.
Interactive data may be as complex as a live 3D scene graph, or as simple as an event trigger.
Interactive data is communicated via the same interfaces as static data, with IOTData<Shape>::Nature() indicating its mutability and volatility, and the IConnectionPoint providing notification of changes.
Data of the same meaning may be provided by multiple interfaces. The only reason for this is if in addition to the simplest representation of the data, there are better performing, format optimised, or more appropriate representations.
· The simple interface MUST always be available. The optimised interface is optional.
· The overarching priority must be for simple, flexible data types and interfaces (if necessary, at the expense of performance).
· The implementer of an Open Tool can easily utilise interface adaptors as an interim step, e.g. providing apparently optimised interfaces that access data via simple, non-optimised interfaces, or vice versa.
There must be no degradation of quality or functionality in the simple interfaces compared to their optimised equivalents. However, the latter may offer a variety of compromises between quality and performance, and may offer a subset of the functionality available via the former.
Any reference by one connector’s data interface to another must be via a method dedicated for this purpose, e.g.
IndexedConnector([in] index,[out] REFIID,[out] IUnknown**).
It must never be left up to the client to determine the appropriate connector.
All data interface names must be careful to restrict themselves to describing the layout or shape of the data, and avoid indicating meaning or usage of the data – that is a task that belongs to the IOTDataIs<> and IOTConnect<> interfaces.
Standard Open Tool data interfaces are the form IOTData<Shape>.
Standard, optimised data interfaces are of the form IOTDataOpt<Shape> and all methods are prefixed with ‘Opt’, e.g. OptNature().
Non-standard data interfaces optimised or otherwise should either:
a) use any name not of the form IOT* or I*OTData*, or
b) should use a name of the form I<Proprietary prefix>OTDataOpt<Shape>, e.g. IGameDevOTDataOptNetworkPacket.
Non-standard data interface methods that replicate standard interface methods, such as Nature(), should prefix them, e.g. EgNature() and EgOptNature(). In general all proprietary data methods should at least be prefixed in case they collide with methods of a subsequent standard data interface.
HRESULT
Error (<0)
Boolean (True=S_OK, False=S_FALSE)
Success code (S_OK, S_FALSE, >1)
index- Index (unsigned, 64bit minimum ceiling, no specific width)
real - Real (appropriate precision, minimum bounds of C ‘double’, no specific width)
Data consists of two interfaces: a meaning and a data interface. This is independent of the purpose it may be ascribed via connector interfaces.
Data may refer to other peer data interfaces explicitly via a supplied interface pointer, or by an unambiguous IOTConnect<> interface. It may not utilise the ‘name’ information from the IOTConnectorDescribe interface.
Data may never directly refer to parent or child data interfaces.
A child data interface is a data interface obtained via IEnumUnknown. This implicitly iterates over child data interfaces. A data interface that supports IEnumUnkown may have data of its own (also supports IOTData<>), but this can only refer to selective peer data and/or implicitly, all child data. There is no ability to refer to child data explicitly or selectively.
In this way we can produce hierarchical data structures.
Given a data interface we can then determine what kind it is using the following test sequence:
1) If it supports IOpenTool, it can be presented as a tool
2) If it supports IOTInput/IOTOutput, it has delivery semantics
3) If it supports IOTData<>, it contains data
4) If it supports IEnumUnknown, it may have child data interfaces (go to 1)
5) If it supports none of these, it is not understood.
A data interface may also support alternate data interfaces that are better optimised for the specific nature of the data involved, e.g. if a series of 8 bit values are to be conveyed, it’s like to be more efficient to communicate these packed into strings or 64bit words, rather than one byte per word as would be the case in the standard IOTDataList interface.
The standard, non-optimised interface must always be available.
Standard, optimised data interfaces are recommended to be made available too.
Non standard, proprietary interfaces may be supplied in addition when standard ones are awkward or inefficient.
Separate/distinct data should be provided via a dedicated data interface. However, sometimes, non-essential data can be provided that saves costly processing. This can be achieved by attaching pre-computed data or hints to expedite later computations.
As long as this data can be computed or obtained separately, it is the only kind of data that is permitted to be piggy-backed onto a given data interface. It is also the only occasion when a data interface is permitted to incorporate an intrinsic meaning.
Data interfaces describing this kind of data have the following naming convention.
I<Proprietary prefix>OTData[<EquivInterface name>]Aug<Meaning>
E.g. IEgDataOptVectorListAugPrecomputedNormals
Their standard equivalent methods should also be similarly prefixed and suffixed, e.g. EgNatureAug(). or EgOptNatureAug().
Each sub-heading represents a separate interface set.
These are the Open Tool and the Open Tool Connector.
Identifying Open Tools.
None
Interfaces describing one or the only connector may, but not necessarily, be obtained from QueryInterface().
Iterating through tool data connectors.
HRESULT Next([in] ULONG celt,[out] IUnknown ** rgelt,[out] ULONG* pceltFetched);
HRESULT Skip([in] ULONG celt);
HRESULT Reset(void);
HRESULT Clone([out] IEnumUnknown** ppenum);
Interfaces describing one or the only connector may also be duplicated via QueryInterface().
The object returned by Clone() is not necessarily the cloned open tool (won’t necessarily support any other interfaces), and it may only enumerate through a snapshot of the connectors – it will not necessarily be aware of later additions (if these occur).
Providing human readable documentation and categorisation information of the tool.
HRESULT Name([out,string] wchar_t** a_pwsName,[out,string] wchar_t** a_pwsAliases);
// Name of tool (alpha only ProperName, no spaces, case preserving & insensitve, alpha only, UK English), aliases in UK English (and local language optional) (UTF-16)
HRESULT Class([out,string] wchar_t** a_pwsClass,[out,string] wchar_t** a_pwsAliases);
// Name of tool class (alpha only ProperName, no spaces, case preserving & insensitve, alpha only, UK English), aliases in UK English (and local language optional) (UTF-16)
HRESULT Description([out,string] wchar_t** a_pwsDescription);
// Description, Category info, etc. In UK English (and local language optional) (UTF-16)
// NB All out strings created by callee using CoTaskMemAlloc, caller to free using CoTaskMemFree.
Methods may yet be added to provide advice for how the tool should be presented for display or interaction purposes.
Unless the method fails, returned strings must be freed with CoTaskMemFree().
Providing advice concerning how the tool should be presented in a UI.
Methods yet to be defined.
Provided by tools capable of interrogating other tools in order to automatically bind this tool’s inputs to one or more other tools’ outputs.
HRESULT ChainIn([in] IOpenTool* p_pIOpenTool);
// Instruction to this Open Tool to attempt to ‘chain in’ the supplied Open Tool.
// (source as many inputs as possible)
// Result indicates number of inputs successfully connected to the supplied tool’s outputs
// (0=success, but no connections, +N=Success, N connections made, <0=error
// A tool chained-in a second time is ignored
// Connections last until the tool is removed, will not be overridden by later added tools
HRESULT Remove([in] IOpenTool* p_pIOpenTool);
// Instruction to this Open Tool to remove the supplied Open Tool if it has been chained-in.
// Detaches all connections
// If successful, returns the number of inputs thus removed
HRESULT RemoveAll();
// Instruction to this Open Tool to remove all Chained-in Open Tools.
// Detaches all connections
HRESULT Enum([out] IEnumUnknown** p_ppIUnkOpenTool);
// Return an interface by which all currently chained-in tools can be enumerated
To support ‘connection point’ behaviour on each input or output.
Standard.
A tool may provide additional interfaces, e.g. for certification, licensing, etc. but naturally, these are optional.
Identifies this as a data connector interface.
HRESULT OpenTool([out] IOpenTool** a_ppIOpenTool); // Returns the parent Open Tool (AddReffed)
HRESULT ConnectorType(); // Indicates what kind of connector this is:
enum EConnectorType
{ e_ConnectorUnknown // Connector type is unknown/unspecified
, e_ConnectorPrimary // Connector is a primary data flow into/out of the tool
, e_ConnectorSecondary // Supplementary data, often optional
, e_ConnectorControl // Minor data typically required to control the processing performed - can be treated as if a parameter or command option
, e_ConnectorDiagnostic // Progress, debugging, diagnostic, non-critical messaging, logs, warnings, etc.
, e_ConnectorError // Critical messages or error controls for expected failures
, e_ConnectorException // Information concening unexpected behaviour (often with severe ramifications)
};
Describes the nature of the input or output in human readable form.
HRESULT ConnectorName([out,string] wchar_t** a_pwsName,[out,string] wchar_t** a_pwsAliases);
// Returns the name of the input or output (alpha only ProperName, no spaces, case preserving & insensitve, alpha only, UK English), and a list of alternate, comma separated compatible aliases, e.g. CameraPosition. UK English (and local language optional) (UTF-16)
HRESULT ConnectorDescription([out,string] wchar_t** a_pwsDescription);
// UK English (and local language optional) (UTF-16)
// NB All out strings created by callee using CoTaskMemAlloc, caller to free using CoTaskMemFree.
Provides information assisting the presentation of the data within a UI for viewing or editing.
At the time of writing, UI presentation advice methods are yet to be defined.
Identifies the purpose of this data connector (possibly in relation to others) according to the function of the tool.
None.
These interfaces are either proprietary or defined within the standard. Open Tools should document the purposes they expose and the purposes they look for.
Represents one of many data input connectors for an Open Tool.
HRESULT Connect(IOTConnector* a_pIOTConnector); // Attempt connection/disconnection
// Supplies the input to be used (or null to disconnect input).
// E_NOINTERFACE=need IOTOutput
// >=0 =Connection accepted: (or, if null supplied, disconnected+S_OK)
// 0=Treated as compatible (though may still be unviable connection, e.g. missing data)
// 1=Perfect connection, positively compatible in all respects
//
// >0=Bit mask [[** dmppnccck **]]
//
// k=0:unknown - ignore other flags, 1:flags have meaning
// ccc= 0:Compatible
// 1:Incompatible (unknown/other reason)
// 2:Data shape, meaning, purpose and Nature ok. Incompatible for other/unknown reason.
// 3:Data shape, meaning, and purpose ok. Incompatible nature.
// 4:Data shape and meaning ok. Incompatible purpose.
// 5:Data shape ok. Incompatible meaning.
// 6:Data shape is incompatible.
// 7:Data shape is incompatible. Reserved
// n= 0:Nature compatibility required, 1:Nature compatibility not required
// pp= 00:Purpose supplied and required, 01:purpose not supplied, but required, 10:purpose supplied, but not required, 11:purpose neither supplied nor required
// m= 0:Meaning required, 1:meaning not required
// d= 0:Shape/data required, 1:Shape/data not required
// If the connection is accepted, it will be used as far as possible (unless data shape or nature prevents), whether or not it is incompatible.
// It is the caller's responsibility to decide whether a connection should be abandoned.
enum EInputCompatibility
{ e_CompatibilityUnknown=0
, e_CompatibilityPerfect=1
, e_CompatibilityFail=3
, e_CompatibilityFailUnknown=5
, e_CompatibilityFailNature=7
, e_CompatibilityFailPurpose=9
, e_CompatibilityFailMeaning=11
, e_CompatibilityFailShape=13
, e_CompatibilityFailReserved=15
, e_CompatibilityNatureRequired=1
, e_CompatibilityNatureNotRequired=17
, e_CompatibilityPurposeSuppliedAndRequired=1
, e_CompatibilityPurposeNotSuppliedButRequired=33
, e_CompatibilityPurposeSuppliedButNotRequired=65
, e_CompatibilityPurposeNotSuppliedNorRequired=97
, e_CompatibilityMeaningRequired=1
, e_CompatibilityMeaningNotRequired=129
, e_CompatibilityShapeRequired=1
, e_CompatibilityShapeNotRequired=257
};
enum EInputCompatibilityMask
{ e_CompatibilityKnown=0x1
, e_CompatibilityFailure=0xe
, e_CompatibilityRequired=0x1f0
};
HRESULT Input([out] IOTConnector** a_ppIOTConnector);
// Returns the current input AddReffed (null+S_OK if none).
// If successful also returns the connection compatibility status code as above (see Connect() ).
Implementers of Connect() are reminded of the utility of IMultiQI which is often automatically implemented, and thus may appear on the supplied a_pIOTConnector.
Represents one of many data output connectors for an Open Tool.
HRESULT Status(); // Returns EOutputStatus
enum EOutputStatus // Designed for binary OR
{ e_StatusUnknown // Consider it fully available and complete
, e_StatusAvailable=0,e_StatusUnavailable=1 // Whether data is ready to be accessed
, e_StatusFull=0,e_StatusPartial=2,e_StatusEmpty=4,e_OnDemand=6 // Empty - no data has yet been provided. Partial-Some data is available. Full-All data is available.
, e_StatusComplete=0,e_StatusIncomplete=8
// Incomplete means the data should not be considered final
// Complete means whether empty, partial or full, no more data is available, and this data should be considered final.
};
HRESULT Changes([in,out] index* a_piState);
// If null is supplied, returns a non-zero success code if this is a mutable/volatile output (interactive data) and it has changed since completion, or zero if not (or the data is static).
// If state zero is supplied, will always return S_OK to indicate no change, and a new state value is returned indicating the data's current state.
// If a non-zero, previously returned state is supplied, will return non-zero success code if the data or its status has changed since returning this value, zero if not. A new state value is returned indicating the data's current state.
// May return E_INVALIDARG if number supplied is not recognised as previously returned
Identifies the meaning of the data provided in the data interface, i.e. how it is to be interpreted, clarifies any ambiguity.
None.
These interfaces are either proprietary or defined within the standard. Open Tools should document the meanings they expose and the meanings they understand.
Provides notification of change to data interface status.
Standard.
Notes
More information concerning implementation will be supplied once an Open Tool reference implementation has been completed.
Provides mechanism for reading data, possibly writing.
All data interfaces have a method that indicates the nature of the data conveyed by that particular interface and this returns a standard conformant code as defined by the standard method Nature().
HRESULT Nature(); // Bit mask: [[** cccdputlvmwhk **]]
//
// Nature of the data: known, hidden, writable, mutable, volatile, latent, temporary, unreliable, privileged, dedicated, etc.
// k=0:unknown, 1:known (if 0, ignore other flags - assume 0)
// h=0:readable, 1:hidden (0=some data is readable, 1=none is readable)
// w=1:writeable, 0:not (1=some data can be affected by the client, 0=none can)
// m=0:static, 1:mutable (1=some data changes of its own accord and is recognised by IsChange(), 0=none changes)
// v=1:volatile, 0:not (1=some data changes of its own accord and is not always recognised by IsChange(), 0=none)
// l=0:immediate, 1:latent (1=some writes will not be immediately reflected in reads, and may never be, 0=writes will affect subsequent reads)
// t=0:permanent, 1:temporary (0=writes have permanent upstream effect, 1=writes only affect data read by client)
// u=0:reliable, 1:unreliable (1:reads and/or writes may be lost in transit, 0:reads & writes are reliably received)
// p=0:accessible, 1:privileged (1:all operations may fail unless prior authorisation is obtained, 0:unrestricted)
// d=0:shared data, 1:dedicated to client (0:data is the same for all clients, 1:data is peculiar to each client) [THIS IS PROBABLY NON-SENSICAL]
// ccc=Consistency while data is available. 0:Data is always fully consistent, 1:Data may not always be complete, but is otherwise consistent,
// 2:Data may be inconsistent at a high level, but this does not render it invalid, 3: Data may be inconsistent at all levels, and may be invalid,
// 4:Data is never or rarely consistent at any level (client must test/remedy inconsistency)
//
// Above flags apply to the data as a whole
// NB Calls to Nature() are advised, but never required prior to using data access methods.
// Nature() simply provides a means of determining how those methods are likely to behave, and the nature of the data they convey.
// It provides advice as to the assumptions a client can make concerning the data.
enum EDataNature
{ e_Unknown
, e_Known=0x1
, e_Hidden=0x2
, e_Writable=0x4
, e_Mutable=0x8
, e_Volatile=0x10
, e_Latent=0x20
, e_Temporary=0x40
, e_Unreliable=0x80
, e_Privileged=0x100
, e_PerClient=0x200
, e_Consistency=0x1C00
, e_ConsistencyFull=0
, e_ConsistencyNotComplete=0x400
, e_ConsistencyNotInvalid=0x800
, e_ConsistencyNotValid=0xc00
, e_ConsistencyNone=0x1000
};
These interfaces are either proprietary or defined within the standard. Open Tools should document the data shapes they expose and the data shapes they understand.
See the section ‘Standard Data Interfaces’ for more information on those defined within the standard.
Given a tool without connection point functionality, this tool aggregates it and support connection points.
Given a tool, this tool produces a formatted text document containing the information provided by the tool’s IOTDescribe, and IOTConnectorDescribe interfaces, along with details of what standard interfaces are supported, etc.
This tool replicates the functionality of a supplied tool and displays its data and status throughout its use. It also enables the manual supply of the tool’s input data, and display of its output.
The purpose of this connector is not recommended for chaining.
None. No purpose has methods.
The connection will be ignored for the purposes of chaining, even if other purpose interfaces are supplied.
The purpose is inherently unambiguous and implicit, if no other matching purpose is available on this connector then connection should proceed on the basis of data compatibility after all other potential connections have been considered.
None. No purpose has methods.
This interface is implied if no purpose interface is supplied.
The data has no meaning or its meaning is unknown and unnecessary. There is inherently no ambiguity possible.
None. No meaning has methods.
This interface is implied if no meaning interface is supplied.
Null data.
HRESULT Nature();// See API documentation for IOTData<Shape>
If no data interface is supplied, one can make the same inference as if IOTDataVoid had been supplied, i.e. that no data is available.
Nature() naturally, has no meaning in the absence of any data and will return zero.
An unordered container of unique indices to a referenced list.
HRESULT Nature();// See API documentation for IOTData<Shape>
HRESULT Indices
( [out,size_is(a_iBufferLength),length_is(*a_piReturned)] index* a_piBuffer // Buffer to receive index words (each set bit in each word returned indicates an index, i.e. a_iStart+bit position)
, index a_iBufferLength // Size of buffer, number of index words required
, [in,out] index* a_piStart // Index corresponding to bit 0 in first word of buffer (not necessarily a multiple of 64) - on return, this is set to the next unread index (or zero if no more, when S_OK is also returned)
, [out] index* a_piReturned // Number of index words returned - if less than requested, this indicates that there are no more indices within the range of the rest of the buffer
, [out] index* a_piRemain // Number of indices remaining beyond the returned value of *a_piStart
// (a_piReturned & a_piRemain, apply for this call only - subsequent calls may obtain more or fewer indices until IOTOutput::Status indicates the data is full)
); // If successful returns the run length of the indices from the returned value of *a_piStart. 0 is returned if no more indices.
Data(IID,IUnknown); // Returns interface pointer to the data interface to which the index applies
An ordered series of indices to a referenced list (indices may be duplicated).
HRESULT Nature();// See API documentation for IOTData<Shape>
Indices(); // Iterates across all indices
Data(IID,IUnknown); // Returns interface pointer to the data interface to which the index applies
An ordered series of Real numbers.
HRESULT Nature();// See API documentation for IOTData<Shape>
HRESULT Vector
( [out,size_is(a_uBufferLength),length_is(*a_puReturned)] real* a_prBuffer // Buffer to receive data
, unsigned a_uBufferLength // Size of buffer, number of reals required
, [in,out,ref] index* a_piStart // Offset within source data of reals required - on return this is set to the offset of the next unread non-zero real (or zero if no more non-zero reals are available)
, [out,ref] unsigned* a_puReturned // Number of reals returned - if less than requested, this indicates that the end of the source data has been reached
, [out,ref] index* a_piRemain // Number of reals remaining
// (a_piReturned & a_piRemain, apply for this call only - subsequent calls may obtain more or less data until IOTOutput::Status indicates the data is full)
); // Returns S_OK.
A ordered series of Vectors.
HRESULT Nature();// See API documentation for IOTData<Shape>
Enum() // Iterates across all vectors
A ordered series of bytes optimised for disk based operations.
HRESULT OptNature(); // See API documentation for IOTData<Shape>
Optimised for frame buffer purposes.
HRESULT OptNature(); // See API documentation for IOTData<Shape>
A single real number.
HRESULT OptNature(); // See API documentation for IOTData<Shape>
HRESULT OptReal([out]double *);
Any kind of textual information. Supports a variety of encoding systems, e.g. ASCII, Unicode.
HRESULT OptNature(); // See API documentation for IOTData<Shape>
These data interfaces are provided where a tool needs to accept geometry in indexed form. There may be any number of peered Point Space, Vertex List, and Topology Data interfaces.
P x IMeaningPointSpace/IOTDataVectorList
V x IMeaningVertexList/IOTDataIndexList
i=VertexList[v][] indexes PointSpace[p][i] // p obtained separately
T x IMeaningTopology/IOTDataIndexList
k=Topology[t][] indexes VertexList[v][k]// v obtained separately
A Vertex List may only reference a single Point Space. A Topology references a single vertex list.
A Topology is of a single kind (point/path/surface/solid).
IOTGeometry/IOTPointCloud/IOTTopography/IOT<other geometry info>
IOTPointCloud/IOTEnumPointLists/IOT<other point cloud info>
n x IOTPointList
IOTTopography/IOTEnumVertexLists/IOTEnumTopologies/IOT<other topographic info>
m x IOTVertexList (each referencing 1 IOTPointList i of n in IOTPointCloud)
p x IOTTopology (each referencing 1 IOTVertexList j of m in IOTTopography)
IOTPointList/IOTEnumPoints/IOT<other point list info>
Dump points as triples
IOTPoint/IOT<other point info>
Get coordinate triple
IOTTopology/IOTEnumVertex
Type // point/path/surface/solid
Characteristics // Circularity, winding rules, convexity, interpenetration, cavities, etc.
Provides a single number.
IOpenTool/IEgOTDataOptReal
1 x Real output
Real([out] real*);
Adds two numbers together.
The value returned
IOpenTool/IEnumUnknown
2 x Summand inputs
1 x Sum output
Will query input for IOTEgDataOptReal
Will query input for IOTEgDataOptReal
IEgOTConnectSum/IOTEgDataOptReal
Q: Is it intended for purposes to be matched when matching connections?
A: Yes