Compare commits

..

18 Commits

Author SHA1 Message Date
peterlu14 58bfc86572 - Fixed parameter value string error 2024-10-04 11:59:28 +08:00
peterlu14 febba59c37 - Fix Elite_2.0 ble_write
- Fix Pel_2.0 parameters
2024-10-01 15:09:23 +08:00
peterlu14 b8f39e984b - Add pulse_e_load_2.0 lib 2024-10-01 11:48:25 +08:00
peterlu14 59585507a5 - Fix missing parameter ADC_VALUE_I 2024-09-20 09:45:35 +08:00
peterlu14 f9f15457cf change default parameter 2024-09-11 16:47:34 +08:00
peterlu14 cf62cfbe04 - Add coundown instruction 2024-09-11 10:00:00 +08:00
peterlu14 33697af99c - Add countdown internal instruction for device 2024-09-11 09:59:24 +08:00
peterlu14 985e043ca5 - Complete CPG 1.1 parameters & instructions 2024-09-10 16:04:48 +08:00
peterlu14 8fd119f4f0 support float domain parameter 2024-09-10 16:04:14 +08:00
peterlu14 b5bd64bf6c - BLE_WRITE change instruction type to ALL 2024-08-21 17:42:05 +08:00
peterlu14 241296f36c -Enable TYPE ALL Instruction 2024-08-21 17:40:58 +08:00
peterlu14 f46073a46a -CPG BLE_WRITE extend to 255 bytes 2024-08-21 14:55:28 +08:00
peterlu14 24fd5ca28b -Extension of list parameter parse rule
-Fix the list paratmeter change with changing it's length
2024-08-21 14:42:36 +08:00
peterlu14 288f261abe - Add CPG library 2024-07-31 15:02:29 +08:00
peterlu14 a32267f1fd update pulse_e_load json 2024-07-30 10:27:58 +08:00
Roy c89c6e88d0 update EDC1.5r2 library and new EDC2.0 library 2024-03-05 17:07:52 +08:00
peterlu14 536bc23896 -update stop update battery when device running 2023-11-21 16:28:40 +08:00
Roy 7deb709946 [update] print msg when connection fail 2023-07-17 17:37:14 +08:00
23 changed files with 4249 additions and 224 deletions
-6
View File
@@ -1595,12 +1595,6 @@ 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,12 +139,6 @@ 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
@@ -210,9 +204,6 @@ 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, ...]:
+29 -33
View File
@@ -429,17 +429,20 @@ class CC2650Device(Device):
self._start_flag = False self._start_flag = False
def _encode_instruction(self, ins_type: int, ins_oper: int, *instruction: int) -> bytes: def _encode_instruction(self, ins_type: int, ins_oper: int, *instruction: int) -> bytes:
# print('_encode_instruction', ins_type, ins_oper, instruction)
length = len(instruction) length = len(instruction)
if length == 1 and instruction[0] < 0: if length == 1 and instruction[0] < 0:
return struct.pack('2B1b', return struct.pack('2B1b',
(ins_type & 0xF0) | (self.device_id & 0x0F), (ins_type & 0xF0) | (self.device_id & 0x0F),
(ins_oper & 0xFF), (ins_oper & 0xFF),
*instruction) *instruction)
if ins_type == None:
return struct.pack('%dB' % (length + 2), return struct.pack('%dB' % length, *instruction)
(ins_type & 0xF0) | (self.device_id & 0x0F), else:
(ins_oper & 0xFF), return struct.pack('%dB' % (length + 2),
*instruction) (ins_type & 0xF0) | (self.device_id & 0x0F),
(ins_oper & 0xFF),
*instruction)
def _decode_data(self, ins_oper: int, data: bytes) -> bytes: def _decode_data(self, ins_oper: int, data: bytes) -> bytes:
"""CIS data decoder. """CIS data decoder.
@@ -494,7 +497,8 @@ class CC2650Device(Device):
@property @property
def battery(self) -> int: def battery(self) -> int:
self.update_battery_info() if self._start_flag == False:
self.update_battery_info()
return self._battery return self._battery
@property @property
@@ -2174,7 +2178,7 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
try: try:
connect_response = self._cc2650.recv_uart(2) connect_response = self._cc2650.recv_uart(2)
except RecvTimeout: except RecvTimeout:
self.log_verbose('[CC2650]', 'connect response timeout') # self.log_verbose('[CC2650]', 'connect response timeout')
if retry < 5: if retry < 5:
self.log_verbose('[CC2650]', 'connect retry') self.log_verbose('[CC2650]', 'connect retry')
continue continue
@@ -2182,6 +2186,7 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
break break
if connect_response is None: if connect_response is None:
self.log_verbose('[CC2650]', 'connect response timeout')
return False return False
pack_len = connect_response[0] pack_len = connect_response[0]
@@ -2207,7 +2212,8 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
self.log_verbose('[CC2650]', DEVICE_CONNECTED, address_s) self.log_verbose('[CC2650]', DEVICE_CONNECTED, address_s)
return ret return ret
self.log_verbose('[CC2650]', 'connect fail') connect_response_hex = ''.join(format(i, '02X') for i in connect_response)
self.log_verbose('[CC2650]', 'connect fail', '0x'+connect_response_hex)
return False return False
@synchronized @synchronized
@@ -2267,7 +2273,10 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
ins = bytearray() ins = bytearray()
ins.append(0x06) ins.append(0x06)
ins.append(len(data)+2) #length = handle + C0C0XXXX(data len) + F1 if len(data) + 2 > 255:
ins.append(255)
else:
ins.append(len(data)+2) #length = handle + C0C0XXXX(data len) + F1
ins.append(handle) ins.append(handle)
ins.extend(data) ins.extend(data)
ins.append(0xF1) ins.append(0xF1)
@@ -2611,34 +2620,21 @@ 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:
master = 4 if master is None:
raise RuntimeError('cannot connect any more device')
else:
raise RuntimeError('rescan please')
# #
self.log_verbose('use', master) self.log_verbose('use', master)
+8
View File
@@ -836,6 +836,9 @@ class DeviceManager(MasterDevice, Synchronized):
elif func == InternalInstruction.PREDEFINED_IDLE: elif func == InternalInstruction.PREDEFINED_IDLE:
self._idle(device, *para) self._idle(device, *para)
elif func == InternalInstruction.PREDEFINED_COUNTDOWN:
self._countdown(device, *para)
elif isinstance(device, DebugDevice): elif isinstance(device, DebugDevice):
if func == InternalInstruction.PREDEFINED_NOTIFY: if func == InternalInstruction.PREDEFINED_NOTIFY:
return True return True
@@ -858,6 +861,11 @@ class DeviceManager(MasterDevice, Synchronized):
self._handler.device_internal_command(device.device_id, self._handler.device_internal_command(device.device_id,
InternalInstruction.PREDEFINED_IDLE, InternalInstruction.PREDEFINED_IDLE,
None) None)
def _countdown(self, device: Device, expr: AnyStr):
self._handler.device_internal_command(device.device_id,
InternalInstruction.PREDEFINED_COUNTDOWN,
expr)
def _device_data_format_cali(self, device: Device, expr: str, cali: bytes = None): def _device_data_format_cali(self, device: Device, expr: str, cali: bytes = None):
if cali is None: if cali is None:
+5 -1
View File
@@ -689,11 +689,13 @@ class DeviceInstruction:
TYP_IIS = -1 TYP_IIS = -1
"""internal instruction""" """internal instruction"""
TYP_ALL = None
__slots__ = () __slots__ = ()
@classmethod @classmethod
def valid_ins_type(cls, ins_type: int): def valid_ins_type(cls, ins_type: int):
if ins_type not in (cls.TYP_RIS, cls.TYP_VIS, cls.TYP_CIS, cls.TYP_IIS): if ins_type not in (cls.TYP_RIS, cls.TYP_VIS, cls.TYP_CIS, cls.TYP_IIS, cls.TYP_ALL):
raise ValueError('unknown instruction type : ' + str(ins_type)) raise ValueError('unknown instruction type : ' + str(ins_type))
@classmethod @classmethod
@@ -704,6 +706,8 @@ class DeviceInstruction:
return cls.TYP_VIS return cls.TYP_VIS
elif ins_type == 'CIS': elif ins_type == 'CIS':
return cls.TYP_CIS return cls.TYP_CIS
elif ins_type == 'ALL':
return cls.TYP_ALL
else: else:
raise RuntimeError('unknown instruction type : ' + ins_type) raise RuntimeError('unknown instruction type : ' + ins_type)
+29 -3
View File
@@ -120,6 +120,9 @@ class ParameterDomain(JsonSerialize, metaclass=abc.ABCMeta):
elif json == 'int': elif json == 'int':
return ParameterIntDomain return ParameterIntDomain
elif json == 'float':
return ParameterFloatDomain
elif json == 'property': elif json == 'property':
return ParameterPropertyDomain return ParameterPropertyDomain
@@ -291,6 +294,25 @@ class ParameterIntDomainType(ParameterTypeDomain):
ParameterIntDomain = ParameterIntDomainType() ParameterIntDomain = ParameterIntDomainType()
class ParameterFloatDomainType(ParameterTypeDomain):
__slots__ = ()
def init_para(self, initial: Optional[Any] = None) -> float:
print('ParameterFloatDomainType')
if initial is None:
return 0
else:
return float(initial)
def valid_para(self, value: int) -> bool:
return True
def __str__(self):
return "float"
ParameterFloatDomain = ParameterFloatDomainType()
class ParameterValueDomain(ParameterDomain, metaclass=abc.ABCMeta): class ParameterValueDomain(ParameterDomain, metaclass=abc.ABCMeta):
"""limited/ranged P value domain """ """limited/ranged P value domain """
@@ -301,7 +323,7 @@ class ParameterValueDomain(ParameterDomain, metaclass=abc.ABCMeta):
if initial is None: if initial is None:
return self.range[0] return self.range[0]
else: else:
initial = int(initial) initial = initial
f, t = self.range f, t = self.range
if f <= initial < t: if f <= initial < t:
@@ -742,6 +764,7 @@ class ParameterListDomain(ParameterCollectionDomain):
sz = len(target) sz = len(target)
i = oper.index(sz) i = oper.index(sz)
v = oper.value(d) v = oper.value(d)
# print('ParameterListDomain', d, sz, i, v, str(d) == 'float', type(d))
if i is None and v is None: if i is None and v is None:
pass pass
@@ -780,8 +803,11 @@ class ParameterListDomain(ParameterCollectionDomain):
if isinstance(v, int): if isinstance(v, int):
v = [v] v = [v]
target[len(v):] = []
target[i] = v if isinstance(d, ParameterFloatDomainType):
target[i] = [float(i) for i in v]
else:
target[i] = v
def _valid_list_limit(self, target: List[Any], inc: int) -> bool: def _valid_list_limit(self, target: List[Any], inc: int) -> bool:
if self._limit is not None: if self._limit is not None:
+1 -2
View File
@@ -880,8 +880,7 @@ class WhenExpression(ComplexExpression[T]):
def value(self, context: Scope) -> Union[str, T]: def value(self, context: Scope) -> Union[str, T]:
value = super().value(context) value = super().value(context)
key = str(int(value))
key = str(value)
if key in self._when: if key in self._when:
return self._when[key].value(context.child(VALUE=value)) return self._when[key].value(context.child(VALUE=value))
+13
View File
@@ -5,6 +5,13 @@ from .device import *
from .instruction import * from .instruction import *
from .parameter import * from .parameter import *
def convert_to_float(lst):
# Check if the element is a list (for handling nested lists)
if isinstance(lst, list):
return [convert_to_float(x) for x in lst]
else:
# If it's not a list, convert it to float
return float(lst)
class MatchRule(JsonSerialize, metaclass=abc.ABCMeta): class MatchRule(JsonSerialize, metaclass=abc.ABCMeta):
"""Device matching rule. Program use this table to find correct library according to the response information from """Device matching rule. Program use this table to find correct library according to the response information from
@@ -444,6 +451,12 @@ class DefaultLibraryLoader:
elif guard is not None: elif guard is not None:
guard = ListGuardExpression([GuardExpression.parse(guard)]) guard = ListGuardExpression([GuardExpression.parse(guard)])
if "float" in str(domain):
initial = convert_to_float(initial)
# print('!@#$%^&')
# print(name, domain, initial, value, value_set)
return ParameterInfo(name, domain, return ParameterInfo(name, domain,
initial=initial, initial=initial,
+27 -14
View File
@@ -1,4 +1,5 @@
import re import re
import struct
from random import randint from random import randint
from time import sleep from time import sleep
from typing import Sequence, Tuple from typing import Sequence, Tuple
@@ -284,6 +285,7 @@ class InternalInstruction(SingleInstruction):
PREDEFINED_CDR = '_cdr' PREDEFINED_CDR = '_cdr'
PREDEFINED_DISABLE_CACHE = '_disable_cache' PREDEFINED_DISABLE_CACHE = '_disable_cache'
PREDEFINED_IDLE = '_idle' PREDEFINED_IDLE = '_idle'
PREDEFINED_COUNTDOWN = '_countdown'
PREDEFINED = ( PREDEFINED = (
PREDEFINED_SLEEP, PREDEFINED_SLEEP,
@@ -296,6 +298,7 @@ class InternalInstruction(SingleInstruction):
PREDEFINED_CDR, PREDEFINED_CDR,
PREDEFINED_DISABLE_CACHE, PREDEFINED_DISABLE_CACHE,
PREDEFINED_IDLE, PREDEFINED_IDLE,
PREDEFINED_COUNTDOWN
) )
__slots__ = ('_expr', '_para') __slots__ = ('_expr', '_para')
@@ -375,7 +378,7 @@ class InternalInstruction(SingleInstruction):
parser.parse_instruction(context, data) parser.parse_instruction(context, data)
# tag2
class ListInstruction(Instruction, ImmutableListNode[Union[Instruction, Expression[str]]]): class ListInstruction(Instruction, ImmutableListNode[Union[Instruction, Expression[str]]]):
"""Instruction group, allow the name of the instruction or a expression it. """Instruction group, allow the name of the instruction or a expression it.
If instruction name is ``None``, ignore it. If instruction name is ``None``, ignore it.
@@ -588,7 +591,7 @@ class InstructionContentWidth:
return InstructionContentWidth(z, t, signed_value=s is not None, little_endian=e == '<'), x return InstructionContentWidth(z, t, signed_value=s is not None, little_endian=e == '<'), x
# The start from the json file parse
class InstructionContent(JsonSerialize): class InstructionContent(JsonSerialize):
""" """
**json format** **json format**
@@ -682,7 +685,7 @@ class InstructionContent(JsonSerialize):
return ins_type(width, expr, **ins_argv, comment=comment) return ins_type(width, expr, **ins_argv, comment=comment)
# tag1
class InstructionDataContent(InstructionContent): class InstructionDataContent(InstructionContent):
""" """
**json format** **json format**
@@ -743,6 +746,7 @@ class InstructionDataContent(InstructionContent):
return ret[index] return ret[index]
def build_instruction(self, context: Scope, buffer: List[int], shift: int = 0) -> int: def build_instruction(self, context: Scope, buffer: List[int], shift: int = 0) -> int:
# print('build_instruction', context, buffer, shift, self.value(context))
if self._width.is_array: if self._width.is_array:
raise NotImplementedError() raise NotImplementedError()
@@ -750,24 +754,33 @@ class InstructionDataContent(InstructionContent):
value = 1 value = 1
elif self.value(context) == 'false': elif self.value(context) == 'false':
value = 0 value = 0
elif isinstance(self.value(context), list):
value = self.value(context)
else: else:
value = int(self.value(context)) value = self.value(context)
# print('value', value, type(value))
# print('self._width.bytes_unit', self._width.bytes_unit)
# print('self._width.size', self._width.size)
if self._width.bytes_unit: if self._width.bytes_unit:
if self._width.size == 1: if self._width.size == 1:
buffer.append(value) buffer.append(value)
else: else:
tmp = [] if isinstance(self.value(context), list):
for _ in range(self._width.size): buffer.extend(value)
tmp.append(value & 0xFF) elif isinstance(value, int):
value >>= 8 tmp = []
for _ in range(self._width.size):
if self._width.little_endian: tmp.append(value & 0xFF)
buffer.extend(tmp) value >>= 8
else:
buffer.extend(tmp[::-1])
if self._width.little_endian:
buffer.extend(tmp)
else:
buffer.extend(tmp[::-1])
elif isinstance(value, float):
buffer.extend(struct.pack('>f', value))
return 8 return 8
else: else:
@@ -1112,7 +1125,7 @@ class SendInstruction(SingleInstruction):
else: else:
for scope in self._foreach_parameter.for_scope(context): for scope in self._foreach_parameter.for_scope(context):
yield ResolvedSendInstruction(self, self._build_instruction(scope)) yield ResolvedSendInstruction(self, self._build_instruction(scope))
# tag3
def _build_instruction(self, context: Scope) -> List[int]: def _build_instruction(self, context: Scope) -> List[int]:
buffer = [] buffer = []
shift = 0 shift = 0
+13 -22
View File
@@ -359,6 +359,7 @@ class DeviceParameter(JsonSerialize):
# initial parameter table # initial parameter table
for para_info in library.parameter_table.values(): for para_info in library.parameter_table.values():
# print('DeviceParameter', para_info)
try: try:
para_value = para_info.init_para() para_value = para_info.init_para()
@@ -522,6 +523,7 @@ class DeviceParameter(JsonSerialize):
raise RuntimeError('not a collection parameter ' + para) raise RuntimeError('not a collection parameter ' + para)
target = self._parameter[para] target = self._parameter[para]
# print(table, info, domain, target, str(domain) == '[float]', oper)
if isinstance(target, set): if isinstance(target, set):
old = set(target) old = set(target)
@@ -968,10 +970,11 @@ 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', '_alert', '_alert_time') '_cache_battery', '_cache_battery_timestamp', '_coeff', '_status', '_occupied_by_project')
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
@@ -1008,8 +1011,6 @@ 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
@@ -1093,22 +1094,6 @@ 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]:
@@ -1145,13 +1130,21 @@ class CompletedDevice(Device):
:param value: new parameter P value :param value: new parameter P value
""" """
info = self._library.parameter_table[name] info = self._library.parameter_table[name]
# print('info', info.domain, info.domain == 'float', info.domain == '[float]')
if isinstance(info.domain, ParameterCollectionDomain): if isinstance(info.domain, ParameterCollectionDomain):
self._parameter.oper_parameter(name, value) self._parameter.oper_parameter(name, value)
elif isinstance(info.domain, ParameterFloatDomainType):
self._parameter.set_parameter(name, float(value))
else: else:
try: try:
value = value
# value int # value int
value = int(value) if isinstance(value, str):
if "." in value:
value = float(value)
else:
value = int(value)
except ValueError as e: except ValueError as e:
# value float # value float
self._parameter.set_parameter(name, value) self._parameter.set_parameter(name, value)
@@ -1332,8 +1325,6 @@ 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):
+2
View File
@@ -433,6 +433,7 @@ class ParameterInfo(JsonSerialize):
return self._on_change return self._on_change
def init_para(self) -> Any: def init_para(self) -> Any:
# print('initial', self._name, self._initial)
""" """
:return: initial P value :return: initial P value
:raises value: illegal initial value :raises value: illegal initial value
@@ -617,6 +618,7 @@ class ParameterInfo(JsonSerialize):
return [] return []
elif isinstance(self._value, ComplexExpression): elif isinstance(self._value, ComplexExpression):
print('cast_value_dependency', self._value.dependency)
return self._value.dependency return self._value.dependency
else: else:
+4 -33
View File
@@ -22,8 +22,6 @@ 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=(',', ':'))
@@ -1028,8 +1026,7 @@ 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', '_trigger_time', '_trigger_time_save', '_threshold', '_threshold_percent', '_first_average') '_mini_create_not_done')
def __init__(self, meta: RecordingMetaFile, device_id, database = None): def __init__(self, meta: RecordingMetaFile, device_id, database = None):
self._meta = meta self._meta = meta
@@ -1113,11 +1110,6 @@ 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:
@@ -1243,21 +1235,9 @@ class RecordingFileWriter:
ret.append(str(_max)) ret.append(str(_max))
return ret return ret
def get_data_iter(self, d, mqtt_thread, mqtt_thread_1): def get_data_iter(self, d, mqtt_thread):
# 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]:
@@ -1350,7 +1330,7 @@ class RecordingFileWriter:
return return
# @calculate_time(1) # @calculate_time(1)
def write(self, data: Union[bytes, RecordingData, List[bytes], List[RecordingData]], mqtt_thread, mqtt_thread_1) -> int: def write(self, data: Union[bytes, RecordingData, List[bytes], List[RecordingData]], mqtt_thread) -> 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
@@ -1406,7 +1386,7 @@ class RecordingFileWriter:
'dec': 0, 'dec': 0,
} }
self.get_data_iter(d, mqtt_thread, mqtt_thread_1) self.get_data_iter(d, mqtt_thread)
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():
@@ -1436,15 +1416,6 @@ 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,10 +717,6 @@ 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',
+30 -21
View File
@@ -2,6 +2,8 @@ from time import sleep
from typing import Iterable from typing import Iterable
from datetime import datetime from datetime import datetime
import json import json
import threading
from time import time
import biopro.impl.vcgencmd as vcg import biopro.impl.vcgencmd as vcg
from biopro.data import DataServerOptions, DataAPI from biopro.data import DataServerOptions, DataAPI
@@ -1093,6 +1095,34 @@ class ControlServer(SocketServer, ControlServerAPI):
device.status = 1 device.status = 1
return True return True
elif oper == InternalInstruction.PREDEFINED_COUNTDOWN:
if value == 'PULSE_OUTPUT':
continue_mode = device.get_parameter('CONTINUE_MODE')
if continue_mode == 0:
stop_time = device.get_parameter('STOP_TIME')
# Function to send the instruction
def send_instruction():
device.call_instruction('deactive_electrode')
# Function to wait for 30 seconds and then execute the instruction
def delayed_execution(stop_time):
sleep(stop_time)
send_instruction()
device.set_parameter('ACTIVATE_ELECTRODE', 0)
self.mqtt_thread.broadcast_command('device_refresh')
# Create a thread for the delayed execution
thread = threading.Thread(target=delayed_execution, args=(stop_time,))
# Start the thread
thread.start()
# Main program continues without waiting for the thread
# print("Main program continues...")
# print('COUNTDOWN!!!', device, oper, value)
# print(device.get_parameter(value), device.get_parameter_value(value))
return True
else: else:
return False return False
@@ -1178,8 +1208,6 @@ 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
@@ -1400,25 +1428,6 @@ 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):
+2 -8
View File
@@ -111,10 +111,6 @@ 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)
@@ -191,8 +187,6 @@ 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)
@@ -251,8 +245,7 @@ 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._mqtt_thread) self._writer.write(ret, self._mqtt_send_data_ch_level)
# print('count', self._writer._count)
# print('write time: ', time() - ctime1) # print('write time: ', time() - ctime1)
# print(ret) # print(ret)
@@ -592,6 +585,7 @@ 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:
File diff suppressed because it is too large Load Diff
+343
View File
@@ -0,0 +1,343 @@
{
"name": "CPG",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite-CPG.*",
"major_product_number": 0,
"minor_product_number": 8,
"major_version_number": 0,
"minor_version_number": 1
},
"constant": {
"TIME_MAX": 100000,
"VOLT_MAX": 65536,
"Const_Current_Range": 1500001,
"BLE_WRITE_MAX": 255
},
"parameters": {
"ADC_VALUE_I": {
"description": "ADC value current value",
"domain": "int"
},
"FREQUENCY": {
"description": "FREQUENCY",
"record_meta": true,
"initial": [80, 0],
"domain": {
"list": [1000000]
}
},
"AMPLITUDE": {
"description": "AMPLITUDE",
"record_meta": true,
"initial": [1, 1],
"domain": {
"list": "float"
}
},
"PULSE_WIDTH": {
"description": "PULSE_WIDTH",
"record_meta": true,
"initial": [250, 250],
"domain": {
"list": [1000000]
}
},
"PATTERN_MODE": {
"description": "SELECT_ELECTRODE",
"record_meta": true,
"initial": [1, 0],
"domain": {
"list": "float"
}
},
"CONTINUE_MODE": {
"description": "CONTINUE_MODE",
"record_meta": true,
"initial": 1,
"domain": [2],
"value": {
"expression": "VALUE"
}
},
"STOP_TIME": {
"description": "STOP_TIME",
"record_meta": true,
"initial": 30,
"domain": [1000000],
"value": {
"expression": "VALUE"
}
},
"ELECTRODE_SELECTOR": {
"description": "ELECTRODE_SELECTOR",
"record_meta": true,
"initial": [true,false,true,false],
"domain": {
"list": [2]
}
},
"WORKING_ELECTRODE": {
"description": "WORKING_ELECTRODE",
"record_meta": true,
"initial": [0, 0, 0, 0],
"domain": {
"list": "float"
}
},
"PATTERN_SELECTOR": {
"description": "PATTERN_SELECTOR",
"record_meta": true,
"initial": [0, 0, 1, 1],
"domain": {
"list": "float"
}
},
"ACTIVATE_ELECTRODE": {
"description": "PATTERN_SELECTOR",
"record_meta": true,
"initial": 0,
"domain": "float"
},
"CHANNEL": {
"description": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"CHANNEL_LABEL": {
"description": "channel label",
"record_meta": true,
"domain": "property",
"value": "['current', 'voltage', 'impedance']"
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"initial": 1000,
"domain": [
1001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_sample_rate"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"MODE": {
"description": "working mode",
"record_meta": true,
"initial": 0,
"value": [
"Electrical stimulation",
"Dev Mode"
]
},
"BLE_WRITE": {
"description": "send msg to elite",
"domain": {
"list": [
"BLE_WRITE_MAX"
]
},
"initial": "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]",
"value": "VALUE"
},
"BLE_READ": {
"description": "receive msg from elite",
"domain": "int"
},
"TIME_UNIT": {
"description": "Duration unit",
"initial": 2,
"value": [
"h",
"m",
"s",
"ms"
]
},
"TIME_DURATION": {
"description": "Run duration",
"record_meta": true,
"initial": 0,
"domain": [
"TIME_MAX"
],
"value": {
"expression": "VALUE"
}
}
},
"instruction": {
"output": [
"active_electrode",
"_countdown('PULSE_OUTPUT')"
],
"active_electrode": {
"type": "RIS",
"parameter": {
"ch0": "ELECTRODE_SELECTOR[0]",
"ch1": "ELECTRODE_SELECTOR[1]",
"ch2": "ELECTRODE_SELECTOR[2]",
"ch3": "ELECTRODE_SELECTOR[3]",
"Amp1": {
"expression": "PATTERN_SELECTOR[0]",
"when": {
"0": "AMPLITUDE[0]",
"1": "AMPLITUDE[1]"
}
},
"Freq1": {
"expression": "PATTERN_SELECTOR[0]",
"when": {
"0": "FREQUENCY[0]",
"1": "FREQUENCY[1]"
}
},
"Pulse1": {
"expression": "PATTERN_SELECTOR[0]",
"when": {
"0": "PULSE_WIDTH[0]",
"1": "PULSE_WIDTH[1]"
}
},
"Amp2": {
"expression": "PATTERN_SELECTOR[2]",
"when": {
"0": "AMPLITUDE[0]",
"1": "AMPLITUDE[1]"
}
},
"Freq2": {
"expression": "PATTERN_SELECTOR[2]",
"when": {
"0": "FREQUENCY[0]",
"1": "FREQUENCY[1]"
}
},
"Pulse2": {
"expression": "PATTERN_SELECTOR[2]",
"when": {
"0": "PULSE_WIDTH[0]",
"1": "PULSE_WIDTH[1]"
}
}
},
"data": [
"1XFF;1X02;1XA0;",
"1bch0;1bch1;6b000000;",
"4BAmp1;4BPulse1;4BFreq1;",
"2b00;1bch2;1bch3;4b0000;",
"4BAmp2;4BPulse2;4BFreq2;",
"4BSTOP_TIME;",
"1bch0;1bch1;1bch2;1bch3;4b0000;"
]
},
"deactive_electrode": {
"type": "RIS",
"parameter": {
"ch0": "ELECTRODE_SELECTOR[0]",
"ch1": "ELECTRODE_SELECTOR[1]",
"ch2": "ELECTRODE_SELECTOR[2]",
"ch3": "ELECTRODE_SELECTOR[3]"
},
"data": [
"1XFF;1X02;1XA1;",
"1bch0;1bch1;1bch2;1bch3;4b0000;"
]
},
"resume_electrode_0": {
"type": "RIS",
"data": [
"1XFF;1X02;1X06;4b1000;4b0000;"
]
},
"resume_electrode_1": {
"type": "RIS",
"data": [
"1XFF;1X02;1X06;4b0100;4b0000;"
]
},
"resume_electrode_2": {
"type": "RIS",
"data": [
"1XFF;1X02;1X06;4b0010;4b0000;"
]
},
"resume_electrode_3": {
"type": "RIS",
"data": [
"1XFF;1X02;1X06;4b0001;4b0000;"
]
},
"suspend_electrode_0": {
"type": "RIS",
"data": [
"1XFF;1X02;1X05;4b1000;4b0000;"
]
},
"suspend_electrode_1": {
"type": "RIS",
"data": [
"1XFF;1X02;1X05;4b0100;4b0000;"
]
},
"suspend_electrode_2": {
"type": "RIS",
"data": [
"1XFF;1X02;1X05;4b0010;4b0000;"
]
},
"suspend_electrode_3": {
"type": "RIS",
"data": [
"1XFF;1X02;1X05;4b0001;4b0000;"
]
},
"idle": [
"_idle()"
],
"data_format": [
"_data_format('I4V4Z4T4')",
{
"expression": "MODE",
"when": {
"0": "_disable_cache(True)",
"1": "_disable_cache(True)",
"*": "_disable_cache(False)"
}
}
],
"ble_instru_send": [
"ble_write",
"_cdr('20X>ADC_VALUE_I')"
],
"ble_write": {
"type": "ALL",
"data": [
"255X>BLE_WRITE;"
]
},
"dev_version": [
"CIS_VERSION",
"_cdr('20X>ADC_VALUE_I')"
],
"dev_battery": [
"CIS_VOLT",
"_cdr('20X>ADC_VALUE_I')"
],
"set_para_DAC_VOLT": {
"type": "RIS",
"data": [
"XE2;X01;2B>DAC_VOLT"
]
}
}
}
@@ -43,28 +43,6 @@
}, },
"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,
@@ -1,12 +1,12 @@
{ {
"name": "Elite_EDC_1.5re", "name": "Elite_EDC_1.5r2",
"version": "1.2.30", "version": "1.2.30",
"match_rule": { "match_rule": {
"local_name_pattern": "Elite.*", "local_name_pattern": "Elite.*",
"major_product_number": 0, "major_product_number": 0,
"minor_product_number": 2, "minor_product_number": 2,
"major_version_number": 1, "major_version_number": 1,
"minor_version_number": 7 "minor_version_number": 8
}, },
"constant": { "constant": {
"TIME_MAX": 100000, "TIME_MAX": 100000,
@@ -931,28 +931,6 @@
"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,28 +931,6 @@
"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,
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
{
"name": "PEL_1.0",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite-PEL.*",
"major_product_number": 0,
"minor_product_number": 7,
"major_version_number": 0,
"minor_version_number": 0
},
"constant": {
"TIME_MAX": 100000,
"VOLT_MAX": 65536,
"Const_Current_Range": 1500001,
"BLE_WRITE_MAX": 255
},
"parameters": {
"CHANNEL": {
"description": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"initial": 1000,
"domain": [
1001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_sample_rate"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"PATTERN_SWITCH": {
"description": "switch of pattern",
"initial": 1,
"domain": [
2
],
"value": {
"expression": "VALUE"
}
},
"PULSE": {
"description": "Pulse Mode Segment Duration 2",
"record_meta": true,
"initial": [
0,
0,
0
],
"domain": {
"list": [
66536
]
},
"value": {
"expression": "VALUE"
}
},
"PATTERN": {
"description": "Pulse Mode Segment Duration 2",
"record_meta": true,
"initial": 1,
"domain": [
44
],
"value": {
"expression": "VALUE"
}
}
},
"instruction": {
"start": [
"pulse_e_load"
],
"pulse_e_load": [
{
"expression": "PATTERN_SWITCH",
"when": {
"0": "pattern",
"1": "manual"
}
}
],
"manual": {
"type": "RIS",
"parameter": {
"pa": "PULSE[0]",
"pb": "PULSE[1]",
"pc": "PULSE[2]"
},
"data": [
"XFF;",
"X62;",
"4b1;4b>pa;4b>pb;4b>pc;"
]
},
"pattern": {
"type": "RIS",
"parameter": {
"pa": "PATTERN"
},
"data": [
"XFF;X62;",
"4b>0;12b>pa;"
]
},
"ble_instru_send": [
"ble_write",
"_cdr('20X>ADC_VALUE_I')"
]
}
}
+155
View File
@@ -0,0 +1,155 @@
{
"name": "PEL_2.0",
"version": "1.2.30",
"match_rule": {
"local_name_pattern": "Elite-PEL.*",
"major_product_number": 0,
"minor_product_number": 7,
"major_version_number": 0,
"minor_version_number": 1
},
"constant": {
"TIME_MAX": 100000,
"VOLT_MAX": 65536,
"Const_Current_Range": 1500001,
"BLE_WRITE_MAX": 255
},
"parameters": {
"CHANNEL": {
"description": "record channels",
"record_meta": true,
"domain": "property",
"value": "[0, 1, 2]"
},
"SAMPLE_RATE": {
"description": "data sampling rate",
"record_meta": true,
"initial": 1000,
"domain": [
1001
],
"value": {
"expression": "VALUE"
},
"on_change": "set_sample_rate"
},
"AMP_GAIN": {
"description": "amp gain",
"record_meta": true,
"domain": "constant",
"value": 1
},
"MODE": {
"description": "working mode",
"record_meta": true,
"initial": 0,
"value": [
"Select Resistor Mode",
"Dev Mode"
]
},
"BLE_WRITE": {
"description": "send msg to elite",
"domain": {
"list": [
"BLE_WRITE_MAX"
]
},
"initial": "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]",
"value": "VALUE"
},
"BLE_READ": {
"description": "receive msg from elite",
"domain": "int"
},
"ADC_VALUE_I": {
"description": "ADC value current value",
"domain": "int"
},
"PATTERN_SWITCH": {
"description": "switch of pattern",
"initial": 1,
"domain": [
2
],
"value": {
"expression": "VALUE"
}
},
"PULSE": {
"description": "Pulse Mode Segment Duration 2",
"record_meta": true,
"initial": [
0,
0,
0
],
"domain": {
"list": [
66536
]
},
"value": {
"expression": "VALUE"
}
},
"PATTERN": {
"description": "Pulse Mode Segment Duration 2",
"record_meta": true,
"initial": 1,
"domain": [
44
],
"value": {
"expression": "VALUE"
}
}
},
"instruction": {
"start": [
"pulse_e_load"
],
"pulse_e_load": [
{
"expression": "PATTERN_SWITCH",
"when": {
"0": "pattern",
"1": "manual"
}
}
],
"manual": {
"type": "RIS",
"parameter": {
"pa": "PULSE[0]",
"pb": "PULSE[1]",
"pc": "PULSE[2]"
},
"data": [
"XFF;",
"X62;",
"4b1;4b>pa;4b>pb;4b>pc;"
]
},
"pattern": {
"type": "RIS",
"parameter": {
"pa": "PATTERN"
},
"data": [
"XFF;X62;",
"4b>0;12b>pa;"
]
},
"ble_instru_send": [
"ble_write",
"_cdr('20X>ADC_VALUE_I')"
],
"ble_write": {
"type": "ALL",
"data": [
"255X>BLE_WRITE;"
]
}
}
}