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