MetaCase Homepage
Forum Home Forum Home > > MetaEdit+
  New Posts New Posts RSS Feed - MetaEdit API to create models
  FAQ FAQ  Forum Search   Events   Register Register  Login Login

MetaEdit API to create models

 Post Reply Post Reply
Author
Message
edward22243 View Drop Down
Major Contributor
Major Contributor
Avatar

Joined: 19.Apr.2019
Points: 41
Post Options Post Options   Thanks (0) Thanks(0)   Quote edward22243 Quote  Post ReplyReply Direct Link To This Post Topic: MetaEdit API to create models
    Posted: 11.Nov.2022 at 10:59
I want to be able to generate models using the API (connect to WSDL-server). I read the https://www.metacase.com/papers/importing_API.pdf paper and I have it working partly. I can generate graphs, and objects and relations.

However I have problems with objects that have as a property a collection of other objects. The errormessage does not really explain too much. I just post the python code here since it is self-explanatory.
(I use Python Zeep btw, https://docs.python-zeep.org/)

def createMEOop(meany):
areaID, objectID = meany.meValue.split('_')
result = factory.MEOop(areaID=int(areaID), objectID=int(objectID))
return result

materialdef = factory.METype(name = "MaterialDef")
n = factory.MEAny( meType = "String", meValue = "'my_matdef'")
creat_mat_role = factory.MEAny( meType = "Boolean", meValue = "true")
# collections
physintout = factory.METype(name = "PhysicalInterfaceOut")
n = factory.MEAny( meType = "String", meValue = "'eerste'")
creat_lib = factory.MEAny( meType = "Boolean", meValue = "true")
p1 = c.service.instProps(physintout, factory.MEAnyArray([nullAny, nullAny]), factory.MEAnyArray([n, creat_lib]) , nullAny,nullAny)
#c.service.addToGraph(createMEOop(p1), createMEOop(newGraphAny)) #?
physin2 = factory.METype(name = "PhysicalInterfaceOut")
n = factory.MEAny( meType = "String", meValue = "'tweede'")
creat_lib = factory.MEAny( meType = "Boolean", meValue = "false")
p2 = c.service.instProps(physin2, factory.MEAnyArray([nullAny, nullAny]), factory.MEAnyArray([n, creat_lib]) , nullAny,nullAny)
# p1 en p2 geen AnyArray ??
collectionout = factory.MEAnyArray([p1,p2])
#c.service.addToGraph(createMEOop(p1), createMEOop(newGraphAny)) #?
physio = factory.METype(name = "PhysicalInterfaceIO")
n = factory.MEAny( meType = "String", meValue = "'derde'")
creat_lib = factory.MEAny( meType = "Boolean", meValue = "false")
p3 = c.service.instProps(physio, factory.MEAnyArray([nullAny, nullAny]), factory.MEAnyArray([n, creat_lib]) , nullAny,nullAny)
collectionio = factory.MEAnyArray([p3])

physin = factory.METype(name = "PhysicalInterfaceIn")
n = factory.MEAny( meType = "String", meValue = "'vierde'")
creat_lib = factory.MEAny( meType = "Boolean", meValue = "false")
p4 = c.service.instProps(physin, factory.MEAnyArray([nullAny, nullAny]), factory.MEAnyArray([n, creat_lib]) , nullAny,nullAny)
collectionin = factory.MEAnyArray([p4])
props_mat_def = factory.MEAnyArray([n, creat_mat_role, collectionout, collectionio, collectionin])
matdef_done = c.service.instProps(materialdef, factory.MEAnyArray([nullAny, nullAny, nullAny, nullAny, nullAny]), props_mat_def , nullAny,nullAny)

The matdef_done line fails,
what I did is instantiate every object (the PhysicalInterfaceIn) and because they are a collection I put the in a MEAnyArray.
Basically they look like:
>>> p4
{
    'meType': 'MEOop',
    'meValue': '3_7397'
}

The error I get is:
TypeError: Any element received object of type 'MEAnyArray', expected lxml.etree._Element or builtins.dict or zeep.objects.MEAny

But everything in the MEAnyArray is already an MEAny?

The metamodel is here:


Furthermore: Could I receive some more (elaborate) examples of using the API to generate more elaborate models (the paper is really basic), thanks.


Edited by edward22243 - 11.Nov.2022 at 11:14
Back to Top
stevek View Drop Down
MetaCase
MetaCase
Avatar

Joined: 11.Mar.2008
Points: 641
Post Options Post Options   Thanks (1) Thanks(1)   Quote stevek Quote  Post ReplyReply Direct Link To This Post Posted: 11.Nov.2022 at 13:01
I love your "the Python code is self-explanatory" Wink

Without knowing the details of Zeep, I'd suggest looking at the red lines for collectionout/io/in, where you create a property value that is a collection of objects. That value is an MEAny (it's going to be put in an MEAnyArray - cf. the Zeep error message saying you've put an MEAnyArray in an MEAnyArray). The format for a collection of MEOops in an MEAny is shown in the list of types used by the API:
any.meType="OrderedCollection"; 
any.meValue="3_123 3_125 3_127";
An MEAny's meValue is only a string, hence the need for a simple format rather than actual MEOop objects.

So you're probably going to need to replace those three lines to look more like this:
collectionout = factory.MEAny(meType = "OrderedCollection", meValue = "3_111 3_222");
where 3_111 is replaced with p1's areaID and objectID, and 3_222 similarly for p2. (I generally write little helper routines to map such strings to MEOops and back, or to wrap and unwrap things in MEAny's.)

For more documentation and examples of the API, have you looked at the manual's API chapter?
Back to Top
edward22243 View Drop Down
Major Contributor
Major Contributor
Avatar

Joined: 19.Apr.2019
Points: 41
Post Options Post Options   Thanks (0) Thanks(0)   Quote edward22243 Quote  Post ReplyReply Direct Link To This Post Posted: 11.Nov.2022 at 13:41
About the Python-code, just started working with it yesterday and it seems quit straightforward, and you clearly understood it Smile

Thanks for the quick answer, very helpfull, could not get this from the API description so quickly.  I did not do that correctly. So in the string with oids , they are seperated with a space. Will try that.

About helper-functions, I think it should be possible to generate API-access code (don't know any better name) from any metamodel to create models via the API. What I mean: what I am doing by hand now to be able to make graphs, and it contents (objects, etc) could be generated. 
We are in the modelling business to avoid having to handcode this, aren't we Smile

The only access to the metamodel I have via an exported mxt-model. 

Probably, you have made this years ago already (I would be disappointed if you did not make it already ). Would you be willing to share this? 



Back to Top
stevek View Drop Down
MetaCase
MetaCase
Avatar

Joined: 11.Mar.2008
Points: 641
Post Options Post Options   Thanks (0) Thanks(0)   Quote stevek Quote  Post ReplyReply Direct Link To This Post Posted: 11.Nov.2022 at 15:03
While it would be possible to generate an API at the level of the metamodel, rather than the metametamodel, there just don't seem enough API programs for a given metamodel and given programming language to make that worthwhile. Even if we just limit it to the space of programming languages, the last API questions I've answered have been on Python, Node.js, Java, C, and C#. Each of these tends to have more than one possible SOAP library, all with their own peculiarities. There just isn't the critical mass for one choice, nor any particularly clear choices, to make it feasible for us.

Any API built in that way would be very fragile with respect to metamodel evolution. And metamodel evolution is one of the main use cases for the API. Most evolution just works - expanding the metamodel with new object types or relationship types, renaming things - with no need for the API, but where you change the language in a less common way and want existing models to update automatically, the API is your best bet. You'd have to have two versions of the API working simultaneously to be able to do that, and the code would be nearly unreadable, since both ends of the transformation would look so similar.

For importing models from some other format, we tend to ingest that format and create MXM files. Often the source is simple enough that it can be parsed with MERL, which then gives you a familiar environment for processing and outputting the necessary XML. You can build a small hand-made model and export it to MXM to give you an initial example for how things should look (and there are lots of things you can omit - see the XML chapter in the Workbench User's Guide).

As always, the best approach depends on the task, source, target, processing, programming language and existing familiarity. Any of the approaches can work, including building a metamodel-level API level. I think I've only seen that once, although bits of low-hanging fruit are often picked from that area. That's the normal approach to coding: do it with the existing system for the first 2-3 occurrences, but if you have to write similar code often, make a function. 

Using the API is just programming after all, so the work gets optimized as you go along in building something to meet your particular needs - it's just hard for us to provide a once-and-for-all generic solution that meets each user's needs. Of course the users have all the familiar programmer tools on their side of the API, so if they want a metamodel-level API wrapper, they can build it - in a way that's appropriate to them. That will generally be a much simpler metamodel-level API than one made by a generic process (where it would have to support e.g. n-ary relationships with dynamic ports and complex properties in roles and relationships, whereas most use cases would just have simple binary relationships with maybe a single property).

Having seen the maintainability problems of metamodel-level APIs and XML schemas, we went with the metametamodel level. I don't think there's a right and wrong answer, but I do still feel that the right level for a metamodelling tool is the metametamodel level. The functions or schema are then the same for everybody, and we can sensibly document them and provide examples. The metamodel-specific parts are in data, not hard-coded in function names or element names - and from the point of view of a metamodelling tool, metamodels are data, so that seems a good fit. 


Edited by stevek - 11.Nov.2022 at 15:04
Back to Top
edward22243 View Drop Down
Major Contributor
Major Contributor
Avatar

Joined: 19.Apr.2019
Points: 41
Post Options Post Options   Thanks (0) Thanks(0)   Quote edward22243 Quote  Post ReplyReply Direct Link To This Post Posted: 14.Nov.2022 at 10:53
Thank you for the reply.

I could define the "MaterialDef" when I use the OrderedCollection as you described.

The materialdef (matdef_done) is succesfully generated (

matdef_done = {
    'meType': 'MEOop',
    'meValue': '3_7458'
}


Furthermore, I want to be able to generate a Material. The picture of my previous post (Screenshot) shows that the Material has two properties. A name (string) and the MaterialDef I just generated.

So I define a material:
product = factory.METype(name = "Material")
n = factory.MEAny( meType = "String", meValue = "'my_product'")
prod_props = factory.MEAnyArray([n, matdef_done]) 

So prod_props:
[{
    'meType': 'String',
    'meValue': "'my_product'"
}, {
    'meType': 'MEOop',
    'meValue': '3_7458'
}]

However, generating a Material leads to error: Number of values does not match number of properties.

Did a bit of fiddling, and turns out a Material can be generated when I just use the string property, so when I omit the matdef-property. 
I can not add this generated material to the graph though (  error: Bad receiver type for addToGraph ).

Maybe not strange, since it is still missing the MaterialDef property. 

Things get stranger though,
I am able to add the generated MaterialDef to the graph! While I only have defined the MaterialDef as a property of Material, and not in the graph itself. So I can do things programmatically that I would not be able to do in the diagram.

To summarise: below in the pic you see a OperationsDefintion3-graph with contens object (Material, NOT MaterialDef) and I can generate a MaterialDef in the graph.

Question: I would like to define the MaterialDef as a property of Material programmatically and be able to add that Material to the graph (as I do noramlly when using the Diagram GUI). How to continue?






Edited by edward22243 - 14.Nov.2022 at 10:58
Back to Top
stevek View Drop Down
MetaCase
MetaCase
Avatar

Joined: 11.Mar.2008
Points: 641
Post Options Post Options   Thanks (0) Thanks(0)   Quote stevek Quote  Post ReplyReply Direct Link To This Post Posted: 14.Nov.2022 at 11:52
Since Material is only expecting one property, it's probably a different type than the one you intended: i.e. you have more than one type called Material. From the picture you have a Graph type called Material too, and that will be found first. See the entry for METype, and as mentioned there either use the internal name (check from an MXT export) or use subTypeNamed(). With only 4 types in the API (MEOop, MEAny, METype, MENull), it's well worth reading that page of the manual. 

That explains your next question: you created a Graph, not an object, and you can't add a Graph into a Graph.

A Graph is fine with containing instances of object types that wouldn't now be allowed by the UI - you could have removed an old object type to sunset it, so users can't add new instances to the graph, but any existing instances there would still be fine (any generators for them would still work, so having them isn't a problem). The API can work with these cases too: since API programs are generally written by the metamodeller not the modeller, we trust that he knows well enough what he's doing. 
Back to Top
edward22243 View Drop Down
Major Contributor
Major Contributor
Avatar

Joined: 19.Apr.2019
Points: 41
Post Options Post Options   Thanks (0) Thanks(0)   Quote edward22243 Quote  Post ReplyReply Direct Link To This Post Posted: 14.Nov.2022 at 12:25
Changing the METype name to the name used in the mxm-file worked. Now able to add the Material to the graph. 

I quess being able to add a type to the graph that cannot be done in the GUI (by modelers, rather metamodellers) can be considered a feature and usefull for metaprogrammers. 

Thanks again.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down

Forum Software by Web Wiz Forums® version 12.05
Copyright ©2001-2022 Web Wiz Ltd.

This page was generated in 0.063 seconds.