solax-py-library 1.0.0.24__py3-none-any.whl → 1.0.0.2501__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.
Files changed (59) hide show
  1. solax_py_library/__init__.py +1 -1
  2. solax_py_library/device/constant/inverter_model_info.py +312 -312
  3. solax_py_library/device/core/interver/__init__.py +36 -36
  4. solax_py_library/device/core/interver/base.py +215 -215
  5. solax_py_library/device/types/inverter_config.py +41 -41
  6. solax_py_library/device/types/modbus_point.py +30 -30
  7. solax_py_library/exception.py +10 -10
  8. solax_py_library/smart_scene/__init__.py +0 -0
  9. solax_py_library/smart_scene/constant/__init__.py +0 -0
  10. solax_py_library/smart_scene/constant/message_entry.py +338 -0
  11. solax_py_library/smart_scene/core/__init__.py +0 -0
  12. solax_py_library/smart_scene/core/action/__init__.py +0 -0
  13. solax_py_library/smart_scene/core/condition/__init__.py +0 -0
  14. solax_py_library/smart_scene/core/condition/base.py +12 -0
  15. solax_py_library/smart_scene/core/condition/cabinet_condition.py +104 -0
  16. solax_py_library/smart_scene/core/condition/date_condition.py +26 -0
  17. solax_py_library/smart_scene/core/condition/price_condition.py +105 -0
  18. solax_py_library/smart_scene/core/condition/system_condition.py +41 -0
  19. solax_py_library/smart_scene/core/condition/weather_condition.py +67 -0
  20. solax_py_library/smart_scene/exceptions/__init__.py +0 -0
  21. solax_py_library/smart_scene/exceptions/price.py +5 -0
  22. solax_py_library/smart_scene/exceptions/smart_scene.py +81 -0
  23. solax_py_library/smart_scene/exceptions/weather.py +5 -0
  24. solax_py_library/smart_scene/types/__init__.py +0 -0
  25. solax_py_library/smart_scene/types/action.py +156 -0
  26. solax_py_library/smart_scene/types/condition.py +293 -0
  27. solax_py_library/smart_scene/types/smart_scene_content.py +173 -0
  28. solax_py_library/snap_shot/__init__.py +3 -3
  29. solax_py_library/snap_shot/constant/__init__.py +5 -5
  30. solax_py_library/snap_shot/constant/crc_table.py +258 -258
  31. solax_py_library/snap_shot/core/__init__.py +9 -9
  32. solax_py_library/snap_shot/core/base_modbus.py +14 -14
  33. solax_py_library/snap_shot/exceptions/__init__.py +3 -3
  34. solax_py_library/snap_shot/exceptions/snap_shot.py +9 -9
  35. solax_py_library/snap_shot/types/__init__.py +15 -15
  36. solax_py_library/snap_shot/types/address.py +39 -39
  37. solax_py_library/upload/__init__.py +3 -3
  38. solax_py_library/upload/api/__init__.py +3 -3
  39. solax_py_library/upload/api/service.py +24 -24
  40. solax_py_library/upload/core/__init__.py +3 -3
  41. solax_py_library/upload/core/data_adapter/__init__.py +5 -5
  42. solax_py_library/upload/core/data_adapter/base.py +9 -9
  43. solax_py_library/upload/core/data_adapter/csv.py +26 -26
  44. solax_py_library/upload/core/upload_service/__init__.py +15 -15
  45. solax_py_library/upload/core/upload_service/base.py +43 -43
  46. solax_py_library/upload/exceptions/__init__.py +8 -8
  47. solax_py_library/upload/exceptions/upload_error.py +21 -21
  48. solax_py_library/upload/test/test_ftp.py +113 -113
  49. solax_py_library/upload/types/__init__.py +11 -11
  50. solax_py_library/upload/types/client.py +19 -19
  51. solax_py_library/upload/types/ftp.py +37 -37
  52. solax_py_library/utils/cloud_client.py +213 -0
  53. solax_py_library/utils/common.py +38 -38
  54. solax_py_library/utils/struct_util.py +30 -30
  55. solax_py_library/utils/time_util.py +5 -0
  56. {solax_py_library-1.0.0.24.dist-info → solax_py_library-1.0.0.2501.dist-info}/METADATA +1 -1
  57. solax_py_library-1.0.0.2501.dist-info/RECORD +68 -0
  58. solax_py_library-1.0.0.24.dist-info/RECORD +0 -46
  59. {solax_py_library-1.0.0.24.dist-info → solax_py_library-1.0.0.2501.dist-info}/WHEEL +0 -0
@@ -1,113 +1,113 @@
1
- import asyncio
2
- import unittest
3
-
4
- from solax_py_library.upload.api.service import upload
5
- from solax_py_library.upload.core.upload_service import FTPUploadService
6
- from solax_py_library.upload.exceptions import ConnectError, LoginError
7
- from solax_py_library.upload.types.client import UploadType, UploadData
8
- from solax_py_library.upload.types.ftp import FTPFileType
9
-
10
-
11
- class FTPTest(unittest.TestCase):
12
- def test_connect(self):
13
- ftp_config = {
14
- "host": "10.1.31.181", # 测试host
15
- "port": 21,
16
- "user": "solax",
17
- "password": "123456",
18
- "remote_path": "/xixi",
19
- }
20
- ftp = FTPUploadService(**ftp_config)
21
- ftp.connect()
22
-
23
- def test_connect_error_1(self):
24
- ftp_config = {
25
- "host": "10.1.31.182", # 测试host
26
- "port": 21,
27
- "user": "solax",
28
- "password": "123456",
29
- "remote_path": "/xixi",
30
- }
31
- ftp = FTPUploadService(**ftp_config)
32
- try:
33
- ftp.connect()
34
- except ConnectError:
35
- ...
36
-
37
- def test_connect_error_2(self):
38
- ftp_config = {
39
- "host": "10.1.31.181", # 测试host
40
- "port": 21,
41
- "user": "solax123",
42
- "password": "123456",
43
- "remote_path": "/xixi",
44
- }
45
- ftp = FTPUploadService(**ftp_config)
46
- try:
47
- ftp.connect()
48
- except LoginError:
49
- ...
50
-
51
- def test_ftp_upload_to_windows(self):
52
- ftp_config = {
53
- "host": "10.1.31.181", # 测试host
54
- "port": 21,
55
- "user": "solax",
56
- "password": "123456",
57
- "remote_path": "嘻嘻",
58
- }
59
- asyncio.run(
60
- upload(
61
- upload_type=UploadType.FTP,
62
- configuration=ftp_config,
63
- upload_data=UploadData(
64
- upload_type=UploadType.FTP,
65
- data=dict(
66
- file_type=FTPFileType.CSV,
67
- file_name="中文",
68
- data=[
69
- {
70
- "EMS1000序列号": "XMG11A011L",
71
- "EMS1000本地时间": "2025-02-11 15:39:10",
72
- "EMS1000版本号": "V007.11.1",
73
- "电站所在国家和地区": None,
74
- "电站所在当前时区": None,
75
- "电站系统类型": None,
76
- }
77
- ],
78
- ),
79
- ),
80
- )
81
- )
82
-
83
- def test_ftp_upload_to_linux(self):
84
- ftp_config = {
85
- "host": "920729yofx76.vicp.fun", # 测试host
86
- "port": 59477,
87
- "user": "test",
88
- "password": "test123456",
89
- "remote_path": "test",
90
- }
91
- asyncio.run(
92
- upload(
93
- upload_type=UploadType.FTP,
94
- configuration=ftp_config,
95
- upload_data=UploadData(
96
- upload_type=UploadType.FTP,
97
- data=dict(
98
- file_type=FTPFileType.CSV,
99
- file_name="中文",
100
- data=[
101
- {
102
- "EMS1000序列号": "XMG11A011L",
103
- "EMS1000本地时间": "2025-02-11 15:39:10",
104
- "EMS1000版本号": "V007.11.1",
105
- "电站所在国家和地区": None,
106
- "电站所在当前时区": None,
107
- "电站系统类型": None,
108
- }
109
- ],
110
- ),
111
- ),
112
- )
113
- )
1
+ import asyncio
2
+ import unittest
3
+
4
+ from solax_py_library.upload.api.service import upload
5
+ from solax_py_library.upload.core.upload_service import FTPUploadService
6
+ from solax_py_library.upload.exceptions import ConnectError, LoginError
7
+ from solax_py_library.upload.types.client import UploadType, UploadData
8
+ from solax_py_library.upload.types.ftp import FTPFileType
9
+
10
+
11
+ class FTPTest(unittest.TestCase):
12
+ def test_connect(self):
13
+ ftp_config = {
14
+ "host": "10.1.31.181", # 测试host
15
+ "port": 21,
16
+ "user": "solax",
17
+ "password": "123456",
18
+ "remote_path": "/xixi",
19
+ }
20
+ ftp = FTPUploadService(**ftp_config)
21
+ ftp.connect()
22
+
23
+ def test_connect_error_1(self):
24
+ ftp_config = {
25
+ "host": "10.1.31.182", # 测试host
26
+ "port": 21,
27
+ "user": "solax",
28
+ "password": "123456",
29
+ "remote_path": "/xixi",
30
+ }
31
+ ftp = FTPUploadService(**ftp_config)
32
+ try:
33
+ ftp.connect()
34
+ except ConnectError:
35
+ ...
36
+
37
+ def test_connect_error_2(self):
38
+ ftp_config = {
39
+ "host": "10.1.31.181", # 测试host
40
+ "port": 21,
41
+ "user": "solax123",
42
+ "password": "123456",
43
+ "remote_path": "/xixi",
44
+ }
45
+ ftp = FTPUploadService(**ftp_config)
46
+ try:
47
+ ftp.connect()
48
+ except LoginError:
49
+ ...
50
+
51
+ def test_ftp_upload_to_windows(self):
52
+ ftp_config = {
53
+ "host": "10.1.31.181", # 测试host
54
+ "port": 21,
55
+ "user": "solax",
56
+ "password": "123456",
57
+ "remote_path": "嘻嘻",
58
+ }
59
+ asyncio.run(
60
+ upload(
61
+ upload_type=UploadType.FTP,
62
+ configuration=ftp_config,
63
+ upload_data=UploadData(
64
+ upload_type=UploadType.FTP,
65
+ data=dict(
66
+ file_type=FTPFileType.CSV,
67
+ file_name="中文",
68
+ data=[
69
+ {
70
+ "EMS1000序列号": "XMG11A011L",
71
+ "EMS1000本地时间": "2025-02-11 15:39:10",
72
+ "EMS1000版本号": "V007.11.1",
73
+ "电站所在国家和地区": None,
74
+ "电站所在当前时区": None,
75
+ "电站系统类型": None,
76
+ }
77
+ ],
78
+ ),
79
+ ),
80
+ )
81
+ )
82
+
83
+ def test_ftp_upload_to_linux(self):
84
+ ftp_config = {
85
+ "host": "920729yofx76.vicp.fun", # 测试host
86
+ "port": 59477,
87
+ "user": "test",
88
+ "password": "test123456",
89
+ "remote_path": "test",
90
+ }
91
+ asyncio.run(
92
+ upload(
93
+ upload_type=UploadType.FTP,
94
+ configuration=ftp_config,
95
+ upload_data=UploadData(
96
+ upload_type=UploadType.FTP,
97
+ data=dict(
98
+ file_type=FTPFileType.CSV,
99
+ file_name="中文",
100
+ data=[
101
+ {
102
+ "EMS1000序列号": "XMG11A011L",
103
+ "EMS1000本地时间": "2025-02-11 15:39:10",
104
+ "EMS1000版本号": "V007.11.1",
105
+ "电站所在国家和地区": None,
106
+ "电站所在当前时区": None,
107
+ "电站系统类型": None,
108
+ }
109
+ ],
110
+ ),
111
+ ),
112
+ )
113
+ )
@@ -1,11 +1,11 @@
1
- from .client import UploadType, UploadData
2
- from .ftp import FTPData, FTPParsedData, FTPFileType, FTPServiceConfig
3
-
4
- __all__ = [
5
- "FTPData",
6
- "FTPParsedData",
7
- "FTPServiceConfig",
8
- "UploadType",
9
- "UploadData",
10
- "FTPFileType",
11
- ]
1
+ from .client import UploadType, UploadData
2
+ from .ftp import FTPData, FTPParsedData, FTPFileType, FTPServiceConfig
3
+
4
+ __all__ = [
5
+ "FTPData",
6
+ "FTPParsedData",
7
+ "FTPServiceConfig",
8
+ "UploadType",
9
+ "UploadData",
10
+ "FTPFileType",
11
+ ]
@@ -1,19 +1,19 @@
1
- from enum import IntEnum
2
- from pydantic import BaseModel
3
- from typing import Optional, Union
4
-
5
- from solax_py_library.upload.types.ftp import FTPData
6
-
7
-
8
- class UploadType(IntEnum):
9
- FTP = 1
10
-
11
-
12
- class UploadData(BaseModel):
13
- data: Optional[Union[dict, FTPData]]
14
- upload_type: UploadType
15
-
16
- def build_data(self):
17
- dict_obj_data = self.data if isinstance(self.data, dict) else self.data.dict()
18
- if self.upload_type == UploadType.FTP:
19
- return FTPData(**dict_obj_data)
1
+ from enum import IntEnum
2
+ from pydantic import BaseModel
3
+ from typing import Optional, Union
4
+
5
+ from solax_py_library.upload.types.ftp import FTPData
6
+
7
+
8
+ class UploadType(IntEnum):
9
+ FTP = 1
10
+
11
+
12
+ class UploadData(BaseModel):
13
+ data: Optional[Union[dict, FTPData]]
14
+ upload_type: UploadType
15
+
16
+ def build_data(self):
17
+ dict_obj_data = self.data if isinstance(self.data, dict) else self.data.dict()
18
+ if self.upload_type == UploadType.FTP:
19
+ return FTPData(**dict_obj_data)
@@ -1,37 +1,37 @@
1
- import os
2
- from enum import IntEnum
3
- from typing import Any, Optional
4
-
5
- from pydantic import BaseModel
6
-
7
-
8
- class FTPFileType(IntEnum):
9
- CSV = 1
10
-
11
- @classmethod
12
- def get_file_suffix(cls, value):
13
- return {
14
- FTPFileType.CSV: ".csv",
15
- }.get(value)
16
-
17
-
18
- class FTPData(BaseModel):
19
- file_type: FTPFileType
20
- file_name: str
21
- data: Any
22
-
23
- def build_full_path(self, remote_path) -> str:
24
- return os.path.join(remote_path, self.file_name)
25
-
26
-
27
- class FTPServiceConfig(BaseModel):
28
- host: str
29
- port: int
30
- user: Optional[str]
31
- password: Optional[str]
32
- remote_path: str
33
-
34
-
35
- class FTPParsedData(BaseModel):
36
- file_name: str
37
- file_path: str
1
+ import os
2
+ from enum import IntEnum
3
+ from typing import Any, Optional
4
+
5
+ from pydantic import BaseModel
6
+
7
+
8
+ class FTPFileType(IntEnum):
9
+ CSV = 1
10
+
11
+ @classmethod
12
+ def get_file_suffix(cls, value):
13
+ return {
14
+ FTPFileType.CSV: ".csv",
15
+ }.get(value)
16
+
17
+
18
+ class FTPData(BaseModel):
19
+ file_type: FTPFileType
20
+ file_name: str
21
+ data: Any
22
+
23
+ def build_full_path(self, remote_path) -> str:
24
+ return os.path.join(remote_path, self.file_name)
25
+
26
+
27
+ class FTPServiceConfig(BaseModel):
28
+ host: str
29
+ port: int
30
+ user: Optional[str]
31
+ password: Optional[str]
32
+ remote_path: str
33
+
34
+
35
+ class FTPParsedData(BaseModel):
36
+ file_name: str
37
+ file_path: str
@@ -0,0 +1,213 @@
1
+ import json
2
+ import traceback
3
+ from datetime import datetime
4
+
5
+ import requests
6
+
7
+ from solax_py_library.utils.time_util import trans_str_time_to_index
8
+
9
+
10
+ class CloudClient:
11
+ def get_token(self, cloud_url, ems_sn, sn_secret):
12
+ token_url = cloud_url + "/device/token/getByRegistrationSn"
13
+ try:
14
+ response = requests.post(
15
+ token_url,
16
+ json={
17
+ "registrationSn": ems_sn,
18
+ "snSecret": sn_secret,
19
+ },
20
+ timeout=5
21
+ )
22
+ if response.content:
23
+ response_data = json.loads(response.content)
24
+ print(f"获取token结果 {response_data}")
25
+ if response_data.get("code") == 0 and response_data.get("result"):
26
+ token = response_data["result"]
27
+ return token
28
+ except Exception as e:
29
+ print(f"访问token接口失败: {str(e)}")
30
+
31
+ def get_weather_data_from_cloud(self, cloud_url, ems_sn, sn_secret):
32
+ """获取未来24小时天气数据"""
33
+ try:
34
+ token = self.get_token(cloud_url, ems_sn, sn_secret)
35
+ if not token:
36
+ return False
37
+ weather_url = cloud_url + "/ess/web/v1/powerStation/station/solcast/get"
38
+ headers = {"token": token, "Content-Type": "application/json"}
39
+ post_dict = {"registerNo": ems_sn, "day": 1}
40
+ response = requests.post(
41
+ url=weather_url, data=json.dumps(post_dict), headers=headers, timeout=5
42
+ )
43
+ # 访问失败或获取数据失败,则重复插入最后一条数据
44
+ if response.status_code != 200:
45
+ print(f"获取天气数据失败 状态码 {response.status_code}")
46
+ return False
47
+ response_data = response.json()
48
+ if response_data.get("result") is None:
49
+ print(f"获取天气数据失败 返回数据 {response_data}")
50
+ return False
51
+ weather_info = {
52
+ "timeList": [],
53
+ "irradiance": {"valueList": []},
54
+ "temperature": {"valueList": []},
55
+ "humidity": {"valueList": []},
56
+ "wind": {"valueList": []},
57
+ "barometricPressure": {"valueList": []},
58
+ "rain": {"valueList": []},
59
+ }
60
+ for info in response_data["result"]:
61
+ weather_info["timeList"].append(info["localTime"])
62
+ weather_info["irradiance"]["valueList"].append(float(info["ghi"]))
63
+ weather_info["temperature"]["valueList"].append(float(info["air_temp"]))
64
+ weather_info["humidity"]["valueList"].append(
65
+ float(info["relative_humidity"])
66
+ )
67
+ weather_info["wind"]["valueList"].append(float(info["wind_speed_10m"]))
68
+ weather_info["barometricPressure"]["valueList"].append(
69
+ float(info.get("surface_pressure", 0))
70
+ )
71
+ rain = 1 if float(info["precipitation_rate"]) > 2.5 else 0
72
+ weather_info["rain"]["valueList"].append(rain)
73
+ data_length = len(weather_info["irradiance"]["valueList"])
74
+ if weather_info["timeList"] == []:
75
+ weather_info = {}
76
+ else:
77
+ weather_info["irradiance"]["maxValue"] = max(
78
+ weather_info["irradiance"]["valueList"]
79
+ )
80
+ weather_info["irradiance"]["avgValue"] = round(
81
+ sum(weather_info["irradiance"]["valueList"]) / data_length, 3
82
+ )
83
+ weather_info["irradiance"]["minValue"] = min(
84
+ weather_info["irradiance"]["valueList"]
85
+ )
86
+
87
+ weather_info["temperature"]["maxValue"] = max(
88
+ weather_info["temperature"]["valueList"]
89
+ )
90
+ weather_info["temperature"]["avgValue"] = round(
91
+ sum(weather_info["temperature"]["valueList"]) / data_length, 3
92
+ )
93
+ weather_info["temperature"]["minValue"] = min(
94
+ weather_info["temperature"]["valueList"]
95
+ )
96
+
97
+ weather_info["humidity"]["maxValue"] = max(
98
+ weather_info["humidity"]["valueList"]
99
+ )
100
+ weather_info["humidity"]["avgValue"] = round(
101
+ sum(weather_info["humidity"]["valueList"]) / data_length, 3
102
+ )
103
+ weather_info["humidity"]["minValue"] = min(
104
+ weather_info["humidity"]["valueList"]
105
+ )
106
+
107
+ weather_info["wind"]["maxValue"] = max(
108
+ weather_info["wind"]["valueList"]
109
+ )
110
+ weather_info["wind"]["avgValue"] = round(
111
+ sum(weather_info["wind"]["valueList"]) / data_length, 3
112
+ )
113
+ weather_info["wind"]["minValue"] = min(
114
+ weather_info["wind"]["valueList"]
115
+ )
116
+
117
+ weather_info["barometricPressure"]["maxValue"] = max(
118
+ weather_info["barometricPressure"]["valueList"]
119
+ )
120
+ weather_info["barometricPressure"]["avgValue"] = round(
121
+ sum(weather_info["barometricPressure"]["valueList"]) / data_length,
122
+ 3,
123
+ )
124
+ weather_info["barometricPressure"]["minValue"] = min(
125
+ weather_info["barometricPressure"]["valueList"]
126
+ )
127
+
128
+ weather_info["rain"]["maxValue"] = max(
129
+ weather_info["rain"]["valueList"]
130
+ )
131
+ weather_info["rain"]["minValue"] = min(
132
+ weather_info["rain"]["valueList"]
133
+ )
134
+ return weather_info
135
+ print("获取天气数据成功")
136
+ except Exception:
137
+ print(f"获取天气数据失败 异常 {traceback.format_exc()}")
138
+ return False
139
+
140
+ def get_electrovalence_data_from_cloud(self, cloud_url, ems_sn, sn_secret):
141
+ try:
142
+ token = self.get_token(cloud_url, ems_sn, sn_secret)
143
+ if not token:
144
+ return False
145
+ price_url = cloud_url + "/powerStation/station/getCurrentElectrovalence"
146
+ response = requests.post(
147
+ url=price_url,
148
+ headers={"token": token, "Content-Type": "application/json"},
149
+ json={"registerNo": ems_sn},
150
+ timeout=5
151
+ )
152
+ # 访问失败或获取数据失败,则重复插入最后一条数据
153
+ if response.status_code != 200:
154
+ print(f"获取电价数据失败 状态码 {response.status_code}")
155
+ return False
156
+ response_data = response.json()
157
+ if response_data.get("result") is None:
158
+ print(f"获取电价数据失败 返回数据 {response_data}")
159
+ return False
160
+ today = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S")
161
+ ele_price_info = {
162
+ "buy": [None] * 192,
163
+ "sell": [None] * 192,
164
+ "date": today,
165
+ }
166
+ ele_price_info["ele_unit"] = response_data["result"]["unit"]
167
+ if ele_price_info["ele_unit"] == "¥":
168
+ rate = 1
169
+ ele_price_info["ele_unit"] = "¥/kWh"
170
+ else:
171
+ rate = 100
172
+ ele_price_info["ele_unit"] = "Cents €/kWh"
173
+ for detail_info in response_data["result"]["list"]:
174
+ start_index = trans_str_time_to_index(detail_info["startTime"])
175
+ end_index = trans_str_time_to_index(detail_info["endTime"])
176
+ # 处理欧分的情况
177
+ if detail_info["buyPrice"] is not None:
178
+ buy_price = round(detail_info["buyPrice"] * rate, 5)
179
+ else:
180
+ buy_price = detail_info["buyPrice"]
181
+ if detail_info["salePrice"] is not None:
182
+ sale_price = round(detail_info["salePrice"] * rate, 5)
183
+ else:
184
+ sale_price = detail_info["salePrice"]
185
+ ele_price_info["buy"][start_index:end_index] = [buy_price] * (
186
+ end_index - start_index
187
+ )
188
+ ele_price_info["sell"][start_index:end_index] = [sale_price] * (
189
+ end_index - start_index
190
+ )
191
+ if response_data["result"].get("tomorrow") is not None:
192
+ for detail_info in response_data["result"]["tomorrow"]:
193
+ start_index = trans_str_time_to_index(detail_info["startTime"]) + 96
194
+ end_index = trans_str_time_to_index(detail_info["endTime"]) + 96
195
+ if detail_info["buyPrice"] is not None:
196
+ buy_price = round(detail_info["buyPrice"] * rate, 5)
197
+ else:
198
+ buy_price = detail_info["buyPrice"]
199
+ if detail_info["salePrice"] is not None:
200
+ sale_price = round(detail_info["salePrice"] * rate, 5)
201
+ else:
202
+ sale_price = detail_info["salePrice"]
203
+ ele_price_info["buy"][start_index:end_index] = [buy_price] * (
204
+ end_index - start_index
205
+ )
206
+ ele_price_info["sell"][start_index:end_index] = [sale_price] * (
207
+ end_index - start_index
208
+ )
209
+ print("获取电价数据成功")
210
+ return ele_price_info
211
+ except Exception:
212
+ print(f"获取电价数据失败 异常 {traceback.format_exc()}")
213
+ return False
@@ -1,38 +1,38 @@
1
- import asyncio
2
- from decimal import Decimal, ROUND_HALF_UP
3
-
4
-
5
- def retry(max_attempts=3, delay=0.5, assign_exception=Exception):
6
- def decorator(func):
7
- async def wrapper(*args, **kwargs):
8
- attempts = 0
9
- while attempts < max_attempts:
10
- try:
11
- return await func(*args, **kwargs)
12
- except Exception as e:
13
- print(f"尝试 {attempts+1} 失败: {e}")
14
- await asyncio.sleep(delay)
15
- attempts += 1
16
- raise assign_exception(f"操作失败 {max_attempts} 次")
17
-
18
- return wrapper
19
-
20
- return decorator
21
-
22
-
23
- def round_value(value, decimal_places=0):
24
- """
25
- 实现四舍五入 代替round函数 遇到5向上取整
26
- @param value: 传入数值
27
- @param decimal_places: 保留几位小数
28
- @return:
29
- """
30
- if isinstance(value, int):
31
- return value
32
- else:
33
- # 优化小数位 Decimal函数太耗时 曲线数据直接使用round函数
34
- # rounded_value = ("{:.%df}" % decimal_places).format(value)
35
- rounded_value = Decimal(str(value)).quantize(
36
- Decimal("1e-" + str(decimal_places)), rounding=ROUND_HALF_UP
37
- )
38
- return float(rounded_value)
1
+ import asyncio
2
+ from decimal import Decimal, ROUND_HALF_UP
3
+
4
+
5
+ def retry(max_attempts=3, delay=0.5, assign_exception=Exception):
6
+ def decorator(func):
7
+ async def wrapper(*args, **kwargs):
8
+ attempts = 0
9
+ while attempts < max_attempts:
10
+ try:
11
+ return await func(*args, **kwargs)
12
+ except Exception as e:
13
+ print(f"尝试 {attempts+1} 失败: {e}")
14
+ await asyncio.sleep(delay)
15
+ attempts += 1
16
+ raise assign_exception(f"操作失败 {max_attempts} 次")
17
+
18
+ return wrapper
19
+
20
+ return decorator
21
+
22
+
23
+ def round_value(value, decimal_places=0):
24
+ """
25
+ 实现四舍五入 代替round函数 遇到5向上取整
26
+ @param value: 传入数值
27
+ @param decimal_places: 保留几位小数
28
+ @return:
29
+ """
30
+ if isinstance(value, int):
31
+ return value
32
+ else:
33
+ # 优化小数位 Decimal函数太耗时 曲线数据直接使用round函数
34
+ # rounded_value = ("{:.%df}" % decimal_places).format(value)
35
+ rounded_value = Decimal(str(value)).quantize(
36
+ Decimal("1e-" + str(decimal_places)), rounding=ROUND_HALF_UP
37
+ )
38
+ return float(rounded_value)