Compare commits

..

6 Commits

Author SHA1 Message Date
peterlu14 54f3cdbffa [update] check spi routine 2022-04-08 18:23:20 +08:00
peterlu14 718e7c3245 [update] print message 2022-04-08 18:23:20 +08:00
peterlu14 6427c5ecdb [update] add info of no data time 2022-04-08 18:23:20 +08:00
peterlu14 ff305819df [update] change delta time 2022-04-08 18:23:20 +08:00
peterlu14 f4ac79093d [update] add check spi data 2022-04-08 18:23:20 +08:00
peterlu14 1a688b3585 [update] change queue log 2022-04-08 18:23:20 +08:00
15 changed files with 881 additions and 592 deletions
+8 -8
View File
@@ -253,7 +253,6 @@ class DeviceInternalCommandHandler:
return True
# why inherit MasterDevice ?
class DeviceManager(MasterDevice, Synchronized):
"""device manager. It manager the device libraries, found device, connected device
and the control of the master device.
@@ -268,6 +267,7 @@ class DeviceManager(MasterDevice, Synchronized):
options: DeviceManagerOptions,
handler: Optional[DeviceInternalCommandHandler] = None):
"""
:param options: manager options.
:param handler: device internal command handler
"""
@@ -277,7 +277,6 @@ class DeviceManager(MasterDevice, Synchronized):
if handler is None:
handler = DeviceInternalCommandHandler()
# control server api
self._handler: DeviceInternalCommandHandler = handler
self._options: DeviceManagerOptions = options
@@ -287,9 +286,7 @@ class DeviceManager(MasterDevice, Synchronized):
# hardware
self._interface = options.interface
self._master: Optional[MasterDevice] = None
# memory board
self._centralMaster: Optional[MasterDevice] = None
# connected device
self._device: List[CompletedDevice] = []
# demo device
@@ -339,10 +336,6 @@ class DeviceManager(MasterDevice, Synchronized):
def library_path(self) -> List[Path]:
return self._repository.library_path
def get_device_status(self, mac_address) -> Optional[DeviceLibrary]:
device = self.get_device(mac_address)
return device.status
@logging_info
def reload_library(self):
"""reload device library"""
@@ -569,6 +562,7 @@ class DeviceManager(MasterDevice, Synchronized):
@logging_verbose
def get_device(self, device: Union[int, DeviceInfo]) -> Optional[CompletedDevice]:
print('get_device', device)
"""get connected device
:param device: device ID or response info
@@ -1030,9 +1024,15 @@ class DeviceManager(MasterDevice, Synchronized):
raise RuntimeError(DEVICE_NOT_FOUND, device)
if DeviceCommonInstruction(instruction) is not None:
# print('deviceCommonInstr')
# print(instruction)
device.call_instruction(instruction)
elif instruction in device.library.instruction_table:
# print('instruction')
# print(instruction)
device.call_instruction(instruction)
else:
raise RuntimeError(INSTRUCTION_UNKNOWN, instruction)
+2 -4
View File
@@ -1268,15 +1268,14 @@ class InstructionTable(DictNode['Instruction']):
instruction: str):
"""call instruction. chain context with :class:`DeviceInternalInstructionScope`
:param master: DeviceManager
:param device: CompletedDevice
:param master: master device
:param device: device
:param context: scope
:param instruction: instruction name
:raises InstructionNotFound: If instruction not found.
"""
try:
# ex. start
ins = self[instruction]
except KeyError as e:
@@ -1285,7 +1284,6 @@ class InstructionTable(DictNode['Instruction']):
else:
context = context.chain(InstructionTableScope(self))
# ex. _data_format, _sync, _notify
for single in ins.eval_instruction(context):
if isinstance(single, InternalInstruction):
if not single.handle_internal_instruction(master, device, context):
+2 -18
View File
@@ -966,7 +966,7 @@ class CompletedDevice(Device):
"""
__slots__ = ('_master', '_device_id', '_device', '_library', '_context',
'_parameter', '_configuration', '_lock', '_feature_mask',
'_cache_battery', '_cache_battery_timestamp', '_coeff', '_status')
'_cache_battery', '_cache_battery_timestamp', '_coeff')
def __init__(self, master: MasterDevice, library: DeviceLibrary, device_id: int, device: Device):
"""
@@ -976,15 +976,11 @@ class CompletedDevice(Device):
:param device_id: device id
:param device: slave device implementation
"""
# DeviceManager
self._master = master
self._device = device
self._device_id = device_id
self._library = library
# device status ( 0: idle, 1: working, 2: error, 3: power off)
self._status = 0
# context
s1 = library.constant
s2 = DeviceScope(self)
@@ -1024,10 +1020,6 @@ class CompletedDevice(Device):
def mac_address(self) -> ADDRESS:
return self._device.mac_address
@property
def mac_address_in_str(self) -> str:
return ':'.join('{:02x}'.format(b) for b in self._device.mac_address)
@property
def device(self) -> Device:
return self._device
@@ -1064,18 +1056,11 @@ class CompletedDevice(Device):
return self._library
# device lock
@property
def lock(self) -> DeviceLock:
return self._lock
@property
def status(self) -> int:
return self._status
@status.setter
def status(self, new_status):
self._status = new_status
# device parameter getter/setter
def parameters(self) -> List[str]:
@@ -1276,7 +1261,6 @@ class CompletedDevice(Device):
'device_id': self.device_id,
'device_name': self.device_name,
'device_address': list(self.mac_address),
'device_status': self._status,
'battery': self.battery,
'parent': self.parent,
'recording_file_name': self.recording_file_name,
+62 -20
View File
@@ -1,26 +1,68 @@
class Action():
def __init__(self, action_id, action):
self._id: str = action_id
self._type: str = action['type']
self._target: str = action['target']
self._condition : list[str] = action['condition']
# self._duration = action.get('duration', None)
# self._goto = action.get('goto', None)
# self._cycle = action.get('cycle', None)
@property
def id(self):
return self._id
def __init__(self, task, action_id, action):
self._task = task
self._id:str = action_id
self._type:str = action['type']
self._target:str = action['target']
self._condition_list:list[str] = action['condition']
self._duration = action.get('duration', None)
self._goto = action.get('goto', None)
self._cycle = action.get('cycle', None)
self._instruction = None
self.update_instruction()
@property
def type(self):
return self._type
@property
def target(self):
return self._target
@property
def condition(self):
return self._condition
def update_instruction(self):
instruction_type = self._type
if instruction_type == 'stop':
instruction_type = 'interrupt'
self._instruction = {
"device_instruction": {
"header": "call_instruction",
"device": self._target,
"arguments": {
"instruction": instruction_type,
}
},
"device_parameter": {
"header": "set_multi_parameters",
"device": self._target,
"arguments": {
"parameter": self._task.get_parameter_set_by_device(self._target),
}
},
"device_recording_file_name": {
"header": "update_recording_file_name_info",
"device": self._target,
"arguments": {
"content": self._task.file_name,
}
},
"device_parent": {
"header": "update_parent_info",
"device": self._target,
"arguments": {
"content": self._task.parent
}
}
}
def get_condition_list(self):
return self._condition_list
def get_instruction(self, value):
return self._instruction[value]
def get_instruction_list(self):
instruction_set = {
'start': ['device_parent', 'device_recording_file_name', 'device_parameter', 'device_instruction'],
'stop': ['device_instruction'],
}
return map(self.get_instruction, instruction_set[self._type])
+25 -38
View File
@@ -7,7 +7,6 @@ class Condition():
self._type = condition['type']
self._comparsion = condition['comparsion']
self._value = condition['value']
self._active = False
@property
def id(self):
@@ -25,24 +24,16 @@ class Condition():
def value(self):
return self._value
@id.setter
def id(self, new_id):
self._id = new_id
@type.setter
def type(self, new_type):
self._type = new_type
def set_comparsion(self, comparsion):
self._comparsion = comparsion
@comparsion.setter
def comparsion(self, new_comparsion):
self._comparsion = new_comparsion
def set_type(self, type):
self._type = type
@value.setter
def value(self, new_value):
self._value = new_value
def set_value(self, value):
self._value = value
def compareWith(self, operator, x, y) -> bool:
# print(operator, x, y)
def compareWith(self, operator:str, x, y) -> bool:
cases = {
"equal": lambda a, b: a == b,
"bigger": lambda a, b: a > b,
@@ -56,38 +47,34 @@ class Condition():
return getattr(self, self.type)(**kwargs)
def absolute_time(self, **kwargs):
now = int(time())
time_condition = round(self.datetime_to_timestamp(self.str_to_datetime(self._value)))
now = time()
time_condition = self.datetime_to_timestamp(self.str_to_datetime(self._value))
return self.compareWith(self.comparsion, now, time_condition)
return self.compareWith(self.comparsion, int(now), int(time_condition))
def after_project_run(self, **kwargs):
def relative_time(self, **kwargs):
project_start_time = kwargs['project_start_time']
task_start_time = kwargs['task_start_time']
delay_time = kwargs['delay_time']
now = time()
time_diff = int(time() - project_start_time - delay_time)
return self.compareWith(self.comparsion, time_diff, int(self._value))
def after_task_run(self, **kwargs):
# print('relative_time_from_task', kwargs,kwargs['task_start_time'],kwargs['delay_time'])
if len(kwargs['task_start_time']) == 0:
return False
task_start_time = kwargs['task_start_time'][-1]
delay_time = kwargs['delay_time']
time_diff = int(time() - task_start_time - delay_time)
# print('time_diff', time_diff)
time_diff = int(now - project_start_time - delay_time)
return self.compareWith(self.comparsion, time_diff, int(self._value))
def device(self, **kwargs):
print('device')
def previous_task_done(self, **kwargs):
running_task = kwargs['running_task']
if running_task.status == 2 and self._active == False:
self._active = True
return True
return False
def task_status(self, **kwargs):
print('task_status')
def method_mapping(self, method_name):
methods = {
"relative_time": self.relative_time,
"absolute_time": self.absolute_time,
"device": self.device,
"task_status": self.task_status,
}
return methods[method_name]
def str_to_datetime(self, time_str):
return datetime.strptime(time_str,'%Y-%m-%dT%H:%M')
-32
View File
@@ -1,32 +0,0 @@
class Instruction():
def __init__(self):
self._instruction_set = {
'set_file_name': {
'method': 'update_recording_file_name_info',
'arguments': ['file_name']
},
'set_parent': {
'method': 'update_parent_info',
'arguments': ['parent']
},
'set_parameter':{
'method': 'set_multi_parameters',
'arguments': ['parameter']
},
'call_instruction': {
'method': 'call_instruction',
'arguments': ['instruction']
},
}
self._start_instruction = list(map(lambda ins: self._instruction_set[ins] ,['set_file_name', 'set_parent', 'set_parameter', 'call_instruction']))
self._stop_instruction = list(map(lambda ins: self._instruction_set[ins] ,['call_instruction']))
@property
def start(self) -> list:
return self._start_instruction
@property
def stop(self) -> list:
return self._stop_instruction
+45 -172
View File
@@ -1,22 +1,14 @@
from .task import Task
from .task_manager import TaskManager
import sys
import json
import threading
from time import time, sleep
import time
from datetime import datetime
from collections import deque
from copy import copy
from uuid import uuid4
from .task import Task
from .task_manager import TaskManager
from .instruction import Instruction
from biopro.device.manager import DeviceManager
from biopro.text import *
key_list = {
'deviceList': 'device',
}
from copy import copy
class Project(threading.Thread):
def __init__(self, project, device_manager: DeviceManager, mqttThread = None, name="project"):
@@ -26,184 +18,65 @@ class Project(threading.Thread):
self._mqtt_thread = mqttThread
self._time_interval = 1
self._start_time = None
self._end_time = None
self._id = None
self._uuid = uuid4()
self._name = None
self._desc = None
self._device = None
self._complete_device = []
self._status = None
self._instruction_set = Instruction()
self._stop_flag = False
self.setup_project(project)
self.setup_device(self._device)
def setup_project(self, project):
for (key, value) in project.items():
if key in key_list.keys():
key = key_list[key]
if key == 'task':
self._task_manager = TaskManager(project['task'])
else:
setattr(self, key, value)
def setup_device(self, device_list):
for device in device_list:
complete_device = self._device_manager.get_device(device['connectDevice']['device_address'])
self._complete_device.append(complete_device)
@property
def id(self) -> int:
return self._id
@id.setter
def id(self, new_id):
self._id = new_id
@property
def uuid(self) -> str:
return self._uuid
@uuid.setter
def uuid(self, new_uuid):
self._uuid = new_uuid
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, new_name):
self._name = new_name
self._task_manager = TaskManager(self._project['taskList'])
self._stop = False
@property
def desc(self) -> str:
return self._desc
@desc.setter
def desc(self, new_desc):
self._desc = new_desc
return self._project['desc']
@property
def status(self) -> str:
return self._status
@status.setter
def status(self, new_status):
self._status = new_status
return self._project['status']
@property
def device(self) -> list:
return self._device
@device.setter
def device(self, new_device):
self._device = new_device
@property
def task_list(self):
return self.task_manager.task_list
return self._project['deviceList']
@property
def mqtt_thread(self):
return self._mqtt_thread
def task(self) -> list:
return self._task_manager.get()
@property
def get_current_task(self) -> str:
return self._task_manager.get_current_task()
def run(self):
self._start_time = time()
self._start_time = time.time()
self.mqtt_thread.broadcast_command('project:' + self._name + ' start_at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])
while not self._stop_flag :
# check running task first
while not self._stop:
# current task
task_check_list = copy(self._task_manager.get_check_task_list())
delay_time = 0
check_list = copy(self._task_manager.check_list)
for task in task_check_list:
# [<match_condition>, ...]
match_condition_list = task.check_condition(project_start_time = self._start_time, delay_time = delay_time)
# [[<match_action>, ...], ...]
match_action_list = task.get_match_action_list(match_condition_list)
for action_list in match_action_list:
for action in action_list:
if action.type == 'start':
task.reset_start_time()
self._task_manager.set_current_task(task)
task.do_action(action)
now = time.time()
while len(task.instruction_list) > 0:
instruction = task.instruction_list.pop(0)
if instruction.get('device', None) != None:
device = self._device_manager.get_device(self.device[instruction['device']]['connectDevice']['device_address'])
for task in check_list:
if task != None:
now = time()
# print('round task', task.name)
# print('running task', self._task_manager.running_task.name, self._task_manager.running_task.status)
# if self._task_manager.prev_task != None:
# print('previous_task', self._task_manager.prev_task.name, self._task_manager.prev_task.status)
if device is None:
raise RuntimeError(DEVICE_NOT_FOUND, device)
match_condition_list = task.check_condition(
project_start_time = self._start_time,
delay_time = delay_time,
running_task= self._task_manager.running_task,
previous_task = self._task_manager.prev_task
)
# print('match_condition_list', match_condition_list)
for condition in match_condition_list:
match_action_list = task.get_match_action(condition.id)
for action in match_action_list:
# print('match_action', action.type, action.target)
if action.type == 'start' and task.status != 1:
self._task_manager.set_running_task(task)
self.mqtt_thread.broadcast_command('project:task ' + task.name + ' start at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])
getattr(device, instruction['header'])(*instruction['arguments'].values())
delay_time += (time.time() - now)
time.sleep(self._time_interval)
device = self._complete_device[action.target]
task_info = task.get_task_info(action)
instruction_set = getattr(self._instruction_set, action.type, None)
# print('instruction_set',instruction_set)
if instruction_set != None:
for instruction in instruction_set:
args = list(map(lambda arg: task_info[arg], instruction['arguments']))
threading.Thread(target=getattr(device, instruction['method'])(*args))
delay_time += (time() - now)
# check task not running then stop
if self.check_running_task_not_run() == True:
self.mqtt_thread.broadcast_command('project:task ' + str(self._task_manager.running_task.name) + ' stop at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])
self._task_manager.running_task.stop()
# check project done then close project
if self.check_project_done() == True:
self.mqtt_thread.broadcast_command('project:project ' + str(self._name) + ' stop at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])
print('project stop at', datetime.now())
self.close()
if self._time_interval - delay_time > 0:
sleep(self._time_interval - delay_time)
def pause(self):
# TODO
pass
def stop(self):
self._stop = True
def close(self):
self._end_time = time()
self._stop_flag = True
def check_project_done(self):
for task in self._task_manager.task_list:
# if task never start or still task is running then reject
if task.status == 1 or task.status == -1:
return False
return True
def check_running_task_not_run(self):
# if no running task
if self._task_manager.running_task == None:
return False
device_list = self._task_manager.running_task.device
for device in device_list:
if self._complete_device[device].status == 1:
return False
return True
def as_json(self):
return {
'id': self._id,
'name': self._name,
'uuid': self._uuid,
'desc': self._desc,
'status': self._status,
'device': self._device,
'task_list': self.task_list,
}
self._stop = True
+1 -1
View File
@@ -18,7 +18,7 @@ class ProjectManager():
new_project = Project(project, self._device_manager, self._mqtt_thread)
self._project_list.append(new_project)
return new_project
def remove(self, index):
self._project_list[index].stop()
del self._project_list[index]
+95 -178
View File
@@ -2,186 +2,112 @@ from typing import List
from .condition import Condition
from .action import Action
from time import time
from datetime import datetime
key_list = {
'deviceList': 'device',
'parameterSet': 'parameter_set'
}
class Task:
def __init__(self, task):
self._id = None
self._name = None
self._parent = None
self._cycle = None
self._device = None
self._event = None
self._trigger = None
self._parameter_set = None
self._condition = None
self._action = None
# -1: initial 0: start(idle), 1: running, 2: close(idle)
self._status = 0
self._next = None
self._task = task
self._condition_list: List[Condition] = []
self._action_list: List[Action] = []
self._instruction_list = []
self._record_list = []
self._condition_list:List[Condition] = []
self._action_list:List[Action] = []
self._start_time = []
self._start_time = time()
self._idle_time = []
self._end_time = []
self._period = None
self._next_time = None
self._last_time = None
self.load_task(task)
self._instruction_list = []
self.setup()
def load_task(self, task) -> None:
# update task attribute
for key, value in task.items():
if key in key_list.keys():
key = key_list[key]
if key == 'condition':
self.update_condition_list(value)
if key == 'action':
self.update_action_list(value)
# print(key, value)
setattr(self, key, value)
def setup(self) -> None:
# add condition
for key, condition in self._task['condition'].items():
new_condition = Condition(key, condition)
self._condition_list.append(new_condition)
def update_condition_list(self, condition_dict):
self._condition_list.clear()
for key, condition in condition_dict.items():
self._condition_list.append(Condition(key, condition))
def update_action_list(self, action_dict):
self._action_list.clear()
for key, action in action_dict.items():
self._action_list.append(Action(key, action))
# add action
for key, action in self._task['action'].items():
new_action = Action(self, key, action)
self._action_list.append(new_action)
@property
def id(self) -> str:
return self._id
@id.setter
def id(self, new_id):
self._id = new_id
return self._task['id']
@property
def uuid(self) -> str:
return self._uuid
@uuid.setter
def uuid(self, new_uuid):
self._uuid = new_uuid
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
self._name = new_name
def file_name(self):
return self._task['name']
@property
def parent(self):
if self._parent == None:
return {"folder": [2]}
return self._parent
@parent.setter
def parent(self, new_parent):
self._parent = new_parent
return self._task.get('parent', {"folder": [2]})
@property
def instruction_list(self):
return self._instruction_list
@property
def cycle(self) -> int:
return self._cycle
@cycle.setter
def cycle(self, new_cycle):
self._cycle = new_cycle
@property
def device(self) -> dict:
return self._device
@device.setter
def device(self, new_device):
self._device = new_device
@property
def event(self) -> list:
return self._event
@event.setter
def event(self, new_event):
self._event = new_event
return self._task['cycle']
@property
def trigger(self) -> list:
return self._trigger
def order(self) -> int:
return self._task['order']
@property
def max_cycle(self) -> int:
return self._task['max_cycle']
@trigger.setter
def trigger(self, new_trigger):
self._trigger = new_trigger
@property
def devices(self) -> dict:
return self._task['devices']
@property
def devices_id(self) -> list:
return [device_id for device_id in self.devices]
@property
def events(self) -> list:
return self._task['events']
@property
def triggers(self) -> list:
return self._task['triggers']
@property
def parameter_set(self) -> list:
return self._parameter_set
return self._task['parameterSet']
@parameter_set.setter
def parameter_set(self, new_parameter_set):
self._parameter_set = new_parameter_set
@property
def instructions(self) -> list:
return self._task['instructions']
@property
def condition(self) -> list:
return self._condition
@condition.setter
def condition(self, new_condition):
self._condition = new_condition
def conditions(self) -> list:
return self._task['conditions']
@property
def action(self) -> list:
return self._action
@action.setter
def action(self, new_action):
self._action = new_action
def actions(self) -> list:
return self._task['actions']
@property
def condition_list(self) -> list:
return self._condition_list
@property
def status(self) -> str:
return self._status
return self._task['status']
@status.setter
def status(self, new_status):
self._status = new_status
@property
def uuid(self) -> str:
return self._task['uuid']
@property
def next(self) -> str:
return self._next
return self._task['next']
@next.setter
def next(self, new_next):
self._next = new_next
@property
def instruction_list(self) -> List:
return self._instruction_list
@property
def start_time(self) -> List:
return self._start_time
@property
def end_time(self) -> List:
return self._end_time
def new_start_time(self):
self._start_time.append(time())
def new_end_time(self):
self._end_time.append(time())
def reset_start_time(self):
self._start_time = time()
def get_parameter_set_by_device(self, device):
return [parameter_set for parameter_set in self.parameter_set.values() if device == parameter_set['target']]
@@ -198,55 +124,46 @@ class Task:
return '|'.join(name_list) if name_or_value == 'name' else '|'.join(value_list)
def run(self):
print(self._name, 'run at', datetime.now())
self._status = 1
self._start_time.append(time())
self._status = 'running'
self._start_time = time()
def pause(self):
self._status = 'pause'
def set_cycle(self, cycle):
self._cycle = cycle
def set_status(self, status):
self._status = status
def start(self):
self.status = 1
self.set_status('running')
def stop(self):
self.status = 2
if self.status == 'running':
self.do_action('stop')
self.set_status('idle')
def done(self):
self.set_status('done')
def end(self):
self.set_status('end')
def get_match_action_list(self, match_condition_list):
return map(lambda condition: [x for x in self._action_list if condition.id in x.get_condition_list()], match_condition_list)
def get_match_action(self, condition_id):
return [action for action in self._action_list if condition_id in action.condition]
def do_action(self, action):
for instruction in action.get_instruction_list():
self._instruction_list.append(instruction)
def status_mapping(self, status):
statuses = {
"start": self.task_start,
"done": self.task_done,
}
return statuses[status]
def check_condition(self, **kwargs):
kwargs['task_start_time'] = self._start_time
return [condition for condition in self._condition_list if condition.match_or_not(**kwargs)]
def get_task_info(self, action):
_type = action.type
if _type == 'stop':
_type = 'interrupt'
return {
'device': action.target,
'parameter': self.get_parameter_set_by_device(action.target),
'file_name': self._name + '-' + str(action.target),
'parent': self.parent,
'instruction': _type
}
def as_json(self):
return {
'id': self.id,
'name': self.name,
'parent': self.parent,
'cycle': self.cycle,
'device': self.device,
'event': self.event,
'trigger': self.trigger,
'parameter_set': self.parameter_set,
'condition': self.condition,
'action': self.action,
'status': self.status,
'next': self.next,
}
return [condition for condition in self._condition_list if condition.match_or_not(**kwargs)]
+33 -61
View File
@@ -1,5 +1,4 @@
from json import loads as json_parse, dumps as _json_stringify
from socket import if_nametoindex
from typing import Dict, List, Optional, Any
from xml.dom.expatbuilder import parseString
import paho.mqtt.client as mqtt
@@ -13,72 +12,45 @@ def json_stringify(o: Any) -> str:
class TaskManager():
def __init__(self, task_list):
self._current_task = None
self._task_list = []
self._next_task = []
self._prev_task = None
self._running_task = None
self.load_task_list(task_list)
self._need_to_check_task_list = []
# self.set_running_task(self._task_list[0])
# self._running_task.run()
self._next_task.append(self._task_list[0])
@property
def task_list(self):
return self._task_list
@property
def prev_task(self):
return self._prev_task
@property
def running_task(self):
return self._running_task
@property
def next_task(self):
return self._next_task
@property
def check_list(self):
return [self._running_task, *self._next_task]
@property
def check_list(self):
return [self._running_task, *self._next_task]
def load_task_list(self, task_list):
for task in task_list:
task = Task(task)
self._task_list.append(task)
self.set_current_task(self._task_list[0])
self._current_task.run()
def set_running_task(self, task):
try:
# if there is task running & same task active ,then reject
if self._running_task != None and self._running_task.uuid == task.uuid:
return False
# save running task
self._prev_task = self._running_task
# clear next task list
self._next_task.clear()
self._running_task = task
self._running_task.run()
if self._prev_task != None:
# if previous task is still running, then need to close
if self._prev_task.status == 1:
self._prev_task.stop()
print('prev', 'run', self._prev_task.name, self._running_task.name)
for task_uuid in self._running_task.next:
_task = next((task for task in self._task_list if task.uuid == task_uuid), None)
if _task != None:
self._next_task.append(_task)
except RuntimeError as e:
print(e)
def remove(self, index):
pass
def get_task(self, task_id):
return self._task_list[task_id]
def get_check_task_list(self) -> List[Task]:
return self._need_to_check_task_list
def get_current_task(self) -> Task:
return self._current_task
def get_next_task(self):
return self._current_task.next
def set_current_task(self, task):
try:
if self._current_task != None and self._current_task.id != task.id:
self._current_task.stop()
self._current_task = task
self._need_to_check_task_list = []
# append current task
self._need_to_check_task_list.append(self._current_task)
# append next task
for task_uuid in self._current_task.next:
task = next((task for task in self._task_list if task.uuid == task_uuid), None)
self._need_to_check_task_list.append(task)
except Exception as e:
print(e)
+17 -3
View File
@@ -9,9 +9,23 @@ _jwt = None
def controller_device_id() -> str:
### device ID
f = open("/sys/class/net/eth0/address", "r")
mac = f.read()[:-1]
return mac
return 'b8:27:eb:52:ea:97'
ret = None
try:
with open('/etc/BPS/device_id') as f:
for line in f:
if line.startswith('#'):
continue
ret = line.strip()
break
except FileNotFoundError:
pass
if ret is None or len(ret) == 0:
return str(uuid.uuid1())
else:
return ret
def get_api_jwt() -> str:
global _jwt
+45 -33
View File
@@ -104,9 +104,18 @@ class DataServer(SocketServer, DataAPI):
self.log_verbose,
'MQTT-data-server')
# api socket
self._queue_spi = queue.Queue()
# self._queue_spi_dict = {}
self._queue_ds_dict = {}
self._queue_db = Queue()
self._queue_msg = Queue()
self._queue_db_error = Queue()
# self._queue_ds = Queue()
# if len(self._queue_spi_dict) == 0:
# self._queue_spi_dict[4] = Queue()
# self._queue_spi_dict[5] = Queue()
# self._queue_spi_dict[6] = Queue()
# self._queue_spi_dict[7] = Queue()
if len(self._queue_ds_dict) == 0:
self._queue_ds_dict[4] = Queue()
@@ -114,9 +123,12 @@ class DataServer(SocketServer, DataAPI):
self._queue_ds_dict[6] = Queue()
self._queue_ds_dict[7] = Queue()
self.database_process = DataBaseProcess('data_server', self.log_verbose, self._queue_db, self._queue_ds_dict, self._queue_msg)
self.database_process = DataBaseProcess('data_server', self.log_verbose, self._queue_db, self._queue_ds_dict, self._queue_db_error)
self.database_process.start()
self._no_data_total_duration = 0
self._recv_memory_cost_time = 0
@logging_info
def setup(self):
self._available_channel.clear()
@@ -137,6 +149,7 @@ class DataServer(SocketServer, DataAPI):
try:
self._stop_runtime()
self.stop_sync()
self._queue_spi.queue.clear()
finally:
self._configurations.clear()
@@ -175,6 +188,9 @@ class DataServer(SocketServer, DataAPI):
self._configurations.clear()
with CacheClient() as client:
client.clear()
# reopen spi, which has close at close()
if spi_mode is None:
spi_mode = self._spi_mode
@@ -404,7 +420,7 @@ class DataServer(SocketServer, DataAPI):
c.join()
self._spi.set_wait_flag(c._device, True)
data_runtime_process = RecordingProcess('data_server', self.log_verbose, device_id,
Queue(), self._queue_ds_dict[int(device_id)], self._queue_msg, meta_file,
Queue(), self._queue_ds_dict[int(device_id)], meta_file,
json_stringify(_device), _parameter, _parent, _recording_file_name,
data_format, self.socket_mqtt_ip, self.socket_mqtt_port,
database = self.database_process )
@@ -605,18 +621,8 @@ class DataServer(SocketServer, DataAPI):
return
def open_cache_client(self):
self.log_verbose('open cache client')
try:
client = CacheClient()
client.open_socket()
except RuntimeError as e:
self.log_warn(e)
else:
client.clear()
self._cache_client = client
self._cache_client = None
return
def _ensure_cache_client(self) -> Optional[CacheClient]:
if self._cache_client is None:
@@ -666,24 +672,26 @@ class DataServer(SocketServer, DataAPI):
print("q size= ", self._configurations[device]._queue_rec.qsize())
self._configurations[device].put_rec_queue(data)
self._no_data_total_duration = 0
ret = True
else:
time_diff = time() - self._recv_memory_cost_time
self._no_data_total_duration += time_diff
print('data is None', device, datetime.now(), self._no_data_total_duration)
self._recv_memory_cost_time = time()
return ret
def rec_update(self) -> bool:
ds_db_msg = self._queue_msg.get()
with CacheClient() as client:
if isinstance(ds_db_msg, List):
if ds_db_msg[0] == 'ds':
client.send_command('device_instruction', ds_db_msg[1], ds_db_msg[2])
else:
self.mqtt_thread.broadcast_command('runtime error: ' + str(ds_db_msg))
content = {
'header': 'device_instruction/0',
'device': ds_db_msg,
'instruction': 'interrupt'
}
self.mqtt_thread.publish('device_instruction', json_stringify(content), inter = True)
device_id = self._queue_db_error.get()
self.mqtt_thread.broadcast_command('runtime error: ' + str(device_id))
content = {
'header': 'device_instruction/0',
'device': device_id,
'instruction': 'interrupt'
}
self.mqtt_thread.publish('device_instruction', json_stringify(content), inter = True)
return True
@@ -910,7 +918,7 @@ class DeviceDataRuntime(DataRuntime):
__slots__ = ( '_start_time', '_data_counter', '_last_data_counter', '_data_ch',
'_data_ch_time', '_mqtt_send_data_ch_level', '_timer', '_timer_of_not_send',
'_get_rec_data', '_cycle_start_time', '_time_stamp_list', '_prev_data', '_prev_delta_time', '_prev_time_stamp', '_task_fininsh')
'_get_rec_data', '_cycle_start_time', '_time_stamp_list', '_prev_data', '_prev_delta_time', '_prev_time_stamp', '_task_fininsh', '_check_spi_routine_timer')
def __init__(self, server: DataServer, device: int, meta_file: RecordingMetaFile, data_format: AnyStr):
super().__init__(server, device, meta_file, data_format)
@@ -923,6 +931,7 @@ class DeviceDataRuntime(DataRuntime):
self._mqtt_send_data_ch_level = {}
self._timer = None #int(time() * 10)
self._check_spi_routine_timer = None #int(time() * 10)
self._timer_of_not_send = None
self._get_rec_data = False
@@ -1148,6 +1157,7 @@ class SpiRuntimeThread(ServerThread):
self._time_stamp = 0.0
self._interval = 0.045
self._timer = time()
self._check_spi_routine_timer = time()
self._read_memory_board_idx = 0
def setup(self) -> None:
@@ -1164,7 +1174,6 @@ class SpiRuntimeThread(ServerThread):
def routine(self, event = None) -> None: # routine 50 ms
server = self._server
self._timer = time()
sync = server.get_spi_obj()
# select channel
@@ -1180,14 +1189,17 @@ class SpiRuntimeThread(ServerThread):
run_time = time() - self._timer
real_run_time = run_time
if run_time > 0.045:
# print('time, recv_data_form_spi_routine_time', time(), run_time)
run_time = 0.045
if run_time >= 0.030:
print('time, recv_data_form_spi_routine_time >= 0.030', 'device:', c, datetime.now(), time(), real_run_time)
print('time, recv_data_form_spi_routine_time >= 0.030', 'device:', c, datetime.now(), time(), run_time)
spi_routine_run_time = time() - self._check_spi_routine_timer
if spi_routine_run_time > 1:
print('spi_routine_time > 1', 'device:', c, datetime.now(), spi_routine_run_time)
self._check_spi_routine_timer = time()
event.wait(self._interval - run_time)
sync.set_pin_mem_sel(True)
+4 -4
View File
@@ -160,7 +160,7 @@ class DataBaseProcess(Process):
getattr(self, do)(*args)
return None
# @calculate_time(1)
@calculate_time(1)
def data_meta_write(self, _data: JSON_OBJECT, device_id, _uuid = None, _id = None):
if _id > 0:
sql_str = 'UPDATE "public"."recording_data_metas" SET '
@@ -376,7 +376,7 @@ class DataBaseProcess(Process):
print('recording error', e)
return None
# @calculate_time(1)
@calculate_time(1)
def data_raw_recording_new(self, _id_dict, _channel_list, _data_dict, _end_time_dict, _size_dict):
try:
para_list = []
@@ -446,7 +446,7 @@ class DataBaseProcess(Process):
return True
# @calculate_time(1)
@calculate_time(1)
def data_mini_recording_new(self, _id_dict, _channel_list, _data_mean_dict):
try:
para_list = []
@@ -508,7 +508,7 @@ class DataBaseProcess(Process):
return True
# @calculate_time()
@calculate_time(1)
def data_meta_update_all_channel_raw_id(self, meta_id:str, raw_id_dict:dict):
try:
sql_str = "UPDATE recording_data_metas SET raw_data = jsonb_insert(raw_data, '{%s, -1}', '%s', TRUE) WHERE id = %s;"
+532 -8
View File
@@ -43,7 +43,7 @@ def _fc(c):
return c % len(FORE_COLOR) + FORE_COLOR[0]
class ControlServer(SocketServer, ControlServerAPI):
class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler):
"""Data Server"""
def __init__(self, *options):
@@ -60,7 +60,7 @@ class ControlServer(SocketServer, ControlServerAPI):
:param options: server options
"""
super().__init__((ControlAPI), 'Controller', self.ADDR,
super().__init__((ControlAPI, CacheAPI), 'Controller', self.ADDR,
socket_listen=10,
thread_pool_count=16)
@@ -87,6 +87,21 @@ class ControlServer(SocketServer, ControlServerAPI):
data_options.repository = device_option.repository
self.data_server = DataServerProcess(data_options)
# experimental protocol
# exp_options = ExpManagerOption.get_options(*options)
# self.exp_manager = ExpManager(self, exp_options)
# cache
self.cache_manager = CacheManager(self,
c.cache_server_cache_size,
# time unit from sec to ms
c.cache_server_cache_time * 1000)
# websocket server ###
### self.websocket_thread = WebsocketThread(self,
### c.websocket_address,
### c.websocket_port)
# remote server / MQTT
self.mqtt_thread = None
if not c.flag_disable_remote_server:
@@ -97,6 +112,17 @@ class ControlServer(SocketServer, ControlServerAPI):
self.log_verbose,
'MQTT-control-server')
# routine trash thread
self._routine_trash_thread = None
# if not c.flag_disable_routine_trash:
# self._routine_trash_thread = RoutineTrashThread(self._file_manager,
# c.trash_routine_time,
# c.trash_expire_time,
# c.trash_remove_expire_time)
# routine data routine
self._data_stream_thread = None
# LED control
self.led_thread = LEDControlThread(disable=c.flag_disable_led_control,
led_count=c.led_count,
@@ -105,8 +131,6 @@ class ControlServer(SocketServer, ControlServerAPI):
# device detect
self._detect_device_thread = RoutineConnectDeviceThread(self._device_manager, self.mqtt_thread, self.led_thread, 3)
self._project_manager = ProjectManager(self.device_manager, self.mqtt_thread)
if not c.flag_disable_led_control:
self.led_thread.set_state(LED.COLOR_RED)
@@ -120,6 +144,8 @@ class ControlServer(SocketServer, ControlServerAPI):
self._session = {}
self._user_session_expire = c.user_session_expire_days * 24 * 60 * 60 # sec
# temp use variable
# indicate those device together
self._device_instruction_group = None
@@ -129,6 +155,9 @@ class ControlServer(SocketServer, ControlServerAPI):
# _api request build
self._api = ApiRequests()
self._project_manager = ProjectManager(self.device_manager, self.mqtt_thread)
# init method
def _init_file_manager(self, option: ControlServerOptions) -> FileManager:
@@ -220,16 +249,32 @@ class ControlServer(SocketServer, ControlServerAPI):
# device manager reset
self.submit_thread(self._setup_device)
# self.submit_thread(self._setup_controller)
self._setup_controller()
# self.run_thread(self._detect_device_thread)
# experimental protocol manager
# if not self._controller_options.flag_disable_exp_manager:
# self.exp_manager.setup()
# self.exp_manager.reload_protocol()
# websocket server
### self.log_verbose('setup websocket server', pc(self.websocket_thread.websocket_address, YELLOW))
# websocket server thread run ###
### self.run_thread(self.websocket_thread)
# broadcast greeting
self.broadcast_command(DataMessage.greeting())
# routine trashing
if self._routine_trash_thread is not None:
self.run_thread(self._routine_trash_thread)
# remote server
if self.mqtt_thread is not None:
self.mqtt_thread.start()
# self.run_thread(self.mqtt_thread)
def _setup_file_manager(self):
self.log_verbose('setup file manager')
@@ -404,14 +449,90 @@ class ControlServer(SocketServer, ControlServerAPI):
self.broadcast_command('shutdown')
self.data_server.stop_process(external_interrupt)
self._stop_data_stream_thread()
self.device_manager.shutdown()
self.file_manager.shutdown(umount_external=True)
self.cache_manager.shutdown()
### self.websocket_thread.close()
self.led_thread.close()
if self.mqtt_thread is not None:
self.mqtt_thread.shutdown()
# self.mqtt_thread.close()
# user session
def new_user_session(self) -> str:
session = UserSession(None)
self._session[session.user_session] = session
self.log_info('new user session', session.user_session)
return session.user_session
@logging_info
def del_user_session(self, user_session: Optional[str] = None):
if user_session is None:
self._session.clear()
else:
try:
del self._session[user_session]
except KeyError:
pass
def get_user_session(self,
user_session: Optional[str],
new_session = False,
allow_empty = False) -> Optional[UserSession]:
"""
:param user_session: user session ID
:param new_session: new a user session if *user_session* not found.
:param allow_empty: consider empty *user_session* None
:return: user session
"""
if user_session is None:
session = None
elif len(user_session) == 0:
if allow_empty:
session = None
else:
raise None
else:
session = self._session.get(user_session, None)
if session is not None:
session.time()
elif new_session:
session = UserSession(user_session)
self._session[session.user_session] = session
self.log_info('new user session', session.user_session)
return session
def expire_user_session(self):
current_time = time()
for user_session in list(self._session.keys()):
session = self._session[user_session]
if session.time(update=False) - current_time > self._user_session_expire:
del self._session[user_session]
# general post action
def _post_device_list(self) -> List[CompletedDevice]:
"""general post action for getting list of connected devices.
@@ -748,6 +869,12 @@ class ControlServer(SocketServer, ControlServerAPI):
def get_device_info(self, device: int) -> Optional[CompletedDevice]:
return super().get_device_info(device)
# @logging_info
# def device_call(self, device: int) -> bool:
# self.device_manager.call_device(device)
#
# return True
@logging_info
def device_instruction_all(self, device: int) -> List[str]:
return self.device_manager.list_device_instruction(device)
@@ -921,6 +1048,8 @@ class ControlServer(SocketServer, ControlServerAPI):
@logging_info
def device_parameter(self, device: int, parameter: str, content: Optional[str] = None) -> Any:
# if content is not None:
# self.broadcast_command('parameter:' + str(device) + '/' + parameter + '/' + content)
return self.device_manager.handle_device_parameter_command(device, parameter, content)
@logging_info
@@ -1012,12 +1141,256 @@ class ControlServer(SocketServer, ControlServerAPI):
def device_history_delete(self, user: str, device: Optional[int]) -> bool:
raise NotImplementedError()
# server provide method implement : file manager functions
@logging_info
def file_clean_storage(self) -> bool:
return super().file_clean_storage()
@logging_info
def file_index(self) -> RootInfo:
return super().file_index()
@logging_info
def file_list(self, path: str) -> DirectoryInfo:
return super().file_list(path)
@logging_info
def file_list_range(self, path: str, start: int, end: int) -> DirectoryInfo:
return super().file_list_range(path, start, end)
@logging_info
def file_rename(self, path: str, content: str) -> FileInfo:
return super().file_rename(path, content)
@logging_info
def file_mkdir(self, path: str) -> DirectoryInfo:
return super().file_mkdir(path)
@logging_info
def file_delete(self, path: str) -> bool:
return super().file_delete(path)
@logging_info
def file_info(self, path: str) -> FileInfo:
return super().file_info(path)
@logging_info
def file_meta_info(self, path: str) -> RecordingMetaFile:
return super().file_meta_info(path)
@logging_info
def file_export_info(self, path: str) -> Dict[str, Optional[Path]]:
return super().file_export_info(path)
@logging_info
def file_export_functions(self) -> Tuple[ExternalExportFunction, ...]:
return super().file_export_functions()
@logging_info
def file_export_parameters(self, ftype: str) -> Tuple[ExternalParameter, ...]:
return super().file_export_parameters(ftype)
@logging_info
def file_export(self, ftype: str, path: str, **options: str) -> str:
return super().file_export(ftype, path, **options)
@logging_info
def file_segment(self, method: str, path: str) -> Union[None, bool, FileSegment]:
return super().file_segment(method, path)
@logging_info
def file_segment_get(self, segment: int, path: str):
return super().file_segment_get(segment, path)
@logging_info
def file_handle_open(self, content: str) -> Optional[FileHandle]:
info = self.file_manager.info(content)
meta = info.meta_file
if meta is None:
raise RuntimeError('not a meta file')
handle = self.cache_manager.file_open(meta)
return self.file_handle_info(handle)
@logging_info
def file_handle_info(self, handle: int) -> Optional[FileHandle]:
info = self.cache_manager.file_info(handle)
if info is None:
return None
# modify file path
info.shadow_path(self.file_manager.shadow_path(Path(info.filepath)))
return info
@logging_info
def file_handle_close(self, handle: int):
self.cache_manager.file_close(handle)
@logging_info
def user_all(self) -> List[str]:
raise super().user_all()
@logging_info
def user_info(self, user: str, *, user_session: Optional[str] = None) -> Optional[AbstractUserSetting]:
return super().user_info(user, user_session=user_session)
@logging_info
def user_login(self, user: str, content: str, *,
user_session = None) -> ComplexResponse[AbstractUserSetting]:
raise super().user_login(user, content, user_session=user_session)
@logging_info
def user_logout(self, user_session: Optional[str] = None) -> None:
super().user_logout(user_session)
@logging_info
def user_add(self, user: str, content: str, *,
user_session = None) -> ComplexResponse[AbstractUserSetting]:
return super().user_add(user, content, user_session=user_session)
@logging_info
def user_del(self, user: str, content: str) -> bool:
return super().user_del(user, content)
@logging_info
def user_sync(self, user_session: Optional[str]) -> bool:
return super().user_sync(user_session)
@logging_info
def user_configuration_list(self, user_session: Optional[str], **options: str) -> List[str]:
return super().user_configuration_list(user_session, **options)
@logging_info
def user_configuration_get(self, name: str, user_session: Optional[str] = None) -> Optional[RecordingMetaFile]:
return super().user_configuration_get(name, user_session)
@logging_info
def user_configuration_add(self, name: str, content: str, user_session: Optional[str] = None) -> bool:
return super().user_configuration_add(name, content, user_session)
@logging_info
def user_configuration_del(self, name: str, user_session: Optional[str] = None) -> bool:
return super().user_configuration_del(name, user_session)
@logging_info
def user_configuration_clone(self, name: str, content: str, user_session: Optional[str] = None) -> bool:
return super().user_configuration_clone(name, content, user_session)
@logging_info
def user_setup_list(self, user_session: Optional[str] = None) -> List[str]:
return super().user_setup_list(user_session)
@logging_info
def user_setup_info(self, name: str, user_session: Optional[str] = None) -> Optional[UserSetup]:
return super().user_setup_info(name, user_session)
@logging_info
def user_setup_new(self, name: str, user_session: Optional[str] = None) -> Optional[UserSetup]:
return super().user_setup_new(name, user_session)
@logging_info
def user_setup_del(self, name: str, user_session: Optional[str] = None) -> bool:
return super().user_setup_del(name, user_session)
@logging_info
def user_alias_list(self, user_session: Optional[str] = None) -> List[UserDeviceAlias]:
return super().user_alias_list(user_session)
@logging_info
def user_alias_info(self, name: str, user_session: Optional[str] = None) -> Optional[UserDeviceAlias]:
return super().user_alias_info(name, user_session)
@logging_info
def user_alias_edit(self, name: str, content: str, user_session: Optional[str] = None) -> Optional[UserDeviceAlias]:
return super().user_alias_edit(name, content, user_session)
# @logging_info
# def exp_pro_list(self) -> List[ExpProtocolInfo]:
# return self.exp_manager.list_available_protocol()
@logging_info
def exp_pro_create(self, content: str, *,
user_session = None) -> int:
user_name = self._session_user_name(user_session)
if user_name is None:
raise RuntimeError('require user login')
self.file_manager.user_setting.user_info(user_name)
# p = self.exp_manager.create_protocol(content, user_name)
return
@logging_info
def run_project(self, project) -> bool:
project = self._project_manager.create(project)
self._project_manager.run(project)
return False
# @logging_info
# def exp_pro_options(self, protocol: int) -> List[ExpOption]:
# return self.exp_manager.protocol_options(protocol)
# @logging_info
# def exp_pro_option(self, protocol: int, option: str, content: Optional[str] = None) -> bool:
# if content == '':
# # option reset
# if option == '':
# # reset all options
# self.exp_manager.protocol_reset(protocol)
# else:
# self.exp_manager.protocol_reset(protocol, *option.split(','))
# else:
# # option get/set
# self.exp_manager.protocol_option(protocol, option, content)
# return True
# @logging_info
# def exp_pro_status_all(self) -> List[AbstractExpProtocolState]:
# ret = []
# for p in self.exp_manager.list_protocol():
# s = self.exp_manager.protocol_status(p)
# if s is not None:
# ret.append(s)
# else:
# ret.append(AbstractExpProtocolState(p))
# return ret
# @logging_info
# def exp_pro_status(self, protocol: int) -> Optional[ExpProtocolState]:
# return self.exp_manager.protocol_status(protocol)
# @logging_info
# def exp_pro_command(self, protocol: int, content: str) -> bool:
# if content == 'ensure':
# return self.exp_manager.ensure_protocol(protocol)
# elif content == 'start':
# self.exp_manager.submit_protocol(protocol)
# elif content == 'interrupt':
# self.exp_manager.interrupt_protocol(protocol)
# elif content == 'suspend':
# self.exp_manager.suspend_protocol(protocol)
# elif content == 'resume':
# self.exp_manager.resume_protocol(protocol)
# else:
# raise RuntimeError('unknown protocol command : ' + content)
# return True
@logging_info
def device_internal_command(self, device: int, oper: str, value: Any) -> bool:
device = self.device_manager.get_device(device)
@@ -1033,6 +1406,8 @@ class ControlServer(SocketServer, ControlServerAPI):
return True
elif oper == InternalInstruction.PREDEFINED_DISABLE_CACHE:
if device is not None:
self._device_set_disable_cache(device, value)
return True
else:
@@ -1084,6 +1459,10 @@ class ControlServer(SocketServer, ControlServerAPI):
def _device_set_sync(self, device: CompletedDevice, sync: bool):
if sync:
self.log_verbose('start sync', device.device_id)
self.cache_manager.drop_data(device)
client = self.data_server.client()
if client is not None:
info = self.file_manager.use(device)
@@ -1094,14 +1473,22 @@ class ControlServer(SocketServer, ControlServerAPI):
filename = format_filename(self.TEMP_FILENAME_EXPR, device)
info = self.file_manager.save(device, filename)
client.update_device_configuration(device, info.meta_file, RawDataDecoder())
print('***456')
if self._device_instruction_group is None:
client.start_sync(device)
device.status = 1
else:
if self._device_instruction_group is None:
# broadcast
self.broadcast_command(DataMessage.start(device.device_id))
else:
self.log_verbose('stop sync', device.device_id)
if self._device_instruction_group is None:
start_sync = False
client = self.data_server.client()
if client is not None:
with client:
@@ -1114,10 +1501,12 @@ class ControlServer(SocketServer, ControlServerAPI):
if start_sync:
# unset file info, but keep file path cache
self.file_manager.unset(device.device_id)
device.status = 0
# broadcast
self.broadcast_command(DataMessage.stop(device.device_id))
# server provide method implement : websocket broadcast functions
@logging_info
def broadcast_command(self, command: str, device: Optional[int] = None):
self.log_verbose(pc('broadcast_command', GREEN),
@@ -1127,6 +1516,96 @@ class ControlServer(SocketServer, ControlServerAPI):
if self.mqtt_thread is not None:
self.mqtt_thread.broadcast_command(command, device=device)
# data stream thread
def data_request_stream(self, start: bool, handler: DataMessageHandler):
if start:
t = self._start_data_stream_thread()
t.add_handle(handler)
else:
t = self._data_stream_thread
if t is not None:
t.del_handle(handler)
if not t.has_handle():
self._stop_data_stream_thread()
def _start_data_stream_thread(self) -> DataStreamThread:
t = self._data_stream_thread
if self._data_stream_thread is None:
self._data_stream_thread = t = self.run_thread(DataStreamThread(self))
return t
def _stop_data_stream_thread(self):
t = self._data_stream_thread
self._data_stream_thread = None
if t is not None:
t.close()
# noinspection PyShadowingNames
@logging_info
def websocket_command(self, command: str) -> Union[None, str, bytes, List[str]]:
if command == DataMessage.GREETING:
return DataMessage.greeting()
elif command == DataMessage.SYNC:
return self._websocket_command_sync(map(lambda d: d.device_id, self._post_device_list()))
elif command.startswith(DataMessage.SYNC):
_, device_list = part_suffix(command, ':')
if ',' in device_list:
return self._websocket_command_sync(map(int, device_list.split(',')))
else:
return self._websocket_command_sync([int(device_list)])
elif command.startswith(DataMessage.BROADCAST):
_, message = part_suffix(command, ':')
if message is not None:
self.broadcast_command(DataMessage.broadcast(message))
return None
# noinspection PyShadowingNames
def _websocket_command_sync(self, device_list: Iterable[int]) -> Union[str, List[str]]:
client = self.data_server.client()
if client is None:
return DataMessage.sync(False)
else:
result_start = []
result_stop = []
with client:
for device in device_list:
if client.is_sync(device):
result_start.append(device)
else:
result_stop.append(device)
return [DataMessage.start(result_start), DataMessage.stop(result_stop)]
# noinspection PyShadowingNames
def websocket_data_request(self, handler: DataMessageHandler, command: Optional[str]):
try:
self._handle_command_response(handler, command)
except (ValueError, IndexError) as e:
self.log_warn(e)
handler.on_error(DataMessage.warn(str(e)))
except BaseException as e:
self.log_warn(e)
handler.on_error(DataMessage.error(str(e)))
# noinspection PyShadowingNames
def _handle_command_response(self, handler: DataMessageHandler, command: Optional[str]):
"""
@@ -1148,7 +1627,29 @@ class ControlServer(SocketServer, ControlServerAPI):
else:
self.cache_manager.get_data_message(handler, response)
# server provide method implement : cache API
@logging_verbose
def clear(self):
self.cache_manager.clear()
def push_data(self, data: Union[bytes, List[bytes]]):
self.cache_manager.push_data(data)
@logging_verbose
def drop_data(self, *device: int):
self.cache_manager.drop_data(*device)
def stop_data(self, device: int):
self.cache_manager.stop_data(device)
# noinspection PyShadowingNames
@logging_verbose
def notify_data(self, device: int, message: str):
self.broadcast_command(message, device=device)
# led function
def get_led_state(self) -> str:
return self.led_thread.state
@@ -1156,6 +1657,7 @@ class ControlServer(SocketServer, ControlServerAPI):
self.led_thread.set_state(state)
# hardware send test
if not _RUNTIME_COMPILE:
def _hardware_send_test(self, options: Dict[str, str]):
@@ -1198,9 +1700,30 @@ class ControlServer(SocketServer, ControlServerAPI):
'export_root': str(self.file_manager.export_root)
}
if section is None or 'cache' in section:
ret['cache'] = {
'cache_size': self.cache_manager.cache_size,
'cache_time': self.cache_manager.cache_time,
}
# if section is None or 'websocket' in section:
# ret['websocket'] = {
# 'address': self.websocket_thread.websocket_address,
# 'port': self.websocket_thread.websocket_port,
# }
if section is None or 'mqtt' in section:
ret['mqtt'] = None if self.mqtt_thread is None else self.mqtt_thread.hardware_test()
if section is None or 'trash' in section:
trash_manager = self._routine_trash_thread
ret['trash'] = None if trash_manager is None else {
'routine_interval': trash_manager.routine_interval,
'trash_expire': trash_manager.trash_expire,
'remove_expire': trash_manager.remove_expire,
}
if section is None or 'led' in section:
ret['led'] = {
'state_all': list(LED.COLOR.keys()),
@@ -1329,6 +1852,7 @@ class ControlServer(SocketServer, ControlServerAPI):
# TODO write options files
return False
class _RandomCrashThread(ServerThread):
def __init__(self):
super().__init__('Crash')
+10 -12
View File
@@ -41,7 +41,7 @@ class RecordingProcess(Process):
connect to DB and send sql command
"""
def __init__(self, client_id: str, log_verbose, device_id, queue_rec, queue_ds, queue_msg, meta_path,
def __init__(self, client_id: str, log_verbose, device_id, queue_rec, queue_ds, meta_path,
device_info, parameter, parent, recording_file_name, data_format,
mqtt_ip, mqtt_port,
name = 'Recording-Process', database = None):
@@ -82,7 +82,6 @@ class RecordingProcess(Process):
self._queue_rec = queue_rec
self._queue_ds = queue_ds
self._queue_msg = queue_msg
self.queue_flag = True
self._is_close = True
@@ -166,11 +165,11 @@ class RecordingProcess(Process):
"""
q = None
try:
# now = time()
now = time()
q = self._queue_rec.get()
# get_queue_time_cost = time() - now
# if get_queue_time_cost > 2:
# print('get queue cost time', get_queue_time_cost)
get_queue_time_cost = time() - now
if get_queue_time_cost > 1.1:
print('get queue cost time', get_queue_time_cost, 'q_size', self._queue_rec.qsize())
except:
return False
finally:
@@ -231,12 +230,11 @@ class RecordingProcess(Process):
try:
if isinstance(decoder, I4V4Z4T4DataDecoder) or isinstance(decoder, EISZeroOneDataDecoder):
if decoder.isFinishMode is not None and decoder.isFinishMode() == 1:
self._queue_msg.put(['ds', self._device, 'interrupt'])
# content = {}
# content['header'] = 'device_instruction/0'
# content['device'] = result.device
# content['instruction'] = 'interrupt'
# self._mqtt_thread.publish('device_instruction',json_stringify(content), True)
content = {}
content['header'] = 'device_instruction/0'
content['device'] = result.device
content['instruction'] = 'interrupt'
self._mqtt_thread.publish('device_instruction',json_stringify(content), True)
# self.server.stop_sync(self.device)
except RuntimeError as e:
print(e)