corva-worker-python 2.0.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.
- corva_worker_python-2.0.0.dist-info/METADATA +30 -0
- corva_worker_python-2.0.0.dist-info/RECORD +63 -0
- corva_worker_python-2.0.0.dist-info/WHEEL +5 -0
- corva_worker_python-2.0.0.dist-info/top_level.txt +1 -0
- worker/__init__.py +5 -0
- worker/app/__init__.py +291 -0
- worker/app/modules/__init__.py +265 -0
- worker/app/modules/activity_module.py +141 -0
- worker/app/modules/connection_module.py +21 -0
- worker/app/modules/depth_activity_module.py +21 -0
- worker/app/modules/scheduler.py +44 -0
- worker/app/modules/time_activity_module.py +21 -0
- worker/app/modules/trigger.py +43 -0
- worker/constants.py +51 -0
- worker/data/__init__.py +0 -0
- worker/data/activity/__init__.py +132 -0
- worker/data/activity/activity_grouping.py +242 -0
- worker/data/alert.py +89 -0
- worker/data/api.py +155 -0
- worker/data/enums.py +141 -0
- worker/data/json_encoder.py +18 -0
- worker/data/math.py +104 -0
- worker/data/operations.py +477 -0
- worker/data/serialization.py +110 -0
- worker/data/task_handler.py +82 -0
- worker/data/two_way_dict.py +17 -0
- worker/data/unit_conversions.py +5 -0
- worker/data/wits.py +323 -0
- worker/event/__init__.py +53 -0
- worker/event/event_handler.py +90 -0
- worker/event/scheduled.py +64 -0
- worker/event/stream.py +48 -0
- worker/exceptions.py +26 -0
- worker/mixins/__init__.py +0 -0
- worker/mixins/logging.py +119 -0
- worker/mixins/rollbar.py +87 -0
- worker/partial_rerun_merge/__init__.py +0 -0
- worker/partial_rerun_merge/merge.py +500 -0
- worker/partial_rerun_merge/models.py +91 -0
- worker/partial_rerun_merge/progress.py +241 -0
- worker/state/__init__.py +96 -0
- worker/state/mixins.py +111 -0
- worker/state/state.py +46 -0
- worker/test/__init__.py +3 -0
- worker/test/lambda_function_test_run.py +196 -0
- worker/test/local_testing/__init__.py +0 -0
- worker/test/local_testing/to_local_transfer.py +360 -0
- worker/test/utils.py +51 -0
- worker/wellbore/__init__.py +0 -0
- worker/wellbore/factory.py +496 -0
- worker/wellbore/measured_depth_finder.py +12 -0
- worker/wellbore/model/__init__.py +0 -0
- worker/wellbore/model/ann.py +103 -0
- worker/wellbore/model/annulus.py +113 -0
- worker/wellbore/model/drillstring.py +196 -0
- worker/wellbore/model/drillstring_components.py +439 -0
- worker/wellbore/model/element.py +102 -0
- worker/wellbore/model/enums.py +92 -0
- worker/wellbore/model/hole.py +297 -0
- worker/wellbore/model/hole_section.py +51 -0
- worker/wellbore/model/riser.py +22 -0
- worker/wellbore/sections_mixin.py +64 -0
- worker/wellbore/wellbore.py +289 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from itertools import groupby
|
|
2
|
+
|
|
3
|
+
from worker import constants
|
|
4
|
+
from worker.app.modules.__init__ import Module
|
|
5
|
+
from worker.data import operations
|
|
6
|
+
from worker.data.operations import get_config_by_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ActivityModule(Module):
|
|
10
|
+
"""
|
|
11
|
+
This is an abstract base module that needs to be extended by an actual module.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
# module_key is used for redis access and state of this module
|
|
15
|
+
module_key = "activity_module"
|
|
16
|
+
collection = "activity_module_collection"
|
|
17
|
+
|
|
18
|
+
# override
|
|
19
|
+
def run(self, wits_stream: list):
|
|
20
|
+
"""
|
|
21
|
+
:param wits_stream: a wits stream event
|
|
22
|
+
:return:
|
|
23
|
+
"""
|
|
24
|
+
super().run(wits_stream)
|
|
25
|
+
|
|
26
|
+
dataset = self.load_dataset(wits_stream)
|
|
27
|
+
|
|
28
|
+
# this guarantees every group has the same config
|
|
29
|
+
config_grouped_dataset = self.group_data_stream_based_on_config(dataset)
|
|
30
|
+
|
|
31
|
+
for group in config_grouped_dataset:
|
|
32
|
+
results = self.check_for_string_change(group)
|
|
33
|
+
|
|
34
|
+
if self.should_run_processor(group):
|
|
35
|
+
results = self.run_module(group, results)
|
|
36
|
+
|
|
37
|
+
self.save_state()
|
|
38
|
+
self.store_output(self.global_state["asset_id"], results)
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def group_data_stream_based_on_config(dataset):
|
|
42
|
+
"""Group our datasets by the metadata such as drillstring, and then reflatten to be a list of lists
|
|
43
|
+
for processing"""
|
|
44
|
+
|
|
45
|
+
groups = groupby(dataset, lambda x: x["metadata"])
|
|
46
|
+
grouped_dataset = [list(dataset) for group, dataset in groups]
|
|
47
|
+
|
|
48
|
+
return grouped_dataset
|
|
49
|
+
|
|
50
|
+
# override
|
|
51
|
+
def should_run_processor(self, event):
|
|
52
|
+
running_string = constants.get("{}.{}.running-string".format(self.app_key, self.module_key))
|
|
53
|
+
if running_string and self.state.get("active_string_type", "") in running_string:
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
def check_for_string_change(self, data):
|
|
59
|
+
"""
|
|
60
|
+
To check if the active string in the wellbore has been changed and
|
|
61
|
+
export proper text to the output.
|
|
62
|
+
Note that the last_exported_timestamp is not set here.
|
|
63
|
+
Note that all the configs of this data have the same config properties.
|
|
64
|
+
:param data:
|
|
65
|
+
:return:
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
previous_string = self.state.get("active_string_id", "")
|
|
69
|
+
running_string = constants.get("{}.{}.running-string".format(self.app_key, self.module_key))
|
|
70
|
+
reset_config = constants.get("{}.{}.reset-config".format(self.app_key, self.module_key))
|
|
71
|
+
|
|
72
|
+
if not running_string:
|
|
73
|
+
return []
|
|
74
|
+
|
|
75
|
+
if not data:
|
|
76
|
+
return []
|
|
77
|
+
|
|
78
|
+
first_wits = data[0]
|
|
79
|
+
drillstring_id = first_wits.get("metadata", {}).get("drillstring", None)
|
|
80
|
+
casing_id = first_wits.get("metadata", {}).get("casing", None)
|
|
81
|
+
|
|
82
|
+
if drillstring_id:
|
|
83
|
+
active_string_type = "drillstring"
|
|
84
|
+
active_string_id = drillstring_id
|
|
85
|
+
elif casing_id:
|
|
86
|
+
active_string_type = "casing"
|
|
87
|
+
active_string_id = casing_id
|
|
88
|
+
else:
|
|
89
|
+
active_string_type = None
|
|
90
|
+
active_string_id = None
|
|
91
|
+
|
|
92
|
+
if previous_string == active_string_id:
|
|
93
|
+
return []
|
|
94
|
+
|
|
95
|
+
main_structure = self.build_empty_output(first_wits)
|
|
96
|
+
|
|
97
|
+
warning = None
|
|
98
|
+
|
|
99
|
+
if active_string_type in running_string:
|
|
100
|
+
if active_string_type not in reset_config:
|
|
101
|
+
return []
|
|
102
|
+
else:
|
|
103
|
+
message = "This module does not run while {} is the active string in the well.".format(active_string_type)
|
|
104
|
+
warning = {"message": message}
|
|
105
|
+
|
|
106
|
+
res = self.configure_output_at_config_change(main_structure, active_string_type, active_string_id, warning)
|
|
107
|
+
|
|
108
|
+
return [res]
|
|
109
|
+
|
|
110
|
+
def configure_output_at_config_change(self, main_structure, active_string_type, active_string_id, warning=None):
|
|
111
|
+
extra_elements = self.create_output_for_new_config(active_string_type, active_string_id)
|
|
112
|
+
main_structure["data"] = operations.merge_dicts(main_structure["data"], extra_elements)
|
|
113
|
+
|
|
114
|
+
if warning:
|
|
115
|
+
main_structure["data"] = operations.merge_dicts(main_structure["data"], {"warning": warning})
|
|
116
|
+
|
|
117
|
+
return main_structure
|
|
118
|
+
|
|
119
|
+
def create_output_for_new_config(self, active_string_type, active_string_id):
|
|
120
|
+
self.set_state("active_string_type", active_string_type)
|
|
121
|
+
self.set_state("active_string_id", active_string_id)
|
|
122
|
+
|
|
123
|
+
drillstring_number = None
|
|
124
|
+
if active_string_type == "drillstring":
|
|
125
|
+
drillstring = get_config_by_id(active_string_id, collection="data.drillstring")
|
|
126
|
+
|
|
127
|
+
if drillstring:
|
|
128
|
+
drillstring_number = drillstring.get("data", {}).get("id", None)
|
|
129
|
+
|
|
130
|
+
self.set_state("drillstring_number", drillstring_number)
|
|
131
|
+
|
|
132
|
+
return self.get_config_properties()
|
|
133
|
+
|
|
134
|
+
def get_config_properties(self):
|
|
135
|
+
properties = {
|
|
136
|
+
"active_string_type": self.state.get("active_string_type", ""),
|
|
137
|
+
"active_string_id": self.state.get("active_string_id", ""),
|
|
138
|
+
"drillstring_number": self.state.get("drillstring_number", None),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return properties
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from worker.app.modules.activity_module import ActivityModule
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ConnectionModule(ActivityModule):
|
|
5
|
+
"""
|
|
6
|
+
This is an abstract module that needs to be extended by actual modules.
|
|
7
|
+
An example of this module would be running an app after a connection occur.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
module_key = "connection_module"
|
|
11
|
+
collection = "connection_collection"
|
|
12
|
+
|
|
13
|
+
# override
|
|
14
|
+
def process_module_state(self, state):
|
|
15
|
+
state = super().process_module_state(state)
|
|
16
|
+
|
|
17
|
+
if self.global_state.get("reset", False):
|
|
18
|
+
# todo
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
return state
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from worker.app.modules.activity_module import ActivityModule
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DepthActivityModule(ActivityModule):
|
|
5
|
+
"""
|
|
6
|
+
This is an abstract module that needs to be extended by actual modules.
|
|
7
|
+
An example of this module would be running an app every 1 ft.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
module_key = "depth_activity_module"
|
|
11
|
+
collection = "depth_activity_collection"
|
|
12
|
+
|
|
13
|
+
# override
|
|
14
|
+
def process_module_state(self, state):
|
|
15
|
+
state = super().process_module_state(state)
|
|
16
|
+
|
|
17
|
+
if self.global_state.get("reset", False):
|
|
18
|
+
# todo
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
return state
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from worker import constants
|
|
2
|
+
from worker.app.modules.__init__ import Module
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Scheduler(Module):
|
|
6
|
+
"""
|
|
7
|
+
This is an abstract module that needs to be extended by actual modules.
|
|
8
|
+
An example of this module would be running an app at the frequency of
|
|
9
|
+
once every X seconds regardless of the activity.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
module_key = "scheduler_module"
|
|
13
|
+
collection = "scheduler_collection"
|
|
14
|
+
module_state_fields = {"last_processed_timestamp": int}
|
|
15
|
+
|
|
16
|
+
# override
|
|
17
|
+
def run(self, wits_stream: list):
|
|
18
|
+
super().run(wits_stream)
|
|
19
|
+
|
|
20
|
+
dataset = self.load_dataset(wits_stream)
|
|
21
|
+
|
|
22
|
+
results = []
|
|
23
|
+
for el in dataset:
|
|
24
|
+
if self.should_run_processor(el):
|
|
25
|
+
results = self.run_module(el, results)
|
|
26
|
+
|
|
27
|
+
self.save_state()
|
|
28
|
+
self.store_output(self.global_state["asset_id"], results)
|
|
29
|
+
|
|
30
|
+
# override
|
|
31
|
+
def should_run_processor(self, event: dict):
|
|
32
|
+
"""
|
|
33
|
+
check one wits event and test against the last processed timestamp
|
|
34
|
+
:param event:
|
|
35
|
+
:return:
|
|
36
|
+
"""
|
|
37
|
+
timestamp = event.get("timestamp", 0)
|
|
38
|
+
running_frequency = constants.get("{}.{}.running_frequency".format(self.app_key, self.module_key))
|
|
39
|
+
if self.state.get("last_processed_timestamp", 0) + running_frequency <= timestamp:
|
|
40
|
+
# update the last_processed_timestamp
|
|
41
|
+
self.set_state("last_processed_timestamp", timestamp)
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
return False
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from worker.app.modules.activity_module import ActivityModule
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TimeActivityModule(ActivityModule):
|
|
5
|
+
"""
|
|
6
|
+
This is an abstract time-base module that needs to be extended by actual modules.
|
|
7
|
+
An example of this module would be running an app every 1 minute.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
module_key = "time_activity_module"
|
|
11
|
+
collection = "time_activity_collection"
|
|
12
|
+
|
|
13
|
+
# override
|
|
14
|
+
def process_module_state(self, state):
|
|
15
|
+
state = super().process_module_state(state)
|
|
16
|
+
|
|
17
|
+
if self.global_state.get("reset", False):
|
|
18
|
+
# todo
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
return state
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from worker import constants
|
|
2
|
+
from worker.app.modules.__init__ import Module
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Trigger(Module):
|
|
6
|
+
"""
|
|
7
|
+
This is an abstract module that needs to be extended by actual modules.
|
|
8
|
+
The idea behind this module is that it runs once for a bunch of data
|
|
9
|
+
only if the the time step is passed.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
module_key = "trigger_module"
|
|
13
|
+
collection = "trigger_collection"
|
|
14
|
+
module_state_fields = {"last_processed_timestamp": int}
|
|
15
|
+
|
|
16
|
+
# override
|
|
17
|
+
def run(self, wits_stream: list):
|
|
18
|
+
super().run(wits_stream)
|
|
19
|
+
|
|
20
|
+
dataset = self.load_dataset(wits_stream)
|
|
21
|
+
last_wits = dataset[-1]
|
|
22
|
+
results = []
|
|
23
|
+
if self.should_run_processor(last_wits):
|
|
24
|
+
results = self.run_module(last_wits, results)
|
|
25
|
+
|
|
26
|
+
self.save_state()
|
|
27
|
+
self.store_output(self.global_state["asset_id"], results)
|
|
28
|
+
|
|
29
|
+
# override
|
|
30
|
+
def should_run_processor(self, event: dict):
|
|
31
|
+
"""
|
|
32
|
+
check one wits event and test against the last processed timestamp
|
|
33
|
+
:param event:
|
|
34
|
+
:return:
|
|
35
|
+
"""
|
|
36
|
+
timestamp = event.get("timestamp", 0)
|
|
37
|
+
running_frequency = constants.get("{}.{}.running_frequency".format(self.app_key, self.module_key))
|
|
38
|
+
if self.state.get("last_processed_timestamp", 0) + running_frequency < timestamp:
|
|
39
|
+
# update the last_processed_timestamp
|
|
40
|
+
self.set_state("last_processed_timestamp", timestamp)
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
return False
|
worker/constants.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
this constants module can be overridden by your new constants module by importing
|
|
3
|
+
the this module and updating it like this:
|
|
4
|
+
from worker import constants
|
|
5
|
+
constants.update({
|
|
6
|
+
...
|
|
7
|
+
})
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from worker.data.operations import get_data_by_path
|
|
11
|
+
|
|
12
|
+
parameters = {
|
|
13
|
+
"global": {
|
|
14
|
+
"app-name": "my-app",
|
|
15
|
+
"app-key": "my-app-key",
|
|
16
|
+
"event-type": "scheduler",
|
|
17
|
+
"query-limit": 1000,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get(path, default=None):
|
|
23
|
+
"""
|
|
24
|
+
Get the value at the specified path in the parameters dictionary.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
path (str): The path to the value to retrieve.
|
|
28
|
+
default (Any, optional): The default value to return if the path
|
|
29
|
+
is not found. Defaults to None.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Any: The value at the specified path, or the default value
|
|
33
|
+
if the path is not found.
|
|
34
|
+
"""
|
|
35
|
+
return get_data_by_path(data=parameters, path=path, default=default)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def update(additional_parameters):
|
|
39
|
+
"""
|
|
40
|
+
The purpose of this method is to update the existing parameters data
|
|
41
|
+
with the provided additional parameters. Note that the globals will
|
|
42
|
+
be replaced if the additional parameters contains global node.
|
|
43
|
+
:param additional_parameters:
|
|
44
|
+
:return:
|
|
45
|
+
"""
|
|
46
|
+
_globals = parameters.pop("global")
|
|
47
|
+
additional_globals = additional_parameters.pop("global", {})
|
|
48
|
+
_globals.update(additional_globals)
|
|
49
|
+
parameters.update(additional_parameters)
|
|
50
|
+
if _globals:
|
|
51
|
+
parameters["global"] = _globals
|
worker/data/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from worker.data.serialization import serialization
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PipeMovementStatus(Enum):
|
|
7
|
+
UNCLASSIFIED = -1
|
|
8
|
+
IN_SLIPS = 0
|
|
9
|
+
STATIC = 1
|
|
10
|
+
ON_BOTTOM = 2
|
|
11
|
+
PULL_OUT_OF_HOLE = 3
|
|
12
|
+
RUN_IN_HOLE = 4
|
|
13
|
+
DRILLING = 5
|
|
14
|
+
|
|
15
|
+
def is_moving_out(self) -> bool:
|
|
16
|
+
move_actions = [
|
|
17
|
+
PipeMovementStatus.PULL_OUT_OF_HOLE,
|
|
18
|
+
]
|
|
19
|
+
return self in move_actions
|
|
20
|
+
|
|
21
|
+
def is_moving_in(self) -> bool:
|
|
22
|
+
move_actions = [PipeMovementStatus.RUN_IN_HOLE, PipeMovementStatus.DRILLING]
|
|
23
|
+
return self in move_actions
|
|
24
|
+
|
|
25
|
+
def is_moving(self) -> bool:
|
|
26
|
+
return self.is_moving_in() or self.is_moving_out()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class PumpStatus(Enum):
|
|
30
|
+
OFF = 0
|
|
31
|
+
ON = 1
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RotationStatus(Enum):
|
|
35
|
+
OFF = 0
|
|
36
|
+
ON = 1
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@serialization
|
|
40
|
+
class Activity(bytes, Enum):
|
|
41
|
+
ROTARY_DRILLING = ("Rotary Drilling", PipeMovementStatus.DRILLING, PumpStatus.ON, RotationStatus.ON)
|
|
42
|
+
SLIDE_DRILLING = ("Slide Drilling", PipeMovementStatus.DRILLING, PumpStatus.ON, RotationStatus.OFF)
|
|
43
|
+
|
|
44
|
+
RUN_IN_HOLE = ("Run in Hole", PipeMovementStatus.RUN_IN_HOLE, PumpStatus.OFF, RotationStatus.OFF)
|
|
45
|
+
WASHING_DOWN = ("Washing Down", PipeMovementStatus.RUN_IN_HOLE, PumpStatus.ON, RotationStatus.OFF)
|
|
46
|
+
DRY_REAMING_DOWN = ("Dry Reaming Down", PipeMovementStatus.RUN_IN_HOLE, PumpStatus.OFF, RotationStatus.ON)
|
|
47
|
+
REAMING_DOWN = ("Reaming Down", PipeMovementStatus.RUN_IN_HOLE, PumpStatus.ON, RotationStatus.ON)
|
|
48
|
+
|
|
49
|
+
PULL_OUT_OF_HOLE = ("Pull out of Hole", PipeMovementStatus.PULL_OUT_OF_HOLE, PumpStatus.OFF, RotationStatus.OFF)
|
|
50
|
+
WASHING_UP = ("Washing Up", PipeMovementStatus.PULL_OUT_OF_HOLE, PumpStatus.ON, RotationStatus.OFF)
|
|
51
|
+
DRY_REAMING_UP = ("Dry Reaming Up", PipeMovementStatus.PULL_OUT_OF_HOLE, PumpStatus.OFF, RotationStatus.ON)
|
|
52
|
+
REAMING_UP = ("Reaming Up", PipeMovementStatus.PULL_OUT_OF_HOLE, PumpStatus.ON, RotationStatus.ON)
|
|
53
|
+
|
|
54
|
+
STATIC_OFF_BOTTOM = ("Static Off Bottom", PipeMovementStatus.STATIC, PumpStatus.OFF, RotationStatus.OFF)
|
|
55
|
+
CIRCULATING = ("Circulating", PipeMovementStatus.STATIC, PumpStatus.ON, RotationStatus.OFF)
|
|
56
|
+
DRY_ROTARY_OFF_BOTTOM = ("Dry Rotary Off Bottom", PipeMovementStatus.STATIC, PumpStatus.OFF, RotationStatus.ON)
|
|
57
|
+
ROTARY_OFF_BOTTOM = ("Rotary Off Bottom", PipeMovementStatus.STATIC, PumpStatus.ON, RotationStatus.ON)
|
|
58
|
+
|
|
59
|
+
STATIC_ON_BOTTOM = ("Static On Bottom", PipeMovementStatus.ON_BOTTOM, PumpStatus.OFF, RotationStatus.OFF)
|
|
60
|
+
CIRCULATING_ON_BOTTOM = ("Circulating On Bottom", PipeMovementStatus.ON_BOTTOM, PumpStatus.ON, RotationStatus.OFF)
|
|
61
|
+
DRY_ROTARY_ON_BOTTOM = ("Dry Rotary On Bottom", PipeMovementStatus.ON_BOTTOM, PumpStatus.OFF, RotationStatus.ON)
|
|
62
|
+
CIRCULATING_AND_ROTARY_ON_BOTTOM = (
|
|
63
|
+
"Circ. & Rot. On Bottom",
|
|
64
|
+
PipeMovementStatus.ON_BOTTOM,
|
|
65
|
+
PumpStatus.ON,
|
|
66
|
+
RotationStatus.ON,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
IN_SLIPS = ("In Slips", PipeMovementStatus.IN_SLIPS, PumpStatus.OFF, RotationStatus.OFF)
|
|
70
|
+
|
|
71
|
+
UNCLASSIFIED = ("Unclassified", PipeMovementStatus.DRILLING, PumpStatus.OFF, RotationStatus.OFF)
|
|
72
|
+
|
|
73
|
+
def __new__(cls, activity_name: str, pipe_movement_status, pump_status, rotation_status):
|
|
74
|
+
obj = bytes.__new__(cls)
|
|
75
|
+
|
|
76
|
+
obj._value_ = activity_name # using the activity name as the main value
|
|
77
|
+
obj.pipe_movement_status = pipe_movement_status
|
|
78
|
+
obj.pump_status = pump_status
|
|
79
|
+
obj.rotation_status = rotation_status
|
|
80
|
+
|
|
81
|
+
return obj
|
|
82
|
+
|
|
83
|
+
def is_pipe_moving(self) -> bool:
|
|
84
|
+
return self.pipe_movement_status.is_moving()
|
|
85
|
+
|
|
86
|
+
def is_pipe_moving_out(self) -> bool:
|
|
87
|
+
return self.pipe_movement_status.is_moving_out()
|
|
88
|
+
|
|
89
|
+
def is_pipe_moving_in(self) -> bool:
|
|
90
|
+
return self.pipe_movement_status.is_moving_in()
|
|
91
|
+
|
|
92
|
+
def is_pumping(self) -> bool:
|
|
93
|
+
return self.pump_status == PumpStatus.ON
|
|
94
|
+
|
|
95
|
+
def is_rotating(self) -> bool:
|
|
96
|
+
return self.rotation_status == RotationStatus.ON
|
|
97
|
+
|
|
98
|
+
def is_drilling(self) -> bool:
|
|
99
|
+
return self.pipe_movement_status == PipeMovementStatus.DRILLING
|
|
100
|
+
|
|
101
|
+
def is_reaming(self) -> bool:
|
|
102
|
+
"""
|
|
103
|
+
Pipe moves up and down with rotation
|
|
104
|
+
:return:
|
|
105
|
+
"""
|
|
106
|
+
movement_status = self.pipe_movement_status in [
|
|
107
|
+
PipeMovementStatus.PULL_OUT_OF_HOLE,
|
|
108
|
+
PipeMovementStatus.RUN_IN_HOLE,
|
|
109
|
+
]
|
|
110
|
+
rotation_status = self.rotation_status == RotationStatus.ON
|
|
111
|
+
return movement_status and rotation_status
|
|
112
|
+
|
|
113
|
+
def is_tripping(self) -> bool:
|
|
114
|
+
"""
|
|
115
|
+
Pipe moves up and down
|
|
116
|
+
:return:
|
|
117
|
+
"""
|
|
118
|
+
if self == self.RUN_IN_HOLE or self == self.PULL_OUT_OF_HOLE:
|
|
119
|
+
return True
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
def __eq__(self, other):
|
|
123
|
+
return self is other
|
|
124
|
+
|
|
125
|
+
def __ne__(self, other):
|
|
126
|
+
return not self.__eq__(other)
|
|
127
|
+
|
|
128
|
+
def __hash__(self):
|
|
129
|
+
return hash(self.name)
|
|
130
|
+
|
|
131
|
+
def __str__(self):
|
|
132
|
+
return self.value
|