carconnectivity-plugin-database 0.1a16__py3-none-any.whl → 0.1a21__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.

Potentially problematic release.


This version of carconnectivity-plugin-database might be problematic. Click here for more details.

@@ -18,9 +18,11 @@ if TYPE_CHECKING:
18
18
  from sqlalchemy.orm.session import Session
19
19
 
20
20
  from carconnectivity.attributes import EnumAttribute
21
+ from carconnectivity.vehicle import GenericVehicle
21
22
 
22
23
  from carconnectivity.climatization import Climatization
23
24
 
25
+ from carconnectivity_plugins.database.plugin import Plugin
24
26
  from carconnectivity_plugins.database.model.vehicle import Vehicle
25
27
 
26
28
 
@@ -28,19 +30,21 @@ LOG: logging.Logger = logging.getLogger("carconnectivity.plugins.database.agents
28
30
 
29
31
 
30
32
  class ClimatizationAgent(BaseAgent):
31
- def __init__(self, session_factory: scoped_session[Session], vehicle: Vehicle) -> None:
32
- if vehicle is None or vehicle.carconnectivity_vehicle is None:
33
+ def __init__(self, database_plugin: Plugin, session_factory: scoped_session[Session], vehicle: Vehicle, carconnectivity_vehicle: GenericVehicle) -> None:
34
+ if vehicle is None or carconnectivity_vehicle is None:
33
35
  raise ValueError("Vehicle or its carconnectivity_vehicle attribute is None")
36
+ self.database_plugin: Plugin = database_plugin
34
37
  self.session_factory: scoped_session[Session] = session_factory
35
38
  self.vehicle: Vehicle = vehicle
39
+ self.carconnectivity_vehicle: GenericVehicle = carconnectivity_vehicle
36
40
 
37
41
  with self.session_factory() as session:
38
42
  self.last_state: Optional[ClimatizationState] = session.query(ClimatizationState).filter(ClimatizationState.vehicle == vehicle)\
39
43
  .order_by(ClimatizationState.first_date.desc()).first()
40
44
  self.last_state_lock: threading.RLock = threading.RLock()
41
45
 
42
- vehicle.carconnectivity_vehicle.climatization.state.add_observer(self.__on_state_change, Observable.ObserverEvent.UPDATED)
43
- self.__on_state_change(vehicle.carconnectivity_vehicle.climatization.state, Observable.ObserverEvent.UPDATED)
46
+ self.carconnectivity_vehicle.climatization.state.add_observer(self.__on_state_change, Observable.ObserverEvent.UPDATED)
47
+ self.__on_state_change(self.carconnectivity_vehicle.climatization.state, Observable.ObserverEvent.UPDATED)
44
48
  self.session_factory.remove()
45
49
 
46
50
  def __on_state_change(self, element: EnumAttribute[Climatization.ClimatizationState], flags: Observable.ObserverEvent) -> None:
@@ -51,8 +55,9 @@ class ClimatizationAgent(BaseAgent):
51
55
  if self.last_state is not None:
52
56
  self.last_state = session.merge(self.last_state)
53
57
  session.refresh(self.last_state)
54
- if (self.last_state is None or self.last_state.state != element.value) \
55
- and element.last_updated is not None:
58
+ if element.last_updated is not None \
59
+ and (self.last_state is None or (self.last_state.state != element.value
60
+ and element.last_updated > self.last_state.last_date)):
56
61
  new_state: ClimatizationState = ClimatizationState(vin=self.vehicle.vin, first_date=element.last_updated,
57
62
  last_date=element.last_updated, state=element.value)
58
63
  try:
@@ -63,6 +68,7 @@ class ClimatizationAgent(BaseAgent):
63
68
  except DatabaseError as err:
64
69
  session.rollback()
65
70
  LOG.error('DatabaseError while adding climatizationstate for vehicle %s to database: %s', self.vehicle.vin, err)
71
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
66
72
 
67
73
  elif self.last_state is not None and self.last_state.state == element.value and element.last_updated is not None:
68
74
  if self.last_state.last_date is None or element.last_updated > self.last_state.last_date:
@@ -73,4 +79,5 @@ class ClimatizationAgent(BaseAgent):
73
79
  except DatabaseError as err:
74
80
  session.rollback()
75
81
  LOG.error('DatabaseError while updating climatizationstate for vehicle %s in database: %s', self.vehicle.vin, err)
82
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
76
83
  self.session_factory.remove()
@@ -8,7 +8,7 @@ import logging
8
8
  from sqlalchemy.exc import DatabaseError
9
9
 
10
10
  from carconnectivity.observable import Observable
11
- from carconnectivity.drive import GenericDrive, ElectricDrive, CombustionDrive
11
+ from carconnectivity.drive import ElectricDrive, CombustionDrive
12
12
 
13
13
  from carconnectivity_plugins.database.agents.base_agent import BaseAgent
14
14
  from carconnectivity_plugins.database.model.drive_level import DriveLevel
@@ -23,7 +23,9 @@ if TYPE_CHECKING:
23
23
 
24
24
  from carconnectivity.attributes import LevelAttribute, RangeAttribute, EnumAttribute, EnergyAttribute, VolumeAttribute, EnergyConsumptionAttribute, \
25
25
  FuelConsumptionAttribute
26
+ from carconnectivity.drive import GenericDrive
26
27
 
28
+ from carconnectivity_plugins.database.plugin import Plugin
27
29
  from carconnectivity_plugins.database.model.drive import Drive
28
30
 
29
31
 
@@ -31,87 +33,98 @@ LOG: logging.Logger = logging.getLogger("carconnectivity.plugins.database.agents
31
33
 
32
34
 
33
35
  class DriveStateAgent(BaseAgent):
34
- def __init__(self, session_factory: scoped_session[Session], drive: Drive) -> None:
35
- if drive is None or drive.carconnectivity_drive is None:
36
- raise ValueError("Drive or its carconnectivity_drive attribute is None")
36
+ def __init__(self, database_plugin: Plugin, session_factory: scoped_session[Session], drive: Drive, carconnectivity_drive: GenericDrive) -> None:
37
+ self.database_plugin: Plugin = database_plugin
37
38
  self.session_factory: scoped_session[Session] = session_factory
38
39
  self.drive: Drive = drive
40
+ self.drive_lock: threading.RLock = threading.RLock()
41
+ self.carconnectivity_drive: GenericDrive = carconnectivity_drive
42
+ with self.drive_lock:
43
+ with self.session_factory() as session:
44
+ self.drive = session.merge(self.drive)
45
+ session.refresh(self.drive)
39
46
 
40
- drive.carconnectivity_drive.type.add_observer(self.__on_type_change, Observable.ObserverEvent.VALUE_CHANGED, on_transaction_end=True)
41
- self.type_lock: threading.RLock = threading.RLock()
42
- self.__on_type_change(drive.carconnectivity_drive.type, Observable.ObserverEvent.VALUE_CHANGED)
43
-
44
- drive.carconnectivity_drive.range_wltp.add_observer(self.__on_range_wltp_change, Observable.ObserverEvent.VALUE_CHANGED, on_transaction_end=True)
45
- self.range_wltp_lock: threading.RLock = threading.RLock()
46
- self.__on_range_wltp_change(drive.carconnectivity_drive.range_wltp, Observable.ObserverEvent.VALUE_CHANGED)
47
-
48
- with self.session_factory() as session:
49
- if isinstance(drive.carconnectivity_drive, ElectricDrive):
50
- drive.carconnectivity_drive.battery.total_capacity.add_observer(self.__on_electric_total_capacity_change,
51
- Observable.ObserverEvent.VALUE_CHANGED, on_transaction_end=True)
52
- self.total_capacity_lock: threading.RLock = threading.RLock()
53
- self.__on_electric_total_capacity_change(drive.carconnectivity_drive.battery.total_capacity, Observable.ObserverEvent.VALUE_CHANGED)
54
-
55
- drive.carconnectivity_drive.battery.available_capacity.add_observer(self.__on_electric_available_capacity_change,
56
- Observable.ObserverEvent.VALUE_CHANGED, on_transaction_end=True)
57
- self.available_capacity_lock: threading.RLock = threading.RLock()
58
- self.__on_electric_available_capacity_change(drive.carconnectivity_drive.battery.available_capacity, Observable.ObserverEvent.VALUE_CHANGED)
59
-
60
- self.last_electric_consumption: Optional[DriveConsumption] = session.query(DriveConsumption).filter(DriveConsumption.drive_id == drive.id) \
61
- .order_by(DriveConsumption.first_date.desc()).first()
62
- self.last_electric_consumption_lock: threading.RLock = threading.RLock()
63
- drive.carconnectivity_drive.consumption.add_observer(self.__on_electric_consumption_change,
64
- Observable.ObserverEvent.VALUE_CHANGED)
65
- self.__on_electric_consumption_change(drive.carconnectivity_drive.consumption, Observable.ObserverEvent.UPDATED)
66
-
67
- elif isinstance(drive.carconnectivity_drive, CombustionDrive):
68
- drive.carconnectivity_drive.fuel_tank.available_capacity.add_observer(self.__on_fuel_available_capacity_change,
69
- Observable.ObserverEvent.VALUE_CHANGED, on_transaction_end=True)
70
- self.fuel_available_capacity_lock: threading.RLock = threading.RLock()
71
- self.__on_fuel_available_capacity_change(drive.carconnectivity_drive.fuel_tank.available_capacity, Observable.ObserverEvent.VALUE_CHANGED)
72
-
73
- self.last_fuel_consumption: Optional[DriveConsumption] = session.query(DriveConsumption).filter(DriveConsumption.drive_id == drive.id) \
74
- .order_by(DriveConsumption.first_date.desc()).first()
75
- self.last_fuel_consumption_lock: threading.RLock = threading.RLock()
76
- drive.carconnectivity_drive.consumption.add_observer(self.__on_fuel_consumption_change,
77
- Observable.ObserverEvent.VALUE_CHANGED)
78
- self.__on_fuel_consumption_change(drive.carconnectivity_drive.consumption, Observable.ObserverEvent.UPDATED)
79
-
80
- self.last_level: Optional[DriveLevel] = session.query(DriveLevel).filter(DriveLevel.drive_id == drive.id) \
81
- .order_by(DriveLevel.first_date.desc()).first()
82
- self.last_level_lock: threading.RLock = threading.RLock()
83
- self.last_range: Optional[DriveRange] = session.query(DriveRange).filter(DriveRange.drive_id == drive.id) \
84
- .order_by(DriveRange.first_date.desc()).first()
85
- self.last_range_lock: threading.RLock = threading.RLock()
86
- self.last_range_estimated_full: Optional[DriveRangeEstimatedFull] = session.query(DriveRangeEstimatedFull) \
87
- .filter(DriveRangeEstimatedFull.drive_id == drive.id).order_by(DriveRangeEstimatedFull.first_date.desc()).first()
88
- self.last_range_estimated_full_lock: threading.RLock = threading.RLock()
89
-
90
- drive.carconnectivity_drive.level.add_observer(self.__on_level_change, Observable.ObserverEvent.UPDATED)
91
- if drive.carconnectivity_drive.level.enabled:
92
- self.__on_level_change(drive.carconnectivity_drive.level, Observable.ObserverEvent.UPDATED)
93
-
94
- drive.carconnectivity_drive.range.add_observer(self.__on_range_change, Observable.ObserverEvent.UPDATED)
95
- if drive.carconnectivity_drive.range.enabled:
96
- self.__on_range_change(drive.carconnectivity_drive.range, Observable.ObserverEvent.UPDATED)
97
-
98
- drive.carconnectivity_drive.range_estimated_full.add_observer(self.__on_range_estimated_full_change, Observable.ObserverEvent.UPDATED)
99
- if drive.carconnectivity_drive.range_estimated_full.enabled:
100
- self.__on_range_estimated_full_change(drive.carconnectivity_drive.range_estimated_full, Observable.ObserverEvent.UPDATED)
101
- session_factory.remove()
47
+ if self.drive is None or self.carconnectivity_drive is None:
48
+ raise ValueError("Drive or its carconnectivity_drive attribute is None")
49
+
50
+ self.carconnectivity_drive.type.add_observer(self.__on_type_change, Observable.ObserverEvent.VALUE_CHANGED, on_transaction_end=True)
51
+ self.type_lock: threading.RLock = threading.RLock()
52
+ self.__on_type_change(self.carconnectivity_drive.type, Observable.ObserverEvent.VALUE_CHANGED)
53
+
54
+ self.carconnectivity_drive.range_wltp.add_observer(self.__on_range_wltp_change, Observable.ObserverEvent.VALUE_CHANGED,
55
+ on_transaction_end=True)
56
+ self.range_wltp_lock: threading.RLock = threading.RLock()
57
+ self.__on_range_wltp_change(self.carconnectivity_drive.range_wltp, Observable.ObserverEvent.VALUE_CHANGED)
58
+
59
+ if isinstance(self.carconnectivity_drive, ElectricDrive):
60
+ self.carconnectivity_drive.battery.total_capacity.add_observer(self.__on_electric_total_capacity_change,
61
+ Observable.ObserverEvent.VALUE_CHANGED, on_transaction_end=True)
62
+ self.total_capacity_lock: threading.RLock = threading.RLock()
63
+ self.__on_electric_total_capacity_change(self.carconnectivity_drive.battery.total_capacity, Observable.ObserverEvent.VALUE_CHANGED)
64
+
65
+ self.carconnectivity_drive.battery.available_capacity.add_observer(self.__on_electric_available_capacity_change,
66
+ Observable.ObserverEvent.VALUE_CHANGED, on_transaction_end=True)
67
+ self.available_capacity_lock: threading.RLock = threading.RLock()
68
+ self.__on_electric_available_capacity_change(self.carconnectivity_drive.battery.available_capacity,
69
+ Observable.ObserverEvent.VALUE_CHANGED)
70
+
71
+ self.last_electric_consumption: Optional[DriveConsumption] = session.query(DriveConsumption) \
72
+ .filter(DriveConsumption.drive_id == self.drive.id).order_by(DriveConsumption.first_date.desc()).first()
73
+ self.last_electric_consumption_lock: threading.RLock = threading.RLock()
74
+ self.carconnectivity_drive.consumption.add_observer(self.__on_electric_consumption_change,
75
+ Observable.ObserverEvent.VALUE_CHANGED)
76
+ self.__on_electric_consumption_change(self.carconnectivity_drive.consumption, Observable.ObserverEvent.UPDATED)
77
+
78
+ elif isinstance(self.carconnectivity_drive, CombustionDrive):
79
+ self.carconnectivity_drive.fuel_tank.available_capacity.add_observer(self.__on_fuel_available_capacity_change,
80
+ Observable.ObserverEvent.VALUE_CHANGED, on_transaction_end=True)
81
+ self.fuel_available_capacity_lock: threading.RLock = threading.RLock()
82
+ self.__on_fuel_available_capacity_change(self.carconnectivity_drive.fuel_tank.available_capacity, Observable.ObserverEvent.VALUE_CHANGED)
83
+
84
+ self.last_fuel_consumption: Optional[DriveConsumption] = session.query(DriveConsumption) \
85
+ .filter(DriveConsumption.drive_id == self.drive.id).order_by(DriveConsumption.first_date.desc()).first()
86
+ self.last_fuel_consumption_lock: threading.RLock = threading.RLock()
87
+ self.carconnectivity_drive.consumption.add_observer(self.__on_fuel_consumption_change,
88
+ Observable.ObserverEvent.VALUE_CHANGED)
89
+ self.__on_fuel_consumption_change(self.carconnectivity_drive.consumption, Observable.ObserverEvent.UPDATED)
90
+
91
+ self.last_level: Optional[DriveLevel] = session.query(DriveLevel).filter(DriveLevel.drive_id == self.drive.id) \
92
+ .order_by(DriveLevel.first_date.desc()).first()
93
+ self.last_level_lock: threading.RLock = threading.RLock()
94
+ self.last_range: Optional[DriveRange] = session.query(DriveRange).filter(DriveRange.drive_id == self.drive.id) \
95
+ .order_by(DriveRange.first_date.desc()).first()
96
+ self.last_range_lock: threading.RLock = threading.RLock()
97
+ self.last_range_estimated_full: Optional[DriveRangeEstimatedFull] = session.query(DriveRangeEstimatedFull) \
98
+ .filter(DriveRangeEstimatedFull.drive_id == self.drive.id).order_by(DriveRangeEstimatedFull.first_date.desc()).first()
99
+ self.last_range_estimated_full_lock: threading.RLock = threading.RLock()
100
+
101
+ if self.carconnectivity_drive is not None:
102
+ self.carconnectivity_drive.level.add_observer(self.__on_level_change, Observable.ObserverEvent.UPDATED)
103
+ if self.carconnectivity_drive.level.enabled:
104
+ self.__on_level_change(self.carconnectivity_drive.level, Observable.ObserverEvent.UPDATED)
105
+
106
+ self.carconnectivity_drive.range.add_observer(self.__on_range_change, Observable.ObserverEvent.UPDATED)
107
+ if self.carconnectivity_drive.range.enabled:
108
+ self.__on_range_change(self.carconnectivity_drive.range, Observable.ObserverEvent.UPDATED)
109
+
110
+ self.carconnectivity_drive.range_estimated_full.add_observer(self.__on_range_estimated_full_change, Observable.ObserverEvent.UPDATED)
111
+ if self.carconnectivity_drive.range_estimated_full.enabled:
112
+ self.__on_range_estimated_full_change(self.carconnectivity_drive.range_estimated_full, Observable.ObserverEvent.UPDATED)
113
+ session_factory.remove()
102
114
 
103
115
  def __on_level_change(self, element: LevelAttribute, flags: Observable.ObserverEvent) -> None:
104
116
  del flags
105
117
  if element.enabled:
106
- with self.last_level_lock:
118
+ with self.last_level_lock, self.drive_lock:
107
119
  with self.session_factory() as session:
108
120
  self.drive = session.merge(self.drive)
109
121
  session.refresh(self.drive)
110
122
  if self.last_level is not None:
111
123
  self.last_level = session.merge(self.last_level)
112
124
  session.refresh(self.last_level)
113
- if (self.last_level is None or self.last_level.level != element.value) \
114
- and element.last_updated is not None:
125
+ if element.last_updated is not None \
126
+ and (self.last_level is None or (self.last_level.level != element.value
127
+ and element.last_updated > self.last_level.last_date)):
115
128
  new_level: DriveLevel = DriveLevel(drive_id=self.drive.id, first_date=element.last_updated, last_date=element.last_updated,
116
129
  level=element.value)
117
130
  try:
@@ -122,6 +135,7 @@ class DriveStateAgent(BaseAgent):
122
135
  except DatabaseError as err:
123
136
  session.rollback()
124
137
  LOG.error('DatabaseError while adding level for drive %s to database: %s', self.drive.id, err)
138
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
125
139
  elif self.last_level is not None and self.last_level.level == element.value \
126
140
  and element.last_updated is not None:
127
141
  if self.last_level.last_date is None or element.last_updated > self.last_level.last_date:
@@ -132,20 +146,22 @@ class DriveStateAgent(BaseAgent):
132
146
  except DatabaseError as err:
133
147
  session.rollback()
134
148
  LOG.error('DatabaseError while updating level for drive %s in database: %s', self.drive.id, err)
149
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
135
150
  self.session_factory.remove()
136
151
 
137
152
  def __on_range_change(self, element: RangeAttribute, flags: Observable.ObserverEvent) -> None:
138
153
  del flags
139
154
  if element.enabled:
140
- with self.last_range_lock:
155
+ with self.last_range_lock, self.drive_lock:
141
156
  with self.session_factory() as session:
142
157
  self.drive = session.merge(self.drive)
143
158
  session.refresh(self.drive)
144
159
  if self.last_range is not None:
145
160
  self.last_range = session.merge(self.last_range)
146
161
  session.refresh(self.last_range)
147
- if (self.last_range is None or self.last_range.range != element.value) \
148
- and element.last_updated is not None:
162
+ if element.last_updated is not None \
163
+ and (self.last_range is None or (self.last_range.range != element.value
164
+ and element.last_updated > self.last_range.last_date)):
149
165
  new_range: DriveRange = DriveRange(drive_id=self.drive.id, first_date=element.last_updated, last_date=element.last_updated,
150
166
  range=element.value)
151
167
  try:
@@ -156,6 +172,7 @@ class DriveStateAgent(BaseAgent):
156
172
  except DatabaseError as err:
157
173
  session.rollback()
158
174
  LOG.error('DatabaseError while adding range for drive %s to database: %s', self.drive.id, err)
175
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
159
176
  elif self.last_range is not None and self.last_range.range == element.value \
160
177
  and element.last_updated is not None:
161
178
  if self.last_range.last_date is None or element.last_updated > self.last_range.last_date:
@@ -166,20 +183,22 @@ class DriveStateAgent(BaseAgent):
166
183
  except DatabaseError as err:
167
184
  session.rollback()
168
185
  LOG.error('DatabaseError while updating range for drive %s in database: %s', self.drive.id, err)
186
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
169
187
  self.session_factory.remove()
170
188
 
171
189
  def __on_range_estimated_full_change(self, element: RangeAttribute, flags: Observable.ObserverEvent) -> None:
172
190
  del flags
173
191
  if element.enabled:
174
- with self.last_range_estimated_full_lock:
192
+ with self.last_range_estimated_full_lock, self.drive_lock:
175
193
  with self.session_factory() as session:
176
194
  self.drive = session.merge(self.drive)
177
195
  session.refresh(self.drive)
178
196
  if self.last_range_estimated_full is not None:
179
197
  self.last_range_estimated_full = session.merge(self.last_range_estimated_full)
180
198
  session.refresh(self.last_range_estimated_full)
181
- if (self.last_range_estimated_full is None or self.last_range_estimated_full.range_estimated_full != element.value) \
182
- and element.last_updated is not None:
199
+ if element.last_updated is not None \
200
+ and (self.last_range_estimated_full is None or (self.last_range_estimated_full.range_estimated_full != element.value
201
+ and element.last_updated > self.last_range_estimated_full.last_date)):
183
202
  new_range: DriveRangeEstimatedFull = DriveRangeEstimatedFull(drive_id=self.drive.id, first_date=element.last_updated,
184
203
  last_date=element.last_updated, range_estimated_full=element.value)
185
204
  try:
@@ -190,6 +209,7 @@ class DriveStateAgent(BaseAgent):
190
209
  except DatabaseError as err:
191
210
  session.rollback()
192
211
  LOG.error('DatabaseError while adding range_estimated_full for drive %s to database: %s', self.drive.id, err)
212
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
193
213
  elif self.last_range_estimated_full is not None and self.last_range_estimated_full.range_estimated_full == element.value \
194
214
  and element.last_updated is not None:
195
215
  if self.last_range_estimated_full.last_date is None or element.last_updated > self.last_range_estimated_full.last_date:
@@ -200,75 +220,102 @@ class DriveStateAgent(BaseAgent):
200
220
  except DatabaseError as err:
201
221
  session.rollback()
202
222
  LOG.error('DatabaseError while updating range_estimated_full for drive %s in database: %s', self.drive.id, err)
223
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
203
224
  self.session_factory.remove()
204
225
 
205
226
  def __on_type_change(self, element: EnumAttribute[GenericDrive.Type], flags: Observable.ObserverEvent) -> None:
206
227
  del flags
207
- with self.type_lock:
228
+ with self.type_lock, self.drive_lock:
208
229
  with self.session_factory() as session:
209
230
  self.drive = session.merge(self.drive)
210
231
  session.refresh(self.drive)
211
232
  if element.enabled and element.value is not None and self.drive.type != element.value:
212
- self.drive.type = element.value
213
- session.commit()
233
+ try:
234
+ self.drive.type = element.value
235
+ session.commit()
236
+ except DatabaseError as err:
237
+ session.rollback()
238
+ LOG.error('DatabaseError while updating type for drive %s to database: %s', self.drive.id, err)
239
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
214
240
  self.session_factory.remove()
215
241
 
216
242
  def __on_electric_total_capacity_change(self, element: EnergyAttribute, flags: Observable.ObserverEvent) -> None:
217
243
  del flags
218
- with self.total_capacity_lock:
244
+ with self.total_capacity_lock, self.drive_lock:
219
245
  with self.session_factory() as session:
220
246
  self.drive = session.merge(self.drive)
221
247
  session.refresh(self.drive)
222
248
  if element.enabled and element.value is not None and self.drive.capacity_total != element.value:
223
- self.drive.capacity_total = element.value
224
- session.commit()
249
+ try:
250
+ self.drive.capacity_total = element.value
251
+ session.commit()
252
+ except DatabaseError as err:
253
+ session.rollback()
254
+ LOG.error('DatabaseError while updating total capacity for drive %s to database: %s', self.drive.id, err)
255
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
225
256
  self.session_factory.remove()
226
257
 
227
258
  def __on_electric_available_capacity_change(self, element: EnergyAttribute, flags: Observable.ObserverEvent) -> None:
228
259
  del flags
229
- with self.available_capacity_lock:
260
+ with self.available_capacity_lock, self.drive_lock:
230
261
  with self.session_factory() as session:
231
262
  self.drive = session.merge(self.drive)
232
263
  session.refresh(self.drive)
233
264
  if element.enabled and element.value is not None and self.drive.capacity != element.value:
234
- self.drive.capacity = element.value
235
- session.commit()
265
+ try:
266
+ self.drive.capacity = element.value
267
+ session.commit()
268
+ except DatabaseError as err:
269
+ session.rollback()
270
+ LOG.error('DatabaseError while updating available capacity for drive %s to database: %s', self.drive.id, err)
271
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
236
272
  self.session_factory.remove()
237
273
 
238
274
  def __on_range_wltp_change(self, element: RangeAttribute, flags: Observable.ObserverEvent) -> None:
239
275
  del flags
240
- with self.range_wltp_lock:
276
+ with self.range_wltp_lock, self.drive_lock:
241
277
  with self.session_factory() as session:
242
278
  self.drive = session.merge(self.drive)
243
279
  session.refresh(self.drive)
244
280
  if element.enabled and element.value is not None and self.drive.wltp_range != element.value:
245
- self.drive.wltp_range = element.value
246
- session.commit()
281
+ try:
282
+ self.drive.wltp_range = element.value
283
+ session.commit()
284
+ except DatabaseError as err:
285
+ session.rollback()
286
+ LOG.error('DatabaseError while updating WLTP range for drive %s to database: %s', self.drive.id, err)
287
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
247
288
  self.session_factory.remove()
248
289
 
249
290
  def __on_fuel_available_capacity_change(self, element: VolumeAttribute, flags: Observable.ObserverEvent) -> None:
250
291
  del flags
251
- with self.fuel_available_capacity_lock:
292
+ with self.fuel_available_capacity_lock, self.drive_lock:
252
293
  with self.session_factory() as session:
253
294
  self.drive = session.merge(self.drive)
254
295
  session.refresh(self.drive)
255
296
  if element.enabled and element.value is not None and self.drive.capacity != element.value:
256
- self.drive.capacity = element.value
257
- session.commit()
297
+ try:
298
+ self.drive.capacity = element.value
299
+ session.commit()
300
+ except DatabaseError as err:
301
+ session.rollback()
302
+ LOG.error('DatabaseError while updating available capacity for drive %s to database: %s', self.drive.id, err)
303
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
258
304
  self.session_factory.remove()
259
305
 
260
306
  def __on_electric_consumption_change(self, element: EnergyConsumptionAttribute, flags: Observable.ObserverEvent) -> None:
261
307
  del flags
262
308
  if element.enabled:
263
- with self.last_electric_consumption_lock:
309
+ with self.last_electric_consumption_lock, self.drive_lock:
264
310
  with self.session_factory() as session:
265
311
  self.drive = session.merge(self.drive)
266
312
  session.refresh(self.drive)
267
313
  if self.last_electric_consumption is not None:
268
314
  self.last_electric_consumption = session.merge(self.last_electric_consumption)
269
315
  session.refresh(self.last_electric_consumption)
270
- if (self.last_electric_consumption is None or self.last_electric_consumption.consumption != element.value) \
271
- and element.last_updated is not None:
316
+ if element.last_updated is not None \
317
+ and (self.last_electric_consumption is None or (self.last_electric_consumption.consumption != element.value
318
+ and element.last_updated > self.last_electric_consumption.last_date)):
272
319
  new_level: DriveConsumption = DriveConsumption(drive_id=self.drive.id, first_date=element.last_updated, last_date=element.last_updated,
273
320
  consumption=element.value)
274
321
  try:
@@ -279,6 +326,7 @@ class DriveStateAgent(BaseAgent):
279
326
  except DatabaseError as err:
280
327
  session.rollback()
281
328
  LOG.error('DatabaseError while adding consumption for drive %s to database: %s', self.drive.id, err)
329
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
282
330
  elif self.last_electric_consumption is not None and self.last_electric_consumption.consumption == element.value \
283
331
  and element.last_updated is not None:
284
332
  if self.last_electric_consumption.last_date is None or element.last_updated > self.last_electric_consumption.last_date:
@@ -289,20 +337,22 @@ class DriveStateAgent(BaseAgent):
289
337
  except DatabaseError as err:
290
338
  session.rollback()
291
339
  LOG.error('DatabaseError while updating consumption for drive %s in database: %s', self.drive.id, err)
340
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
292
341
  self.session_factory.remove()
293
342
 
294
343
  def __on_fuel_consumption_change(self, element: FuelConsumptionAttribute, flags: Observable.ObserverEvent) -> None:
295
344
  del flags
296
345
  if element.enabled:
297
- with self.last_fuel_consumption_lock:
346
+ with self.last_fuel_consumption_lock, self.drive_lock:
298
347
  with self.session_factory() as session:
299
348
  self.drive = session.merge(self.drive)
300
349
  session.refresh(self.drive)
301
350
  if self.last_fuel_consumption is not None:
302
351
  self.last_fuel_consumption = session.merge(self.last_fuel_consumption)
303
352
  session.refresh(self.last_fuel_consumption)
304
- if (self.last_fuel_consumption is None or self.last_fuel_consumption.consumption != element.value) \
305
- and element.last_updated is not None:
353
+ if element.last_updated is not None \
354
+ and (self.last_fuel_consumption is None or (self.last_fuel_consumption.consumption != element.value
355
+ and element.last_updated > self.last_fuel_consumption.last_date)):
306
356
  new_level: DriveConsumption = DriveConsumption(drive_id=self.drive.id, first_date=element.last_updated, last_date=element.last_updated,
307
357
  consumption=element.value)
308
358
  try:
@@ -313,6 +363,7 @@ class DriveStateAgent(BaseAgent):
313
363
  except DatabaseError as err:
314
364
  session.rollback()
315
365
  LOG.error('DatabaseError while adding consumption for drive %s to database: %s', self.drive.id, err)
366
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
316
367
  elif self.last_fuel_consumption is not None and self.last_fuel_consumption.consumption == element.value \
317
368
  and element.last_updated is not None:
318
369
  if self.last_fuel_consumption.last_date is None or element.last_updated > self.last_fuel_consumption.last_date:
@@ -323,4 +374,5 @@ class DriveStateAgent(BaseAgent):
323
374
  except DatabaseError as err:
324
375
  session.rollback()
325
376
  LOG.error('DatabaseError while updating consumption for drive %s in database: %s', self.drive.id, err)
377
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
326
378
  self.session_factory.remove()
@@ -23,6 +23,7 @@ if TYPE_CHECKING:
23
23
 
24
24
  from carconnectivity.vehicle import GenericVehicle
25
25
 
26
+ from carconnectivity_plugins.database.plugin import Plugin
26
27
  from carconnectivity_plugins.database.model.vehicle import Vehicle
27
28
 
28
29
 
@@ -30,11 +31,13 @@ LOG: logging.Logger = logging.getLogger("carconnectivity.plugins.database.agents
30
31
 
31
32
 
32
33
  class StateAgent(BaseAgent):
33
- def __init__(self, session_factory: scoped_session[Session], vehicle: Vehicle) -> None:
34
- if vehicle is None or vehicle.carconnectivity_vehicle is None:
34
+ def __init__(self, database_plugin: Plugin, session_factory: scoped_session[Session], vehicle: Vehicle, carconnectivity_vehicle: GenericVehicle) -> None:
35
+ if vehicle is None or carconnectivity_vehicle is None:
35
36
  raise ValueError("Vehicle or its carconnectivity_vehicle attribute is None")
37
+ self.database_plugin: Plugin = database_plugin
36
38
  self.session_factory: scoped_session[Session] = session_factory
37
39
  self.vehicle: Vehicle = vehicle
40
+ self.carconnectivity_vehicle: GenericVehicle = carconnectivity_vehicle
38
41
 
39
42
  with self.session_factory() as session:
40
43
  self.last_state: Optional[State] = session.query(State).filter(State.vehicle == vehicle).order_by(State.first_date.desc()).first()
@@ -48,14 +51,14 @@ class StateAgent(BaseAgent):
48
51
  .order_by(OutsideTemperature.first_date.desc()).first()
49
52
  self.last_outside_temperature_lock: threading.RLock = threading.RLock()
50
53
 
51
- vehicle.carconnectivity_vehicle.state.add_observer(self.__on_state_change, Observable.ObserverEvent.UPDATED)
52
- self.__on_state_change(vehicle.carconnectivity_vehicle.state, Observable.ObserverEvent.UPDATED)
54
+ self.carconnectivity_vehicle.state.add_observer(self.__on_state_change, Observable.ObserverEvent.UPDATED)
55
+ self.__on_state_change(self.carconnectivity_vehicle.state, Observable.ObserverEvent.UPDATED)
53
56
 
54
- vehicle.carconnectivity_vehicle.connection_state.add_observer(self.__on_connection_state_change, Observable.ObserverEvent.UPDATED)
55
- self.__on_connection_state_change(vehicle.carconnectivity_vehicle.connection_state, Observable.ObserverEvent.UPDATED)
57
+ self.carconnectivity_vehicle.connection_state.add_observer(self.__on_connection_state_change, Observable.ObserverEvent.UPDATED)
58
+ self.__on_connection_state_change(self.carconnectivity_vehicle.connection_state, Observable.ObserverEvent.UPDATED)
56
59
 
57
- vehicle.carconnectivity_vehicle.outside_temperature.add_observer(self.__on_outside_temperature_change, Observable.ObserverEvent.UPDATED)
58
- self.__on_outside_temperature_change(vehicle.carconnectivity_vehicle.outside_temperature, Observable.ObserverEvent.UPDATED)
60
+ self.carconnectivity_vehicle.outside_temperature.add_observer(self.__on_outside_temperature_change, Observable.ObserverEvent.UPDATED)
61
+ self.__on_outside_temperature_change(self.carconnectivity_vehicle.outside_temperature, Observable.ObserverEvent.UPDATED)
59
62
  self.session_factory.remove()
60
63
 
61
64
  def __on_state_change(self, element: EnumAttribute[GenericVehicle.State], flags: Observable.ObserverEvent) -> None:
@@ -66,8 +69,9 @@ class StateAgent(BaseAgent):
66
69
  if self.last_state is not None:
67
70
  self.last_state = session.merge(self.last_state)
68
71
  session.refresh(self.last_state)
69
- if (self.last_state is None or self.last_state.state != element.value) \
70
- and element.last_updated is not None:
72
+ if element.last_updated is not None \
73
+ and (self.last_state is None or (self.last_state.state != element.value
74
+ and element.last_updated > self.last_state.last_date)):
71
75
  new_state: State = State(vin=self.vehicle.vin, first_date=element.last_updated, last_date=element.last_updated, state=element.value)
72
76
  try:
73
77
  session.add(new_state)
@@ -77,6 +81,7 @@ class StateAgent(BaseAgent):
77
81
  except DatabaseError as err:
78
82
  session.rollback()
79
83
  LOG.error('DatabaseError while adding state for vehicle %s to database: %s', self.vehicle.vin, err)
84
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
80
85
 
81
86
  elif self.last_state is not None and self.last_state.state == element.value and element.last_updated is not None:
82
87
  if self.last_state.last_date is None or element.last_updated > self.last_state.last_date:
@@ -87,6 +92,7 @@ class StateAgent(BaseAgent):
87
92
  except DatabaseError as err:
88
93
  session.rollback()
89
94
  LOG.error('DatabaseError while updating state for vehicle %s in database: %s', self.vehicle.vin, err)
95
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
90
96
  self.session_factory.remove()
91
97
 
92
98
  def __on_connection_state_change(self, element: EnumAttribute[GenericVehicle.ConnectionState], flags: Observable.ObserverEvent) -> None:
@@ -97,8 +103,9 @@ class StateAgent(BaseAgent):
97
103
  if self.last_connection_state is not None:
98
104
  self.last_connection_state = session.merge(self.last_connection_state)
99
105
  session.refresh(self.last_connection_state)
100
- if (self.last_connection_state is None or self.last_connection_state.connection_state != element.value) \
101
- and element.last_updated is not None:
106
+ if element.last_updated is not None \
107
+ and (self.last_connection_state is None or (self.last_connection_state.connection_state != element.value
108
+ and element.last_updated > self.last_connection_state.last_date)):
102
109
  new_connection_state: ConnectionState = ConnectionState(vin=self.vehicle.vin, first_date=element.last_updated,
103
110
  last_date=element.last_updated, connection_state=element.value)
104
111
  try:
@@ -109,6 +116,7 @@ class StateAgent(BaseAgent):
109
116
  except DatabaseError as err:
110
117
  session.rollback()
111
118
  LOG.error('DatabaseError while adding connection state for vehicle %s to database: %s', self.vehicle.vin, err)
119
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
112
120
  elif self.last_connection_state is not None and self.last_connection_state.connection_state == element.value \
113
121
  and element.last_updated is not None:
114
122
  if self.last_connection_state.last_date is None or element.last_updated > self.last_connection_state.last_date:
@@ -119,6 +127,7 @@ class StateAgent(BaseAgent):
119
127
  except DatabaseError as err:
120
128
  session.rollback()
121
129
  LOG.error('DatabaseError while updating connection state for vehicle %s in database: %s', self.vehicle.vin, err)
130
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
122
131
  self.session_factory.remove()
123
132
 
124
133
  def __on_outside_temperature_change(self, element: TemperatureAttribute, flags: Observable.ObserverEvent) -> None:
@@ -129,8 +138,9 @@ class StateAgent(BaseAgent):
129
138
  if self.last_outside_temperature is not None:
130
139
  self.last_outside_temperature = session.merge(self.last_outside_temperature)
131
140
  session.refresh(self.last_outside_temperature)
132
- if (self.last_outside_temperature is None or self.last_outside_temperature.outside_temperature != element.value) \
133
- and element.last_updated is not None:
141
+ if element.last_updated is not None \
142
+ and (self.last_outside_temperature is None or (self.last_outside_temperature.outside_temperature != element.value
143
+ and element.last_updated > self.last_outside_temperature.last_date)):
134
144
  new_outside_temperature: OutsideTemperature = OutsideTemperature(vin=self.vehicle.vin, first_date=element.last_updated,
135
145
  last_date=element.last_updated, outside_temperature=element.value)
136
146
  try:
@@ -141,6 +151,7 @@ class StateAgent(BaseAgent):
141
151
  except DatabaseError as err:
142
152
  session.rollback()
143
153
  LOG.error('DatabaseError while adding outside temperature for vehicle %s to database: %s', self.vehicle.vin, err)
154
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
144
155
  elif self.last_outside_temperature is not None and self.last_outside_temperature.outside_temperature == element.value \
145
156
  and element.last_updated is not None:
146
157
  if self.last_outside_temperature.last_date is None or element.last_updated > self.last_outside_temperature.last_date:
@@ -151,4 +162,5 @@ class StateAgent(BaseAgent):
151
162
  except DatabaseError as err:
152
163
  session.rollback()
153
164
  LOG.error('DatabaseError while updating outside temperature for vehicle %s in database: %s', self.vehicle.vin, err)
165
+ self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
154
166
  self.session_factory.remove()