cloudpss 4.1.1b8__py3-none-any.whl → 4.5.13__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/asyncio/__init__.py +8 -0
- cloudpss/asyncio/job/__init__.py +5 -0
- cloudpss/asyncio/job/job.py +116 -0
- cloudpss/asyncio/job/messageStreamReceiver.py +121 -0
- cloudpss/asyncio/job/messageStreamSender.py +45 -0
- cloudpss/asyncio/model/__init__.py +5 -0
- cloudpss/asyncio/model/model.py +257 -0
- cloudpss/asyncio/model/revision.py +41 -0
- cloudpss/asyncio/model/topology.py +34 -0
- cloudpss/asyncio/utils/__init__.py +6 -0
- cloudpss/{utils → asyncio/utils}/httpAsyncRequest.py +2 -2
- cloudpss/dslab/dataManageModel.py +19 -0
- cloudpss/dslab/dslab.py +35 -9
- cloudpss/function/functionExecution.py +74 -5
- cloudpss/ieslab/DataManageModel.py +144 -9
- cloudpss/ieslab/EvaluationModel.py +80 -9
- cloudpss/ieslab/IESLabOpt.py +235 -0
- cloudpss/ieslab/IESLabPlan.py +82 -4
- cloudpss/ieslab/IESLabSimulation.py +59 -32
- cloudpss/ieslab/PlanModel.py +222 -18
- cloudpss/ieslab/__init__.py +2 -1
- cloudpss/job/TemplateCompiler.py +273 -0
- cloudpss/job/TemplateManager.py +37 -0
- cloudpss/job/job.py +136 -141
- cloudpss/job/jobReceiver.py +8 -2
- cloudpss/job/messageStreamReceiver.py +42 -99
- cloudpss/job/messageStreamSender.py +5 -42
- cloudpss/job/{view/EMTView.py → result/EMTResult.py} +57 -14
- cloudpss/job/result/IESLabSimulationResult.py +5 -0
- cloudpss/job/result/IESLabTypicalDayResult.py +134 -0
- cloudpss/job/{view/IESView.py → result/IESResult.py} +7 -5
- cloudpss/job/{view/PowerFlowView.py → result/PowerFlowResult.py} +2 -2
- cloudpss/job/result/__init__.py +40 -0
- cloudpss/job/{view/view.py → result/result.py} +38 -8
- cloudpss/model/implements/diagram.py +140 -0
- cloudpss/model/jobDefinitions.py +6 -6
- cloudpss/model/model.py +245 -226
- cloudpss/model/revision.py +30 -35
- cloudpss/model/topology.py +26 -15
- cloudpss/runner/IESLabEvaluationResult.py +14 -6
- cloudpss/runner/IESLabPlanResult.py +91 -35
- cloudpss/runner/IESLabTypicalDayResult.py +62 -50
- cloudpss/runner/MessageStreamReceiver.py +5 -100
- cloudpss/runner/result.py +6 -1
- cloudpss/runner/runner.py +97 -53
- cloudpss/utils/IO.py +3 -1
- cloudpss/utils/graphqlUtil.py +3 -2
- cloudpss/utils/httprequests.py +19 -10
- cloudpss/version.py +1 -1
- {cloudpss-4.1.1b8.dist-info → cloudpss-4.5.13.dist-info}/METADATA +2 -2
- cloudpss-4.5.13.dist-info/RECORD +80 -0
- cloudpss/job/jobMachine.py +0 -11
- cloudpss/job/jobPolicy.py +0 -129
- cloudpss/job/jobQueue.py +0 -14
- cloudpss/job/jobTres.py +0 -6
- cloudpss/job/view/IESLabSimulationView.py +0 -5
- cloudpss/job/view/IESLabTypicalDayView.py +0 -27
- cloudpss/job/view/__init__.py +0 -42
- cloudpss-4.1.1b8.dist-info/RECORD +0 -71
- /cloudpss/{utils → asyncio/utils}/AsyncIterable.py +0 -0
- {cloudpss-4.1.1b8.dist-info → cloudpss-4.5.13.dist-info}/WHEEL +0 -0
- {cloudpss-4.1.1b8.dist-info → cloudpss-4.5.13.dist-info}/top_level.txt +0 -0
cloudpss/ieslab/PlanModel.py
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
-
from cloudpss.runner.IESLabPlanResult import IESLabPlanResult
|
2
|
-
from cloudpss.runner.runner import HttpRunner,
|
1
|
+
from cloudpss.runner.IESLabPlanResult import IESLabPlanResult, IESLabOptResult
|
2
|
+
from cloudpss.runner.runner import HttpRunner, HttpOPTRunner
|
3
|
+
from cloudpss.model import Model
|
4
|
+
from cloudpss.model.revision import ModelRevision
|
3
5
|
from ..utils import request, fileLoad
|
4
6
|
import json
|
5
7
|
from enum import IntEnum, unique
|
6
8
|
|
7
9
|
|
8
10
|
class IESLabPlanModel(object):
|
9
|
-
_baseUri = 'api/ieslab-plan/
|
11
|
+
_baseUri = 'api/ieslab-plan/rest'
|
12
|
+
_runUri = 'api/ieslab-plan/taskmanager'
|
10
13
|
|
11
14
|
def __init__(self, simulationId):
|
12
15
|
'''
|
@@ -14,18 +17,18 @@ class IESLabPlanModel(object):
|
|
14
17
|
'''
|
15
18
|
self.simulationId = simulationId
|
16
19
|
self.optimizationInfo = self.GetOptimizationInfo()
|
17
|
-
self.OptimizationMode = OptimizationMode
|
18
20
|
|
19
|
-
def _fetchItemData(self, url):
|
21
|
+
def _fetchItemData(self, url, params):
|
20
22
|
'''
|
21
23
|
获取当前算例的优化目标设置信息
|
22
24
|
|
23
|
-
:return:
|
25
|
+
:return: List 类型,包括优化目标和全局参数储能动作灵敏度,若没有设置则返回 []
|
24
26
|
'''
|
25
|
-
r = request('GET', url, params=
|
27
|
+
r = request('GET', url, params=params)
|
26
28
|
data = json.loads(r.text)
|
27
|
-
return data
|
29
|
+
return data['results']
|
28
30
|
|
31
|
+
|
29
32
|
def GetOptimizationInfo(self):
|
30
33
|
'''
|
31
34
|
获取当前算例的优化目标设置信息
|
@@ -50,8 +53,8 @@ class IESLabPlanModel(object):
|
|
50
53
|
'''
|
51
54
|
self.optimizationInfo = optType
|
52
55
|
return True
|
53
|
-
|
54
|
-
def run(self) ->
|
56
|
+
|
57
|
+
def run(self) -> HttpRunner[IESLabPlanResult]:
|
55
58
|
'''
|
56
59
|
生成方案优选算例
|
57
60
|
|
@@ -61,10 +64,9 @@ class IESLabPlanModel(object):
|
|
61
64
|
if isRunning:
|
62
65
|
raise Exception('该算例正在运行!请从浏览器算例页面点击结束运行或者调用IESPlan对象的kill接口终止计算后重试!')
|
63
66
|
else:
|
64
|
-
url = '
|
65
|
-
if self.optimizationInfo is None
|
66
|
-
|
67
|
-
optType = self.optimizationInfo.value or 0
|
67
|
+
url = f'{self._runUri}/runOptimization'
|
68
|
+
optType = self.optimizationInfo if self.optimizationInfo is not None else OptimizationMode.经济性
|
69
|
+
optTypeValue = optType.value if isinstance(optType, OptimizationMode) else 0
|
68
70
|
try:
|
69
71
|
r = request('GET',
|
70
72
|
url,
|
@@ -73,7 +75,7 @@ class IESLabPlanModel(object):
|
|
73
75
|
self.simulationId,
|
74
76
|
"optPara":
|
75
77
|
json.dumps({
|
76
|
-
"OptimizationMode":
|
78
|
+
"OptimizationMode": optTypeValue,
|
77
79
|
"ProjectPeriod": "20"
|
78
80
|
})
|
79
81
|
})
|
@@ -82,7 +84,7 @@ class IESLabPlanModel(object):
|
|
82
84
|
except:
|
83
85
|
raise Exception('生成方案优选算例失败')
|
84
86
|
|
85
|
-
def GetRunner(self) ->
|
87
|
+
def GetRunner(self) -> HttpRunner[IESLabPlanResult]:
|
86
88
|
'''
|
87
89
|
获得运行实例
|
88
90
|
|
@@ -100,7 +102,7 @@ class IESLabPlanModel(object):
|
|
100
102
|
data = res.get('data', {})
|
101
103
|
if data is not None:
|
102
104
|
taskID = data.get('task_id', '')
|
103
|
-
url = f'
|
105
|
+
url = f'{self._runUri}/removeOptimizationTask'
|
104
106
|
try:
|
105
107
|
r = request('GET',
|
106
108
|
url,
|
@@ -128,7 +130,208 @@ class IESLabPlanModel(object):
|
|
128
130
|
status = data.get('status', '')
|
129
131
|
if status == 'stop':
|
130
132
|
isRunning = False
|
131
|
-
|
133
|
+
try:
|
134
|
+
|
135
|
+
logs = IESLabPlanResult(self.simulationId).GetLogs()
|
136
|
+
if logs is not None:
|
137
|
+
for log in logs:
|
138
|
+
if(log.get('data', '') == 'run ends'):
|
139
|
+
isRunning = False
|
140
|
+
break
|
141
|
+
except:
|
142
|
+
return False
|
143
|
+
return isRunning
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
class IESLabOptModel(object):
|
148
|
+
_baseUri = 'api/ieslab-opt/rest'
|
149
|
+
_runUri = 'api/ieslab-opt/taskmanager'
|
150
|
+
|
151
|
+
|
152
|
+
def __init__(self, simulationId, rid):
|
153
|
+
'''
|
154
|
+
初始化
|
155
|
+
'''
|
156
|
+
self.simulationId = simulationId
|
157
|
+
self.rid = rid
|
158
|
+
self.optimizationInfo = self.GetOptimizationInfo()
|
159
|
+
|
160
|
+
def _fetchItemData(self, url, params):
|
161
|
+
'''
|
162
|
+
获取当前算例的优化目标设置信息
|
163
|
+
|
164
|
+
:return: List 类型,包括优化目标和全局参数储能动作灵敏度,若没有设置则返回 []
|
165
|
+
'''
|
166
|
+
r = request('GET', url, params=params)
|
167
|
+
data = json.loads(r.text)
|
168
|
+
return data['results']
|
169
|
+
|
170
|
+
def GetOptimizationInfo(self):
|
171
|
+
'''
|
172
|
+
获取当前算例的优化目标设置信息
|
173
|
+
|
174
|
+
:return: Dict 类型,例如:{'OptGoal': <OptimizationMode.经济性: 0>, 'StoSen': "10", '@debug': '', 'clustering_algorithm': '0', 'num_method': '0', 'PowUnPrice': '1000', 'HeatAbandonPrice': '1000'}
|
175
|
+
'''
|
176
|
+
try:
|
177
|
+
url = f'{self._baseUri}/simuOpt/'
|
178
|
+
params = {"simu_id": self.simulationId}
|
179
|
+
r = self._fetchItemData(url, params)
|
180
|
+
if (len(r) == 0):
|
181
|
+
return {
|
182
|
+
"OptGoal": OptimizationMode['经济性'],
|
183
|
+
"StoSen": "10",
|
184
|
+
"@debug": "",
|
185
|
+
"clustering_algorithm": "0",
|
186
|
+
"num_method": "0",
|
187
|
+
"PowUnPrice": "1000",
|
188
|
+
"HeatAbandonPrice": "1000"
|
189
|
+
}
|
190
|
+
else:
|
191
|
+
value = json.loads(r[0]['opt_params'])
|
192
|
+
return {
|
193
|
+
"OptGoal": OptimizationMode(int(value['OptGoal'])),
|
194
|
+
"StoSen": value['StoSen'],
|
195
|
+
"@debug": value["@debug"],
|
196
|
+
"clustering_algorithm": value["clustering_algorithm"],
|
197
|
+
"num_method": value["num_method"],
|
198
|
+
"PowUnPrice": value["PowUnPrice"],
|
199
|
+
"HeatAbandonPrice": value["HeatAbandonPrice"]
|
200
|
+
}
|
201
|
+
except:
|
202
|
+
raise Exception('获得优化目标设置失败')
|
203
|
+
|
204
|
+
def SetOptimizationInfo(self, data: dict):
|
205
|
+
'''
|
206
|
+
设置当前算例的优化目标
|
207
|
+
|
208
|
+
:param data: dict 类型,例如:{'OptGoal': <OptimizationMode.经济性: 0>, 'StoSen': "10", '@debug': '', 'clustering_algorithm': '0', 'num_method': '0', 'PowUnPrice': '1000', 'HeatAbandonPrice': '1000'}
|
209
|
+
|
210
|
+
:return: boolean 类型,为 True 则设置成功
|
211
|
+
'''
|
212
|
+
try:
|
213
|
+
url = f'{self._baseUri}/simuOpt/'
|
214
|
+
params = {"simu_id": self.simulationId}
|
215
|
+
r = self._fetchItemData(url, params)
|
216
|
+
opt_params = {
|
217
|
+
"OptGoal": data.get('OptGoal', ''),
|
218
|
+
"StoSen": data.get('StoSen', ''),
|
219
|
+
"@debug": data.get('@debug', ''),
|
220
|
+
"clustering_algorithm": data.get('clustering_algorithm', ''),
|
221
|
+
"num_method": data.get('num_method', ''),
|
222
|
+
"PowUnPrice": data.get('PowUnPrice', ''),
|
223
|
+
"HeatAbandonPrice": data.get('HeatAbandonPrice', '')
|
224
|
+
}
|
225
|
+
if(len(r) == 0):
|
226
|
+
payload = {
|
227
|
+
"simu_id": self.simulationId,
|
228
|
+
"opt_params": json.dumps(opt_params)
|
229
|
+
}
|
230
|
+
r = request('POST',
|
231
|
+
url,
|
232
|
+
data=json.dumps(payload))
|
233
|
+
return True
|
234
|
+
else:
|
235
|
+
url2 = f'{self._baseUri}/simuOpt/{r[0]["id"]}/'
|
236
|
+
payload = {
|
237
|
+
"simu_id": self.simulationId,
|
238
|
+
"opt_params": json.dumps(opt_params),
|
239
|
+
"id": r[0]["id"]
|
240
|
+
}
|
241
|
+
r = request('PUT',
|
242
|
+
url2,
|
243
|
+
data=json.dumps(payload))
|
244
|
+
return True
|
245
|
+
except:
|
246
|
+
return False
|
247
|
+
|
248
|
+
def run(self) -> HttpOPTRunner[IESLabOptResult]:
|
249
|
+
'''
|
250
|
+
生成方案优选算例
|
251
|
+
|
252
|
+
:return: Runner[IESLabOptResult]
|
253
|
+
'''
|
254
|
+
isRunning = self.GetLastTaskResult()
|
255
|
+
if isRunning:
|
256
|
+
raise Exception('该算例正在运行!请从浏览器算例页面点击结束运行或者调用IESPlan对象的kill接口终止计算后重试!')
|
257
|
+
else:
|
258
|
+
# 通过 rid 获取 model
|
259
|
+
model = Model.fetch(self.rid)
|
260
|
+
# 通过 model 获取 revision
|
261
|
+
revision = ModelRevision.create(model.revision, model.revision.hash)
|
262
|
+
hash = revision.get('hash', '')
|
263
|
+
url = f'{self._runUri}/runOptimization'
|
264
|
+
opt = {
|
265
|
+
"OptGoal": self.optimizationInfo.get('OptGoal', 0).value,
|
266
|
+
"StoSen": self.optimizationInfo.get('StoSen', 10),
|
267
|
+
"@debug": self.optimizationInfo.get('@debug', ''),
|
268
|
+
"clustering_algorithm": self.optimizationInfo.get('clustering_algorithm', '0'),
|
269
|
+
"num_method": self.optimizationInfo.get('num_method', '0'),
|
270
|
+
"PowUnPrice": self.optimizationInfo.get('PowUnPrice', '1000'),
|
271
|
+
"HeatAbandonPrice": self.optimizationInfo.get('HeatAbandonPrice', '1000'),
|
272
|
+
}
|
273
|
+
try:
|
274
|
+
r = request('GET',
|
275
|
+
url,
|
276
|
+
params={
|
277
|
+
"simuid":
|
278
|
+
self.simulationId,
|
279
|
+
"optPara":
|
280
|
+
json.dumps(opt),
|
281
|
+
"revision": hash
|
282
|
+
})
|
283
|
+
data = json.loads(r.text)
|
284
|
+
return HttpOPTRunner({}, self.simulationId)
|
285
|
+
except:
|
286
|
+
raise Exception('生成方案优选算例失败')
|
287
|
+
|
288
|
+
def GetRunner(self) -> HttpOPTRunner[IESLabOptResult]:
|
289
|
+
'''
|
290
|
+
获得运行实例
|
291
|
+
|
292
|
+
:return: Runner[IESLabOptResult]
|
293
|
+
'''
|
294
|
+
return HttpOPTRunner({}, self.simulationId)
|
295
|
+
|
296
|
+
def kill(self) -> bool:
|
297
|
+
'''
|
298
|
+
停止并删除当前运行的优化算例
|
299
|
+
'''
|
300
|
+
res = IESLabOptResult(self.simulationId).getLastTaskResult()
|
301
|
+
error = res.get('error', 0)
|
302
|
+
if error == 0:
|
303
|
+
data = res.get('data', {})
|
304
|
+
if data is not None:
|
305
|
+
taskID = data.get('task_id', '')
|
306
|
+
url = f'{self._runUri}/removeOptimizationTask'
|
307
|
+
try:
|
308
|
+
r = request('GET',
|
309
|
+
url,
|
310
|
+
params={
|
311
|
+
"taskid": taskID,
|
312
|
+
"stopFlag": '2'
|
313
|
+
})
|
314
|
+
json.loads(r.text)
|
315
|
+
return True
|
316
|
+
except:
|
317
|
+
return False
|
318
|
+
|
319
|
+
def GetLastTaskResult(self)-> bool:
|
320
|
+
'''
|
321
|
+
获取最后一次运行的taskID的运行结果与日志
|
322
|
+
|
323
|
+
:return: boolean 类型
|
324
|
+
'''
|
325
|
+
isRunning = True
|
326
|
+
res = IESLabOptResult(self.simulationId).getLastTaskResult()
|
327
|
+
error = res.get('error', 0)
|
328
|
+
if error == 0:
|
329
|
+
data = res.get('data', {})
|
330
|
+
if data is not None:
|
331
|
+
status = data.get('status', '')
|
332
|
+
if status == 'stop':
|
333
|
+
isRunning = False
|
334
|
+
logs = IESLabOptResult(self.simulationId).GetLogs()
|
132
335
|
if logs is not None:
|
133
336
|
for log in logs:
|
134
337
|
if(log.get('data', '') == 'run ends'):
|
@@ -137,6 +340,7 @@ class IESLabPlanModel(object):
|
|
137
340
|
return isRunning
|
138
341
|
|
139
342
|
|
343
|
+
|
140
344
|
# @unique
|
141
345
|
class OptimizationMode(IntEnum):
|
142
346
|
经济性 = 0
|
cloudpss/ieslab/__init__.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
from .IESLabSimulation import IESLabSimulation
|
2
2
|
from .IESLabPlan import IESLabPlan
|
3
3
|
from .PlanModel import OptimizationMode
|
4
|
-
|
4
|
+
from .IESLabOpt import IESLabOpt
|
5
|
+
__all__ = ['IESLabSimulation','IESLabPlan', 'OptimizationMode', 'IESLabOpt']
|
@@ -0,0 +1,273 @@
|
|
1
|
+
import json
|
2
|
+
from collections import namedtuple
|
3
|
+
from typing import Any, Dict, List, Union
|
4
|
+
|
5
|
+
PARSER_STATUS_TEXT = 0
|
6
|
+
PARSER_STATUS_EXPRESSION_SIMPLE = 1
|
7
|
+
PARSER_STATUS_EXPRESSION_COMPLEX = 2
|
8
|
+
|
9
|
+
def is_identifier_char(char):
|
10
|
+
char_code = ord(char)
|
11
|
+
return ((char_code >= 97 and char_code <= 122) or
|
12
|
+
(char_code >= 65 and char_code <= 90) or
|
13
|
+
(char_code >= 48 and char_code <= 57) or
|
14
|
+
char_code == 95)
|
15
|
+
|
16
|
+
INTERPOLATION_CHAR = '$'
|
17
|
+
INTERPOLATION_EXPRESSION_START = '{'
|
18
|
+
INTERPOLATION_EXPRESSION_END = '}'
|
19
|
+
|
20
|
+
def parse_interpolation_impl(template, start, length):
|
21
|
+
templates = []
|
22
|
+
values = []
|
23
|
+
current_template = ''
|
24
|
+
current_value = ''
|
25
|
+
expression_complex_depth = 0
|
26
|
+
status = PARSER_STATUS_TEXT
|
27
|
+
end = start + length - 1
|
28
|
+
|
29
|
+
i = start
|
30
|
+
while i <= end:
|
31
|
+
if status == PARSER_STATUS_TEXT:
|
32
|
+
next_interpolation_char = template.find(INTERPOLATION_CHAR, i)
|
33
|
+
if next_interpolation_char < 0 or next_interpolation_char >= end:
|
34
|
+
current_template += template[i:end + 1]
|
35
|
+
break
|
36
|
+
current_template += template[i:next_interpolation_char]
|
37
|
+
next_char = template[next_interpolation_char + 1]
|
38
|
+
i = next_interpolation_char
|
39
|
+
if next_char == INTERPOLATION_CHAR:
|
40
|
+
current_template += INTERPOLATION_CHAR
|
41
|
+
i += 1
|
42
|
+
continue
|
43
|
+
if next_char == INTERPOLATION_EXPRESSION_START:
|
44
|
+
templates.append(current_template)
|
45
|
+
current_template = ''
|
46
|
+
status = PARSER_STATUS_EXPRESSION_COMPLEX
|
47
|
+
expression_complex_depth = 1
|
48
|
+
i += 1
|
49
|
+
continue
|
50
|
+
if is_identifier_char(next_char):
|
51
|
+
templates.append(current_template)
|
52
|
+
current_template = ''
|
53
|
+
current_value = next_char
|
54
|
+
status = PARSER_STATUS_EXPRESSION_SIMPLE
|
55
|
+
i += 1
|
56
|
+
continue
|
57
|
+
current_template += INTERPOLATION_CHAR
|
58
|
+
continue
|
59
|
+
|
60
|
+
char = template[i]
|
61
|
+
if status == PARSER_STATUS_EXPRESSION_SIMPLE:
|
62
|
+
if is_identifier_char(char):
|
63
|
+
current_value += char
|
64
|
+
i += 1
|
65
|
+
continue
|
66
|
+
values.append(current_value)
|
67
|
+
current_value = ''
|
68
|
+
status = PARSER_STATUS_TEXT
|
69
|
+
continue
|
70
|
+
|
71
|
+
if status == PARSER_STATUS_EXPRESSION_COMPLEX:
|
72
|
+
if char == INTERPOLATION_EXPRESSION_START:
|
73
|
+
expression_complex_depth += 1
|
74
|
+
elif char == INTERPOLATION_EXPRESSION_END:
|
75
|
+
expression_complex_depth -= 1
|
76
|
+
if expression_complex_depth == 0:
|
77
|
+
values.append(current_value.strip())
|
78
|
+
current_value = ''
|
79
|
+
status = PARSER_STATUS_TEXT
|
80
|
+
i += 1
|
81
|
+
continue
|
82
|
+
current_value += char
|
83
|
+
i += 1
|
84
|
+
continue
|
85
|
+
|
86
|
+
if status == PARSER_STATUS_TEXT:
|
87
|
+
templates.append(current_template)
|
88
|
+
elif status == PARSER_STATUS_EXPRESSION_SIMPLE:
|
89
|
+
values.append(current_value)
|
90
|
+
templates.append('')
|
91
|
+
else:
|
92
|
+
raise ValueError('Unexpected end of input')
|
93
|
+
|
94
|
+
return {
|
95
|
+
'type': 'interpolation',
|
96
|
+
'templates': templates,
|
97
|
+
'values': values,
|
98
|
+
}
|
99
|
+
|
100
|
+
# 是否为 ArrayBuffer
|
101
|
+
def is_array_buffer(value: Any) -> bool:
|
102
|
+
return isinstance(value, (memoryview, bytearray))
|
103
|
+
|
104
|
+
# 是否为 Error
|
105
|
+
def is_error(value: Any) -> bool:
|
106
|
+
return isinstance(value, Exception)
|
107
|
+
|
108
|
+
|
109
|
+
def parse_template(template: str) -> Any:
|
110
|
+
if not template:
|
111
|
+
return ''
|
112
|
+
if template.startswith('='):
|
113
|
+
return {
|
114
|
+
'type': 'formula',
|
115
|
+
'value': template[1:].strip(),
|
116
|
+
}
|
117
|
+
if template.startswith('$'):
|
118
|
+
result = parse_interpolation_impl(template, 1, len(template)-1)
|
119
|
+
if len(result['templates']) == 0:
|
120
|
+
return result['templates'][0]
|
121
|
+
return result
|
122
|
+
return template
|
123
|
+
# KNOWN_ERRORS = [EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError]
|
124
|
+
|
125
|
+
# 模板序列号
|
126
|
+
seq = 0
|
127
|
+
|
128
|
+
# 创建模板
|
129
|
+
class TemplateCompiler:
|
130
|
+
def __init__(self, template: Any, options: Dict[str, Any]):
|
131
|
+
self.template = template
|
132
|
+
self.options = options
|
133
|
+
self.params = {}
|
134
|
+
self.copyable = []
|
135
|
+
|
136
|
+
# 构建求值
|
137
|
+
def build_eval(self, expression: str, type_: str) -> str:
|
138
|
+
evaluator = self.options['evaluator']
|
139
|
+
if 'evaluator' not in self.params:
|
140
|
+
self.params['evaluator'] = evaluator.get('inject',None)
|
141
|
+
return evaluator['compile'](expression, type_)
|
142
|
+
|
143
|
+
# 构建字符串
|
144
|
+
def build_string(self, str_: str) -> Union[str, bool]:
|
145
|
+
parsed = parse_template(str_)
|
146
|
+
if isinstance(parsed, str):
|
147
|
+
return json.dumps(parsed), False
|
148
|
+
if parsed['type'] == 'formula':
|
149
|
+
return self.build_eval(parsed['value'], parsed['type']), True
|
150
|
+
result = ''
|
151
|
+
for i in range(len(parsed['templates'])):
|
152
|
+
if parsed['templates'][i]:
|
153
|
+
result += (result and '+' or '') + json.dumps(parsed['templates'][i])
|
154
|
+
if i < len(parsed['values']):
|
155
|
+
if not result:
|
156
|
+
result = '""'
|
157
|
+
result += '+' + self.build_eval(parsed['values'][i], parsed['type'])
|
158
|
+
return result, True
|
159
|
+
|
160
|
+
# 构建 Error
|
161
|
+
def build_error(self, err: Exception) -> str:
|
162
|
+
constructor="Error"
|
163
|
+
if err.__class__.__name__ == constructor:
|
164
|
+
return f'new {constructor}({self.build_string(err.message)[0]})'
|
165
|
+
return f'Object.assign(new {constructor}({self.build_string(err.message)[0]}), {{name: {self.build_string(err.name)[0]}}})'
|
166
|
+
|
167
|
+
# 构建数组
|
168
|
+
def build_array(self, arr: List[Any]) -> str:
|
169
|
+
return f'[{", ".join(self.build_value(v) for v in arr)}]'
|
170
|
+
|
171
|
+
# 构建 ArrayBuffer
|
172
|
+
def build_array_buffer(self, buffer: Union[memoryview, bytearray]) -> str:
|
173
|
+
self.copyable.append(buffer[:])
|
174
|
+
return f'copyable[{len(self.copyable) - 1}][:]'
|
175
|
+
|
176
|
+
# 构建 ArrayBufferView
|
177
|
+
def build_array_buffer_view(self, view: memoryview) -> str:
|
178
|
+
self.copyable.append(view.tobytes())
|
179
|
+
return f'new {view.__class__.__name__}(copyable[{len(self.copyable) - 1}][:])'
|
180
|
+
|
181
|
+
# 构建对象
|
182
|
+
def build_object(self, obj: Dict[str, Any]) -> str:
|
183
|
+
result = ''
|
184
|
+
for key, value in obj.items():
|
185
|
+
if result:
|
186
|
+
result += ',\n'
|
187
|
+
if self.options['objectKeyMode'] == 'ignore':
|
188
|
+
result += json.dumps(key)
|
189
|
+
else:
|
190
|
+
e, is_expression = self.build_string(key)
|
191
|
+
if is_expression:
|
192
|
+
result += f'[{e}]'
|
193
|
+
else:
|
194
|
+
result += e
|
195
|
+
result += ':'
|
196
|
+
result += self.build_value(value)
|
197
|
+
return '{' + result + '}'
|
198
|
+
|
199
|
+
# 构建值
|
200
|
+
def build_value(self, value: Any) -> str:
|
201
|
+
if value is None:
|
202
|
+
return 'null'
|
203
|
+
if value is True:
|
204
|
+
return 'true'
|
205
|
+
if value is False:
|
206
|
+
return 'false'
|
207
|
+
if isinstance(value, (int, float)):
|
208
|
+
return str(value)
|
209
|
+
if isinstance(value, str):
|
210
|
+
return self.build_string(value)[0]
|
211
|
+
if isinstance(value, Exception):
|
212
|
+
return self.build_error(value)
|
213
|
+
if isinstance(value, list):
|
214
|
+
return self.build_array(value)
|
215
|
+
if is_array_buffer(value):
|
216
|
+
return self.build_array_buffer(value)
|
217
|
+
if isinstance(value, memoryview):
|
218
|
+
return self.build_array_buffer_view(value)
|
219
|
+
if isinstance(value, dict):
|
220
|
+
return self.build_object(value)
|
221
|
+
raise ValueError(f'Unsupported value: {type(value)}')
|
222
|
+
|
223
|
+
# 构建模板
|
224
|
+
def build(self) -> Any:
|
225
|
+
global seq
|
226
|
+
source = self.build_value(self.template)
|
227
|
+
if self.copyable:
|
228
|
+
self.params['copyable'] = self.copyable
|
229
|
+
params = list(self.params.items())
|
230
|
+
try:
|
231
|
+
result = eval(f'lambda context: ({source})')
|
232
|
+
result.source = source
|
233
|
+
return result
|
234
|
+
except Exception as e:
|
235
|
+
raise ValueError(f'Failed to compile template: {source}\n{str(e)}')
|
236
|
+
|
237
|
+
|
238
|
+
|
239
|
+
def template(templates,options={}):
|
240
|
+
def compile_template(expression, type):
|
241
|
+
if type == 'formula':
|
242
|
+
return f'context[{json.dumps(expression)}]'
|
243
|
+
elif type == 'interpolation':
|
244
|
+
return f"context[{json.dumps(expression)}] ?? ''"
|
245
|
+
raise ValueError(f'Unsupported type: {type}')
|
246
|
+
opt = {
|
247
|
+
'objectKeyMode': 'template',
|
248
|
+
'evaluator':{
|
249
|
+
'compile':compile_template
|
250
|
+
},
|
251
|
+
**options
|
252
|
+
}
|
253
|
+
return TemplateCompiler(templates, opt).build()
|
254
|
+
|
255
|
+
|
256
|
+
|
257
|
+
if __name__ == "__main__":
|
258
|
+
|
259
|
+
message =[1, 1, {'component_load_5_无功功率': [0], 'component_load_5_有功功率': [0], 'time': ['placeholder']}, {'data': {'title': '负荷5功率(kW)', 'traces': [{'name': '有功功率', 'type': 'scatter', 'x': '=time', 'y': '=component_load_5_有功功率'}, {'name': '无功功率', 'type': 'scatter', 'x': '=time', 'y': '=component_load_5_无功功率'}], 'xAxis': {'title': 'time'}, 'yAxis': {'title': '功率(kW)'}}, 'key': '/component_load_5_功率(kW)', 'type': 'plot', 'verb': 'append', 'version': 1}]
|
260
|
+
id =message[0]
|
261
|
+
|
262
|
+
templates=message[3:]
|
263
|
+
|
264
|
+
x= template(templates)
|
265
|
+
|
266
|
+
values=[1, 1, {'component_load_5_无功功率': [5.44544554016478], 'component_load_5_有功功率': [16.3363363363363], 'time': ['2021-08-19 09:00:00']}]
|
267
|
+
|
268
|
+
print(x)
|
269
|
+
|
270
|
+
data= values[2]
|
271
|
+
s=x(data)
|
272
|
+
|
273
|
+
print(s)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import logging
|
2
|
+
from cloudpss.job.TemplateCompiler import template
|
3
|
+
class TemplateManager():
|
4
|
+
|
5
|
+
def __init__(self) -> None:
|
6
|
+
self.templateMap = {}
|
7
|
+
|
8
|
+
def isCreate(self, value):
|
9
|
+
return type(value) == list and len(value) > 3 and type(value[0]) == int and value[1] == 1
|
10
|
+
|
11
|
+
def create(self, value):
|
12
|
+
if not self.isCreate(value):
|
13
|
+
print("is create")
|
14
|
+
return
|
15
|
+
id =value[0]
|
16
|
+
|
17
|
+
templates=value[3:]
|
18
|
+
if self.templateMap.get(id,None) is not None:
|
19
|
+
logging.debug(f"template {id} is already exist")
|
20
|
+
|
21
|
+
self.templateMap[id] = {'template':template(templates),'value':value}
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
def isInvoke(self, value):
|
26
|
+
return type(value) == list and len(value) == 3 and type(value[0]) == int and value[1] == 1
|
27
|
+
|
28
|
+
def invoke(self, value):
|
29
|
+
if not self.isInvoke(value):
|
30
|
+
return []
|
31
|
+
id,_version,args = value
|
32
|
+
|
33
|
+
t = self.templateMap.get(id,None)
|
34
|
+
if t is None:
|
35
|
+
return []
|
36
|
+
return t['template'](args)
|
37
|
+
|