cloudpss 4.0.2a1__py3-none-any.whl → 4.1.1a2__py3-none-any.whl
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.
- cloudpss/__init__.py +2 -2
- cloudpss/function/functionExecution.py +1 -1
- cloudpss/ieslab/IESLabPlan.py +0 -87
- cloudpss/ieslab/IESLabSimulation.py +2 -62
- cloudpss/job/job.py +98 -135
- cloudpss/job/messageStreamReceiver.py +59 -53
- cloudpss/job/messageStreamSender.py +38 -33
- cloudpss/model/implements/diagram.py +0 -113
- cloudpss/model/model.py +365 -364
- cloudpss/model/revision.py +9 -9
- cloudpss/model/topology.py +9 -2
- cloudpss/runner/__init__.py +5 -3
- cloudpss/runner/receiver.py +2 -0
- cloudpss/runner/runner.py +91 -18
- cloudpss/utils/AsyncIterable.py +27 -0
- cloudpss/utils/__init__.py +5 -1
- cloudpss/utils/httpAsyncRequest.py +89 -0
- cloudpss/utils/httprequests.py +6 -1
- cloudpss/utils/yamlLoader.py +40 -41
- cloudpss/version.py +1 -1
- {cloudpss-4.0.2a1.dist-info → cloudpss-4.1.1a2.dist-info}/METADATA +4 -1
- {cloudpss-4.0.2a1.dist-info → cloudpss-4.1.1a2.dist-info}/RECORD +24 -22
- {cloudpss-4.0.2a1.dist-info → cloudpss-4.1.1a2.dist-info}/WHEEL +0 -0
- {cloudpss-4.0.2a1.dist-info → cloudpss-4.1.1a2.dist-info}/top_level.txt +0 -0
cloudpss/model/model.py
CHANGED
@@ -1,52 +1,58 @@
|
|
1
|
+
import asyncio
|
1
2
|
import os
|
2
3
|
import re
|
3
|
-
import io
|
4
|
-
import json
|
5
|
-
import yaml
|
6
|
-
import gzip
|
7
4
|
from copy import deepcopy
|
8
5
|
|
6
|
+
from cloudpss.job.job import Job
|
7
|
+
from cloudpss.utils.IO import IO
|
8
|
+
from cloudpss.utils.httpAsyncRequest import graphql_fetch
|
9
|
+
|
9
10
|
from .revision import ModelRevision
|
10
11
|
from .jobDefinitions import JOB_DEFINITIONS
|
11
|
-
from ..utils import request, fileLoad, graphql_request
|
12
12
|
from ..verify import userName
|
13
13
|
|
14
|
-
from cloudpss.runner.result import
|
15
|
-
from cloudpss.runner.runner import
|
14
|
+
from cloudpss.runner.result import IESResult
|
15
|
+
from cloudpss.runner.runner import Runner
|
16
16
|
|
17
17
|
|
18
18
|
class Model(object):
|
19
19
|
"""
|
20
|
-
|
20
|
+
CloudPSS工程类,用于处理加载后的工程数据
|
21
21
|
|
22
|
-
|
22
|
+
实例变量说明:
|
23
23
|
|
24
|
-
|
24
|
+
rid 项目的 rid
|
25
25
|
|
26
|
-
|
26
|
+
name 项目的名称
|
27
27
|
|
28
|
-
|
28
|
+
description 项目的描述
|
29
29
|
|
30
|
-
|
30
|
+
revision 当前项目的版本信息
|
31
31
|
|
32
|
-
|
32
|
+
configs 当前项目的所有参数方案
|
33
33
|
|
34
|
-
|
34
|
+
jobs 当前项目的所有计算方案
|
35
35
|
|
36
|
-
|
36
|
+
context 当前项目的上下文相关信息
|
37
37
|
|
38
38
|
"""
|
39
|
+
|
39
40
|
context: dict
|
40
41
|
jobs: list
|
41
42
|
configs: list
|
42
43
|
|
43
44
|
def __init__(self, model: dict = {}):
|
44
45
|
"""
|
45
|
-
|
46
|
+
项目初始化
|
46
47
|
"""
|
47
48
|
for k, v in model.items():
|
48
|
-
if k ==
|
49
|
-
|
49
|
+
if k == "revision":
|
50
|
+
if "version" in v and v["version"] < 5:
|
51
|
+
self.revision = ModelRevision(v)
|
52
|
+
else:
|
53
|
+
raise Exception(
|
54
|
+
"当前SDK版本(ver 3.X.X)不兼容该项目文件,请先升级项目文件。具体方法:将该项目文件导入至XStudio 3.X.X平台后重新保存至本地后即可。"
|
55
|
+
)
|
50
56
|
|
51
57
|
else:
|
52
58
|
self.__dict__[k] = v
|
@@ -56,82 +62,42 @@ class Model(object):
|
|
56
62
|
|
57
63
|
def toJSON(self):
|
58
64
|
"""
|
59
|
-
|
60
|
-
|
65
|
+
类对象序列化为 dict
|
66
|
+
:return: dict
|
61
67
|
"""
|
62
|
-
model = {**self.__dict__,
|
68
|
+
model = {**self.__dict__, "revision": self.revision.toJSON()}
|
63
69
|
return model
|
64
70
|
|
65
71
|
def getAllComponents(self):
|
66
72
|
"""
|
67
|
-
|
68
|
-
|
69
|
-
:return: 所有元件信息
|
70
|
-
|
71
|
-
>>> model.getAllComponents()
|
72
|
-
{
|
73
|
-
'canvas_0_2': Component 实例
|
74
|
-
}
|
75
|
-
"""
|
76
|
-
diagramImplement = self.revision.getImplements().getDiagram()
|
77
|
-
if diagramImplement is None:
|
78
|
-
raise ValueError('不存在拓扑实现')
|
79
|
-
return diagramImplement.getAllComponents()
|
80
|
-
|
81
|
-
def addComponent(self, definition, label, args, pins, canvas=None, position=None, size=None):
|
82
|
-
"""
|
83
|
-
添加元件实现
|
73
|
+
获取实现
|
84
74
|
|
85
|
-
:return:
|
75
|
+
:return: 所有元件信息
|
86
76
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
raise ValueError("不存在拓扑实现")
|
92
|
-
return diagramImplement.addComponent(definition, label, args, pins, canvas, position, size)
|
93
|
-
|
94
|
-
def removeComponent(self, key):
|
95
|
-
"""
|
96
|
-
删除元件实现
|
97
|
-
|
98
|
-
:return: boolean
|
99
|
-
|
100
|
-
>>>> model.removeComponent(key)
|
101
|
-
"""
|
102
|
-
diagramImplement = self.revision.getImplements().getDiagram()
|
103
|
-
if diagramImplement is None:
|
104
|
-
raise ValueError("不存在拓扑实现")
|
105
|
-
return diagramImplement.removeComponent(key)
|
106
|
-
|
107
|
-
def updateComponent(self, key, args):
|
108
|
-
"""
|
109
|
-
更新元件实现
|
110
|
-
|
111
|
-
:return: bool
|
112
|
-
|
113
|
-
>>>> model.updateComponent(key, args)
|
77
|
+
>>> model.getAllComponents()
|
78
|
+
{
|
79
|
+
'canvas_0_2': Component 实例
|
80
|
+
}
|
114
81
|
"""
|
115
82
|
diagramImplement = self.revision.getImplements().getDiagram()
|
116
83
|
if diagramImplement is None:
|
117
84
|
raise ValueError("不存在拓扑实现")
|
118
|
-
return diagramImplement.
|
119
|
-
|
85
|
+
return diagramImplement.getAllComponents()
|
120
86
|
|
121
87
|
def getComponentsByRid(self, rid: str):
|
122
88
|
"""
|
123
|
-
|
89
|
+
通过指定元件类型获取元件
|
124
90
|
|
125
|
-
|
91
|
+
:params str: 元件类型
|
126
92
|
|
127
|
-
|
93
|
+
:type rid: str
|
128
94
|
|
129
|
-
|
95
|
+
:return: 按照元件的 rid 过滤后的 dict<>
|
130
96
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
97
|
+
>>> model.getComponentsByRid('model/CloudPSS/newInductorRouter')
|
98
|
+
{
|
99
|
+
'canvas_0_2': Component 实例
|
100
|
+
}
|
135
101
|
|
136
102
|
"""
|
137
103
|
|
@@ -139,8 +105,7 @@ class Model(object):
|
|
139
105
|
|
140
106
|
cells = {}
|
141
107
|
for key, val in v.items():
|
142
|
-
|
143
|
-
if not val.shape.startswith('diagram-component'):
|
108
|
+
if not val.shape.startswith("diagram-component"):
|
144
109
|
continue
|
145
110
|
if val.definition == rid:
|
146
111
|
cells[key] = val
|
@@ -148,14 +113,14 @@ class Model(object):
|
|
148
113
|
|
149
114
|
def getComponentByKey(self, componentKey: str):
|
150
115
|
"""
|
151
|
-
|
116
|
+
通过元件的 key 获取对应的元件
|
152
117
|
|
153
|
-
|
118
|
+
:params key: key 元件的key
|
154
119
|
|
155
|
-
|
120
|
+
:Return: Component 实例
|
156
121
|
|
157
|
-
|
158
|
-
|
122
|
+
>>> model.getComponentByKey('canvas_0_757')
|
123
|
+
Component 实例
|
159
124
|
"""
|
160
125
|
|
161
126
|
v = self.getAllComponents()
|
@@ -163,144 +128,158 @@ class Model(object):
|
|
163
128
|
|
164
129
|
def getModelJob(self, name):
|
165
130
|
"""
|
166
|
-
|
131
|
+
获取指定名称的计算方案
|
167
132
|
|
168
|
-
|
133
|
+
:params Name: Name 参数名称
|
169
134
|
|
170
|
-
|
135
|
+
:return: 同名计算方案数组
|
171
136
|
|
172
|
-
|
137
|
+
>>> model.getModelJob('电磁暂态方案 1')
|
173
138
|
"""
|
174
139
|
jobs = []
|
175
140
|
|
176
141
|
for val in self.jobs:
|
177
|
-
if val[
|
142
|
+
if val["name"] == name:
|
178
143
|
jobs.append(val)
|
179
144
|
|
180
145
|
return jobs
|
181
146
|
|
182
147
|
def createJob(self, jobType: str, name):
|
183
148
|
"""
|
184
|
-
|
185
|
-
|
149
|
+
创建一个计算方案
|
150
|
+
创建出的方案默认不加入到项目中,需要加入请调用 addJob
|
186
151
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
152
|
+
:params jobType: 方案类型
|
153
|
+
电磁暂态仿真方案 emtp
|
154
|
+
移频电磁暂态仿真方案 sfemt
|
155
|
+
潮流计算方案 powerFlow
|
191
156
|
|
192
|
-
|
157
|
+
:return: 返回一个指定类型的计算方案
|
193
158
|
|
194
|
-
|
195
|
-
|
159
|
+
>>> model.createJob('emtp','emtp job')
|
160
|
+
计算方案
|
196
161
|
"""
|
197
162
|
job = deepcopy(JOB_DEFINITIONS[jobType])
|
198
|
-
job[
|
163
|
+
job["name"] = name
|
199
164
|
return job
|
200
165
|
|
201
166
|
def addJob(self, job: dict):
|
202
167
|
"""
|
203
|
-
|
168
|
+
将计算方案添加到工程中
|
204
169
|
|
205
|
-
|
170
|
+
:params job: 计算方案 dict
|
206
171
|
|
207
|
-
|
208
|
-
|
172
|
+
>>> job = model.createJob('emtp','emtp job')
|
173
|
+
model.addJob(job)
|
209
174
|
"""
|
210
175
|
|
211
176
|
self.jobs.append(job)
|
212
177
|
|
213
178
|
def getModelConfig(self, name):
|
214
179
|
"""
|
215
|
-
|
180
|
+
获取指定名称的参数方案
|
216
181
|
|
217
|
-
|
182
|
+
:params name: 参数方案名称
|
218
183
|
|
219
|
-
|
184
|
+
:return: 同名的方案数组
|
220
185
|
|
221
|
-
|
186
|
+
>>> model.getModelConfig('参数方案 1')
|
222
187
|
"""
|
223
188
|
configs = []
|
224
189
|
|
225
190
|
for val in self.configs:
|
226
|
-
if val[
|
191
|
+
if val["name"] == name:
|
227
192
|
configs.append(val)
|
228
193
|
|
229
194
|
return configs
|
230
195
|
|
231
196
|
def createConfig(self, name):
|
232
197
|
"""
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
198
|
+
创建一个参数方案
|
199
|
+
根据项目的第一个参数方案生成一个方案
|
200
|
+
创建出的方案默认不加入到项目中,需要加入请调用 addConfig
|
201
|
+
:params name: 参数方案名称
|
237
202
|
|
238
|
-
|
203
|
+
:return: 返回一个参数方案 dict
|
239
204
|
|
240
|
-
|
241
|
-
|
205
|
+
>>> job = model.createConfig('my config')
|
206
|
+
参数方案
|
242
207
|
"""
|
243
208
|
|
244
209
|
config = deepcopy(self.configs[0])
|
245
|
-
config[
|
210
|
+
config["name"] = name
|
246
211
|
return config
|
247
212
|
|
248
213
|
def addConfig(self, config):
|
249
214
|
"""
|
250
|
-
|
215
|
+
将参数方案添加到工程中
|
251
216
|
|
252
|
-
|
217
|
+
:params config: 参数方案 dict
|
253
218
|
|
254
|
-
|
255
|
-
|
219
|
+
>>> config = model.createConfig('my config')
|
220
|
+
model.addConfig(config)
|
256
221
|
"""
|
257
222
|
|
258
223
|
self.configs.append(config)
|
259
224
|
return config
|
260
225
|
|
261
226
|
@staticmethod
|
262
|
-
def fetchMany(name=None,
|
227
|
+
def fetchMany(name=None, cursor=[]):
|
228
|
+
return asyncio.run(Model.fetchManyAsync(name, cursor))
|
229
|
+
|
230
|
+
@staticmethod
|
231
|
+
async def fetchManyAsync(name=None, cursor=[]):
|
263
232
|
"""
|
264
|
-
|
233
|
+
获取用户可以运行的项目列表
|
265
234
|
|
266
|
-
|
267
|
-
|
268
|
-
:params pageOffset: 分页开始位置
|
235
|
+
:params name: 查询名称,模糊查询
|
236
|
+
:params cursor: 游标
|
269
237
|
|
270
|
-
|
238
|
+
:return: 按分页信息返回项目列表
|
271
239
|
|
272
|
-
|
273
|
-
|
240
|
+
>>> data= await Model.fetchManyAsync()
|
241
|
+
{
|
242
|
+
items: [
|
274
243
|
{'rid': 'model/admin/share-test', 'name': '1234', 'description': '1234'}
|
275
244
|
...
|
276
|
-
]
|
245
|
+
],
|
246
|
+
cursor: ["1699353593000"],
|
247
|
+
|
248
|
+
}
|
249
|
+
|
250
|
+
|
277
251
|
"""
|
252
|
+
query = """query($input:ModelsInput!){models(input:$input){cursor total count items{rid name description owner tags updatedAt pages{pageId resource key path mode}access{permission role}}}}"""
|
253
|
+
variables = {
|
254
|
+
"cursor": cursor,
|
255
|
+
"limit": 10,
|
256
|
+
"orderBy": ["updatedAt<"],
|
257
|
+
"permissionEveryone": ["b_any", 2**16],
|
258
|
+
}
|
259
|
+
if name is not None:
|
260
|
+
variables["_search"] = name
|
278
261
|
|
279
|
-
|
280
|
-
'api/resources/type/model/permission/write',
|
281
|
-
params={
|
282
|
-
'page_offset': pageOffset,
|
283
|
-
'page_size': pageSize,
|
284
|
-
'search': name
|
285
|
-
})
|
286
|
-
modelList = json.loads(r.text)
|
262
|
+
data = await graphql_fetch(query, {"input": variables})
|
287
263
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
} for val in modelList]
|
264
|
+
if "errors" in data:
|
265
|
+
raise Exception(data["errors"][0]["message"])
|
266
|
+
|
267
|
+
return data["data"]["models"]
|
293
268
|
|
294
269
|
@staticmethod
|
295
270
|
def fetch(rid):
|
271
|
+
return asyncio.run(Model.fetchAsync(rid))
|
272
|
+
|
273
|
+
@staticmethod
|
274
|
+
async def fetchAsync(rid):
|
296
275
|
"""
|
297
|
-
|
276
|
+
获取项目
|
298
277
|
|
299
|
-
|
278
|
+
:params rid: 项目 rid
|
300
279
|
|
301
|
-
|
280
|
+
:return: 返回一个项目实例
|
302
281
|
|
303
|
-
|
282
|
+
>>> model=Model.fetch('model/Demo/test')
|
304
283
|
|
305
284
|
"""
|
306
285
|
query = """
|
@@ -313,6 +292,12 @@ class Model(object):
|
|
313
292
|
name
|
314
293
|
rid
|
315
294
|
tags
|
295
|
+
keywords
|
296
|
+
permissions{
|
297
|
+
member
|
298
|
+
moderator
|
299
|
+
everyone
|
300
|
+
}
|
316
301
|
revision {
|
317
302
|
author
|
318
303
|
documentation
|
@@ -327,229 +312,247 @@ class Model(object):
|
|
327
312
|
}
|
328
313
|
}
|
329
314
|
"""
|
330
|
-
data =
|
331
|
-
if
|
332
|
-
raise Exception(data[
|
333
|
-
return Model(data[
|
315
|
+
data = await graphql_fetch(query, {"rid": rid})
|
316
|
+
if "errors" in data:
|
317
|
+
raise Exception(data["errors"][0]["message"])
|
318
|
+
return Model(data["data"]["model"])
|
319
|
+
|
320
|
+
def run(self, job=None, config=None, name=None, policy=None, **kwargs):
|
321
|
+
return asyncio.run(self.runAsync(job, config, name, policy, **kwargs))
|
334
322
|
|
335
|
-
def
|
323
|
+
async def runAsync(self, job=None, config=None, name=None, policy=None, **kwargs):
|
336
324
|
"""
|
337
325
|
|
338
|
-
|
326
|
+
调用仿真
|
339
327
|
|
340
|
-
|
341
|
-
|
342
|
-
|
328
|
+
:params job: 调用仿真时使用的计算方案,不指定将使用算例保存时选中的计算方案
|
329
|
+
:params config: 调用仿真时使用的参数方案,不指定将使用算例保存时选中的参数方案
|
330
|
+
:params name: 任务名称,为空时使用项目的参数方案名称和计算方案名称
|
343
331
|
|
344
|
-
|
332
|
+
:return: 返回一个Job实例
|
345
333
|
|
346
|
-
|
347
|
-
|
334
|
+
>>> job=model.run(job,config,'')
|
335
|
+
job
|
348
336
|
|
349
337
|
"""
|
350
338
|
if job is None:
|
351
|
-
currentJob = self.context[
|
339
|
+
currentJob = self.context["currentJob"]
|
352
340
|
job = self.jobs[currentJob]
|
353
341
|
if config is None:
|
354
|
-
currentConfig = self.context[
|
342
|
+
currentConfig = self.context["currentConfig"]
|
355
343
|
config = self.configs[currentConfig]
|
356
|
-
|
344
|
+
revision = await self.revision.run(
|
345
|
+
job, config, name, self.rid, policy, **kwargs
|
346
|
+
)
|
347
|
+
return await Job.create(
|
348
|
+
revision["hash"], job, config, name, self.rid, policy, **kwargs
|
349
|
+
)
|
357
350
|
|
358
351
|
def iesSimulationRun(self, job=None, config=None, name=None, **kwargs):
|
359
352
|
return self.run(job=job, config=config, name=name, kwargs=kwargs)
|
360
353
|
|
361
354
|
@staticmethod
|
362
|
-
def load(filePath):
|
355
|
+
def load(filePath, format="yaml"):
|
363
356
|
"""
|
364
|
-
|
357
|
+
加载本地项目文件
|
365
358
|
|
366
|
-
|
359
|
+
:params file: 文件目录
|
367
360
|
|
368
|
-
|
361
|
+
:format 默认格式为yaml
|
369
362
|
|
370
|
-
|
363
|
+
:return: 返回一个项目实例
|
364
|
+
|
365
|
+
>>> model = Model.load('filePath')
|
371
366
|
|
372
367
|
"""
|
373
368
|
|
374
369
|
if not os.path.exists(filePath):
|
375
|
-
raise FileNotFoundError(
|
376
|
-
data =
|
370
|
+
raise FileNotFoundError("未找到文件")
|
371
|
+
data = IO.load(filePath, format)
|
377
372
|
return Model(data)
|
378
373
|
|
379
374
|
@staticmethod
|
380
|
-
def dump(model, file):
|
375
|
+
def dump(model, file, format="yaml", compress="gzip"):
|
381
376
|
"""
|
382
|
-
|
377
|
+
下载项目文件
|
383
378
|
|
384
|
-
|
385
|
-
|
379
|
+
:params model: 项目
|
380
|
+
:params file: 文件路径
|
381
|
+
:params format: 文件格式
|
382
|
+
:params compress: 是否压缩 压缩格式目前支持gzip 为None时不压缩
|
386
383
|
|
387
|
-
|
384
|
+
:return: 无
|
388
385
|
|
389
|
-
|
386
|
+
>>> Model.dump(model,file)
|
390
387
|
"""
|
391
388
|
|
392
|
-
|
393
|
-
|
394
|
-
yamlData = yaml.dump(data)
|
395
|
-
with gzip.open(file, 'w') as output:
|
396
|
-
with io.TextIOWrapper(output, encoding='utf-8') as enc:
|
397
|
-
enc.write(yamlData)
|
389
|
+
IO.dump(model, file, format, compress)
|
398
390
|
|
399
391
|
def save(self, key=None):
|
392
|
+
return asyncio.run(self.saveAsync(key))
|
393
|
+
|
394
|
+
async def saveAsync(self, key=None):
|
400
395
|
"""
|
401
|
-
|
396
|
+
保存/创建项目
|
402
397
|
|
403
|
-
|
404
|
-
|
405
|
-
|
398
|
+
key 不为空时如果远程存在相同的资源名称时将覆盖远程项目。
|
399
|
+
key 为空时如果项目 rid 不存在则抛异常,需要重新设置 key。
|
400
|
+
如果保存时,当前用户不是该项目的拥有者时,将重新创建项目,重建项目时如果参数的 key 为空将使用当前当前项目的 key 作为资源的 key ,当资源的 key 和远程冲突时保存失败
|
406
401
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
402
|
+
:params: model 项目
|
403
|
+
:params: key 资源 id 的唯一标识符,
|
404
|
+
|
405
|
+
:return: 保存成功/保存失败
|
406
|
+
|
407
|
+
>>> model.save(model)
|
408
|
+
model.save(model,'newKey') # 另存为新的项目
|
414
409
|
|
415
410
|
"""
|
416
411
|
username = userName()
|
417
412
|
|
418
413
|
if key is not None:
|
419
|
-
matchObj = re.match(r
|
414
|
+
matchObj = re.match(r"^[-_A-Za-z0-9]+$", key, re.I | re.S)
|
420
415
|
if matchObj:
|
421
|
-
self.rid =
|
416
|
+
self.rid = "model/" + username + "/" + key
|
422
417
|
try:
|
423
|
-
|
424
|
-
return Model.update(self)
|
418
|
+
return await Model.updateAsync(self)
|
425
419
|
except:
|
426
|
-
return Model.
|
420
|
+
return await Model.createAsync(self)
|
427
421
|
else:
|
428
|
-
raise Exception(
|
422
|
+
raise Exception("key 能包含字母数子和下划线")
|
429
423
|
else:
|
430
|
-
t =
|
424
|
+
t = "(?<=/)\\S+(?=/)"
|
431
425
|
owner = re.search(t, self.rid)
|
432
426
|
if owner is None:
|
433
|
-
raise Exception(
|
427
|
+
raise Exception("rid 错误,请传入 key")
|
434
428
|
elif owner[0] != username:
|
435
429
|
rid = re.sub(t, username, self.rid)
|
436
430
|
try:
|
437
|
-
|
438
|
-
return Model.create(self)
|
431
|
+
return await Model.createAsync(self)
|
439
432
|
except:
|
440
|
-
raise Exception(rid +
|
433
|
+
raise Exception(rid + " 该资源已存在,无法重复创建,请修改 key")
|
441
434
|
|
442
|
-
return Model.
|
435
|
+
return await Model.updateAsync(self)
|
443
436
|
|
444
437
|
@staticmethod
|
445
438
|
def create(model):
|
439
|
+
return asyncio.run(Model.createAsync(model))
|
440
|
+
|
441
|
+
@staticmethod
|
442
|
+
async def createAsync(model):
|
446
443
|
"""
|
447
|
-
|
444
|
+
新建项目
|
448
445
|
|
449
|
-
|
446
|
+
:params: model 项目
|
450
447
|
|
451
|
-
|
448
|
+
:return: 保存成功/保存失败
|
452
449
|
|
453
|
-
|
454
|
-
|
450
|
+
>>> Model.create(model)
|
451
|
+
保存成功
|
455
452
|
"""
|
456
453
|
# Model.update(model)
|
457
|
-
t =
|
454
|
+
t = "(?<=/)\\S+(?=/)"
|
458
455
|
username = userName()
|
459
456
|
owner = re.search(t, model.rid)
|
460
457
|
|
461
458
|
if owner is None:
|
462
|
-
raise Exception(
|
459
|
+
raise Exception("rid 错误,无法保存")
|
463
460
|
elif owner[0] != username:
|
464
|
-
raise Exception(
|
461
|
+
raise Exception("rid 错误,无法保存")
|
465
462
|
|
466
463
|
modelQuery = """
|
467
464
|
mutation($a:CreateModelInput!){createModel(input:$a){
|
468
465
|
rid
|
469
466
|
}}
|
470
467
|
"""
|
471
|
-
isPublic = model.context.get(
|
472
|
-
isComponent = model.context.get(
|
473
|
-
publicRead = model.context.get(
|
468
|
+
isPublic = model.context.get("auth", "") != "private"
|
469
|
+
isComponent = model.context.get("category", "") == "component"
|
470
|
+
publicRead = model.context.get("publicRead", "") != False
|
474
471
|
auth = (65539 if publicRead else 65537) if isPublic else 0
|
475
|
-
revision = ModelRevision.create(model.revision, model.revision.hash)
|
476
|
-
|
477
|
-
return
|
478
|
-
modelQuery,
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
472
|
+
revision = await ModelRevision.create(model.revision, model.revision.hash)
|
473
|
+
|
474
|
+
return await graphql_fetch(
|
475
|
+
modelQuery,
|
476
|
+
{
|
477
|
+
"a": {
|
478
|
+
"rid": model.rid,
|
479
|
+
"revision": revision["hash"],
|
480
|
+
"context": model.context,
|
481
|
+
"configs": model.configs,
|
482
|
+
"jobs": model.jobs,
|
483
|
+
"name": model.name,
|
484
|
+
"description": model.description,
|
485
|
+
"tags": model.tags,
|
488
486
|
"permissions": {
|
489
487
|
"moderator": 1,
|
490
488
|
"member": 1,
|
491
489
|
"everyone": auth,
|
492
490
|
},
|
493
491
|
}
|
494
|
-
}
|
492
|
+
},
|
493
|
+
)
|
495
494
|
|
496
495
|
@staticmethod
|
497
496
|
def update(model):
|
497
|
+
return asyncio.run(Model.updateAsync(model))
|
498
|
+
|
499
|
+
@staticmethod
|
500
|
+
async def updateAsync(model):
|
498
501
|
"""
|
499
|
-
|
502
|
+
更新项目
|
500
503
|
|
501
|
-
|
504
|
+
:params: model 项目
|
502
505
|
|
503
|
-
|
506
|
+
:return: 保存成功/保存失败
|
504
507
|
|
505
|
-
|
508
|
+
>>> Model.update(model)
|
506
509
|
"""
|
507
510
|
|
508
|
-
t =
|
511
|
+
t = "(?<=/)\\S+(?=/)"
|
509
512
|
username = userName()
|
510
513
|
owner = re.search(t, model.rid)
|
511
514
|
|
512
515
|
if owner is None:
|
513
|
-
raise Exception(
|
516
|
+
raise Exception("rid 错误,无法保存")
|
514
517
|
elif owner[0] != username:
|
515
|
-
raise Exception(
|
518
|
+
raise Exception("rid 错误,无法保存")
|
516
519
|
|
517
520
|
modelQuery = """
|
518
521
|
mutation($a:UpdateModelInput!){updateModel(input:$a){
|
519
522
|
rid
|
520
523
|
}}
|
521
524
|
"""
|
522
|
-
isPublic = model.context.get(
|
523
|
-
isComponent = model.context.get(
|
524
|
-
publicRead = model.context.get(
|
525
|
+
isPublic = model.context.get("auth", "") != "private"
|
526
|
+
isComponent = model.context.get("category", "") == "component"
|
527
|
+
publicRead = model.context.get("publicRead", "") != False
|
525
528
|
auth = (65539 if publicRead else 65537) if isPublic else 0
|
526
|
-
revision = ModelRevision.create(model.revision, model.revision.hash)
|
529
|
+
revision = await ModelRevision.create(model.revision, model.revision.hash)
|
527
530
|
|
528
|
-
xVersion = int(float(os.environ.get(
|
529
|
-
tags= {
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
'tags': tags,
|
531
|
+
xVersion = int(float(os.environ.get("X_CLOUDPSS_VERSION", 4)))
|
532
|
+
tags = {"replace": model.tags}
|
533
|
+
if xVersion == 3:
|
534
|
+
tags = model.tags
|
535
|
+
|
536
|
+
return await graphql_fetch(
|
537
|
+
modelQuery,
|
538
|
+
{
|
539
|
+
"a": {
|
540
|
+
"rid": model.rid,
|
541
|
+
"revision": revision["hash"],
|
542
|
+
"context": model.context,
|
543
|
+
"configs": model.configs,
|
544
|
+
"jobs": model.jobs,
|
545
|
+
"name": model.name,
|
546
|
+
"description": model.description,
|
547
|
+
"tags": tags,
|
546
548
|
"permissions": {
|
547
549
|
"moderator": 1,
|
548
550
|
"member": 1,
|
549
551
|
"everyone": auth,
|
550
552
|
},
|
551
553
|
}
|
552
|
-
}
|
554
|
+
},
|
555
|
+
)
|
553
556
|
|
554
557
|
def fetchTopology(
|
555
558
|
self,
|
@@ -558,201 +561,199 @@ class Model(object):
|
|
558
561
|
maximumDepth=None,
|
559
562
|
):
|
560
563
|
"""
|
561
|
-
|
564
|
+
通过项目信息,获取当前项目对应的拓扑数据
|
562
565
|
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
+
:params implementType: 实现类型
|
567
|
+
:params config: config 项目参数, 不指定将使用算例保存时选中的参数方案
|
568
|
+
:params maximumDepth: 最大递归深度,用于自定义项目中使用 diagram 实现元件展开情况
|
566
569
|
|
567
|
-
|
570
|
+
:return: 一个拓扑实例
|
568
571
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
+
>>> topology=model.fetchTopology()
|
573
|
+
topology=model.fetchTopology(implementType='powerFlow',config=config) # 获取潮流实现的拓扑数据
|
574
|
+
topology=model.fetchTopology(maximumDepth=2) # 获取仅展开 2 层的拓扑数据
|
572
575
|
"""
|
573
576
|
|
574
577
|
if self.revision is not None:
|
575
578
|
if implementType is None:
|
576
|
-
implementType =
|
579
|
+
implementType = "emtp"
|
577
580
|
if config is None:
|
578
|
-
currentConfig = self.context[
|
581
|
+
currentConfig = self.context["currentConfig"]
|
579
582
|
config = self.configs[currentConfig]
|
580
|
-
return self.revision.fetchTopology(implementType, config,
|
581
|
-
maximumDepth)
|
583
|
+
return self.revision.fetchTopology(implementType, config, maximumDepth)
|
582
584
|
return None
|
583
585
|
|
584
|
-
def runEMT(self,job=None,config=None)->
|
585
|
-
|
586
|
-
|
586
|
+
def runEMT(self, job=None, config=None) -> Job:
|
587
|
+
"""
|
588
|
+
运行 emtp 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
|
589
|
+
|
590
|
+
:param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
|
591
|
+
:param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
|
587
592
|
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
:return: runner Runner[EMTResult]
|
592
|
-
'''
|
593
|
+
:return: job 运行结果
|
594
|
+
"""
|
593
595
|
if job is None:
|
594
|
-
currentJob = self.context[
|
596
|
+
currentJob = self.context["currentJob"]
|
595
597
|
job = self.jobs[currentJob]
|
596
|
-
if job[
|
598
|
+
if job["rid"] != "job-definition/cloudpss/emtp":
|
597
599
|
for j in self.jobs:
|
598
|
-
if j[
|
600
|
+
if j["rid"] == "job-definition/cloudpss/emtp":
|
599
601
|
job = j
|
600
602
|
if job is None:
|
601
603
|
raise Exception("找不到电磁暂态运行的计算方案")
|
602
|
-
if job[
|
604
|
+
if job["rid"] != "job-definition/cloudpss/emtp":
|
603
605
|
raise Exception("不是电磁暂态运行生成算法的计算方案")
|
604
606
|
if config is None:
|
605
|
-
currentConfig = self.context[
|
607
|
+
currentConfig = self.context["currentConfig"]
|
606
608
|
config = self.configs[currentConfig]
|
607
609
|
return self.run(job=job, config=config)
|
608
610
|
|
609
|
-
def runSFEMT(self,job=None,config=None)->
|
610
|
-
|
611
|
-
|
611
|
+
def runSFEMT(self, job=None, config=None) -> Job:
|
612
|
+
"""
|
613
|
+
运行 移频电磁暂态 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
|
612
614
|
|
613
|
-
|
614
|
-
|
615
|
+
:param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
|
616
|
+
:param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
|
615
617
|
|
616
|
-
|
617
|
-
|
618
|
+
:return: runner Runner[EMTResult]
|
619
|
+
"""
|
618
620
|
if job is None:
|
619
|
-
currentJob = self.context[
|
621
|
+
currentJob = self.context["currentJob"]
|
620
622
|
job = self.jobs[currentJob]
|
621
|
-
if job[
|
623
|
+
if job["rid"] != "job-definition/cloudpss/sfemt":
|
622
624
|
for j in self.jobs:
|
623
|
-
if j[
|
625
|
+
if j["rid"] == "job-definition/cloudpss/sfemt":
|
624
626
|
job = j
|
625
627
|
if job is None:
|
626
628
|
raise Exception("找不到移频电磁暂态运行的计算方案")
|
627
|
-
if job[
|
629
|
+
if job["rid"] != "job-definition/cloudpss/sfemt":
|
628
630
|
raise Exception("不是移频电磁暂态运行生成算法的计算方案")
|
629
631
|
if config is None:
|
630
|
-
currentConfig = self.context[
|
632
|
+
currentConfig = self.context["currentConfig"]
|
631
633
|
config = self.configs[currentConfig]
|
632
634
|
return self.run(job=job, config=config)
|
633
635
|
|
634
|
-
def runPowerFlow(self,job=None,config=None)->
|
635
|
-
|
636
|
-
|
636
|
+
def runPowerFlow(self, job=None, config=None) -> Job:
|
637
|
+
"""
|
638
|
+
运行 潮流 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
|
637
639
|
|
638
|
-
|
639
|
-
|
640
|
+
:param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
|
641
|
+
:param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
|
640
642
|
|
641
|
-
|
642
|
-
|
643
|
+
:return: runner Runner[PowerFlowResult]
|
644
|
+
"""
|
643
645
|
if job is None:
|
644
|
-
currentJob = self.context[
|
646
|
+
currentJob = self.context["currentJob"]
|
645
647
|
job = self.jobs[currentJob]
|
646
|
-
if job[
|
648
|
+
if job["rid"] != "job-definition/cloudpss/power-flow":
|
647
649
|
for j in self.jobs:
|
648
|
-
if j[
|
650
|
+
if j["rid"] == "job-definition/cloudpss/power-flow":
|
649
651
|
job = j
|
650
652
|
if job is None:
|
651
653
|
raise Exception("找不到潮流内核运行的计算方案")
|
652
|
-
if job[
|
654
|
+
if job["rid"] != "job-definition/cloudpss/power-flow":
|
653
655
|
raise Exception("不是潮流内核运行生成算法的计算方案")
|
654
656
|
if config is None:
|
655
|
-
currentConfig = self.context[
|
657
|
+
currentConfig = self.context["currentConfig"]
|
656
658
|
config = self.configs[currentConfig]
|
657
659
|
return self.run(job=job, config=config)
|
658
660
|
|
659
|
-
def runThreePhasePowerFlow(self,job=None,config=None)->
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
661
|
+
def runThreePhasePowerFlow(self, job=None, config=None) -> Job:
|
662
|
+
"""
|
663
|
+
运行 三相不平衡潮流 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
|
664
|
+
|
665
|
+
:param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
|
666
|
+
:param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
|
665
667
|
|
666
|
-
|
667
|
-
|
668
|
+
:return: runner Runner[PowerFlowResult]
|
669
|
+
"""
|
668
670
|
if job is None:
|
669
|
-
currentJob = self.context[
|
671
|
+
currentJob = self.context["currentJob"]
|
670
672
|
job = self.jobs[currentJob]
|
671
|
-
if job[
|
673
|
+
if job["rid"] != "job-definition/cloudpss/three-phase-powerFlow":
|
672
674
|
for j in self.jobs:
|
673
|
-
if j[
|
675
|
+
if j["rid"] == "job-definition/cloudpss/three-phase-powerFlow":
|
674
676
|
job = j
|
675
677
|
if job is None:
|
676
678
|
raise Exception("找不到三相不平衡潮流内核运行的计算方案")
|
677
|
-
if job[
|
679
|
+
if job["rid"] != "job-definition/cloudpss/three-phase-powerFlow":
|
678
680
|
raise Exception("不是三相不平衡潮流内核运行生成算法的计算方案")
|
679
681
|
if config is None:
|
680
|
-
currentConfig = self.context[
|
682
|
+
currentConfig = self.context["currentConfig"]
|
681
683
|
config = self.configs[currentConfig]
|
682
684
|
return self.run(job=job, config=config)
|
683
|
-
|
684
|
-
def runIESLoadPrediction(self,job=None,config=None)->Runner[IESResult]:
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
685
|
+
|
686
|
+
def runIESLoadPrediction(self, job=None, config=None) -> Runner[IESResult]:
|
687
|
+
"""
|
688
|
+
运行 负荷预测方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
|
689
|
+
|
690
|
+
:param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
|
691
|
+
:param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
|
692
|
+
|
693
|
+
:return: runner Runner[IESResult]
|
694
|
+
"""
|
693
695
|
if job is None:
|
694
|
-
currentJob = self.context[
|
696
|
+
currentJob = self.context["currentJob"]
|
695
697
|
job = self.jobs[currentJob]
|
696
|
-
if job[
|
698
|
+
if job["rid"] != "job-definition/ies/ies-load-prediction":
|
697
699
|
for j in self.jobs:
|
698
|
-
if j[
|
700
|
+
if j["rid"] == "job-definition/ies/ies-load-prediction":
|
699
701
|
job = j
|
700
702
|
if job is None:
|
701
703
|
raise Exception("找不到负荷预测方案内核运行的计算方案")
|
702
|
-
if job[
|
704
|
+
if job["rid"] != "job-definition/ies/ies-load-prediction":
|
703
705
|
raise Exception("不是负荷预测方案内核运行生成算法的计算方案")
|
704
706
|
if config is None:
|
705
|
-
currentConfig = self.context[
|
707
|
+
currentConfig = self.context["currentConfig"]
|
706
708
|
config = self.configs[currentConfig]
|
707
709
|
return self.run(job=job, config=config)
|
708
|
-
|
709
|
-
def runIESPowerFlow(self,job=None,config=None)->Runner[IESResult]:
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
710
|
+
|
711
|
+
def runIESPowerFlow(self, job=None, config=None) -> Runner[IESResult]:
|
712
|
+
"""
|
713
|
+
运行 时序潮流方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
|
714
|
+
|
715
|
+
:param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
|
716
|
+
:param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
|
717
|
+
|
718
|
+
:return: runner Runner[IESResult]
|
719
|
+
"""
|
718
720
|
if job is None:
|
719
|
-
currentJob = self.context[
|
721
|
+
currentJob = self.context["currentJob"]
|
720
722
|
job = self.jobs[currentJob]
|
721
|
-
if job[
|
723
|
+
if job["rid"] != "job-definition/ies/ies-power-flow":
|
722
724
|
for j in self.jobs:
|
723
|
-
if j[
|
725
|
+
if j["rid"] == "job-definition/ies/ies-power-flow":
|
724
726
|
job = j
|
725
727
|
if job is None:
|
726
728
|
raise Exception("找不到时序潮流方案内核运行的计算方案")
|
727
|
-
if job[
|
729
|
+
if job["rid"] != "job-definition/ies/ies-power-flow":
|
728
730
|
raise Exception("不是时序潮流方案内核运行生成算法的计算方案")
|
729
731
|
if config is None:
|
730
|
-
currentConfig = self.context[
|
732
|
+
currentConfig = self.context["currentConfig"]
|
731
733
|
config = self.configs[currentConfig]
|
732
734
|
return self.run(job=job, config=config)
|
733
735
|
|
734
|
-
def runIESEnergyStoragePlan(self,job=None,config=None)->Runner[IESResult]:
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
736
|
+
def runIESEnergyStoragePlan(self, job=None, config=None) -> Runner[IESResult]:
|
737
|
+
"""
|
738
|
+
运行 储能规划方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
|
739
|
+
|
740
|
+
:param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
|
741
|
+
:param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
|
740
742
|
|
741
|
-
|
742
|
-
|
743
|
+
:return: runner Runner[IESResult]
|
744
|
+
"""
|
743
745
|
if job is None:
|
744
|
-
currentJob = self.context[
|
746
|
+
currentJob = self.context["currentJob"]
|
745
747
|
job = self.jobs[currentJob]
|
746
|
-
if job[
|
748
|
+
if job["rid"] != "job-definition/ies/ies-energy-storage-plan":
|
747
749
|
for j in self.jobs:
|
748
|
-
if j[
|
750
|
+
if j["rid"] == "job-definition/ies/ies-energy-storage-plan":
|
749
751
|
job = j
|
750
752
|
if job is None:
|
751
753
|
raise Exception("找不到储能规划方案内核运行的计算方案")
|
752
|
-
if job[
|
754
|
+
if job["rid"] != "job-definition/ies/ies-energy-storage-plan":
|
753
755
|
raise Exception("不是储能规划方案内核运行生成算法的计算方案")
|
754
756
|
if config is None:
|
755
|
-
currentConfig = self.context[
|
757
|
+
currentConfig = self.context["currentConfig"]
|
756
758
|
config = self.configs[currentConfig]
|
757
759
|
return self.run(job=job, config=config)
|
758
|
-
|