wrfrun 0.1.9__py3-none-any.whl → 0.3.0__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 (66) hide show
  1. wrfrun/__init__.py +8 -3
  2. wrfrun/cli.py +74 -31
  3. wrfrun/core/__init__.py +27 -10
  4. wrfrun/core/_config.py +308 -0
  5. wrfrun/core/_constant.py +236 -0
  6. wrfrun/core/_exec_db.py +105 -0
  7. wrfrun/core/_namelist.py +287 -0
  8. wrfrun/core/_record.py +178 -0
  9. wrfrun/core/_resource.py +172 -0
  10. wrfrun/core/base.py +136 -380
  11. wrfrun/core/core.py +196 -0
  12. wrfrun/core/error.py +35 -2
  13. wrfrun/core/replay.py +10 -96
  14. wrfrun/core/server.py +74 -32
  15. wrfrun/core/type.py +171 -0
  16. wrfrun/data.py +312 -149
  17. wrfrun/extension/goos_sst/__init__.py +2 -2
  18. wrfrun/extension/goos_sst/core.py +9 -14
  19. wrfrun/extension/goos_sst/res/__init__.py +0 -1
  20. wrfrun/extension/goos_sst/utils.py +50 -44
  21. wrfrun/extension/littler/core.py +105 -88
  22. wrfrun/extension/utils.py +5 -3
  23. wrfrun/log.py +117 -0
  24. wrfrun/model/__init__.py +11 -7
  25. wrfrun/model/constants.py +52 -0
  26. wrfrun/model/palm/__init__.py +30 -0
  27. wrfrun/model/palm/core.py +145 -0
  28. wrfrun/model/palm/namelist.py +33 -0
  29. wrfrun/model/plot.py +99 -114
  30. wrfrun/model/type.py +116 -0
  31. wrfrun/model/utils.py +9 -19
  32. wrfrun/model/wrf/__init__.py +4 -9
  33. wrfrun/model/wrf/core.py +262 -165
  34. wrfrun/model/wrf/exec_wrap.py +13 -12
  35. wrfrun/model/wrf/geodata.py +116 -99
  36. wrfrun/model/wrf/log.py +103 -0
  37. wrfrun/model/wrf/namelist.py +92 -76
  38. wrfrun/model/wrf/plot.py +102 -0
  39. wrfrun/model/wrf/scheme.py +108 -52
  40. wrfrun/model/wrf/utils.py +39 -24
  41. wrfrun/model/wrf/vtable.py +42 -7
  42. wrfrun/plot/__init__.py +20 -0
  43. wrfrun/plot/wps.py +90 -73
  44. wrfrun/res/__init__.py +115 -5
  45. wrfrun/res/config/config.template.toml +15 -0
  46. wrfrun/res/config/palm.template.toml +23 -0
  47. wrfrun/run.py +121 -77
  48. wrfrun/scheduler/__init__.py +1 -0
  49. wrfrun/scheduler/lsf.py +4 -1
  50. wrfrun/scheduler/pbs.py +4 -1
  51. wrfrun/scheduler/script.py +19 -5
  52. wrfrun/scheduler/slurm.py +4 -1
  53. wrfrun/scheduler/utils.py +14 -2
  54. wrfrun/utils.py +88 -199
  55. wrfrun/workspace/__init__.py +8 -5
  56. wrfrun/workspace/core.py +21 -11
  57. wrfrun/workspace/palm.py +137 -0
  58. wrfrun/workspace/wrf.py +59 -14
  59. wrfrun-0.3.0.dist-info/METADATA +240 -0
  60. wrfrun-0.3.0.dist-info/RECORD +78 -0
  61. wrfrun/core/config.py +0 -767
  62. wrfrun/model/base.py +0 -14
  63. wrfrun-0.1.9.dist-info/METADATA +0 -68
  64. wrfrun-0.1.9.dist-info/RECORD +0 -62
  65. {wrfrun-0.1.9.dist-info → wrfrun-0.3.0.dist-info}/WHEEL +0 -0
  66. {wrfrun-0.1.9.dist-info → wrfrun-0.3.0.dist-info}/entry_points.txt +0 -0
wrfrun/core/core.py ADDED
@@ -0,0 +1,196 @@
1
+ """
2
+ wrfrun.core.core
3
+ ################
4
+
5
+ .. autosummary::
6
+ :toctree: generated/
7
+
8
+ WRFRUNProxy
9
+
10
+ Global variable "WRFRUN"
11
+ ************************
12
+
13
+ ``WRFRUN`` is an instance of :class:`WRFRUNProxy`.
14
+ It holds the instance of :class:`WRFRunConfig <wrfrun.core._config.WRFRunConfig>`,
15
+ :class:`ExecutableDB <wrfrun.core._exec_db.ExecutableDB>`,
16
+ and :class:`ExecutableRecorder <wrfrun.core._record.ExecutableRecorder>`.
17
+ Through this global variable, other submodules of wrfrun and users can access attributes and methods of these classes.
18
+ """
19
+
20
+ from typing import Callable, Literal
21
+
22
+ from ..log import logger
23
+ from ._config import WRFRunConfig
24
+ from ._exec_db import ExecutableDB
25
+ from ._record import ExecutableRecorder
26
+ from .error import ConfigError
27
+
28
+
29
+ class WRFRUNProxy:
30
+ """
31
+ Proxy class to access :class:`WRFRunConfig <wrfrun.core._config.WRFRunConfig>`,
32
+ :class:`ExecutableDB <wrfrun.core._exec_db.ExecutableDB>`,
33
+ and :class:`ExecutableRecorder <wrfrun.core._record.ExecutableRecorder>`.
34
+ """
35
+
36
+ def __init__(self):
37
+ """
38
+ Proxy class to access :class:`WRFRunConfig <wrfrun.core._config.WRFRunConfig>`,
39
+ :class:`ExecutableDB <wrfrun.core._exec_db.ExecutableDB>`,
40
+ and :class:`ExecutableRecorder <wrfrun.core._record.ExecutableRecorder>`.
41
+ """
42
+ self._config: WRFRunConfig | None = None
43
+ self._config_initialized = False
44
+ self._exec_db: ExecutableDB | None = None
45
+ self._exec_db_initialized = False
46
+ self._recorder: ExecutableRecorder | None = None
47
+ self._recorder_initialized = False
48
+
49
+ self._config_register_funcs: list[Callable[["WRFRunConfig"], None]] = []
50
+ self._exec_db_register_funcs: list[Callable[["ExecutableDB"], None]] = []
51
+
52
+ self.init_exec_db()
53
+
54
+ @property
55
+ def config(self) -> WRFRunConfig:
56
+ """
57
+ Access wrfrun config.
58
+
59
+ :return: wrfrun config.
60
+ :rtype: WRFRunConfig
61
+ """
62
+ if self._config is None:
63
+ logger.error("You haven't initialize `CONFIG` yet.")
64
+ raise ConfigError("You haven't initialize `CONFIG` yet.")
65
+ return self._config
66
+
67
+ @property
68
+ def ExecDB(self) -> ExecutableDB:
69
+ """
70
+ Access Executable DB.
71
+
72
+ :return: Executable DB.
73
+ :rtype: ExecutableDB
74
+ """
75
+ if self._exec_db is None:
76
+ logger.error("You haven't initialize `ExecDB` yet.")
77
+ raise ConfigError("You haven't initialize `ExecDB` yet.")
78
+ return self._exec_db
79
+
80
+ @property
81
+ def record(self) -> ExecutableRecorder:
82
+ """
83
+ Access simulation recorder.
84
+
85
+ :return: Simulation recorder.
86
+ :rtype: ExecutableRecorder
87
+ """
88
+ if self._recorder is None:
89
+ logger.error("You haven't initialize simulation recorder yet.")
90
+ raise ConfigError("You haven't initialize simulation recorder yet.")
91
+ return self._recorder
92
+
93
+ def set_exec_db(self, exec_db: ExecutableDB):
94
+ """
95
+ Initialize Executable DB.
96
+
97
+ :param exec_db: Executables DB.
98
+ :type exec_db: ExecutableDB
99
+ """
100
+ self._exec_db = exec_db
101
+ self._exec_db_initialized = True
102
+
103
+ def set_config_register_func(self, func: Callable[["WRFRunConfig"], None]):
104
+ """
105
+ Set register function which will be called by wrfrun config.
106
+ This functions should accept a ``WRFRunConfig`` instance.
107
+
108
+ If wrfrun config hasn't been initialized, the function will be stored
109
+ and called in order by the time wrfrun config is initialized.
110
+
111
+ :param func: Register functions.
112
+ :type func: Callable[["WRFRunConfig"], None]
113
+ """
114
+ if self._config_initialized:
115
+ func(self._config)
116
+
117
+ else:
118
+ if func not in self._config_register_funcs:
119
+ self._config_register_funcs.append(func)
120
+
121
+ def set_exec_db_register_func(self, func: Callable[["ExecutableDB"], None]):
122
+ """
123
+ Set register function which will be called by executables DB.
124
+ This function should accept a :class:`WRFRunExecutableRegisterCenter` instance.
125
+
126
+ If executables DB hasn't been initialized, the function will be stored
127
+ and called in order by the time executables DB is initialized.
128
+
129
+ :param func: Register functions.
130
+ :type func: Callable[["WRFRunExecutableRegisterCenter"], None]
131
+ """
132
+ if self._exec_db_initialized:
133
+ func(self._exec_db)
134
+
135
+ else:
136
+ if func not in self._exec_db_register_funcs:
137
+ self._exec_db_register_funcs.append(func)
138
+
139
+ def is_initialized(self, name: Literal["config", "exec_db", "record"]) -> bool:
140
+ """
141
+ Check if the config has been initialized.
142
+
143
+ :param name: Name of the instance.
144
+ :type name: str
145
+ :return: True or False.
146
+ :rtype: bool
147
+ """
148
+ flag = False
149
+
150
+ match name:
151
+ case "config":
152
+ flag = self._config_initialized
153
+
154
+ case "exec_db":
155
+ flag = self._exec_db_initialized
156
+
157
+ case "record":
158
+ flag = self._recorder_initialized
159
+
160
+ return flag
161
+
162
+ def init_wrfrun_config(self, config_file: str):
163
+ """
164
+ Initialize wrfrun config with the given config file.
165
+
166
+ :param config_file: Config file path.
167
+ :type config_file: str
168
+ """
169
+ logger.info(f"Initialize `WRFRUNConfig` with config: {config_file}")
170
+ self._config = WRFRunConfig.from_config_file(config_file, self._config_register_funcs)
171
+ self._config_initialized = True
172
+
173
+ def init_exec_db(self):
174
+ """
175
+ Initialize Executable DB.
176
+ """
177
+ self._exec_db = ExecutableDB()
178
+ self._exec_db.apply_register_func(self._exec_db_register_funcs)
179
+ self._exec_db_initialized = True
180
+
181
+ def init_recorder(self, save_path: str, include_data: bool):
182
+ """
183
+ Initialize simulation recorder.
184
+
185
+ :param save_path: Save path of the replay file.
186
+ :type save_path: str
187
+ :param include_data: If includes data.
188
+ :type include_data: bool
189
+ """
190
+ self._recorder = ExecutableRecorder(self._config, save_path, include_data)
191
+ self._recorder_initialized = True
192
+
193
+
194
+ WRFRUN = WRFRUNProxy()
195
+
196
+ __all__ = ["WRFRUN", "WRFRUNProxy"]
wrfrun/core/error.py CHANGED
@@ -26,6 +26,7 @@ class WRFRunBasicError(Exception):
26
26
  """
27
27
  Basic exception class of ``wrfrun``. New exception **MUST** inherit this class.
28
28
  """
29
+
29
30
  pass
30
31
 
31
32
 
@@ -33,6 +34,7 @@ class ConfigError(WRFRunBasicError):
33
34
  """
34
35
  Exception indicates the config of ``wrfrun`` or NWP model can't be used.
35
36
  """
37
+
36
38
  pass
37
39
 
38
40
 
@@ -40,6 +42,7 @@ class WRFRunContextError(WRFRunBasicError):
40
42
  """
41
43
  Exception indicates ``wrfrun`` is running out of the ``wrfrun`` context.
42
44
  """
45
+
43
46
  pass
44
47
 
45
48
 
@@ -47,6 +50,7 @@ class CommandError(WRFRunBasicError):
47
50
  """
48
51
  Exception indicates the command of ``Executable`` can't be executed successfully.
49
52
  """
53
+
50
54
  pass
51
55
 
52
56
 
@@ -54,6 +58,7 @@ class OutputFileError(WRFRunBasicError):
54
58
  """
55
59
  Exception indicates ``wrfrun`` can't find any output files with the given rules.
56
60
  """
61
+
57
62
  pass
58
63
 
59
64
 
@@ -61,6 +66,7 @@ class ResourceURIError(WRFRunBasicError):
61
66
  """
62
67
  Exception indicates ``wrfrun`` can't parse the URI.
63
68
  """
69
+
64
70
  pass
65
71
 
66
72
 
@@ -68,6 +74,7 @@ class InputFileError(WRFRunBasicError):
68
74
  """
69
75
  Exception indicates ``wrfrun`` can't find specified input files.
70
76
  """
77
+
71
78
  pass
72
79
 
73
80
 
@@ -75,6 +82,7 @@ class NamelistError(WRFRunBasicError):
75
82
  """
76
83
  Exception indicates ``wrfrun`` can't find the namelist user want to use.
77
84
  """
85
+
78
86
  pass
79
87
 
80
88
 
@@ -82,6 +90,7 @@ class NamelistIDError(WRFRunBasicError):
82
90
  """
83
91
  Exception indicates ``wrfrun`` can't register the specified namelist id.
84
92
  """
93
+
85
94
  pass
86
95
 
87
96
 
@@ -89,6 +98,7 @@ class ExecRegisterError(WRFRunBasicError):
89
98
  """
90
99
  Exception indicates ``wrfrun`` can't register the specified ``Executable``.
91
100
  """
101
+
92
102
  pass
93
103
 
94
104
 
@@ -96,6 +106,7 @@ class GetExecClassError(WRFRunBasicError):
96
106
  """
97
107
  Exception indicates ``wrfrun`` can't find the specified ``Executable``.
98
108
  """
109
+
99
110
  pass
100
111
 
101
112
 
@@ -103,8 +114,30 @@ class ModelNameError(WRFRunBasicError):
103
114
  """
104
115
  Exception indicates ``wrfrun`` can't find config of the specified NWP model in the config file.
105
116
  """
117
+
118
+ pass
119
+
120
+
121
+ class RecordError(WRFRunBasicError):
122
+ """
123
+ Exception indicates ``wrfrun`` can't record simulations.
124
+ """
125
+
106
126
  pass
107
127
 
108
128
 
109
- __all__ = ["WRFRunBasicError", "ConfigError", "WRFRunContextError", "CommandError", "OutputFileError", "ResourceURIError", "InputFileError",
110
- "NamelistError", "ExecRegisterError", "GetExecClassError", "ModelNameError", "NamelistIDError"]
129
+ __all__ = [
130
+ "WRFRunBasicError",
131
+ "ConfigError",
132
+ "WRFRunContextError",
133
+ "CommandError",
134
+ "OutputFileError",
135
+ "ResourceURIError",
136
+ "InputFileError",
137
+ "NamelistError",
138
+ "ExecRegisterError",
139
+ "GetExecClassError",
140
+ "ModelNameError",
141
+ "NamelistIDError",
142
+ "RecordError",
143
+ ]
wrfrun/core/replay.py CHANGED
@@ -2,115 +2,29 @@
2
2
  wrfrun.core.replay
3
3
  ##################
4
4
 
5
- This module provides methods to read config from ``.replay`` file and reproduce the simulation.
5
+ This module provides methods to read configs from replay file and reproduce simulations.
6
6
 
7
7
  .. autosummary::
8
8
  :toctree: generated/
9
9
 
10
- WRFRunExecutableRegisterCenter
11
10
  replay_config_generator
12
-
13
- WRFRUNExecDB
14
- ************
15
-
16
- In order to load ``Executable`` correctly based on the stored ``name`` in ``.replay`` files,
17
- ``wrfrun`` uses ``WRFRUNExecDB``, which is the instance of :class:`WRFRunExecutableRegisterCenter`,
18
- to records all ``Executable`` classes and corresponding ``name``.
19
- When ``wrfrun`` replays the simulation, it gets the right ``Executable`` from ``WRFRUNExecDB`` and executes it.
20
11
  """
21
12
 
22
13
  from collections.abc import Generator
23
14
  from json import loads
24
15
  from os.path import exists
25
16
  from shutil import unpack_archive
26
- from typing import Any
27
-
28
- from .base import ExecutableBase, ExecutableConfig
29
- from .config import WRFRUNConfig
30
- from .error import ExecRegisterError, GetExecClassError
31
- from ..utils import logger
32
-
33
- WRFRUN_REPLAY_URI = ":WRFRUN_REPLAY:"
34
-
35
-
36
- class WRFRunExecutableRegisterCenter:
37
- """
38
- This class provides the method to records ``Executable``'s class with a unique ``name``.
39
- Later you can get the class with the ``name``.
40
- """
41
- _instance = None
42
- _initialized = False
43
-
44
- def __init__(self):
45
- if self._initialized:
46
- return
47
-
48
- self._exec_db = {}
49
-
50
- self._initialized = True
51
-
52
- def __new__(cls, *args, **kwargs):
53
- if cls._instance is None:
54
- cls._instance = super().__new__(cls)
55
-
56
- return cls._instance
57
-
58
- def register_exec(self, name: str, cls: type):
59
- """
60
- Register an ``Executable``'s class with a unique ``name``.
61
-
62
- If the ``name`` has been used, :class:`ExecRegisterError` will be raised.
63
-
64
- :param name: ``Executable``'s unique name.
65
- :type name: str
66
- :param cls: ``Executable``'s class.
67
- :type cls: type
68
- """
69
- if name in self._exec_db:
70
- logger.error(f"'{name}' has been registered.")
71
- raise ExecRegisterError(f"'{name}' has been registered.")
72
-
73
- self._exec_db[name] = cls
74
-
75
- def is_registered(self, name: str) -> bool:
76
- """
77
- Check if an ``Executable``'s class has been registered.
78
-
79
- :param name: ``Executable``'s unique name.
80
- :type name: str
81
- :return: True or False.
82
- :rtype: bool
83
- """
84
- if name in self._exec_db:
85
- return True
86
- else:
87
- return False
88
-
89
- def get_cls(self, name: str) -> type:
90
- """
91
- Get an ``Executable``'s class with the ``name``.
92
-
93
- If the ``name`` can't be found, :class:`GetExecClassError` will be raised.
94
-
95
- :param name: ``Executable``'s unique name.
96
- :type name: str
97
- :return: ``Executable``'s class.
98
- :rtype: type
99
- """
100
- if name not in self._exec_db:
101
- logger.error(f"Executable class '{name}' not found.")
102
- raise GetExecClassError(f"Executable class '{name}' not found.")
103
-
104
- return self._exec_db[name]
105
-
106
17
 
107
- WRFRUNExecDB = WRFRunExecutableRegisterCenter()
18
+ from ..log import logger
19
+ from .base import ExecutableBase
20
+ from .core import WRFRUN
21
+ from .type import ExecutableConfig
108
22
 
109
23
 
110
- def replay_config_generator(replay_config_file: str) -> Generator[tuple[str, ExecutableBase], Any, None]:
24
+ def replay_config_generator(replay_config_file: str) -> Generator[tuple[str, ExecutableBase], None, None]:
111
25
  """
112
26
  This method can read the ``.replay`` file and returns a generator which yields ``Executable`` and their names.
113
- If this method doesn't find ``config.json`` in the ``.replay`` file, ``FileNotFoundError`` will be raised.
27
+ If this method doesn't find ``config.json`` in the ``.replay`` file, :class:`FileNotFoundError` will be raised.
114
28
 
115
29
  The ``Executable`` you get from the generator has been initialized so you can execute it directly.
116
30
 
@@ -124,7 +38,7 @@ def replay_config_generator(replay_config_file: str) -> Generator[tuple[str, Exe
124
38
  :rtype: Generator
125
39
  """
126
40
  logger.info(f"Loading replay resources from: {replay_config_file}")
127
- work_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_WORKSPACE_REPLAY)
41
+ work_path = WRFRUN.config.parse_resource_uri(WRFRUN.config.WRFRUN_WORKSPACE_REPLAY)
128
42
 
129
43
  unpack_archive(replay_config_file, work_path, "zip")
130
44
 
@@ -138,9 +52,9 @@ def replay_config_generator(replay_config_file: str) -> Generator[tuple[str, Exe
138
52
  for _config in replay_config_list:
139
53
  args = _config["class_config"]["class_args"]
140
54
  kwargs = _config["class_config"]["class_kwargs"]
141
- executable: ExecutableBase = WRFRUNExecDB.get_cls(_config["name"])(*args, **kwargs)
55
+ executable: ExecutableBase = WRFRUN.ExecDB.get_cls(_config["name"])(*args, **kwargs)
142
56
  executable.load_config(_config)
143
57
  yield _config["name"], executable
144
58
 
145
59
 
146
- __all__ = ["WRFRUNExecDB", "replay_config_generator"]
60
+ __all__ = ["replay_config_generator"]
wrfrun/core/server.py CHANGED
@@ -2,30 +2,74 @@
2
2
  wrfrun.core.server
3
3
  ##################
4
4
 
5
- Currently, ``wrfrun`` provides the method to reads log file of WRF and calculates simulation progress.
6
- In order to report the progress to user, ``wrfrun`` provides :class:`WRFRunServer` to set up a socket server.
5
+ This module defines the socket server to report simulation progress.
7
6
 
8
7
  .. autosummary::
9
8
  :toctree: generated/
10
9
 
10
+ set_log_parse_func
11
11
  WRFRunServer
12
12
  WRFRunServerHandler
13
13
  stop_server
14
+
15
+ How does wrfrun get simulation progress?
16
+ ****************************************
17
+
18
+ Currently, wrfrun relies on the log parse function provided by :doc:`model </api/model>`.
19
+ The log parse function should accept one parameter ``datetime``,
20
+ and return an integer represents how many seconds the model has simulated.
21
+ For example, the signature of :func:`get_wrf_simulated_seconds <wrfrun.model.wrf.log.get_wrf_simulated_seconds>` is:
22
+
23
+ .. code-block:: Python
24
+
25
+ def get_wrf_simulated_seconds(start_datetime: datetime) -> int:
26
+ pass
27
+
28
+ Socket server API list
29
+ **********************
30
+
31
+ The socket server accepts a JSON string: ``{"message": "MESSAGE"}``.
32
+ The ``MESSAGE`` can be:
33
+
34
+ * "stop": Stop the socket server.
35
+ * "debug": Get the start date of the simulation, and the total seconds it will simulate: ``{"start_date": "...", "total_seconds": "..."}``.
36
+ * Any string else: Simulation progress: ``{"usage": "...", "status": "...", "progress": "..."}``.
14
37
  """
15
38
 
16
39
  import socket
17
40
  import socketserver
41
+ import threading
18
42
  from collections.abc import Callable
19
43
  from datetime import datetime
20
- from json import dumps
44
+ from json import dumps, loads
21
45
  from time import time
22
- from typing import Tuple
46
+ from typing import Any, Tuple
47
+
48
+ from ..log import logger
49
+ from .core import WRFRUN
50
+
51
+
52
+ def _defaut_log_parser(date: datetime) -> int:
53
+ return -1
54
+
23
55
 
24
- from .config import WRFRUNConfig
25
- from ..utils import logger
56
+ SET_LOG_PARSER_LOCK = threading.Lock()
57
+ # default parser returns -1
58
+ LOG_PARSER: Callable[[datetime], int] = _defaut_log_parser
26
59
 
27
- WRFRUN_SERVER_INSTANCE = None
28
- WRFRUN_SERVER_THREAD = None
60
+
61
+ def set_log_parse_func(func: Callable[[datetime], int]):
62
+ """
63
+ Set log parse function used by socket server.
64
+
65
+ :param func: Function used to get simulated seconds from model's log file.
66
+ If the function can't parse the simulated seconds, it should return ``-1``.
67
+ :type func: Callable[[datetime], int]
68
+ """
69
+ global SET_LOG_PARSER_LOCK, LOG_PARSER
70
+
71
+ with SET_LOG_PARSER_LOCK:
72
+ LOG_PARSER = func
29
73
 
30
74
 
31
75
  class WRFRunServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
@@ -155,7 +199,8 @@ class WRFRunServerHandler(socketserver.StreamRequestHandler):
155
199
  ``status`` represents work status,
156
200
  ``progress`` represents simulation progress of the status in percentage.
157
201
  """
158
- def __init__(self, request, client_address, server: WRFRunServer, log_parse_func: Callable[[datetime], int] | None) -> None:
202
+
203
+ def __init__(self, request, client_address, server: WRFRunServer) -> None:
159
204
  """
160
205
  :class:`WRFRunServer` handler.
161
206
 
@@ -165,15 +210,11 @@ class WRFRunServerHandler(socketserver.StreamRequestHandler):
165
210
  :type client_address:
166
211
  :param server: :class:`WRFRunServer` instance.
167
212
  :type server: WRFRunServer
168
- :param log_parse_func: Function used to get simulated seconds from model's log file.
169
- If the function can't parse the simulated seconds, it should return ``-1``.
170
- :type log_parse_func: Callable[[datetime], int]
171
213
  """
172
214
  super().__init__(request, client_address, server)
173
215
 
174
216
  # get server
175
- self.server: WRFRunServer = server
176
- self.log_parse_func = log_parse_func
217
+ self._server: WRFRunServer = server
177
218
 
178
219
  def calculate_time_usage(self) -> int:
179
220
  """
@@ -187,7 +228,7 @@ class WRFRunServerHandler(socketserver.StreamRequestHandler):
187
228
  current_timestamp = datetime.fromtimestamp(time())
188
229
 
189
230
  # get delta second
190
- seconds_diff = current_timestamp - self.server.get_start_time()
231
+ seconds_diff = current_timestamp - self._server.get_start_time()
191
232
  seconds_diff = seconds_diff.seconds
192
233
 
193
234
  return seconds_diff
@@ -200,19 +241,17 @@ class WRFRunServerHandler(socketserver.StreamRequestHandler):
200
241
  ``progress`` represents simulation progress of the status in percentage.
201
242
  :rtype: tuple[str, int]
202
243
  """
203
- start_date, simulate_seconds = self.server.get_model_simulate_settings()
244
+ start_date, simulate_seconds = self._server.get_model_simulate_settings()
204
245
 
205
- if self.log_parse_func is None:
206
- simulated_seconds = -1
207
- else:
208
- simulated_seconds = self.log_parse_func(start_date)
246
+ with SET_LOG_PARSER_LOCK:
247
+ simulated_seconds = LOG_PARSER(start_date)
209
248
 
210
249
  if simulated_seconds > 0:
211
250
  progress = simulated_seconds * 100 // simulate_seconds
212
251
  else:
213
252
  progress = -1
214
253
 
215
- status = WRFRUNConfig.WRFRUN_WORK_STATUS
254
+ status = WRFRUN.config.WRFRUN_WORK_STATUS
216
255
 
217
256
  if status == "":
218
257
  status = "*"
@@ -223,20 +262,20 @@ class WRFRunServerHandler(socketserver.StreamRequestHandler):
223
262
  """
224
263
  Request handler.
225
264
  """
226
-
227
265
  # check if we will to stop server
228
- msg = self.rfile.readline().decode().split('\n')[0]
266
+ request: dict[str, Any] = loads(self.rfile.readline().decode())
267
+ msg = request["message"]
229
268
 
230
269
  if msg == "stop":
231
- self.server.shutdown()
232
- self.wfile.write(f"Server stop\n".encode())
233
-
270
+ self._server.shutdown()
271
+ self.wfile.write("Server stop\n".encode())
272
+
234
273
  elif msg == "debug":
235
- start_date, simulate_seconds = self.server.get_model_simulate_settings()
274
+ start_date, simulate_seconds = self._server.get_model_simulate_settings()
236
275
  start_date = start_date.strftime("%Y-%m-%d %H:%M")
237
-
276
+
238
277
  self.wfile.write(dumps({"start_date": start_date, "total_seconds": simulate_seconds}).encode())
239
-
278
+
240
279
  else:
241
280
  status, progress = self.calculate_progress()
242
281
  time_usage = self.calculate_time_usage()
@@ -258,10 +297,13 @@ def stop_server(socket_ip: str, socket_port: int):
258
297
  sock.connect((socket_ip, socket_port))
259
298
 
260
299
  # send msg to server
261
- sock.sendall("stop\n".encode())
300
+ # TODO:
301
+ # Need to test the new socket server.
302
+ msg = dumps({"message": "stop"})
303
+ sock.sendall(msg.encode())
262
304
 
263
305
  # receive the message
264
- msg = sock.recv(1024).decode().split('\n')[0]
306
+ msg = sock.recv(1024).decode().split("\n")[0]
265
307
 
266
308
  logger.info(f"WRFRunServer: {msg}")
267
309
 
@@ -269,4 +311,4 @@ def stop_server(socket_ip: str, socket_port: int):
269
311
  logger.warning("Fail to stop WRFRunServer, maybe it doesn't start at all.")
270
312
 
271
313
 
272
- __all__ = ["WRFRunServer", "WRFRunServerHandler", "stop_server"]
314
+ __all__ = ["WRFRunServer", "WRFRunServerHandler", "stop_server", "set_log_parse_func"]