ocelescope-module-ocpn-editor 0.10.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ocelescope_module_ocpn_editor-0.10.3/PKG-INFO +33 -0
- ocelescope_module_ocpn_editor-0.10.3/README.md +22 -0
- ocelescope_module_ocpn_editor-0.10.3/pyproject.toml +24 -0
- ocelescope_module_ocpn_editor-0.10.3/pyproject.toml.orig +22 -0
- ocelescope_module_ocpn_editor-0.10.3/src/ocelescope_module_ocpn_editor/__init__.py +0 -0
- ocelescope_module_ocpn_editor-0.10.3/src/ocelescope_module_ocpn_editor/model/editor_ocpn.py +35 -0
- ocelescope_module_ocpn_editor-0.10.3/src/ocelescope_module_ocpn_editor/model/editor_pn.py +53 -0
- ocelescope_module_ocpn_editor-0.10.3/src/ocelescope_module_ocpn_editor/module.py +15 -0
- ocelescope_module_ocpn_editor-0.10.3/src/ocelescope_module_ocpn_editor/routes.py +121 -0
- ocelescope_module_ocpn_editor-0.10.3/src/ocelescope_module_ocpn_editor/util/convert_format.py +126 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: ocelescope-module-ocpn-editor
|
|
3
|
+
Version: 0.10.3
|
|
4
|
+
Summary: An OCPN Editor
|
|
5
|
+
Author: Uy Sa Huynh
|
|
6
|
+
Requires-Dist: ocelescope-backend>=0.10.2,<0.11.0
|
|
7
|
+
Requires-Dist: ocelescope-module-filter>=0.10.2,<0.11.0
|
|
8
|
+
Requires-Dist: ocelescope-module-ocel>=0.10.2,<0.11.0
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# OCPN Editor
|
|
13
|
+
|
|
14
|
+
An OCPN Editor
|
|
15
|
+
|
|
16
|
+
An [Ocelescope](https://www.ocelescope.org) backend module. It is a FastAPI app
|
|
17
|
+
that `ocelescope-backend` discovers through the `ocelescope_backend.modules`
|
|
18
|
+
entry point in `pyproject.toml` and serves under `/modules/ocpnEditor/v1`.
|
|
19
|
+
|
|
20
|
+
- `src/ocelescope_module_ocpn_editor/module.py` — module class `OCPNEditor` (key `ocpnEditor`)
|
|
21
|
+
- `src/ocelescope_module_ocpn_editor/routes.py` — API routes
|
|
22
|
+
|
|
23
|
+
Install it next to `ocelescope-backend` (e.g. as a uv workspace member of an
|
|
24
|
+
Ocelescope module project) and run `ocelescope-backend serve`.
|
|
25
|
+
|
|
26
|
+
## Updating from the template
|
|
27
|
+
|
|
28
|
+
This module was generated with [Copier](https://copier.readthedocs.io). To pull
|
|
29
|
+
in later changes from the template, commit your work and run in this folder:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
uvx copier update
|
|
33
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# OCPN Editor
|
|
2
|
+
|
|
3
|
+
An OCPN Editor
|
|
4
|
+
|
|
5
|
+
An [Ocelescope](https://www.ocelescope.org) backend module. It is a FastAPI app
|
|
6
|
+
that `ocelescope-backend` discovers through the `ocelescope_backend.modules`
|
|
7
|
+
entry point in `pyproject.toml` and serves under `/modules/ocpnEditor/v1`.
|
|
8
|
+
|
|
9
|
+
- `src/ocelescope_module_ocpn_editor/module.py` — module class `OCPNEditor` (key `ocpnEditor`)
|
|
10
|
+
- `src/ocelescope_module_ocpn_editor/routes.py` — API routes
|
|
11
|
+
|
|
12
|
+
Install it next to `ocelescope-backend` (e.g. as a uv workspace member of an
|
|
13
|
+
Ocelescope module project) and run `ocelescope-backend serve`.
|
|
14
|
+
|
|
15
|
+
## Updating from the template
|
|
16
|
+
|
|
17
|
+
This module was generated with [Copier](https://copier.readthedocs.io). To pull
|
|
18
|
+
in later changes from the template, commit your work and run in this folder:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
uvx copier update
|
|
22
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ocelescope-module-ocpn-editor"
|
|
3
|
+
version = "0.10.3"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
description = "An OCPN Editor"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"ocelescope-backend>=0.10.2,<0.11.0",
|
|
9
|
+
"ocelescope-module-filter>=0.10.2,<0.11.0",
|
|
10
|
+
"ocelescope-module-ocel>=0.10.2,<0.11.0",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[[project.authors]]
|
|
14
|
+
name = "Uy Sa Huynh"
|
|
15
|
+
|
|
16
|
+
[project.entry-points."ocelescope_backend.modules"]
|
|
17
|
+
ocpnEditor = "ocelescope_module_ocpn_editor.module:OCPNEditor"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["uv_build>=0.9.26,<0.12.0"]
|
|
21
|
+
build-backend = "uv_build"
|
|
22
|
+
|
|
23
|
+
[tool.uv]
|
|
24
|
+
package = true
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ocelescope-module-ocpn-editor"
|
|
3
|
+
version = "0.10.3"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
description = "An OCPN Editor"
|
|
6
|
+
authors = [{ name = "Uy Sa Huynh" }]
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"ocelescope-backend>=0.10.2,<0.11.0",
|
|
10
|
+
"ocelescope-module-filter>=0.10.2,<0.11.0",
|
|
11
|
+
"ocelescope-module-ocel>=0.10.2,<0.11.0",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.entry-points."ocelescope_backend.modules"]
|
|
15
|
+
ocpnEditor = "ocelescope_module_ocpn_editor.module:OCPNEditor"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["uv_build>=0.9.26,<0.12.0"]
|
|
19
|
+
build-backend = "uv_build"
|
|
20
|
+
|
|
21
|
+
[tool.uv]
|
|
22
|
+
package = true
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EditorPlace(BaseModel):
|
|
5
|
+
id: str
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EditorTransition(BaseModel):
|
|
9
|
+
id: str
|
|
10
|
+
label: str | None = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EditorArc(BaseModel):
|
|
14
|
+
nodes: tuple[str, str]
|
|
15
|
+
weight: int | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EditorOCPN(BaseModel):
|
|
19
|
+
places: list[EditorPlace]
|
|
20
|
+
transitions: list[EditorTransition]
|
|
21
|
+
arcs: list[EditorArc]
|
|
22
|
+
initial_marking: dict[str, int] | None = None
|
|
23
|
+
final_marking: dict[str, int] | None = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OcpnExportRequest(BaseModel):
|
|
27
|
+
petri_net: EditorOCPN
|
|
28
|
+
place_object_type: dict[str, str]
|
|
29
|
+
place_in_out_mult: dict[str, tuple[dict[str, bool], dict[str, bool]]] | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class OcpnImportResponse(BaseModel):
|
|
33
|
+
petri_net: EditorOCPN
|
|
34
|
+
place_object_type: dict[str, str]
|
|
35
|
+
place_in_out_mult: dict[str, tuple[dict[str, bool], dict[str, bool]]]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from ocelescope import Resource
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ImportPlace(BaseModel):
|
|
7
|
+
id: str
|
|
8
|
+
tokens: int = 0
|
|
9
|
+
final_tokens: int = 0
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ImportTransition(BaseModel):
|
|
13
|
+
id: str
|
|
14
|
+
label: str | None = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ImportArc(BaseModel):
|
|
18
|
+
source: str
|
|
19
|
+
target: str
|
|
20
|
+
weight: int = 1
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ImportResponse(BaseModel):
|
|
24
|
+
places: list[ImportPlace]
|
|
25
|
+
transitions: list[ImportTransition]
|
|
26
|
+
arcs: list[ImportArc]
|
|
27
|
+
|
|
28
|
+
#Resource
|
|
29
|
+
|
|
30
|
+
class ClassicPlace(BaseModel):
|
|
31
|
+
id: str
|
|
32
|
+
tokens: int = 0
|
|
33
|
+
final_tokens: int = 0
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ClassicTransition(BaseModel):
|
|
37
|
+
id: str
|
|
38
|
+
label: str | None = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ClassicArc(BaseModel):
|
|
42
|
+
source: str
|
|
43
|
+
target: str
|
|
44
|
+
weight: int = 1
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ClassicPetriNet(Resource):
|
|
48
|
+
label = "Classic Petri Net"
|
|
49
|
+
description = "A classic Petri net"
|
|
50
|
+
|
|
51
|
+
places: list[ClassicPlace] = Field(default_factory=list)
|
|
52
|
+
transitions: list[ClassicTransition] = Field(default_factory=list)
|
|
53
|
+
arcs: list[ClassicArc] = Field(default_factory=list)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from fastapi import FastAPI
|
|
2
|
+
from ocelescope_backend.app.modules import Module, ModuleMeta
|
|
3
|
+
from packaging.version import Version
|
|
4
|
+
from ocelescope_module_ocpn_editor.routes import router
|
|
5
|
+
from ocelescope_module_ocpn_editor.model.editor_pn import ClassicPetriNet
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OCPNEditor(Module):
|
|
9
|
+
meta = ModuleMeta(key="ocpnEditor", version=Version("1.0"))
|
|
10
|
+
resources = [ClassicPetriNet]
|
|
11
|
+
@classmethod
|
|
12
|
+
def create_app(cls) -> FastAPI:
|
|
13
|
+
app = FastAPI(title="OCPN Editor", version=str(cls.meta.version))
|
|
14
|
+
app.include_router(router)
|
|
15
|
+
return app
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from fastapi import APIRouter, UploadFile
|
|
2
|
+
import tempfile
|
|
3
|
+
import os
|
|
4
|
+
from r4pm.petri_net import export_pnml, import_pnml
|
|
5
|
+
from fastapi.responses import PlainTextResponse
|
|
6
|
+
from ocelescope_module_ocpn_editor.util.convert_format import convert_to_OCPN, convert_from_OCPN, convert_to_pnet_resource
|
|
7
|
+
from ocelescope_module_ocpn_editor.model.editor_ocpn import OcpnExportRequest, OcpnImportResponse
|
|
8
|
+
from ocelescope import PetriNet
|
|
9
|
+
from ocelescope_module_ocpn_editor.util.convert_format import convert_from_pnml
|
|
10
|
+
from ocelescope_module_ocpn_editor.model.editor_pn import ImportResponse, ClassicPetriNet
|
|
11
|
+
from ocelescope_backend.app.dependencies import ApiOcel, ApiSession
|
|
12
|
+
from ocelescope_backend.app.internal.model.resource import ResourceStore
|
|
13
|
+
from typing import cast
|
|
14
|
+
|
|
15
|
+
router = APIRouter()
|
|
16
|
+
|
|
17
|
+
@router.get("/{ocel_id}/objects/types", operation_id="objectTypes")
|
|
18
|
+
def get_object_types(ocel:ApiOcel) -> list[str]:
|
|
19
|
+
return ocel.objects.types
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@router.get("/{ocel_id}/events/activities", operation_id="eventActivities")
|
|
23
|
+
def get_event_activities(ocel:ApiOcel) -> list[str]:
|
|
24
|
+
return ocel.events.activities
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ============================================
|
|
28
|
+
|
|
29
|
+
# Classic Petri-Net
|
|
30
|
+
|
|
31
|
+
# ============================================
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Export/Import
|
|
35
|
+
|
|
36
|
+
@router.post("/petri-net/export/pnml", operation_id="exportPetriNetPnml")
|
|
37
|
+
def export_petri_net_pnml(net: dict) -> PlainTextResponse:
|
|
38
|
+
with tempfile.NamedTemporaryFile(suffix=".pnml", delete=False) as tmp:
|
|
39
|
+
tmp_path = tmp.name
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
export_pnml(net, tmp_path)
|
|
43
|
+
with open(tmp_path, "r", encoding="utf-8") as f:
|
|
44
|
+
pnml_text = f.read()
|
|
45
|
+
finally:
|
|
46
|
+
os.unlink(tmp_path)
|
|
47
|
+
|
|
48
|
+
return PlainTextResponse(content=pnml_text)
|
|
49
|
+
|
|
50
|
+
@router.post("/petri-net/import/pnml", operation_id="importPetriNetPnml")
|
|
51
|
+
async def import_petri_net_pnml(file: UploadFile) -> ImportResponse:
|
|
52
|
+
input = await file.read()
|
|
53
|
+
|
|
54
|
+
with tempfile.NamedTemporaryFile(suffix=".pnml", delete=False) as tmp:
|
|
55
|
+
tmp.write(input)
|
|
56
|
+
tmp_path = tmp.name
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
net = import_pnml(tmp_path)
|
|
60
|
+
finally:
|
|
61
|
+
os.unlink(tmp_path)
|
|
62
|
+
|
|
63
|
+
return convert_from_pnml(net)
|
|
64
|
+
|
|
65
|
+
# Load/Save to Session
|
|
66
|
+
|
|
67
|
+
@router.post("/petri-net/export/session")
|
|
68
|
+
def save_to_session_classic(session: ApiSession, pnet: dict, name:str):
|
|
69
|
+
resource_pnet = convert_to_pnet_resource(pnet)
|
|
70
|
+
session.add_resource(ResourceStore.from_resource(resource_pnet, name=name))
|
|
71
|
+
print(session.list_resources())
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ============================================
|
|
76
|
+
|
|
77
|
+
# Object-Centric Petri Net
|
|
78
|
+
|
|
79
|
+
# ============================================
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Export/Import
|
|
83
|
+
|
|
84
|
+
@router.post("/ocpn/export/ocelescope", operation_id="exportOCPN")
|
|
85
|
+
def export_ocpn(ocpn: OcpnExportRequest) -> PlainTextResponse:
|
|
86
|
+
pnet = convert_to_OCPN(ocpn)
|
|
87
|
+
|
|
88
|
+
with tempfile.NamedTemporaryFile(suffix=".ocelescope", delete=False) as tmp:
|
|
89
|
+
tmp_path = tmp.name
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
written_path = pnet.write(tmp_path)
|
|
93
|
+
with open(written_path, "r", encoding="utf-8") as f:
|
|
94
|
+
content = f.read()
|
|
95
|
+
finally:
|
|
96
|
+
os.unlink(written_path)
|
|
97
|
+
|
|
98
|
+
return PlainTextResponse(content=content)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@router.post("/ocpn/import/ocelescope", operation_id="importOCPN")
|
|
102
|
+
async def import_ocpn(file: UploadFile) -> OcpnImportResponse:
|
|
103
|
+
input = await file.read()
|
|
104
|
+
pnet = PetriNet.model_validate_json(input)
|
|
105
|
+
return convert_from_OCPN(pnet)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Load/Save to session
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@router.post("/ocpn/export/session", operation_id="saveSession")
|
|
112
|
+
def save_to_session(session: ApiSession, ocpn: OcpnExportRequest, name:str):
|
|
113
|
+
pnet = convert_to_OCPN(ocpn)
|
|
114
|
+
session.add_resource(ResourceStore.from_resource(pnet, name=name))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@router.post("/ocpn/import/session", operation_id="loadSession")
|
|
118
|
+
async def load_from_session(session:ApiSession, resource_id: str | None = None) -> OcpnImportResponse:
|
|
119
|
+
resource_store = cast(PetriNet,session.get_resource(resource_id))
|
|
120
|
+
petri_net = PetriNet(**resource_store.data)
|
|
121
|
+
return convert_from_OCPN(petri_net)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from ocelescope.resource.default.petri_net import Arc, ArcType, PetriNet, Place, Transition
|
|
2
|
+
from ocelescope_module_ocpn_editor.model.editor_ocpn import OcpnExportRequest, OcpnImportResponse, EditorArc, EditorOCPN, EditorPlace, EditorTransition
|
|
3
|
+
from ocelescope_module_ocpn_editor.model.editor_pn import ImportResponse, ImportPlace,ImportArc,ImportTransition
|
|
4
|
+
from ocelescope_module_ocpn_editor.model.editor_pn import ClassicPetriNet, ClassicArc, ClassicPlace, ClassicTransition
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def convert_to_OCPN(ocpn: OcpnExportRequest) -> PetriNet:
|
|
8
|
+
net = ocpn.petri_net
|
|
9
|
+
object_types = ocpn.place_object_type
|
|
10
|
+
in_out_mult = ocpn.place_in_out_mult if ocpn.place_in_out_mult is not None else {}
|
|
11
|
+
|
|
12
|
+
ocpn = PetriNet()
|
|
13
|
+
|
|
14
|
+
for place in net.places:
|
|
15
|
+
ocpn.add_place(Place(name=place.id, object_type=object_types[place.id]))
|
|
16
|
+
|
|
17
|
+
for transition in net.transitions:
|
|
18
|
+
ocpn.add_transition(Transition(name=transition.id, label=transition.label))
|
|
19
|
+
|
|
20
|
+
place_ids = {place.name for place in ocpn.places}
|
|
21
|
+
|
|
22
|
+
def is_variable(source: str, target: str) -> bool:
|
|
23
|
+
if source in place_ids:
|
|
24
|
+
_, outgoing = in_out_mult.get(source, ({}, {}))
|
|
25
|
+
return bool(outgoing.get(target))
|
|
26
|
+
incoming, _ = in_out_mult.get(target, ({}, {}))
|
|
27
|
+
return bool(incoming.get(source))
|
|
28
|
+
|
|
29
|
+
for arc in net.arcs:
|
|
30
|
+
source, target = arc.nodes
|
|
31
|
+
ocpn.add_arc(
|
|
32
|
+
Arc(
|
|
33
|
+
source=source,
|
|
34
|
+
target=target,
|
|
35
|
+
type=ArcType.VARIABLE if is_variable(source, target) else ArcType.NORMAL,
|
|
36
|
+
weight=arc.weight if arc.weight is not None else 1,
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
ocpn.initial_marking = dict(net.initial_marking if net.initial_marking is not None else {})
|
|
41
|
+
ocpn.final_marking = dict(net.final_marking if net.final_marking is not None else {})
|
|
42
|
+
|
|
43
|
+
return ocpn
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def convert_from_OCPN(ocpn:PetriNet) -> OcpnImportResponse:
|
|
47
|
+
places = []
|
|
48
|
+
place_object_type = {}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
for place in ocpn.places:
|
|
52
|
+
places.append(EditorPlace(id=place.name))
|
|
53
|
+
place_object_type[place.name] = place.object_type
|
|
54
|
+
|
|
55
|
+
transitions = [EditorTransition(id=transition.name, label=transition.label) for transition in ocpn.transitions]
|
|
56
|
+
initial_marking = ocpn.initial_marking if ocpn.initial_marking is not None else {}
|
|
57
|
+
final_marking = ocpn.final_marking if ocpn.final_marking is not None else {}
|
|
58
|
+
|
|
59
|
+
place_ids = {place.id for place in places}
|
|
60
|
+
place_in_out_mult: dict[str, tuple[dict[str, bool], dict[str, bool]]] = {
|
|
61
|
+
ids: ({}, {}) for ids in place_ids
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
arcs = []
|
|
66
|
+
for arc in ocpn.arcs:
|
|
67
|
+
arcs.append(EditorArc(nodes=(arc.source,arc.target), weight=arc.weight))
|
|
68
|
+
if arc.type != ArcType.VARIABLE:
|
|
69
|
+
continue
|
|
70
|
+
if arc.source in place_ids:
|
|
71
|
+
place_id, transition_id = arc.source, arc.target
|
|
72
|
+
incoming, outgoing = place_in_out_mult[place_id]
|
|
73
|
+
place_in_out_mult[place_id] = (incoming, {**outgoing, transition_id: True})
|
|
74
|
+
else:
|
|
75
|
+
transition_id, place_id = arc.source, arc.target
|
|
76
|
+
incoming, outgoing = place_in_out_mult[place_id]
|
|
77
|
+
place_in_out_mult[place_id] = ({**incoming, transition_id: True}, outgoing)
|
|
78
|
+
|
|
79
|
+
return OcpnImportResponse(
|
|
80
|
+
petri_net= EditorOCPN(
|
|
81
|
+
places=places,
|
|
82
|
+
transitions=transitions,
|
|
83
|
+
arcs=arcs,
|
|
84
|
+
initial_marking=initial_marking,
|
|
85
|
+
final_marking=final_marking,
|
|
86
|
+
),
|
|
87
|
+
place_object_type=place_object_type,
|
|
88
|
+
place_in_out_mult=place_in_out_mult,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def convert_from_pnml(net:dict) -> ImportResponse:
|
|
93
|
+
initial_marking = net.get("initial_marking") if net.get("initial_marking") is not None else {}
|
|
94
|
+
final_markings = net.get("final_markings") or []
|
|
95
|
+
final_marking = final_markings[0] if final_markings else {}
|
|
96
|
+
|
|
97
|
+
places = [
|
|
98
|
+
ImportPlace(
|
|
99
|
+
id=pid,
|
|
100
|
+
tokens=initial_marking.get(pid,0),
|
|
101
|
+
final_tokens=final_marking.get(pid,0)
|
|
102
|
+
)
|
|
103
|
+
for pid in net.get("places",{})
|
|
104
|
+
]
|
|
105
|
+
transitions = [
|
|
106
|
+
ImportTransition(
|
|
107
|
+
id=tid,
|
|
108
|
+
label= transition.get("label")
|
|
109
|
+
)
|
|
110
|
+
for tid, transition in net.get("transitions",{}).items()
|
|
111
|
+
]
|
|
112
|
+
arcs = []
|
|
113
|
+
for arc in net.get("arcs", []):
|
|
114
|
+
source, target = arc["from_to"]["nodes"]
|
|
115
|
+
arcs.append(ImportArc(source=source, target=target, weight=arc.get("weight", 1)))
|
|
116
|
+
|
|
117
|
+
return ImportResponse(places=places, transitions=transitions, arcs=arcs)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def convert_to_pnet_resource(net: dict) -> ClassicPetriNet:
|
|
121
|
+
pnet = convert_from_pnml(net)
|
|
122
|
+
return ClassicPetriNet(
|
|
123
|
+
places=[ClassicPlace(**p.model_dump()) for p in pnet.places],
|
|
124
|
+
transitions=[ClassicTransition(**t.model_dump()) for t in pnet.transitions],
|
|
125
|
+
arcs=[ClassicArc(**a.model_dump()) for a in pnet.arcs],
|
|
126
|
+
)
|