Compare commits

...

8 Commits

Author SHA1 Message Date
peterlu14 99e9b84bde - Add device mapping memory board No. 2024-08-09 17:44:16 +08:00
peterlu14 1f0ec7a350 - Add device attributes alert & alert_time
- Add reset_trigger function with parameter device
- Add api reset_trigger and device_alert to handle alerting problems
2024-04-12 17:37:23 +08:00
peterlu14 b7a78a229d -fix trigger time equal 0 error 2023-11-21 15:59:24 +08:00
peterlu14 10d563fca0 - fix len(c) is equal to zero 2023-11-16 14:19:42 +08:00
peterlu14 626ce1e2f5 -change test to device id 2023-11-15 15:17:27 +08:00
peterlu14 fd91546b8a -fix library 2023-07-18 18:36:39 +08:00
peterlu14 8feab043d4 -add trigger reset api
-change threshold to % & trigger to time
2023-07-18 11:13:30 +08:00
peterlu14 8d95fd3d5d -update threshold then send message 2023-07-14 09:54:58 +08:00
11 changed files with 192 additions and 19 deletions
+6
View File
@@ -1595,6 +1595,12 @@ class ControlAPI(metaclass=Router):
def show_device_data(self, device) -> bool: def show_device_data(self, device) -> bool:
raise NotImplementedError() raise NotImplementedError()
def reset_trigger(self, device) -> bool:
raise NotImplementedError()
def device_alert(self, device) -> bool:
raise NotImplementedError()
# noinspection PyAbstractClass # noinspection PyAbstractClass
class ControlClient(SocketClient, ControlAPI, metaclass=SocketClientMacro(ControlAPI)): class ControlClient(SocketClient, ControlAPI, metaclass=SocketClientMacro(ControlAPI)):
"""Connect to controller server through the socket. """Connect to controller server through the socket.
+9
View File
@@ -139,6 +139,12 @@ class DataAPI(metaclass=abc.ABCMeta):
:param device: device ID :param device: device ID
""" """
pass pass
@abc.abstractmethod
def reset_trigger(self, device):
"""reset trigger
"""
pass
# noinspection PyAbstractClass # noinspection PyAbstractClass
@@ -204,6 +210,9 @@ class DataClient(SocketClient, DataAPI, metaclass=SocketClientMacro(DataAPI)):
def show_data(self, device: int): def show_data(self, device: int):
self.send_command('show_data', device) self.send_command('show_data', device)
def reset_trigger(self, device):
self.send_command('reset_trigger', device)
@staticmethod @staticmethod
def _to_device_id(*device: Union[int, Device]) -> Tuple[int, ...]: def _to_device_id(*device: Union[int, Device]) -> Tuple[int, ...]:
+24 -11
View File
@@ -2611,21 +2611,34 @@ class CC2650MultiMasterCentralDevice(CC2650MasterDevice, Synchronized):
master = None master = None
for master in self._foreach_empty_master(): # for master in self._foreach_empty_master():
m = self._cc2650[master] # m = self._cc2650[master]
d = self._device[master] # d = self._device[master]
if direct_connect is True and d is None: # if direct_connect is True and d is None:
break # break
elif response in m.found() and d is None: # elif response in m.found() and d is None:
break # break
# else:
# if master is None:
# raise RuntimeError('cannot connect any more device')
# else:
# raise RuntimeError('rescan please')
mac_address = address_str(response.mac_address)
if mac_address == 'A4:DA:32:D4:E6:CD':
master = 4
elif mac_address == 'A4:DA:32:D4:E8:5F':
master = 5
elif mac_address == 'A4:DA:32:D4:E7:E5':
master = 6
elif mac_address == 'A4:DA:32:D4:EF:D8':
master = 7
else: else:
if master is None: master = 4
raise RuntimeError('cannot connect any more device')
else:
raise RuntimeError('rescan please')
# #
self.log_verbose('use', master) self.log_verbose('use', master)
+21 -2
View File
@@ -968,11 +968,10 @@ class CompletedDevice(Device):
""" """
__slots__ = ('_master', '_device_id', '_device', '_library', '_context', __slots__ = ('_master', '_device_id', '_device', '_library', '_context',
'_parameter', '_configuration', '_lock', '_feature_mask', '_parameter', '_configuration', '_lock', '_feature_mask',
'_cache_battery', '_cache_battery_timestamp', '_coeff', '_status', '_occupied_by_project') '_cache_battery', '_cache_battery_timestamp', '_coeff', '_status', '_occupied_by_project', '_alert', '_alert_time')
def __init__(self, master: MasterDevice, library: DeviceLibrary, device_id: int, device: Device): def __init__(self, master: MasterDevice, library: DeviceLibrary, device_id: int, device: Device):
""" """
:param master: owner master device :param master: owner master device
:param library: device library :param library: device library
:param device_id: device id :param device_id: device id
@@ -1009,6 +1008,8 @@ class CompletedDevice(Device):
self._cache_battery: Optional[int] = None self._cache_battery: Optional[int] = None
self._cache_battery_timestamp: Optional[float] = None self._cache_battery_timestamp: Optional[float] = None
self._coeff = b'' self._coeff = b''
self._alert = False
self._alert_time = None
# device property for information # device property for information
@@ -1092,6 +1093,22 @@ class CompletedDevice(Device):
def occupied_by_project(self, new_occupied_by_project): def occupied_by_project(self, new_occupied_by_project):
self._occupied_by_project = new_occupied_by_project self._occupied_by_project = new_occupied_by_project
@property
def alert(self) -> str:
return self._alert
@alert.setter
def alert(self, new_alert) -> str:
self._alert = new_alert
@property
def alert_time(self) -> str:
return self._alert_time
@alert_time.setter
def alert_time(self, new_alert_time) -> str:
self._alert_time = new_alert_time
# device parameter getter/setter # device parameter getter/setter
def parameters(self) -> List[str]: def parameters(self) -> List[str]:
@@ -1315,6 +1332,8 @@ class CompletedDevice(Device):
'library_version': str(self._library.version), 'library_version': str(self._library.version),
'occupied_by_project': self._occupied_by_project, 'occupied_by_project': self._occupied_by_project,
'configuration': self.configuration.as_json(list_hide=True), 'configuration': self.configuration.as_json(list_hide=True),
'alert': self.alert,
'alert_time': str(self.alert_time),
} }
def __repr__(self): def __repr__(self):
+33 -4
View File
@@ -22,6 +22,8 @@ import random
from copy import copy from copy import copy
import numpy as np
def json_stringify(o) -> str: def json_stringify(o) -> str:
return _json_stringify(o, separators=(',', ':')) return _json_stringify(o, separators=(',', ':'))
@@ -1026,7 +1028,8 @@ class RecordingFileWriter:
'_mini_scale_list', '_time_real_time', '_data_rl', '_data_db', '_mini_scale_list', '_time_real_time', '_data_rl', '_data_db',
'_raw_save', '_mini_save', '_data_time_ch', '_data_value_ch_for_rl', '_raw_save', '_mini_save', '_data_time_ch', '_data_value_ch_for_rl',
'_data_time_ch_for_rl', '_device_id', '_send_data', '_data_mqtt_ch', '_id_db_save', '_raw_create_not_done', '_data_time_ch_for_rl', '_device_id', '_send_data', '_data_mqtt_ch', '_id_db_save', '_raw_create_not_done',
'_mini_create_not_done') '_mini_create_not_done', '_trigger_time', '_trigger_time_save', '_threshold', '_threshold_percent', '_first_average')
def __init__(self, meta: RecordingMetaFile, device_id, database = None): def __init__(self, meta: RecordingMetaFile, device_id, database = None):
self._meta = meta self._meta = meta
@@ -1110,6 +1113,11 @@ class RecordingFileWriter:
self._raw_create_not_done = True self._raw_create_not_done = True
self._mini_create_not_done = True self._mini_create_not_done = True
self._trigger_time = 0
self._trigger_time_save = 0
self._threshold = 0
self._threshold_percent = 0
self._first_average = 0
@property @property
def meta_file(self) -> RecordingMetaFile: def meta_file(self) -> RecordingMetaFile:
@@ -1235,9 +1243,21 @@ class RecordingFileWriter:
ret.append(str(_max)) ret.append(str(_max))
return ret return ret
def get_data_iter(self, d, mqtt_thread): def get_data_iter(self, d, mqtt_thread, mqtt_thread_1):
# print('****d size', d.data_size) # print('****d size', d.data_size)
for t, c, v in d.entry_iter(): for t, c, v in d.entry_iter():
if c == 2:
# print(t,c,v, self._threshold, self._trigger_time)
# print(time(), time() - self._trigger_time)
if v > self._threshold:
self._trigger_time_save = time()
if (time() - self._trigger_time_save) > self._trigger_time and self._trigger_time_save != 0 and self._trigger_time != 0:
self._trigger_time_save = time()
content = {
'header': 'device_alert/0',
'device': self._device_id,
}
mqtt_thread_1.publish('device_alert', json_stringify(content), inter = True)
if c in self._data_db: if c in self._data_db:
### send real-time ### send real-time
if len(self._data_rl[c]) > 0 and self._send_data[c]: if len(self._data_rl[c]) > 0 and self._send_data[c]:
@@ -1330,7 +1350,7 @@ class RecordingFileWriter:
return return
# @calculate_time(1) # @calculate_time(1)
def write(self, data: Union[bytes, RecordingData, List[bytes], List[RecordingData]], mqtt_thread) -> int: def write(self, data: Union[bytes, RecordingData, List[bytes], List[RecordingData]], mqtt_thread, mqtt_thread_1) -> int:
# check size # check size
ths = self.splitting_threshold_size ths = self.splitting_threshold_size
tht = self.splitting_threshold_time tht = self.splitting_threshold_time
@@ -1386,7 +1406,7 @@ class RecordingFileWriter:
'dec': 0, 'dec': 0,
} }
self.get_data_iter(d, mqtt_thread) self.get_data_iter(d, mqtt_thread, mqtt_thread_1)
if len(self._recording_file_dict) > 0: if len(self._recording_file_dict) > 0:
for ch in self._data_db.keys(): for ch in self._data_db.keys():
@@ -1416,6 +1436,15 @@ class RecordingFileWriter:
if len(self._recording_file_dict) > 0: if len(self._recording_file_dict) > 0:
for ch in self._data_db.keys(): for ch in self._data_db.keys():
if self._time_now - self._time[ch] > 5000000: if self._time_now - self._time[ch] > 5000000:
if ch == 2:
if self._first_average == 0:
a = np.array(self._data_db[ch], dtype=np.int64)
b = np.where(a % 2 == 1)[0]
c = a[b]
if len(c) != 0:
self._first_average = sum(c)/ len(c)
# print(c, self._first_average, self._threshold_percent)
self._threshold = self._first_average * (1 - (self._threshold_percent / 100))
if self._recording_file_dict[ch]._status: if self._recording_file_dict[ch]._status:
_data = ' '.join(self._data_db[ch]) _data = ' '.join(self._data_db[ch])
write_sz = self._recording_file_dict[ch].write(_data, self._channel_list) write_sz = self._recording_file_dict[ch].write(_data, self._channel_list)
+4
View File
@@ -717,6 +717,10 @@ class DataServer(SocketServer, DataAPI):
def show_data(self, device): def show_data(self, device):
self._configurations[device].put_rec_queue('show_data') self._configurations[device].put_rec_queue('show_data')
def reset_trigger(self, device):
if self._configurations.get(device, None) != None:
self._configurations[device].put_rec_queue('reset_trigger')
class DataRuntime(metaclass=abc.ABCMeta): class DataRuntime(metaclass=abc.ABCMeta):
__slots__ = ('_server', '_device', '_meta_file', '_data_format', __slots__ = ('_server', '_device', '_meta_file', '_data_format',
+21
View File
@@ -1178,6 +1178,8 @@ class ControlServer(SocketServer, ControlServerAPI):
# unset file info, but keep file path cache # unset file info, but keep file path cache
self.file_manager.unset(device.device_id) self.file_manager.unset(device.device_id)
device.status = 0 device.status = 0
device.alert = False
device.alert_time = None
# server provide method implement : websocket broadcast functions # server provide method implement : websocket broadcast functions
@@ -1398,6 +1400,25 @@ class ControlServer(SocketServer, ControlServerAPI):
if client is not None: if client is not None:
with client: with client:
client.show_data(device) client.show_data(device)
@logging_info
def reset_trigger(self, device) -> bool:
device = self.device_manager.get_device(device)
client = self.data_server.client()
if client is not None:
with client:
device.alert = False
device.alert_time = None
client.reset_trigger(device.device_id)
self.broadcast_command('refresh')
@logging_info
def device_alert(self, device) -> bool:
device = self.device_manager.get_device(device)
device.alert = True
if device.alert_time == None:
device.alert_time = datetime.now()
self.broadcast_command('refresh')
class _RandomCrashThread(ServerThread): class _RandomCrashThread(ServerThread):
def __init__(self): def __init__(self):
+8 -2
View File
@@ -111,6 +111,10 @@ class RecordingProcess(Process):
self._decoder = self.data_format() self._decoder = self.data_format()
self._start_time = time() self._start_time = time()
self._writer = RecordingFileWriter(self._meta_file, self._device, self._database)
self._writer._threshold_percent = self._meta_file.configuration.get_parameter('THRESHOLD')
self._writer._trigger_time = self._meta_file.configuration.get_parameter('TRIGGER_TIME')
def ensure_data_format(self) -> DataDecodeFormat: def ensure_data_format(self) -> DataDecodeFormat:
if isinstance(self._data_format, (str, bytes)): if isinstance(self._data_format, (str, bytes)):
self._data_format = DataDecodeFormat.parse(self._data_format) self._data_format = DataDecodeFormat.parse(self._data_format)
@@ -187,6 +191,8 @@ class RecordingProcess(Process):
return False return False
elif q == 'show_data': elif q == 'show_data':
self._decoder._show_data = not self._decoder._show_data self._decoder._show_data = not self._decoder._show_data
elif q == 'reset_trigger':
self._writer._trigger_time_save = time()
else: else:
self.rec_update() self.rec_update()
self.sync_data(q) self.sync_data(q)
@@ -245,7 +251,8 @@ class RecordingProcess(Process):
if self._writer is not None and len(ret) > 0: if self._writer is not None and len(ret) > 0:
if len(self._writer.channel_list) == 0: if len(self._writer.channel_list) == 0:
self._writer.channels_update(ret[0].channels()) self._writer.channels_update(ret[0].channels())
self._writer.write(ret, self._mqtt_send_data_ch_level) self._writer.write(ret, self._mqtt_send_data_ch_level, self._mqtt_thread)
# print('count', self._writer._count)
# print('write time: ', time() - ctime1) # print('write time: ', time() - ctime1)
# print(ret) # print(ret)
@@ -585,7 +592,6 @@ class RecordingProcess(Process):
self._is_close = False self._is_close = False
if self._mqtt_thread is not None: if self._mqtt_thread is not None:
self._mqtt_thread.start() self._mqtt_thread.start()
self._writer = RecordingFileWriter(self._meta_file, self._device, self._database)
self.routine() self.routine()
def routine(self) -> None: def routine(self) -> None:
@@ -43,6 +43,28 @@
}, },
"value": "VALUE" "value": "VALUE"
}, },
"TRIGGER_TIME": {
"description": "threshold ohm",
"record_meta": true,
"initial": 0,
"domain": [
"TIME_MAX"
],
"value": {
"expression": "VALUE"
}
},
"THRESHOLD": {
"description": "threshold ohm",
"record_meta": true,
"initial": 0,
"domain": [
1000
],
"value": {
"expression": "VALUE"
}
},
"DPV_e_init": { "DPV_e_init": {
"description": "DPV initial voltage ", "description": "DPV initial voltage ",
"record_meta": true, "record_meta": true,
@@ -931,6 +931,28 @@
"ms" "ms"
] ]
}, },
"TRIGGER_TIME": {
"description": "threshold ohm",
"record_meta": true,
"initial": 0,
"domain": [
"TIME_MAX"
],
"value": {
"expression": "VALUE"
}
},
"THRESHOLD": {
"description": "threshold ohm",
"record_meta": true,
"initial": 0,
"domain": [
1000
],
"value": {
"expression": "VALUE"
}
},
"TIME_DURATION": { "TIME_DURATION": {
"description": "Run duration", "description": "Run duration",
"record_meta": true, "record_meta": true,
@@ -931,6 +931,28 @@
"ms" "ms"
] ]
}, },
"TRIGGER_TIME": {
"description": "threshold ohm",
"record_meta": true,
"initial": 0,
"domain": [
"TIME_MAX"
],
"value": {
"expression": "VALUE"
}
},
"THRESHOLD": {
"description": "threshold ohm",
"record_meta": true,
"initial": 0,
"domain": [
1000
],
"value": {
"expression": "VALUE"
}
},
"TIME_DURATION": { "TIME_DURATION": {
"description": "Run duration", "description": "Run duration",
"record_meta": true, "record_meta": true,