Compare commits

..

3 Commits

Author SHA1 Message Date
Roy 5e7c0710b9 flow of power on controller 2022-01-07 16:14:06 +08:00
Roy 5131535bda merge Spi & mitiSpi class 2022-01-03 11:08:33 +08:00
Roy ac5a6ef77a [update] data length: 120 bytes 2021-12-22 14:39:28 +08:00
173 changed files with 3229 additions and 8438 deletions
-9
View File
@@ -1,9 +0,0 @@
.DS_Store
*.cpython-37.opt-2.pyc
*.cpython-39-arm-linux-gnueabihf.so
*.
*.pyc
*/__pycache__
/.vscode
/media
python/biopro/sever/_identify.py
+38 -174
View File
@@ -4,7 +4,7 @@ New goal on pi4 without kotlin and django, and with only one dataserver.
# Installation
### Get Raspbrain into SD card
###### Download Raspbian Bullseye Lite
###### Download Raspbian Buster Lite
>https://www.raspberrypi.org/downloads/raspberry-pi-os/
@@ -14,7 +14,7 @@ New goal on pi4 without kotlin and django, and with only one dataserver.
lsblk
# copy data to sd card
sudo dd bs=4M if=yyyy-mm-dd-raspios-bullseye-armhf-lite.img of=/dev/sdX conv=fsync status=progress
sudo dd bs=4M if=yyyy-mm-dd-raspbian-buster.img of=/dev/sdX conv=fsync status=progress
# enable ssh on first boot
add ssh.txt in boot folder
@@ -77,22 +77,18 @@ add ssh.txt in boot folder
###### Install dependencies
sudo apt-get install vim git wiringpi build-essential libi2c-dev i2c-tools python-dev libffi-dev python3-pip postgresql npm
###### wiringpi conflict
need to install wiringpi: https://github.com/WiringPi/WiringPi
install instructions: https://github.com/WiringPi/WiringPi/blob/master/INSTALL
sudo apt-get install vim git wiringpi build-essential libi2c-dev i2c-tools python-dev libffi-dev python3-pip postgresql
### Setup project
###### Clone data server project
###### Clone project
```
cd ~
git clone https://gitlab.com/wisetop/controller/wisetopdataserver.git
git clone https://gitlab.com/bioproscientific/bioprorelife.git
```
###### Install python dependencies
```
cd ~/wisetopdataserver
cd ~/bioprorelife
pip3 install -r requirements.txt
```
##### Packages version
@@ -116,14 +112,14 @@ psycopg2 2.8.5
###### Build C extension
```
cd ~/wisetopdataserver/python/biopro/ext/
cd ~/bioprorelife/python/biopro/ext/
rm -rf *.so build
python3 setup.py build_ext --inplace
```
###### Check setup-uart file
```
cd ~/wisetopdataserver/
cd ~/bioprorelife
sudo vim ./run-setup-uart.sh
```
##### Modify the code as following
@@ -153,72 +149,44 @@ sudo raspi-config
reboot
```
###### Set pi password
```
$cd ~
$passwd
Current password:raspberry
New password:5k6zj454778225
Retype new password:5k6zj454778225
```
###### Auto run program when device start
```
sudo vim /etc/rc.local
```
###### add the code following as:
add the code following as:
```
sudo -H -u pi nohup /home/pi/wisetopdataserver/run-data-server.sh > /home/pi/.data_server.out &
#sudo -H -u pi nohup /home/pi/wisetopdataserver/run-data-server.sh > /home/pi/.wisetop_data_$now.out &
sudo -H -u pi nohup /home/pi/bioprorelife/run-data-server.sh > /home/pi/.data_server.out &
```
##### /etc/rc.local
```
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
printf "My IP address is %s\n" "$_IP"
fi
now=$(date +'%F_%T')
sudo nohup systemctl restart apache2
sudo nohup systemctl restart mosquitto
nohup node /home/pi/wisetopapiserver/dist/app.js > /home/pi/.api_server.out &
sleep 5
sudo -H -u pi nohup /home/pi/wisetopdataserver/run-data-server.sh > /home/pi/.data_server.out &
#sudo -H -u pi nohup /home/pi/wisetopdataserver/run-data-server.sh > /home/pi/.wisetop_data_$now.out &
exit 0
```
### MQTT broker install
###### 1.Install mosquitto
$sudo apt-get install mosquitto mosquitto-clients
###### 2.Enable Remote Access
$sudo vim /etc/mosquitto/conf.d/default.conf
1. Install mosquitto
```
sudo apt-get install mosquitto mosquitto-clients
```
2. Enable Remote Access
```
sudo vim /etc/mosquitto/conf.d/default.conf
```
add the code following as:
```
listener 1883
protocol mqtt
listener 8083
protocol websockets
allow_anonymous true
```
###### 3.restart mqtt broker
$sudo systemctl restart mosquitto
restart mqtt broker
```
sudo systemctl restart mosquitto
```
###### 4.Robust MQTT
3. Robust MQTT
create mosquitto_restart.sh
$cd ~
$sudo vim mosquitto_restart.sh
```
cd ~
sudo vim mosquitto_restart.sh
```
add the code following as:
```
if [ "`ps -aux | grep /usr/sbin/mosquitto | wc -l`" == "1" ]
@@ -232,129 +200,25 @@ fi
echo "$SERVICE is currently running" >> /home/ubuntu/cron.log
exit 0
```
###### 5.add mosquitto_restart.sh to root routing
$sudo chmod +x mosquitto_restart.sh
$sudo -i
$crontab -e
add mosquitto_restart.sh to root routing
```
sudo chmod +x mosquitto_restart.sh
sudo -i
crontab -e
```
add the code following as:
```
*/5 * * * * /home/pi/mosquitto_restart.sh
```
###### 6.Auto run MQTT broker when when pi-start
$sudo vim /etc/rc.local
3. Auto run MQTT broker when when pi-start
```
sudo vim /etc/rc.local
```
add the code following as:
```
sudo nohup systemctl restart mosquitto
```
### Setting up a Routed Wireless Access Point
###### 1.Install AP and Management Software
$sudo apt install hostapd
$sudo systemctl unmask hostapd
$sudo systemctl enable hostapd
$sudo apt install dnsmasq
$sudo DEBIAN_FRONTEND=noninteractive apt install -y netfilter-persistent iptables-persistent
###### 2.Set up the Network Router
$sudo nano /etc/dhcpcd.conf
```
interface wlan0
static ip_address=192.168.2.1/24
nohook wpa_supplicant
```
$sudo nano /etc/sysctl.d/routed-ap.conf
```
# Enable IPv4 routing
net.ipv4.ip_forward=1
```
$sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
$sudo netfilter-persistent save
$sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig
$sudo nano /etc/dnsmasq.conf
```
interface=wlan0 # Listening interface
dhcp-range=192.168.2.2,192.168.2.255,255.255.255.0,24h
# Pool of IP addresses served via DHCP
domain=wlan # Local wireless DNS domain
address=/gw.wlan/192.168.2.1
# Alias for this router
```
###### 3.Ensure Wireless Operation
$sudo rfkill unblock wlan
###### 4.Configure the AP Software
$sudo nano /etc/hostapd/hostapd.conf
```
country_code=TW
interface=wlan0
ssid=WTP_NONE
hw_mode=g
channel=7
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=00000000
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
# 802.11n support
ieee80211n=1
# make raspberry pi dongle fast
wme_enabled=1
# ht_capab: HT capabilities, enable 40MHz
ht_capab=[HT40+][SHORT-GI-40]
```
###### 5.Running the new Wireless AP
$sudo systemctl reboot
### Set web site
$sudo vim ./sites-available/000-default.conf
```
<VirtualHost *:8080> #80------>8080
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
#ServerName www.example.com
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
</VirtualHost>
<VirtualHost *:80>
ServerAdmin bioprovue@localhost
DocumentRoot /var/www/bioprovuebuild
</VirtualHost>
```
### Setup routed wireless access point
##### raspberry website document:
> https://www.raspberrypi.org/documentation/configuration/wireless/access-point-routed.md
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -4
View File
@@ -18,10 +18,9 @@ class ControllerAPI():
@staticmethod
def getByMac(mac_address):
try:
if mac_address != None:
ret = requests.get(API_URL + 'api/controller/get_by_mac/' + mac_address, headers= AuthAPI.get_key())
if ret.status_code == 200:
return ret.json()
ret = requests.get(API_URL + 'api/controller/get_by_mac/' + mac_address, headers= AuthAPI.get_key())
if ret.status_code == 200:
return ret.json()
except (requests.exceptions.ConnectionError, json.decoder.JSONDecodeError) as e:
print('get controller fail', e)
return []
-8
View File
@@ -1576,15 +1576,7 @@ class ControlAPI(metaclass=Router):
should handle this exit code and re-run server process.
"""
raise NotImplementedError()
def run_project(self, project) -> bool:
raise NotImplementedError()
def get_running_project(self) -> bool:
raise NotImplementedError()
def stop_project(self, project) -> bool:
raise NotImplementedError()
# noinspection PyAbstractClass
class ControlClient(SocketClient, ControlAPI, metaclass=SocketClientMacro(ControlAPI)):
+248 -119
View File
@@ -315,6 +315,14 @@ class CC2650MasterDevice(MasterDevice, metaclass=abc.ABCMeta):
return cls.CC2650_RESET_PIN
@classmethod
def get_uart_irq_pin(cls) -> OutputPin:
if cls.CC2650_UART_IRQ is None:
cls.CC2650_UART_IRQ = OutputPin.get_used(P3Pin.MEM_RST, initial=True)
return cls.CC2650_UART_IRQ
class CC2650Device(Device):
def __init__(self,
@@ -536,7 +544,7 @@ class CC2650Device(Device):
self._master.log_warn('device', self.device_id, 'update_battery_info no response')
else:
if data is not None and len(data) > 2 :
if data is not None and len(data) == 4 :
battery = struct.unpack('<H', data[1:3])[0]
if battery is not None:
self._battery = battery
@@ -947,6 +955,9 @@ class CC2650SingleMasterDevice(CC2650MasterDevice, Synchronized):
self._cc2650.recv_util(..., when=self._cond, timeout=timeout)
def available_device(self) -> List[int]:
return [0]
@synchronized
def scan_callback(self, callback: Callable[[DeviceResponseInfo], None], timeout=5, all_device=False) -> bool:
if self._scan_max_time != timeout:
@@ -1431,6 +1442,11 @@ class CC2650MultiMasterDevice(CC2650MasterDevice, Synchronized):
super().shutdown()
self._selector.close()
def available_device(self) -> List[int]:
return list(map(lambda it: it[0],
filter(lambda it: it[1] is not None,
enumerate(self._cc2650))))
def get_device(self, device: int) -> Optional[Device]:
return self._device[device]
@@ -1926,34 +1942,44 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
self.log_verbose('reset (hardware)')
pin.output(False)
# sleep(0.001)
sleep(0.1)
pin.output(True)
sleep(0.1)
@synchronized
def reset_software(self):
"""software reset cc2650 (master)"""
# print('resettt---------14')
# print()
# self.log_verbose('reset (software)')
self.log_verbose('reset (software)')
# try:
# #self._cc2650.send("3B", 1, 0, 0)
# pass
try:
#self._cc2650.send("3B", 1, 0, 0)
pass
# except SerialTimeoutException as e:
# raise RecvTimeout('device CC2650 reset fail') from e
except SerialTimeoutException as e:
raise RecvTimeout('device CC2650 reset fail') from e
finally:
sleep(0.1)
# finally:
# sleep(0.1)
print("pass")
print()
@synchronized
def init_hardware(self, timeout=1):
"""initialize cc2650 (master)"""
pass
def available_device(self) -> List[int]:
return [0]
def scan_send_ins(self):
# send scan command
try:
print(':: scan_send_ins ::', self.CC2650_COMMAND_LEN, 3, 0, 0)
print()
self._cc2650.send(self.CC2650_COMMAND_LEN, 3, 0, 0)
except SerialTimeoutException as e:
@@ -1965,16 +1991,21 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
clean_buf = self._cc2650.receive_timeout("20B", timeout=3)
print("clean_buf = ", clean_buf)
# def cc2650_uart_irq(self):
# uart_irq = self.get_uart_irq_pin()
# uart_irq.output(False)
# sleep(0.001)
# uart_irq.output(True)
def cc2650_uart_irq(self):
uart_irq = self.get_uart_irq_pin()
uart_irq.output(False)
sleep(0.001)
uart_irq.output(True)
@synchronized
def scan_callback(self, callback: Callable[[DeviceResponseInfo], None], timeout=5, all_device=False) -> bool:
# if self._scan_max_time != timeout:
# self.log_info('set max scan limit time', timeout)
# self._scan_max_time = timeout
self._found = found = []
self._found_with_id = []
number_of_device = 0
hdr_BPHS = [66, 80, 72, 83]
scan_response: Union[Optional[tuple], Any] = None
@@ -1984,16 +2015,42 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
scan_ins.append(1) #length
scan_ins.append(0xF1)
# print('send_scan', bytes(scan_ins))
print('send_scan', list(scan_ins))
self._cc2650.send("bytes", bytes(scan_ins))
# for wait_scan_response in range(5):
# try:
# scan_response = self._cc2650.recv_uart(0.2)
# except RecvTimeout:
# if wait_scan_response == 4:
# self.reset_internal()
# self.reset_hardware()
# self._interface.flush()
# return True
# else:
# self.log_info("wait for scan response")
# continue
# # check if response is valid
# if scan_response is None:
# if wait_scan_response == 4:
# self.reset_internal()
# self.reset_hardware()
# self._interface.flush()
# else:
# self.log_info("wait for scan response")
# continue
# else:
# break
try:
scan_response = self._cc2650.recv_uart(timeout)
scan_response = self._cc2650.recv_uart(0.2)
except RecvTimeout:
# self.reset_internal()
# self.reset_hardware()
# self._interface.flush()
return False
self.reset_internal()
self.reset_hardware()
self._interface.flush()
return True
# instruction format:
# ins[0]: get_scan_response = 0x04
@@ -2035,7 +2092,6 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
# print("local_name = ", local_name)
# addr type is don't care in BMD380
print('scan_response:', list(scan_response))
local_addr_type = int(scan_response[index:index + 1][0])
# local_addr_type = 0xFF
index = index + 1
@@ -2089,35 +2145,6 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
def found(self) -> List[DeviceResponseInfo]:
return self._found
def check_mem_survive(self) -> Optional[CC2650Device]:
ack = []
ins = bytearray()
ins.append(10)
ins.append(1) #length
ins.append(0xF1)
# print('ins', list(ins))
try:
self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException as e:
raise RecvTimeout('device CC2650 check_mem_survive timeout') from e
else:
try:
ack = self._cc2650.recv_uart(0.001)
except RecvTimeout:
self.log_info("no memory board")
# else:
# print('ack=', ack)
if ack == [3]:
return True
return False
@synchronized
def connect(self, response: DeviceResponseInfo, direct_connect: bool = False) -> Optional[CC2650Device]:
if self._handle is not None:
@@ -2148,6 +2175,7 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
# send device mac and addrType
try:
print('send_connect..',list(connect_ins))
self._cc2650.send("bytes", bytes(connect_ins))
except SerialTimeoutException as e:
@@ -2180,39 +2208,26 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
# send device mac and addrType
try:
# print('send_connect',bytes(connect_ins))
print('send_connect',list(connect_ins))
self._cc2650.send("bytes", bytes(connect_ins))
except SerialTimeoutException as e:
raise RecvTimeout('device CC2650 connect fail') from e
else:
sleep(2)
# connection establish done?
for retry_recv_ack in range(5):
self._cc2650.send("bytes", bytes((0, 0, 0, 0)))
sleep(1)
try:
con_done = self._cc2650.recv_uart(timeout = 0.1)
except RecvTimeout:
self.log_info("recv connection timeout, retry... ")
continue
self.log_info("recv connection timeout, fail... ")
# is the ack valid?
if con_done is None:
continue
elif con_done[0] is 46 and \
if con_done[0] is 46 and \
con_done[1] is 80 and \
con_done[2] is 48 and \
con_done[3] is 4:
connected = True
# print('con_done=', con_done)
break
else:
continue
print('con_done=', con_done)
# if select device is invalid or connect failed
self._interface.flush_input()
@@ -2233,6 +2248,36 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
return ret
def check_mem_survive(self) -> Optional[CC2650Device]:
ack = []
ins = bytearray()
ins.append(10)
ins.append(1) #length
ins.append(0xF1)
try:
self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException as e:
raise RecvTimeout('device CC2650 check_mem_survive timeout') from e
else:
# sleep(1)
try:
ack = self._cc2650.recv_uart(timeout = 0.1)
except RecvTimeout:
self.log_info("no survive device")
else:
print('ack=', ack)
if ack == [3]:
return True
return False
@synchronized
def disconnect(self, device: int, force=False) -> bool:
self.log_info(DEVICE_DISCONNECTING, device)
@@ -2243,7 +2288,7 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
ins.append(0xF1)
try:
# print('send_disconnect',bytes(ins))
print('send_disconnect',list(ins))
self._cc2650.send("bytes", bytes(ins))
except RecvTimeout:
@@ -2278,28 +2323,20 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
ins.append(handle)
ins.append(0xF1)
for _ in range(2):
try:
# print('send_read',bytes(ins))
self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException:
raise RecvTimeout('device CC2650 send read_characteristic fail')
try:
print('read_characteristic',list(ins))
self._cc2650.send("bytes", bytes(ins))
except SerialTimeoutException:
raise RecvTimeout('device CC2650 send read_characteristic fail')
try:
ret = self._cc2650.recv_uart(timeout = 2)
except Exception as e2:
raise RecvTimeout()
else:
# print("======== read ret = ", ret)
self._interface.flush_input()
# try:
# ret = self._cc2650.recv_uart(timeout = 1)
# except Exception as e2:
# raise RecvTimeout()
# else:
# # print("read_characteristic ret = ", ret)
# pass
try:
ret = self._cc2650.recv_uart(timeout = 2)
except Exception as e2:
raise RecvTimeout()
else:
# print("======== read ret = ", ret)
self._interface.flush_input()
if ret is None:
return None
@@ -2327,23 +2364,21 @@ class CC2650SingleMasterCentralDevice(CC2650MasterDevice, Synchronized):
try:
self._cc2650.send("bytes", bytes(data_array))
# print('send_write',bytes(data_array))
self._cc2650.send("bytes", list(data_array))
print('send_write',list(data_array))
except SerialTimeoutException as e:
raise RecvTimeout('device CC2650 send instruction fail') from e
# read error code
try:
ret = self._cc2650.recv_uart()
ret = self._cc2650.recv_uart(timeout = 0.1)
print('ret=', ret)
# print("_______ write ack = ", ret)
except Exception as e2:
pass
else:
pass
self._interface.flush_input()
self._interface.flush_output()
@@ -2520,44 +2555,143 @@ class CC2650MultiMasterCentralDevice(CC2650MasterDevice, Synchronized):
else:
self.log_warn('cannot %s notify for device : %s' % (message, device.__class__.__name__))
# def reset(self, device: Optional[List[int]] = None, software_reset=True):
# print('resettt---------15')
# print()
# # self.log_verbose('reset')
# # reset hardware
# # print("reset device = ", device)
# print('device:', device, software_reset)
# print('self._device', list(self._device))
# print('self._cc2650_once:', list(self._cc2650))
# print('|')
# print('|')
# for i in range(len(self._cc2650)):
# print('i', i)
# if device is None or i in device:
# self._device[i] = None
# m = self._cc2650[i]
# if m is None:
# m = CC2650SingleMasterCentralDevice(self._master, self._interface, self._options)
# self._interface.flush()
# self._selector.select(i)
# try:
# self.log_info('reset device', i)
# with m:
# m.reset_internal()
# m.reset_hardware()
# if software_reset:
# check_mem_survive_flag = m.check_mem_survive()
# if check_mem_survive_flag:
# self._cc2650[i] = m
# except RecvTimeout:
# self.log_warn('reset device', i, 'fail')
# self._cc2650[i] = None
# print('self._cc2650::', list(self._cc2650))
# return
def reset(self, device: Optional[List[int]] = None, software_reset=True):
print('resettt---------15')
print()
# self.log_verbose('reset')
# reset hardware
# print("reset device = ", device)
print('device:', device, software_reset)
print('self._device', list(self._device))
print('self._cc2650_once:', list(self._cc2650))
print('|')
print('|')
for i in range(len(self._cc2650)):
if device is None or i in device:
if device is None:
self._device[i] = None
m = self._cc2650[i]
if m is None:
m = CC2650SingleMasterCentralDevice(self._master, self._interface, self._options)
self._cc2650[i] = m
self._interface.flush()
self._selector.select(i)
try:
self.log_info('reset device', i)
with m:
m.reset_internal()
m.reset_hardware()
m.reset_software()
if not software_reset:
check_mem_survive_flag = m.check_mem_survive()
if check_mem_survive_flag:
self._cc2650[i] = m
except RecvTimeout:
self.log_warn('reset device', i, 'fail')
self._cc2650[i] = None
if not software_reset:
return
print('self._cc2650::', list(self._cc2650))
def _foreach_empty_master(self) -> Iterable[int]:
for i in range(len(self._device)):
return
def reset_empty_master(self, device: Optional[List[int]] = None, software_reset=True):
self.log_verbose('reset')
# reset hardware
# print("reset device = ", device)
for i in range(len(self._cc2650)):
# print(i)
m = self._cc2650[i]
d = self._device[i]
if d is None:
# print(m, d)
if m is not None and d is None:
m = CC2650SingleMasterCentralDevice(self._master, self._interface, self._options)
# m.set_log_level(self._cc2650_log_level)
self._interface.flush()
self._selector.select(i)
self._mem_selector.select(i)
try:
self.log_info('reset device', i)
with m:
m.reset_internal()
m.reset_hardware()
except RecvTimeout:
self.log_warn('reset device', i, 'fail')
self._cc2650[i] = None
def available_device(self) -> List[int]:
return list(map(lambda it: it[0],
filter(lambda it: it[1] is not None,
enumerate(self._cc2650))))
def _foreach_empty_master(self) -> Iterable[int]:
for i in range(len(self._cc2650)):
m = self._cc2650[i]
d = self._device[i]
if m is not None and d is None:
yield i
def scan_callback(self, callback: Callable[[DeviceResponseInfo], None], timeout=5, all_device=False) -> bool:
@@ -2565,35 +2699,28 @@ class CC2650MultiMasterCentralDevice(CC2650MasterDevice, Synchronized):
all_result = False
for device in range(len(self._cc2650)):
for device in self._foreach_empty_master():
self.log_verbose('use', device)
m = self._cc2650[device]
d = self._device[device]
if d is None:
if m is not None and d is None:
error = None
with self:
self._interface.flush()
self._selector.select(device)
# self._mem_selector.select(device)
try:
if m.check_mem_survive() == False:
continue
result = m.scan_callback(callback, timeout=0.001)
sleep(0.0001)
result = m.scan_callback(callback, timeout=timeout)
except RuntimeError as e:
error = e
else:
if result == False:
result = True
all_result = all_result or result
continue
all_result = all_result or result
if result and not all_device:
@@ -2601,7 +2728,8 @@ class CC2650MultiMasterCentralDevice(CC2650MasterDevice, Synchronized):
if error is not None:
self.log_warn('suppressed error : ' + str(error))
# self.reset(device)
sleep(1)
return all_result
def found(self) -> List[DeviceResponseInfo]:
@@ -2654,6 +2782,7 @@ class CC2650MultiMasterCentralDevice(CC2650MasterDevice, Synchronized):
with self:
self._interface.flush()
self._selector.select(master)
# self._mem_selector.select(device)
sleep(0.0001)
d = m.connect(response, direct_connect)
+17 -14
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.
@@ -262,12 +261,14 @@ class DeviceManager(MasterDevice, Synchronized):
__slots__ = ('_handler', '_options',
'_library_path', '_library',
'_interface', '_master', '_found_history', '_found', '_device', '_master_log_level')
'_interface', '_master', '_found_history', '_found', '_device', '_master_log_level',
'_mem_survive_channel')
def __init__(self,
options: DeviceManagerOptions,
handler: Optional[DeviceInternalCommandHandler] = None):
"""
:param options: manager options.
:param handler: device internal command handler
"""
@@ -277,7 +278,6 @@ class DeviceManager(MasterDevice, Synchronized):
if handler is None:
handler = DeviceInternalCommandHandler()
# control server api
self._handler: DeviceInternalCommandHandler = handler
self._options: DeviceManagerOptions = options
@@ -287,9 +287,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 +337,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"""
@@ -388,8 +382,16 @@ class DeviceManager(MasterDevice, Synchronized):
"""reset master device"""
self._device.clear()
self._demo.clear()
print('resettt---------21')
print()
self._centralMaster.reset(device, software_reset)
def get_mem_survive_channel(self):
return self._mem_survive_channel
def available_device(self) -> List[int]:
return self._centralMaster.available_device()
@logging_info
def shutdown(self, release_resource=True):
# shutdown reset memeryboard
@@ -579,11 +581,6 @@ class DeviceManager(MasterDevice, Synchronized):
if slave.device_id == device:
return slave
elif isinstance(device, str):
for slave in self._device:
if ':'.join('{:02x}'.format(b) for b in slave.mac_address) == device.lower():
return slave
elif isinstance(device, DeviceInfo):
for slave in self._device:
if device.match(slave):
@@ -1030,9 +1027,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)
+55 -72
View File
@@ -6,8 +6,6 @@ from datetime import datetime
from biopro.recording import RecordingData, RecordingFileDataFormat
import numpy as np
# from biopro.util.console import hex_line
T = TypeVar('T')
@@ -883,24 +881,20 @@ class I4V4Z4T4DataDecoder(RecDataDecoder):
def decode(self, data: bytes) -> Optional[RecordingData]:
if len(data) < 18:
return None
voltage = 0
mem_cnt = data[1]
time_stamp: float = struct.unpack('<I', data[4:8])[0] # unit: ms 0x18030000
current = struct.unpack('<i', data[8:12])[0] # unit: nA
ch2 = struct.unpack('<i', data[12:16])[0] # unit: uV
impedance = struct.unpack('<i', data[16:20])[0] # unit: mOm
if self._mode == 16:
voltage = impedance - ch2
else:
voltage = ch2
cycle_number = struct.unpack('<H', data[20:22])[0]
finish_mode_falg = data[22]
battery = struct.unpack('<i', data[23:27])[0]
elite_notify_times = data[27]
current = struct.unpack('>i', data[1:5])[0] # unit: 1/1000 nA
voltage = struct.unpack('>i', data[5:9])[0] # unit: mV
impedance = struct.unpack('>i', data[9:13])[0] # unit: kOm
time_stamp: float = struct.unpack('<I', data[13:17])[0] # unit: ms
cycle_number = struct.unpack('>H', data[17:19])[0]
finish_mode_falg = data[19]
mem_wrong_information = struct.unpack('<i', data[43:47])[0] # mem_wrong_information = green retry, green wrong, red retry, red wrong
ram_num = data[47]
# print('decode', list(data[20:]))
mem_wrong = data[120]
mem_retry_cnt = data[121]
mem_green_wrong = data[122]
mem_green_retry_cnt = data[123]
ram_num = data[124]
broken_flag = data[-1]
if (finish_mode_falg & 0b11110000 == 0b10100000):
@@ -921,13 +915,10 @@ class I4V4Z4T4DataDecoder(RecDataDecoder):
print("error timeStamp full data:", list(data), datetime.now(), '\n')
return None
else:
# print('|', time_stamp, '|', delta, '|', int(time_stamp * 1000 / 2),
# '|', current, '|', voltage, '|', impedance,
# print('|', time_stamp, '|', delta, '|', current, '|', voltage, '|', impedance,
# '|', cycle_number, '|', finishMode, '@', str(self.device))
# print('|', '{:10}'.format(time_stamp),
# '|', '{:4}'.format(delta),
# '|', '{:10}'.format(int(time_stamp * 1000 / 2)),
# '|', '{:10}'.format(delta),
# '|', '{:10}'.format(current),
# '|', '{:10}'.format(voltage),
# '|', '{:10}'.format(impedance),
@@ -935,9 +926,12 @@ class I4V4Z4T4DataDecoder(RecDataDecoder):
# '|', '{:1}'.format(finishMode),
# '@', str(self.device), '|')
# print('|', '{:5}'.format(mem_wrong_information),
# '|', '{:2}'.format(ram_num),
# '|', '{:2}'.format(broken_flag),
# print('|', '{:5}'.format(mem_wrong),
# '|', '{:5}'.format(mem_retry_cnt),
# '|', '{:5}'.format(mem_green_wrong),
# '|', '{:5}'.format(mem_green_retry_cnt),
# '|', '{:5}'.format(ram_num),
# '|', '{:5}'.format(broken_flag),
# '@', str(self.device), '|')
pass
@@ -952,20 +946,14 @@ class I4V4Z4T4DataDecoder(RecDataDecoder):
ret.append_data(1, voltage)
ret.append_data(2, impedance)
ret.append_data(3, cycle_number)
# ret.append_data(4, battery)
# ret.append_data(5, elite_notify_times)
# ret.append_data(6, mem_cnt)
# # memoryboard information
# ret.append_data(7, ram_num)
# ret.append_data(8, broken_flag)
# try:
# ret.append_data(9, mem_wrong_information)
# # print('append_data success, mem_wrong_information:', mem_wrong_information, hex(mem_wrong_information))
# except:
# print('append_data fail, mem_wrong_information:', mem_wrong_information, hex(mem_wrong_information))
# memoryboard information
ret.append_data(4, mem_wrong)
ret.append_data(5, mem_retry_cnt)
ret.append_data(6, mem_green_wrong)
ret.append_data(7, mem_green_retry_cnt)
ret.append_data(8, ram_num)
ret.append_data(9, broken_flag)
if cycle_number != self._cycle_number:
# notify cycle_number change
@@ -1390,46 +1378,47 @@ class EISZeroOneDataDecoder(RecDataDecoder):
index = 20
for i in range(index, index+16, 8):
phase_para_a.append(struct.unpack('>i', cali_coeff[i+1:i+5])[0])
phase_para_a.append(struct.unpack('>I', cali_coeff[i+1:i+5])[0])
phase_para_b.append(struct.unpack('>i', cali_coeff[i+5:i+9])[0])
index = 40
for i in range(index, index+16, 8):
phase_para_a.append(struct.unpack('>i', cali_coeff[i+1:i+5])[0])
phase_para_a.append(struct.unpack('>I', cali_coeff[i+1:i+5])[0])
phase_para_b.append(struct.unpack('>i', cali_coeff[i+5:i+9])[0])
#Lv[0] 160k
index = 60
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+1:index+5])[0]/1e8)
hsrtia_a.append(struct.unpack('>I', cali_coeff[index+1:index+5])[0]/1e8)
hsrtia_b.append(struct.unpack('>i', cali_coeff[index+5:index+9])[0]/1e8)
hsrtia_c.append(struct.unpack('>i', cali_coeff[index+9:index+13])[0]/1e4)
hsrtia_d.append(struct.unpack('>B', cali_coeff[index+13:index+14])[0])
#Lv[1] 20k
index = 80
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+1:index+5])[0]/1e8)
hsrtia_a.append(struct.unpack('>I', cali_coeff[index+1:index+5])[0]/1e8)
hsrtia_b.append(struct.unpack('>i', cali_coeff[index+5:index+9])[0]/1e8)
hsrtia_c.append(struct.unpack('>i', cali_coeff[index+9:index+13])[0]/1e4)
hsrtia_c.append(struct.unpack('>I', cali_coeff[index+9:index+13])[0]/1e4)
hsrtia_d.append(struct.unpack('>B', cali_coeff[index+13:index+14])[0])
#Lv[2] 5k
index = 100
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+1:index+5])[0]/1e8)
hsrtia_a.append(struct.unpack('>I', cali_coeff[index+1:index+5])[0]/1e8)
hsrtia_b.append(struct.unpack('>i', cali_coeff[index+5:index+9])[0]/1e8)
hsrtia_c.append(struct.unpack('>i', cali_coeff[index+9:index+13])[0]/1e4)
hsrtia_c.append(struct.unpack('>I', cali_coeff[index+9:index+13])[0]/1e4)
hsrtia_d.append(struct.unpack('>B', cali_coeff[index+13:index+14])[0])
#Lv[3] 200R
index = 120
hsrtia_a.append(struct.unpack('>i', cali_coeff[index+1:index+5])[0]/1e8)
hsrtia_b.append(struct.unpack('>i', cali_coeff[index+5:index+9])[0]/1e8)
hsrtia_c.append(struct.unpack('>i', cali_coeff[index+9:index+13])[0]/1e4)
# hsrtia_a.append(struct.unpack('>I', cali_coeff[index+1:index+5])[0])
# hsrtia_b.append(struct.unpack('>I', cali_coeff[index+5:index+9])[0]/1e6)
# hsrtia_c.append(struct.unpack('>I', cali_coeff[index+9:index+13])[0]/1e5)
hsrtia_a.append(struct.unpack('>I', cali_coeff[index+1:index+5])[0])
hsrtia_b.append(struct.unpack('>I', cali_coeff[index+5:index+9])[0]/1e6)
hsrtia_c.append(struct.unpack('>I', cali_coeff[index+9:index+13])[0]/1e5)
hsrtia_d.append(struct.unpack('>I', cali_coeff[index+13:index+17])[0]/1e6)
# print('cutoff_freq', cutoff_freq)
# print('hsrtia_a', hsrtia_a)
# print('hsrtia_b', hsrtia_b)
# print('hsrtia_c', hsrtia_c)
# print('hsrtia_d', hsrtia_d)
# print('phase_para_a', phase_para_a)
# print('phase_para_b', phase_para_b)
@@ -1457,12 +1446,12 @@ class EISZeroOneDataDecoder(RecDataDecoder):
if len(data) < 18:
return None
ch1 = struct.unpack('>i', data[1+3:5+3])[0] # unit: 1/1000 nA
ch2 = struct.unpack('>i', data[5+3:9+3])[0] # unit: mV
ch3 = struct.unpack('>i', data[9+3:13+3])[0] # unit: kOm
time_stamp: float = struct.unpack('<I', data[13+3:17+3])[0] # unit: ms
cycle_number = struct.unpack('>H', data[17+3:19+3])[0]
d19 = data[19+3]
ch1 = struct.unpack('>i', data[1:5])[0] # unit: 1/1000 nA
ch2 = struct.unpack('>i', data[5:9])[0] # unit: mV
ch3 = struct.unpack('>i', data[9:13])[0] # unit: kOm
time_stamp: float = struct.unpack('<I', data[13:17])[0] # unit: ms
cycle_number = struct.unpack('>H', data[17:19])[0]
d19 = data[19]
gain = (d19 & 0x0F)
finishMode = (d19 & 0x80) >> 7
@@ -1503,19 +1492,13 @@ class EISZeroOneDataDecoder(RecDataDecoder):
voltage_mag = math.sqrt(img ** 2 + real ** 2) * (1 + freq ** 2 / cutoff_freq ** 2)
# if (gain == 3):
# current = hsrtia_a[gain] * math.exp(hsrtia_b[gain] * voltage_mag) + hsrtia_c[gain] * math.exp(hsrtia_d[gain] * voltage_mag)
# else:
current = voltage_mag ** 2 * hsrtia_a[gain] + voltage_mag * hsrtia_b[gain] + hsrtia_c[gain]
# print(current)
# print(voltage_mag)
# print(hsrtia_a[gain])
# print(hsrtia_b[gain])
# print(hsrtia_c[gain])
if (gain == 3):
current = hsrtia_a[gain] * math.exp(hsrtia_b[gain] * voltage_mag) + hsrtia_c[gain] * math.exp(hsrtia_d[gain] * voltage_mag)
else:
current = voltage_mag ** 2 * hsrtia_a[gain] + voltage_mag * hsrtia_b[gain] + hsrtia_c[gain]
if (current != 0):
# impedance = voltage_amp * 1000_000 / 1.414213 / current
impedance = voltage_amp * 707106.78 / current
impedance = voltage_amp * 1000000 / current
else:
impedance = 0
@@ -1586,10 +1569,10 @@ class EISZeroOneDataDecoder(RecDataDecoder):
ret = RecordingData(self.device, int(time_stamp * 1000 / 2), 0)
if (self._mode == 0): #EIS Mode
ret.append_data(3, cycle_number)
ret.append_data(0, ch1) #Raw Imag
ret.append_data(1, ch2) #Raw Real
ret.append_data(2, ch3 * 10) #Frequency [mHz]
ret.append_data(3, cycle_number)
ret.append_data(4, round(imag_after_cal)) #Z_imag [Ohm]
ret.append_data(5, round(real_after_cal)) #Z_real [Ohm]
ret.append_data(6, round(impedance)) #Impedance [Ohm]
@@ -1598,10 +1581,10 @@ class EISZeroOneDataDecoder(RecDataDecoder):
ret.append_data(9, gain) #Gain Level
else: #CV Mode
ret.append_data(3, cycle_number)
ret.append_data(0, ch1) #Iin [nA]
ret.append_data(1, ch2) #Vset [nV]
ret.append_data(2, ch3) #Vout [nV]
ret.append_data(3, cycle_number)
if cycle_number != self._cycle_number:
# notify cycle_number change
+15
View File
@@ -653,9 +653,15 @@ class DeviceInstruction:
VIS_CC_ZERO = 0x40
VIS_ASK = 0x30
"""ask in virtual instruction"""
VIS_STI = 0xC0
"""stimulation on virtual instruction"""
VIS_FUH = 0x90
"""flush virtual instruction"""
VIS_INT = 0x60
"""interrupt virtual instruction"""
@@ -744,6 +750,8 @@ class DeviceCommonInstruction:
return '_sync(False)', '_notify(False)', 'VIS_INT'
elif instruction == cls.CLOSE:
return '_sync(False)', '_notify(False)', 'VIS_INT'
elif instruction == cls.FLUSH:
return 'VIS_FUH',
elif instruction == cls.CALL:
return 'VIS_CAL',
elif instruction == cls.VIS_DEVICE_DETECT:
@@ -955,6 +963,10 @@ class MasterDevice(LoggerFlag, metaclass=abc.ABCMeta):
"""
pass
@abc.abstractmethod
def available_device(self) -> List[int]:
pass
def scan(self, timeout=5, all_device=False) -> List[DeviceResponseInfo]:
"""scan the nearby device.
@@ -1089,6 +1101,9 @@ class NullMasterDevice(MasterDevice):
def shutdown(self, release_resource=True):
pass
def available_device(self) -> List[int]:
return []
def scan(self, timeout=5, all_device=False) -> List[DeviceResponseInfo]:
return []
+2 -15
View File
@@ -4,7 +4,6 @@ from biopro.util.text import part_suffix
from . import *
from .expression import *
import json
class DeviceParameterError(RuntimeError):
__slots__ = ()
@@ -310,8 +309,6 @@ class ParameterValueDomain(ParameterDomain, metaclass=abc.ABCMeta):
return f
def valid_para(self, value: Any) -> bool:
if isinstance(value, list):
return True
f, t = self.range
return f <= float(value) < t
@@ -492,12 +489,8 @@ class ParameterCollectionValueOperator(metaclass=abc.ABCMeta):
@classmethod
def parse(cls, expr: Union[int, str]) -> 'ParameterCollectionValueOperator':
# parse epression then create value_operator
if isinstance(expr, int):
return SingleValueOperator(None, expr)
elif isinstance(expr, list):
return CollectValueOperator((0, len(expr)), expr)
elif expr == '*':
return AddAllValueOperator
@@ -505,10 +498,6 @@ class ParameterCollectionValueOperator(metaclass=abc.ABCMeta):
elif expr == ':=':
return CollectValueOperator((None, None), [])
elif '[' in expr and ']' in expr:
v = json.loads(expr)
return CollectValueOperator((0, len(v)), v)
elif '=' in expr:
a, v = part_suffix(expr, '=')
@@ -519,7 +508,6 @@ class ParameterCollectionValueOperator(metaclass=abc.ABCMeta):
return CollectValueOperator((None, None), v)
else:
# get list length (old way)
a, b = part_suffix(a, ':')
a = int(a) if len(a) else None
b = int(b) if len(b) else None
@@ -614,8 +602,8 @@ class ParameterCollectionDomain(ParameterDomain, metaclass=abc.ABCMeta):
def __init__(self, domain: ParameterDomain):
super().__init__()
# if isinstance(domain, ParameterCollectionDomain):
# raise RuntimeError('cannot compose with parameter collection domain')
if isinstance(domain, ParameterCollectionDomain):
raise RuntimeError('cannot compose with parameter collection domain')
self._domain = domain
@@ -737,7 +725,6 @@ class ParameterListDomain(ParameterCollectionDomain):
if oper == AddAllValueOperator:
raise ValueError("list parameter cannot accept '*' (all) value")
# d: domain, sz: lsit length, i: slice(a, b), v: list value
d = self.element_domain
sz = len(target)
i = oper.index(sz)
+3 -7
View File
@@ -119,10 +119,7 @@ class DeviceConfigurationEncoder:
e.write(b'L\0')
else:
parameter_type = cls._value_type(max(value))
for v in value:
if isinstance(v, int) and v < 0:
parameter_type = parameter_type.lower()
parameter_type = cls._value_type(value[0])
e.write(b'L')
e.write(parameter_type.encode())
e.u8(sz)
@@ -156,8 +153,7 @@ class DeviceConfigurationEncoder:
if isinstance(value, bool):
e.u8(1 if value else 0)
elif isinstance(value, str) or isinstance(value, list):
value = str(value)
elif isinstance(value, str):
e.encode_string(value)
elif isinstance(value, int):
@@ -174,7 +170,7 @@ class DeviceConfigurationEncoder:
if isinstance(value, bool):
return '?'
elif isinstance(value, str) or isinstance(value, list):
elif isinstance(value, str):
return 'p'
elif isinstance(value, int):
+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):
+6 -39
View File
@@ -510,10 +510,9 @@ class DeviceParameter(JsonSerialize):
:param para: parameter name
:param oper: operator expression
"""
# parse operator return CollectValueOperator instance
if not isinstance(oper, ParameterCollectionValueOperator):
oper = ParameterCollectionValueOperator.parse(oper)
table = self._library.parameter_table
info = table[para]
domain = info.domain
@@ -529,7 +528,6 @@ class DeviceParameter(JsonSerialize):
old = list(target)
try:
# replace target with new oper
domain.oper_para(target, oper, self._context)
except RuntimeError as e:
raise IllegalParameterValueError(para, str(oper)) from e
@@ -968,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):
"""
@@ -978,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)
@@ -1014,10 +1008,6 @@ class CompletedDevice(Device):
def device_id(self) -> int:
return self._device_id
@property
def memory_board(self) -> int:
return self._device_id
@property
def device_name(self) -> str:
return self._device.device_name
@@ -1030,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
@@ -1070,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]:
@@ -1103,11 +1082,6 @@ class CompletedDevice(Device):
:return: parameter V value
"""
return self._configuration.get_parameter(name, False)
def set_multi_parameters(self, parameter):
for (name, value) in parameter[0].items():
if name != 'target':
self.set_parameter(name, value)
def set_parameter(self, name: str, value: Union[int, str]):
"""replace parameter value with *value*
@@ -1136,8 +1110,7 @@ class CompletedDevice(Device):
self._master.log_warn('on_change instruction not found : ' + on_change)
else:
if self._status == 1:
self.call_instruction(on_change)
self.call_instruction(on_change)
# device instruction call
@@ -1280,20 +1253,14 @@ class CompletedDevice(Device):
def as_json(self) -> JSON_OBJECT:
return {
'id': self.device_id,
'name': self.device_name,
'status': self._status,
'device_id': self.device_id,
'device_name': self.device_name,
'device_address': list(self.mac_address),
'device_status': self._status,
'mac_address': self.mac_address_in_str,
'memory_board': self.memory_board,
'serial_number': self.serial_number.as_json(),
'version': self.device_version,
'battery': self.battery,
'parent': self.parent,
'recording_file_name': self.recording_file_name,
'device_version': self.device_version,
'serial_number': self.serial_number.as_json(),
'library_name': self._library.name,
'library_version': str(self._library.version),
'configuration': self.configuration.as_json(list_hide=True),
+4 -1
View File
@@ -3720,9 +3720,12 @@ class CC2650Central(LoggerFlag):
if ret is not None and len(ret) > 0:
return ret
elif _time() - start > 0.01: # read timeout
elif _time() - start > 1:
return None
else:
sleep(0.01)
def _recv_event(self, timeout: Optional[float] = 1) -> Optional[list]:
code = self._recv_byte()
# print("code = ", code)
+120 -177
View File
@@ -16,6 +16,8 @@ MEM_REG_READ = 0x05
DEFAULT_REGISTER_VALUE = 0b0100_0011 # 67
MEM_SIZE = 0x1000
_RUNTIME_COMPILE = False
_SLEEP_TIME_ = 0.001
@@ -24,15 +26,12 @@ def zero_buffer(size: int) -> List[int]:
return [0] * size
class MultiExtMemSpiInterface(LowLevelHardwareInterface):
MEM_INS_MARKED1 = [MEM_INS_WRITE, 0, 2, 0x5A, 0xA5]
MEM_INS_MARKED2 = [MEM_INS_WRITE, 0, 6, 0x5A, 0xA5]
MEM_INS_MARKED3 = [MEM_INS_WRITE, 0, 10, 0x5A, 0xA5]
# MEM_INS_RESET = [MEM_INS_WRITE, 0, 2, 1, 1, 0, 0xFF]
MEM_INS_MARKED = [MEM_INS_WRITE, 0, 2, 1, 1]
MEM_INS_RESET = [MEM_INS_WRITE, 0, 2, 1, 1, 0, 0xFF]
__slots__ = ('_selector', '_wait_for_first_data', '_spi', '_tx_buffer_header', '_tx_buffer_data',
'pin_busy', 'pin_mem_req', 'pin_mem_sel', 'pin_ram_sel',
__slots__ = ('_selector', '_wait_for_first_data', '_spi', '_tx_buffer', '_tx_buffer_header', '_tx_buffer_data',
'pin_busy', 'pin_request', 'pin_reset', 'pin_sel',
'_pin_sel_val',
'_pin_ram_sel_value', '_pin_mem_sel_value', '_pin_mem_req_value',
'_read_green_times','_read_red_times',
'_elite_data_len', '_mem_header_len', '_mem_tailer_len', '_single_data_len',
'_head_wrong_cnt')
@@ -42,26 +41,24 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
device: Tuple[int, int] = None):
self._spi = HardwareImplSpiInterface(device,
spi_speed=12_000_000)
spi_speed=12_000_000 # XXX temp parameter
)
self._elite_data_len = 40
self._elite_data_len = 120
self._mem_header_len = 3
self._mem_tailer_len = 6
self._mem_tailer_len = 8
self._single_data_len = self._elite_data_len + self._mem_header_len + self._mem_tailer_len
# buffer
self._tx_buffer_header = [0] * 19
self._tx_buffer = [0] * 512
self._tx_buffer_header = [0] * 11
self._tx_buffer_data = [0] * (self._single_data_len * 10 + 3)
# memory control pin
self.pin_busy = OutputPin.get_used(P3Pin.MEM_BZY, True)
self.pin_mem_req = OutputPin.get_used(P3Pin.MEM_REQ, False)
self.pin_mem_sel = OutputPin.get_used(P3Pin.MEM_RST, True) # MEM_RST -> actually which memory board is assign
self.pin_ram_sel: Optional[InputPin] = InputPin.get_used(P3Pin.MEM_SEL) # MEM_SEL -> actually is RAM_SEL, which RAM is assign
self._pin_ram_sel_value = [bool(self.pin_ram_sel) for _ in range(Selector.SIZE)]
self._pin_mem_sel_value = [bool(self.pin_mem_sel) for _ in range(Selector.SIZE)]
self._pin_mem_req_value = [bool(self.pin_mem_req) for _ in range(Selector.SIZE)]
self.pin_request = OutputPin.get_used(P3Pin.MEM_REQ, True)
self.pin_reset = OutputPin.get_used(P3Pin.MEM_RST, True)
self.pin_sel: Optional[InputPin] = InputPin.get_used(P3Pin.MEM_SEL)
self._read_green_times = 0
self._read_red_times = 0
@@ -74,30 +71,6 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
self._pin_sel_val = [False for _ in range(Selector.SIZE)]
self._wait_for_first_data = [True for _ in range(Selector.SIZE)]
def set_pin_mem_req(self, value: bool):
channel = self.select
# if self._pin_mem_req_value[channel] == value:
# print('last_req_sig[channel] == value', channel, self._pin_mem_req_value[channel], value, datetime.now())
self.pin_mem_req.output(value)
self._pin_mem_req_value[channel] = value
def set_pin_mem_sel(self, value: bool):
channel = self.select
self.pin_mem_sel.output(value)
self._pin_mem_sel_value[channel] = value
def get_pin_mem_req(self):
channel = self.select
return self._pin_mem_req_value[channel]
def get_pin_ram_sel(self):
channel = self.select
if self.pin_ram_sel.input() == 0:
self._pin_ram_sel_value[channel] = False
else:
self._pin_ram_sel_value[channel] = True
return self._pin_ram_sel_value[channel]
@property
def select(self) -> int:
return self._selector.channel
@@ -106,35 +79,71 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
def select(self, value: int):
self._selector.select(value)
def changed(self, flip=False) -> bool:
channel = self._selector.channel
old = self._pin_sel_val[channel]
value = bool(self.pin_sel)
if flip:
self._pin_sel_val[channel] = value
return value != old
def reset(self):
self._spi.reset()
def flush(self):
pass
def close(self):
self._selector.close()
self._spi.close()
def flush(self):
self.pin_reset.output(False)
self.pin_reset.output(True)
def send_byte(self, data: bytes):
raise RuntimeError()
def recv_byte(self, size: int) -> Optional[bytes]:
raise RuntimeError()
# def flush_all(self):
# for _ in self._selector.foreach():
# self.pin_reset.output(False)
# self.pin_reset.output(True)
def foreach(self) -> Iterable[int]:
for channel in self._selector.foreach():
yield channel
def set_wait_flag(self, spi_idx:int = None, value:bool = None):
self._wait_for_first_data[spi_idx] = value
def get_wait_flag(self, spi_idx:int = None) -> Optional[bool]:
if spi_idx < Selector.SIZE:
return self._wait_for_first_data[spi_idx]
else:
return None
def request_data(self):
self.pin_request.output(False)
sleep(0.001)
# count = 0
# for i in range(300):
# count = count + 1
self.pin_request.output(True)
# sleep(0.001) -> 1.2ms
# no sleep -> 5us ~ 12us (central can't receive)
# for i in range
# (1000) -> 500us ~ 1ms
# (500) -> 200us ~ 500us
# (300) -> 120us ~ 270us (seldom)
# (200) -> 70us ~ 160us (skip req about every 64 time) x
# (100) -> 40us ~ 100us (skip req sometimes) x
def compare_green_data(self, data_first: Union[bytes, List[int]], data_second: Union[bytes, List[int]], data_third: Union[bytes, List[int]], length: int):
data = []
data.append(255)
@@ -169,52 +178,6 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
print("read red data times", self._read_red_times)
return data
def _compare_green_data_addr_and_flag(self, data: Union[bytes, List[int]], device: int):
green_data_section1 = data[3:7]
green_data_section2 = data[7:11]
green_data_section3 = data[11:15]
green_data_section = []
if green_data_section1 == green_data_section2:
green_data_section = green_data_section1
elif(green_data_section2 == green_data_section3 or green_data_section1 == green_data_section3):
print("green data not equal: = ", data[3:15])
print("green data print:", data, device, datetime.now())
green_data_section = green_data_section3
else:
print("green data not equal: = ", data[3:15])
print("green data print:", data, device, datetime.now())
green_data_section = green_data_section3 # use last data
return green_data_section
def _print_ram_all_data(self):
addr = 0
red_length = int(7000 / 2)
tx_temp = [0] * (red_length + 3)
while True:
tx_temp[0] = MEM_INS_READ
tx_temp[1] = ((addr >> 8) & 0xFF)
tx_temp[2] = (addr & 0xFF)
ram_data = []
ram_data = self._spi.send_byte(tx_temp)
ram_data[0:3] = [255, 255, 255]
print(list(ram_data), len(ram_data), addr)
addr += len(ram_data) - 3
if (7000 < addr + len(ram_data)):
del ram_data
break
print()
return
def recv_memory(self, device: int) -> Optional[bytes]:
self.pin_busy.output(False)
@@ -239,9 +202,9 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
if (data[0] != 255 or data[1] != 255 or data[2] != 255):
self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
if (self._head_wrong_cnt[device] <= 5): # print 5 times
print('data_first[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times')
print(list(data))
if (self._head_wrong_cnt[device] < 6):
print('data_first[0:3] != [255, 255, 255]', device)
print(list(data[0:7]))
data[0:3] = [255, 255, 255]
# ----------------------------------------------------------------------------------------------
@@ -259,14 +222,14 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
# if (data_first[0] != 255 or data_first[1] != 255 or data_first[2] != 255):
# self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
# if (self._head_wrong_cnt[device] < 10):
# print('data_first[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times')
# print(list(data_first))
# print('data_first[0:3] != [255, 255, 255]', device)
# print(list(data_first[0:7]))
# if (data_second[0] != 255 or data_second[1] != 255 or data_second[2] != 255):
# self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
# if (self._head_wrong_cnt[device] < 10):
# print('data_second[0:3] != [255, 255, 255], device:', device, ',', self._head_wrong_cnt[device], 'times')
# print(list(data_second))
# print('data_second[0:3] != [255, 255, 255]', device)
# print(list(data_second[0:7]))
# if (data_first[3:] == data_second[3:]):
# data = data_first
@@ -278,47 +241,59 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
# data = self.compare_green_data(data_first, data_second, data_third, len(tx_h))
# ----------------------------------------------------------------------------------------------
green_data_section = self._compare_green_data_addr_and_flag(data, device)
# debug use
# green_data = []
# green_data = data
# print("_[Debug] @ spi green data = ", green_data)
length = (green_data_section[0] << 8) | green_data_section[1]
header = data[15]
elite_data_len = data[17]
if length <= 12:
if length < 12:
print("length < 12")
print("green data print:", data, device, datetime.now())
# if length == 0:
# self._print_ram_all_data()
length = (data[3] << 8) | data[4]
header = data[7]
elite_data_len = data[9]
if length <= 4:
# print("length <= 4")
# print("green data print:", data, device, datetime.now())
return None
if green_data_section[2] != 0xA5 or green_data_section[3] != 0x5A:
if data[5] != 0 or data[6] != 0:
# give a default length if the data header did not update
print("green data: is not [0xA5, 0x5A], = ", data[5:7])
length = 7000
print("green data: is not [0,0], = ", data[5:7])
print("green data print:", data, device, datetime.now())
return None
if (length >= 4000):
if (length >= 7000):
flag_print = True
print("green data: big length:", length)
if (header != 255):
flag_print = True
print("green data: header is not 255: ", header)
# self._print_ram_all_data()
if (elite_data_len != self._elite_data_len):
flag_print = True
print("green data: length is not", self._elite_data_len, ": ", elite_data_len)
if (flag_print):
print("green data print:", data, device, datetime.now(), '\n')
print("green data print:", data, device, datetime.now())
address = 12
# neulive
# address += len(data) - 11
# tx_d = self._tx_buffer_data
# red_length = len(tx_d)
# elite read all data of ram
# address = 4
# red_length = length - 1
# tx_d = [0] * red_length
address = 4
red_length = int(length / 2)
tx_d = [0] * (red_length + 3)
tx_d = [0] * red_length
# elite read len(_tx_buffer_data) byte
# address = 4
# tx_d = self._tx_buffer_data
while True:
tx_d[0] = MEM_INS_READ
@@ -357,60 +332,22 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
address += len(data) - 3
del data
# last_index = 0
# print("_[Debug] @ spi recv data rx, ram_select:", self._pin_ram_sel_value[device], ",", datetime.now())
# print("_[Debug] @ spi recv data rx = ")
# for i in range(0, len(rx), self._single_data_len):
# last_index = i
# # print(rx[i:i+self._single_data_len])
# if i == 0:
# print(rx[0:8], 'ram:', rx[47])
# if last_index != 0:
# print(rx[last_index:last_index+8], 'ram:', rx[last_index+47])
# print(rx[i:i+self._single_data_len])
# print()
# /*
# * red data formate:
# * ramHdr, ramHdr, ramHdr, (3B)
# * 255, #, data_length, (3B)
# * data, (40B)
# * data, (20B)
# * red_wrong, red_retry_cnt, green_wrong, green_retry_cnt, (4B)
# * check_num, (1B)
# * 255, #, data_length, (3B)
# * */
# read again if check num is wrong
index = 0
check_number_print = False
for i in range(0, len(rx), self._single_data_len):
check_sum = sum(rx[i : i + self._single_data_len - 1]) & 0b11111111
# print(check_sum, rx[i + self._single_data_len - 1])
if (check_sum != rx[i + self._single_data_len - 1]):
# print('check_sum wrong, origin value:', 'check_sum =', check_sum, rx[i : i + self._single_data_len])
tx_d = [0] * (self._single_data_len + 3)
address = 12 + self._single_data_len * index
tx_d[0] = MEM_INS_READ
tx_d[1] = ((address >> 8) & 0xFF)
tx_d[2] = (address & 0xFF)
data = []
data = self._spi.send_byte(tx_d)
data[0:3] = [255, 255, 255]
rx[i : i + self._single_data_len] = data[3:]
print('check_sum wrong, read again:', 'check_sum =', check_sum, rx[i : i + self._single_data_len])
check_number_print = True
index = index + 1
if check_number_print:
print('check_sum wrong:', device, datetime.now())
for i in range(0, len(rx), self._single_data_len):
print(rx[i:i+self._single_data_len])
print()
# mark read
self._spi.send_byte(self.MEM_INS_MARKED1)
self._spi.send_byte(self.MEM_INS_MARKED2)
self._spi.send_byte(self.MEM_INS_MARKED3)
self._spi.send_byte(self.MEM_INS_MARKED)
except BaseException as e:
print(e)
@@ -445,37 +382,39 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
class ExtMemManager:
def __init__(self, ext_mem: MultiExtMemSpiInterface):
self._mem_sel = InputPin.get_used(P3Pin.MEM_SEL, pull_up_down=True)
self._mem_req = OutputPin.get_used(P3Pin.MEM_REQ, initial=True)
self._ext_mem = ext_mem
print('ExtMemManager.....')
print()
def mem_request(self):
self._mem_req.pulse()
sleep(_SLEEP_TIME_)
def get_ext_mem_register(self) -> List[Tuple[Optional[int], Optional[int]]]:
print('get_ext_mem_register......')
print()
ret = [(None, None) for _ in range(Selector.SIZE)]
for channel in self._ext_mem.foreach():
r = [None, None]
self._ext_mem.set_pin_mem_sel(False)
pin_value = self._ext_mem.get_pin_mem_req()
self._ext_mem.set_pin_mem_req(not pin_value)
m1 = int(self._ext_mem.get_pin_ram_sel())
self.mem_request()
sleep(0.01)
m1 = int(self._mem_sel)
# print("m1 = ", m1)
self._ext_mem.write_register(DEFAULT_REGISTER_VALUE)
r[m1] = self._ext_mem.read_register()
sleep(0.040)
self._ext_mem.set_pin_mem_sel(True)
sleep(0.200)
self._ext_mem.set_pin_mem_sel(False)
pin_value = self._ext_mem.get_pin_mem_req()
self._ext_mem.set_pin_mem_req(not pin_value)
m2 = int(self._ext_mem.get_pin_ram_sel())
self.mem_request()
sleep(0.01)
m2 = int(self._mem_sel)
# print("m2 = ", m2)
self._ext_mem.write_register(DEFAULT_REGISTER_VALUE)
r[m2] = self._ext_mem.read_register()
sleep(0.040)
self._ext_mem.set_pin_mem_sel(True)
sleep(0.200)
ret[channel] = tuple(r)
@@ -502,9 +441,12 @@ class ExtMemManager:
return 0
def get_available_channel(self, result: List[Tuple[Optional[int], Optional[int]]] = None) -> List[int]:
print('get_available_channel......')
print()
if result is None:
result = self.get_ext_mem_register()
ret = []
for channel, result in enumerate(result):
@@ -517,3 +459,4 @@ class ExtMemManager:
ret.append(channel)
return ret
+1 -1
View File
@@ -53,7 +53,7 @@ class HardwareImplSpiInterface(LowLevelHardwareInterface):
*MISO*, *SCLK*, *GE0_N*
"""
__slots__ = ('_device', '_spi', '_spi_mode', '_spi_speed')
__slots__ = ('_device', '_spi', '_spi_mode', 'pin_request', '_spi_speed')
def __init__(self,
device: Tuple[int, int] = None,
+1 -1
View File
@@ -28,7 +28,7 @@ class UARTInterface(LowLevelHardwareInterface):
self._serial = serial.Serial(self._port,
baudrate=self._baudrate,
timeout=0,
writeTimeout=0.01)
writeTimeout=1)
# deprecate function name which change at version 3.0
self._serial.flushInput()
self._serial.flushOutput()
+8 -8
View File
@@ -117,14 +117,14 @@ class Selector:
self._p1.output(p[1])
self._p2.output(p[2])
# if (value == 4 and self._last_sel != 6):
# print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel, datetime.now())
# elif (value == 5 and self._last_sel != 4):
# print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel, datetime.now())
# elif (value == 7 and self._last_sel != 5):
# print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel, datetime.now())
# elif (value == 6 and self._last_sel != 7):
# print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel, datetime.now())
if (value == 4 and self._last_sel != 6):
print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel)
elif (value == 5 and self._last_sel != 4):
print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel)
elif (value == 7 and self._last_sel != 5):
print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel)
elif (value == 6 and self._last_sel != 7):
print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel)
self._last_sel = value
+17 -6
View File
@@ -280,11 +280,14 @@ class Main(CliMain):
@cli_command('controller')
def _controller_server(self, command: str, argv: List[str]):
"""controller server"""
print(self, command, argv)
print('##_controller_server')
return _ControlServer
@cli_command('data')
def _data_server(self, command: str, argv: List[str]):
"""data server"""
print('##_data_server')
return _DataServer
@cli_command('export')
@@ -302,6 +305,7 @@ class Main(CliMain):
return LEDControlMain(command)
def run(self):
print('RUN!!!!!!!!!!!!!!!!!!!')
self.print_help()
@@ -335,15 +339,16 @@ class _ServerMain(CliSubCommandMain, metaclass=abc.ABCMeta):
def run(self):
exit_code = 1
server = self.create_server()
print('create_server')
if _RUNTIME_COMPILE:
exit_code = server.main()
else:
with self.profiling_options.generate_profile_context():
exit_code = server.main()
server = self.create_server()
exit_code = server.main()
print('....after create_server')
print('.. ..')
print()
sys.exit(exit_code)
# noinspection PyUnusedLocal
@@ -355,6 +360,7 @@ class _ControlServer(_ServerMain):
from biopro.device.manager import DeviceManagerOptions
from biopro.server.data import DataServerOptions
from biopro.exp_pro.manager import ExpManagerOption
print('command', command)
super().__init__(command)
@@ -456,6 +462,8 @@ class _ControlServer(_ServerMain):
def create_server(self) -> SocketServer:
from biopro.server.main import ControlServer
print('..create_server')
return ControlServer(self.server_device_options,
self.server_data_options,
self.server_controller_options,
@@ -477,8 +485,11 @@ class _DataServer(_ServerMain):
def create_server(self) -> SocketServer:
from biopro.server.data import DataServer
print('.1.create_server')
return DataServer(self.server_data_options)
if __name__ == '__main__':
print('START!!!!!!!!!!!!!!!!!!!!!!!!')
Main().main()
View File
-26
View File
@@ -1,26 +0,0 @@
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
@property
def type(self):
return self._type
@property
def target(self):
return self._target
@property
def condition(self):
return self._condition
-96
View File
@@ -1,96 +0,0 @@
from datetime import datetime
from time import time
class Condition():
def __init__(self, id, condition):
self._id = id
self._type = condition['type']
self._comparsion = condition['comparsion']
self._value = condition['value']
self._active = False
@property
def id(self):
return self._id
@property
def type(self):
return self._type
@property
def comparsion(self):
return self._comparsion
@property
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
@comparsion.setter
def comparsion(self, new_comparsion):
self._comparsion = new_comparsion
@value.setter
def value(self, new_value):
self._value = new_value
def compareWith(self, operator, x, y) -> bool:
# print(operator, x, y)
cases = {
"equal": lambda a, b: a == b,
"bigger": lambda a, b: a > b,
"smaller": lambda a, b: a < b,
}
return cases[operator](x, y)
def match_or_not(self, **kwargs):
# print(self._type, self._comparsion, self._value)
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)))
return self.compareWith(self.comparsion, now, time_condition)
def after_project_run(self, **kwargs):
project_start_time = kwargs['project_start_time']
delay_time = kwargs['delay_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)
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 str_to_datetime(self, time_str):
return datetime.strptime(time_str,'%Y-%m-%dT%H:%M')
def datetime_to_timestamp(self, date):
return datetime.timestamp(date)
-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
-224
View File
@@ -1,224 +0,0 @@
import sys
import json
import threading
from time import time, sleep
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',
}
class Project(threading.Thread):
def __init__(self, project, device_manager: DeviceManager, mqttThread = None, name="project"):
super(Project, self).__init__(name = name)
self._project = project
self._device_manager = device_manager
self._mqtt_thread = mqttThread
self._time_interval = 1
self._start_time = None
self._end_time = None
self._id = None
self._uuid = str(uuid4())
self._name = None
self._desc = None
self._device = None
self._complete_device = []
self._status = 0
self._instruction_set = Instruction()
self._stop_flag = False
self._task_manager = None
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'])
elif key == 'uuid':
pass
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
@property
def desc(self) -> str:
return self._desc
@desc.setter
def desc(self, new_desc):
self._desc = new_desc
@property
def status(self) -> str:
return self._status
@status.setter
def status(self, new_status):
self._status = new_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.export_task_list
@property
def mqtt_thread(self):
return self._mqtt_thread
def run(self):
self._status = 1
self._start_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
delay_time = 0
check_list = copy(self._task_manager.check_list)
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)
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])
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:
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._task_manager.running_task.stop()
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._status = 2
self._end_time = time()
self._stop_flag = True
self.mqtt_thread.broadcast_command('project:project ' + str(self._name) + ' stop at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])
def close(self):
self._status = 2
self._end_time = time()
self._stop_flag = True
self.mqtt_thread.broadcast_command('project:project ' + str(self._name) + ' stop at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3])
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
for device in self._task_manager.running_task.device:
if self._complete_device[device].status == 1:
return False
return True
def as_json(self):
data = {
'id': self._id,
'name': self._name,
'uuid': self._uuid,
'desc': self._desc,
'status': self._status,
'device': self._device,
'task': self.task_list,
'running_task': self._task_manager.running_task.as_json()
}
return data
-39
View File
@@ -1,39 +0,0 @@
from io import StringIO
from json import loads as json_parse, dumps as _json_stringify
from typing import Dict, Optional, Any
import paho.mqtt.client as mqtt
from .project import Project
from biopro.device.manager import DeviceManager
def json_stringify(o: Any) -> str:
return _json_stringify(o, separators=(',', ':'))
class ProjectManager():
def __init__(self, device_manager: DeviceManager, mqtt_thread=None) :
self._project_list = []
self._mqtt_thread = mqtt_thread
self._device_manager = device_manager
def create(self, project):
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]
def get(self, project_uuid = None):
if project_uuid == None:
return self._project_list
else:
for project in self._project_list:
if project.uuid == project_uuid:
return project
def run_project(self, project):
project.start()
def stop_project(self, project):
_project = self.get(project)
_project.stop()
-252
View File
@@ -1,252 +0,0 @@
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._condition_list: List[Condition] = []
self._action_list: List[Action] = []
self._instruction_list = []
self._record_list = []
self._start_time = []
self._idle_time = []
self._end_time = []
self._period = None
self.load_task(task)
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 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))
@property
def id(self) -> str:
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):
return self._name
@name.setter
def name(self, new_name):
self._name = new_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
@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
@property
def trigger(self) -> list:
return self._trigger
@trigger.setter
def trigger(self, new_trigger):
self._trigger = new_trigger
@property
def parameter_set(self) -> list:
return self._parameter_set
@parameter_set.setter
def parameter_set(self, new_parameter_set):
self._parameter_set = new_parameter_set
@property
def condition(self) -> list:
return self._condition
@condition.setter
def condition(self, new_condition):
self._condition = new_condition
@property
def action(self) -> list:
return self._action
@action.setter
def action(self, new_action):
self._action = new_action
@property
def status(self) -> str:
return self._status
@status.setter
def status(self, new_status):
self._status = new_status
@property
def next(self) -> str:
return self._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 get_parameter_set_by_device(self, device):
return [parameter_set for parameter_set in self.parameter_set.values() if device == parameter_set['target']]
def get_parameter(self, name_or_value, device):
name_list = []
value_list = []
for parameter in self.get_parameter_set_by_device(device):
for key, value in parameter.items():
if key != 'target':
name_list.append(key)
value_list.append(str(value))
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())
def start(self):
self.status = 1
def stop(self):
self.status = 2
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 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,
}
-88
View File
@@ -1,88 +0,0 @@
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
from biopro.text import *
from .task import Task
_RUNTIME_COMPILE = False
def json_stringify(o: Any) -> str:
return _json_stringify(o, separators=(',', ':'))
class TaskManager():
def __init__(self, task_list):
self._task_list = []
self._next_task = []
self._prev_task = None
self._running_task = None
self.load_task_list(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 export_task_list(self):
return [d.as_json() for d in 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)
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 get_task(self, task_id):
return self._task_list[task_id]
+85 -161
View File
@@ -8,7 +8,6 @@ import gc
from biopro.util.json import JSON
from biopro.util.stack import print_exception
from biopro.util.logger import calculate_time
from .data import RecordingData
from .loader import *
@@ -20,13 +19,6 @@ from statistics import mean
import random
# from numba import jit
from .lttb import largest_triangle_three_buckets
import math
from scipy import signal
from copy import copy
def json_stringify(o) -> str:
return _json_stringify(o, separators=(',', ':'))
@@ -378,45 +370,6 @@ class RecordingMetaFile(JsonSerialize):
self._database.put_queue(['data_meta_write', meta_data, self._device_id, str(self._file_uuid), self._id_db])
# database.put_queue(['data_meta_write', self, meta_data, _path, self._id_db])
return None
def update_subfile_time_size(self, database = None):
meta_data = {
'channels': str(self._channel_mask.channels()),
'size': str(self._size),
'time_duration': str(self._last_time),
'uuid': str(self._file_uuid),
}
_path = str(self._filepath).replace('/', '^')
if database is not None :
self._database = database
self._database.put_queue(['data_meta_write', meta_data, self._device_id, str(self._file_uuid), self._id_db])
# database.put_queue(['data_meta_write', self, meta_data, _path, self._id_db])
return None
def update_subfile_raw(self, database = None):
meta_data = {
'raw_data': self._recording_sub_file,
}
_path = str(self._filepath).replace('/', '^')
if database is not None :
self._database = database
self._database.put_queue(['data_meta_write', meta_data, self._device_id, str(self._file_uuid), self._id_db])
# database.put_queue(['data_meta_write', self, meta_data, _path, self._id_db])
return None
def update_subfile_mini(self, database = None):
meta_data = {
'mini_data': self._recording_sub_mini,
}
_path = str(self._filepath).replace('/', '^')
if database is not None :
self._database = database
self._database.put_queue(['data_meta_write', meta_data, self._device_id, str(self._file_uuid), self._id_db])
# database.put_queue(['data_meta_write', self, meta_data, _path, self._id_db])
return None
@property
def filename(self) -> str:
@@ -564,6 +517,11 @@ class RecordingMetaFile(JsonSerialize):
'start_time': str(_start_time),
}
# if database is not None:
# database.data_raw_create(self, f, raw_data, _channel)
# self._recording_file_ch[_channel]
return f, raw_data
def new_recording_mini(self, _channel: int, _start_time, scale, database = None) -> 'RecordingMini':
@@ -609,6 +567,9 @@ class RecordingMetaFile(JsonSerialize):
'start_time': str(_start_time),
}
# if database is not None:
# database.data_mini_create(self, f, mini_data, _channel, scale)
return f, mini_data
def clear_recording_file(self):
@@ -667,6 +628,15 @@ class RecordingMetaFile(JsonSerialize):
'device': self._device,
}
# d = self._device
# if d is not None:
# ret['device'] = {
# 'device_name': d.device_name,
# 'device_address': list(d.mac_address),
# 'serial_number': d.serial_number.as_json(),
# }
return ret
@@ -822,10 +792,14 @@ class RecordingFile:
'end_time': str(_end_time),
'size': str(self._size)
}
# if self._meta_file._id_db == 0:
# _path = str(self._meta_file.filepath).replace('/', '^')
# self._database.put_queue(['data_meta_id_get_by_path', _path])
# # self._meta_file._id_db = self._database.put_queue(['data_meta_id_get_by_path', _path])
if self._meta_file._id_db != 0:
_data['parent'] = self._meta_file._id_db
self._database.put_queue(['data_raw_update', self._id_db, self._channel, _data])
# self._database.put_queue(['data_raw_update', self._id_db, self._channel, _data])
self._status = False
return None
@@ -991,7 +965,10 @@ class RecordingMini:
'end_time': str(_end_time),
'size': str(self._size)
}
# if self._meta_file._id_db == 0:
# _path = str(self._meta_file.filepath).replace('/', '^')
# self._database.put_queue(['data_meta_id_get_by_path', _path])
# # self._meta_file._id_db = self._database.put_queue(['data_meta_id_get_by_path', _path])
if self._meta_file._id_db != 0:
_data['parent'] = self._meta_file._id_db
self._database.put_queue(['data_mini_update', self._id_db, self._channel, _data])
@@ -1019,8 +996,7 @@ class RecordingFileWriter:
'_data_value_ch', '_close', '_data_mini_ch',
'_mini_scale_list', '_time_real_time', '_data_rl', '_data_db',
'_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',
'_mini_create_not_done', '_data_all')
'_data_time_ch_for_rl', '_device_id', '_send_data', '_data_mqtt_ch')
def __init__(self, meta: RecordingMetaFile, device_id, database = None):
self._meta = meta
@@ -1041,9 +1017,7 @@ class RecordingFileWriter:
self._raw_save = {
'id': {},
'data': {},
'end_time': {},
'size': {}
'data': {}
}
self._mini_save = {
'10': {
@@ -1078,9 +1052,6 @@ class RecordingFileWriter:
self._data_value_ch_for_rl = {}
self._data_time_ch_for_rl = {}
self._data_time_ch = {}
self._id_db_save = {}
self._data_all = {}
# mini data
self._data_mini_ch = {}
@@ -1097,15 +1068,12 @@ class RecordingFileWriter:
# splitting
self.splitting_threshold_time = 30 * 60 * 1000 # one minute
self.splitting_threshold_size = 16 * 1024 # 16 * 16KB
self.splitting_threshold_size = 16 * 16 * 1024 # 16 * 16KB
self._writer_batch_size = 8192
self._splitting_size = None
self._raw_create_not_done = True
self._mini_create_not_done = True
@property
def meta_file(self) -> RecordingMetaFile:
@@ -1138,17 +1106,14 @@ class RecordingFileWriter:
self._splitting_size = 0
return None
def close(self, mqtt_thread):
def close(self):
# if self._recording_file is not None:
# self._recording_file.close()
# self._recording_file = None
print('close1')
self._close = True
if len(self._recording_file_dict) > 0:
for ch in self._data_db.keys():
if len(self._data_rl[ch]) > 0:
self._data_rl[ch].append(str(int(self._time_now)))
mes = ' '.join(self._data_rl[ch])
mqtt_thread[ch].on_message(mes)
self._data_rl[ch].clear()
self._send_data[ch] = False
if self._recording_file_dict[ch]._status:
_data = ' '.join(self._data_db[ch])
self._raw_save['data'][ch] = _data
@@ -1158,7 +1123,7 @@ class RecordingFileWriter:
self._meta._size += self._recording_file_dict[ch]._size
# self._data_db.clear()
if self._database is not None:
self._database.put_queue(['data_raw_recording', self._raw_save['id'], self._channel_list, self._raw_save['data'], self._id_db_save])
self._database.put_queue(['data_raw_recording', self._raw_save['id'], self._channel_list, self._raw_save['data']])
# self._database.put_queue(['data_raw_recording', self._raw_save['id'], self._channel_list, self._raw_save['data']])
self._recording_file_dict.clear()
for scale in self._mini_scale_list:
@@ -1234,17 +1199,17 @@ class RecordingFileWriter:
def get_data_iter(self, d, mqtt_thread):
# print('****d size', d.data_size)
for t, c, v in d.entry_iter():
# print(t,c,v)
if c in self._data_db:
self._data_all[c].append([int(t), v])
### send real-time
if len(self._data_rl[c]) > 0 and self._send_data[c]:
self._data_rl[c].append(str(int(t)))
mes = ' '.join(self._data_rl[c])
# print('message1', mes)
# self._data_mqtt_ch[c] = self._data_rl[c].copy()
# self._data_mqtt_ch[c].append(str(int(t)))
mqtt_thread[c].on_message(mes)
self._data_rl[c].clear()
self._send_data[c] = False
### send real-time
sample_rate_rl = 1
# rec data
@@ -1257,35 +1222,40 @@ class RecordingFileWriter:
self._data_value_ch[c].append(int(v))
self._data_value_ch_for_rl[c].append(int(v))
self._data_time_ch_for_rl[c].append(int(t))
# print('self._data_value_ch[c]', self._data_value_ch[c])
# print('self._data_value_ch_for_rl[c]', self._data_value_ch_for_rl[c])
# print('self._data_time_ch_for_rl[c]', self._data_time_ch_for_rl[c])
# self._data_time_ch[c].append(int(t))
for scale in self._mini_scale_list:
if self._data_mini_ch[c][str(scale)]['start_time'] is None:
self._data_mini_ch[c][str(scale)]['start_time'] = str(int(t))
# print('len(self._data_value_ch_for_rl[c])', len(self._data_value_ch_for_rl[c]))
# print('sample_rate_rl:', sample_rate_rl)
if len(self._data_value_ch_for_rl[c]) >= sample_rate_rl:
if len(self._data_value_ch_for_rl[c]) == 1:
if len(self._data_rl[c]) == 0:
self._data_rl[c].append(str(int(t)))
# self._data_rl[c].append(str(self._data_time_ch_for_rl[c][0]))
self._data_rl[c].append(str(self._data_value_ch_for_rl[c][0]))
else:
if len(self._data_rl[c]) == 0:
self._data_rl[c].append(str(int(t)))
# self._data_rl[c].append(str(self._data_time_ch_for_rl[c][0]))
# self._data_rl[c].append(str(self._data_value_ch_for_rl[c][0]))
_max = max(self._data_value_ch_for_rl[c])
_max_index = self._data_value_ch_for_rl[c].index(_max)
_min = min(self._data_value_ch_for_rl[c])
_min_index = self._data_value_ch_for_rl[c].index(_min)
# _mean = mean(self._data_value_ch_for_rl[c])
# print('_max, _max_index, _min, _min_index', _max, _max_index, _min, _min_index)
# _first_time = self._data_time_ch_for_rl[c][0]
# _last_time = self._data_time_ch_for_rl[c][-1]
if _max_index < _min_index:
# self._data_rl[c].append(str(_first_time))
self._data_rl[c].append(str(_max))
# self._data_rl[c].append(str(_last_time))
self._data_rl[c].append(str(_min))
else:
# self._data_rl[c].append(str(_first_time))
self._data_rl[c].append(str(_min))
# self._data_rl[c].append(str(_last_time))
self._data_rl[c].append(str(_max))
self._data_value_ch_for_rl[c].clear()
@@ -1294,6 +1264,9 @@ class RecordingFileWriter:
# mini picture
if len(self._data_value_ch[c]) >= 10:
self._data_mini_ch[c]['10']['mean'].append( int(mean(self._data_value_ch[c][0:9])) )
# self._data_mini_ch[c]['10']['random'].append( str(self._data_value_ch[c][random.randint(0,9)]) )
# _bar = self.get_bar(self._data_value_ch[c], None)
# self._data_mini_ch[c]['10']['bar'].extend(_bar)
self._data_value_ch[c].clear()
if int(len(self._data_mini_ch[c]['10']['mean']) / 10) - self._data_mini_ch[c]['10']['dec'] > 0:
self._data_mini_ch[c]['100']['mean'].append( int(mean(self._data_mini_ch[c]['10']['mean'][-10:])) )
@@ -1315,16 +1288,8 @@ class RecordingFileWriter:
self._data_db[c].append(str(int(t)))
self._data_db[c].append(str(v))
self._time_now = int(t)
# print('self._data_rl',self._data_rl)
# print('self._data_db', self._data_db)
# print('self._data_value_ch', self._data_value_ch)
# print('self._data_value_ch_for_rl', self._data_value_ch_for_rl)
# print('self._data_time_ch_for_rl', self._data_time_ch_for_rl)
# print('self._data_time_ch', self._data_time_ch)
# print('self._id_db_save', self._id_db_save)
return
# @calculate_time(1)
def write(self, data: Union[bytes, RecordingData, List[bytes], List[RecordingData]], mqtt_thread) -> int:
# check size
ths = self.splitting_threshold_size
@@ -1369,7 +1334,6 @@ class RecordingFileWriter:
self._data_db[ch] = []
self._time[ch] = 0
self._time_real_time[ch] = 0
self._data_all[ch] = []
if len(self._data_mini_ch) == 0:
for ch in self._channel_list:
self._data_mini_ch[ch] = {}
@@ -1383,86 +1347,43 @@ class RecordingFileWriter:
}
self.get_data_iter(d, mqtt_thread)
print('data', len(self._data_all[0]))
test = []
# for ch in self._data_all:
# if len(self._data_all[ch]) <= 1000:
# print('math.ceil(len(self._data_all[ch]) / 10) + 1', math.ceil(len(self._data_all[ch]) / 10) + 1)
# test = largest_triangle_three_buckets(self._data_all[ch], math.ceil(len(self._data_all[ch]) / 10) + 1)
# else:
# self._data_all[ch] = largest_triangle_three_buckets(self._data_all[ch], 1000)
# test = self._data_all[ch]
# print('test', test)
# a = [' '.join(list(map(str, b))) for b in test]
# print('a', a)
# mes = ' '.join(a)
# mqtt_thread[50+ch].on_message(mes)
for ch in self._data_all:
if len(self._data_all[ch]) <= 1000:
self._data_all[ch] = signal.savgol_filter(self._data_all[ch], 32, 3)
else:
self._data_all[ch] = signal.savgol_filter(self._data_all[ch], 32, 3)
print('test', test)
a = [' '.join(list(map(str, b))) for b in self._data_all[ch]]
print('a', a)
mes = ' '.join(a)
mqtt_thread[50+ch].on_message(mes)
if len(self._recording_file_dict) > 0:
for ch in self._data_db.keys():
if self._time_now - self._time_real_time[ch] > 1000000:
self._send_data[ch] = True
self._time_real_time[ch] = self._time_now
for ch in self._recording_file_dict:
if self._recording_file_dict[ch]._id_db == 0:
return None
for ch in self._data_db.keys():
for scale in self._mini_scale_list:
if self._recording_mini_dict[str(scale)][ch]._id_db == 0:
return None
if self._raw_create_not_done :
self._raw_create_not_done = False
self._meta.update_subfile_raw(database = self._database)
if self._mini_create_not_done :
self._mini_create_not_done = False
self._meta.update_subfile_mini(database = self._database)
data_save = False
mini_save = False
if len(self._recording_file_dict) > 0:
for ch in self._data_db.keys():
if self._time_now - self._time_real_time[ch] > 1000000:
self._send_data[ch] = True
self._time_real_time[ch] = self._time_now
# # if self._time_now - self._time_real_time[ch] > 500000:
# self._data_rl[ch].append(str(self._time_now))
# mes = ' '.join(self._data_rl[ch])
# # print(mes)
# # if mqtt_thread is not None:
# # mqtt_thread.publish('data_server/device_data_stream/' + str(self._device_id) + '/' + str(ch), mes)
# # mqtt_queue.put([self._device_id, ch, mes])
# mqtt_thread[ch].on_message(mes)
# self._data_rl[ch].clear()
# # self._time_real_time[ch] = self._time_now
if self._time_now - self._time[ch] > 5000000:
if self._recording_file_dict[ch]._status:
_data = ' '.join(self._data_db[ch])
write_sz = self._recording_file_dict[ch].write(_data, self._channel_list)
sz = sz + write_sz
self._raw_save['data'][ch] = _data
self._raw_save['id'][ch] = self._recording_file_dict[ch]._id_db
self._raw_save['end_time'][ch] = self._time_now
self._raw_save['size'][ch] = self._recording_file_dict[ch]._size
data_save = True
self._data_db[ch].clear()
self._time[ch] = self._time_now
self._splitting_size += sz
self._meta._last_time = self._time_now
self._splitting_size += write_sz
data_save = True
self._recording_file_dict[ch].update_endtime(self._time_now)
if len(self._data_mini_ch[ch]['1000']['mean']) >= 10:
mini_save = True
for scale in self._mini_scale_list:
str_mean = [str(int) for int in self._data_mini_ch[ch][str(scale)]['mean']]
data_mean = str(self._data_mini_ch[ch][str(scale)]['start_time']) + ' ' + ' '.join(str_mean) + '"***"'
self._mini_save[str(scale)]['id'][ch] = self._recording_mini_dict[str(scale)][ch]._id_db
self._mini_save[str(scale)]['start_time'][ch] = str(self._data_mini_ch[ch][str(scale)]['start_time'])
self._mini_save[str(scale)]['data_mean'][ch] = data_mean
self._mini_save[str(scale)]['data_mean'][ch] = self._data_mini_ch[ch][str(scale)]['mean'].copy()
# self._mini_save[str(scale)]['data_random'][ch] = self._data_mini_ch[ch][str(scale)]['random'].copy()
# self._mini_save[str(scale)]['data_bar'][ch] = self._data_mini_ch[ch][str(scale)]['bar'].copy()
self._recording_mini_dict[str(scale)][ch].write(self._mini_save[str(scale)]['data_mean'][ch])
@@ -1473,25 +1394,26 @@ class RecordingFileWriter:
if data_save is True:
if self._database is not None:
recording_input = ['data_raw_recording_new', copy(self._raw_save['id']), copy(self._channel_list), copy(self._raw_save['data']), copy(self._raw_save['end_time']), copy(self._raw_save['size'])]
self._database.put_queue(recording_input)
self._meta.update_subfile_time_size(database = self._database)
self._database.put_queue(['data_raw_recording', self._raw_save['id'], self._channel_list, self._raw_save['data']])
self._meta.update_subfile(database = self._database)
if mini_save is True:
if self._database is not None:
for scale in self._mini_scale_list:
self._database.put_queue(['data_mini_recording_new', copy(self._mini_save[str(scale)]['id']), self._channel_list, copy(self._mini_save[str(scale)]['data_mean'])])
self._meta.update_subfile_time_size(database = self._database)
self._database.put_queue(['data_mini_recording', self._mini_save[str(scale)]['id'], self._channel_list, self._mini_save[str(scale)]['start_time'], self._mini_save[str(scale)]['data_mean']])
self._meta.update_subfile(database = self._database)
del data
return None
def _switch_recording_file(self):
self._raw_create_not_done = True
# if self._recording_file is not None:
# self._recording_file.close()
if len(self._recording_file_dict) > 0:
for ch in self._recording_file_dict.keys():
self._id_db_save[ch] = self._recording_file_dict[ch]._id_db
self._recording_file_dict[ch].close(self._time_now)
self._meta._size += self._recording_file_dict[ch]._size
@@ -1507,16 +1429,16 @@ class RecordingFileWriter:
if self._database is not None:
self._database.put_queue(['data_raw_create', raw_data_dict, self._channel_list, self._device_id])
# self._database.put_queue(['data_raw_create', self._meta, self._recording_file_dict, raw_data_dict, self._channel_list])
self._meta._last_time = self._time_now
# self._meta.update_subfile(database = self._database)
self._meta.update_subfile(database = self._database)
self._splitting_size = 0
return None
def _switch_recording_mini(self, scale):
self._mini_create_not_done = True
if len(self._recording_mini_dict[str(scale)]) > 0:
for ch in self._recording_mini_dict[str(scale)].keys():
@@ -1534,12 +1456,14 @@ class RecordingFileWriter:
if self._database is not None:
self._database.put_queue(['data_mini_create', mini_data_dict, self._channel_list, scale, self._device_id])
# self._database.put_queue(['data_mini_create', self._meta, self._recording_mini_dict[str(scale)], mini_data_dict, self._channel_list, scale])
self._meta._last_time = self._time_now
# self._meta.update_subfile(database = self._database)
self._meta.update_subfile(database = self._database)
return None
def update_meta_id(self, _id):
# print('update meta')
self._meta._id_db = _id
return None
-108
View File
@@ -1,108 +0,0 @@
"""
The MIT License (MIT)
Copyright (c) 2015 Olivier Devoisin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import math
class LttbException(Exception):
pass
def largest_triangle_three_buckets(data, threshold):
"""
Return a downsampled version of data.
Parameters
----------
data: list of lists/tuples
data must be formated this way: [[x,y], [x,y], [x,y], ...]
or: [(x,y), (x,y), (x,y), ...]
threshold: int
threshold must be >= 2 and <= to the len of data
Returns
-------
data, but downsampled using threshold
"""
# Check if data and threshold are valid
if not isinstance(data, list):
raise LttbException("data is not a list")
if not isinstance(threshold, int) or threshold <= 2 or threshold >= len(data):
raise LttbException("threshold not well defined")
for i in data:
if not isinstance(i, (list, tuple)) or len(i) != 2:
raise LttbException("datapoints are not lists or tuples")
# Bucket size. Leave room for start and end data points
every = (len(data) - 2) / (threshold - 2)
a = 0 # Initially a is the first point in the triangle
next_a = 0
max_area_point = (0, 0)
sampled = [data[0]] # Always add the first point
for i in range(0, threshold - 2):
# Calculate point average for next bucket (containing c)
avg_x = 0
avg_y = 0
avg_range_start = int(math.floor((i + 1) * every) + 1)
avg_range_end = int(math.floor((i + 2) * every) + 1)
avg_rang_end = avg_range_end if avg_range_end < len(data) else len(data)
avg_range_length = avg_rang_end - avg_range_start
while avg_range_start < avg_rang_end:
avg_x += data[avg_range_start][0]
avg_y += data[avg_range_start][1]
avg_range_start += 1
avg_x /= avg_range_length
avg_y /= avg_range_length
# Get the range for this bucket
range_offs = int(math.floor((i + 0) * every) + 1)
range_to = int(math.floor((i + 1) * every) + 1)
# Point a
point_ax = data[a][0]
point_ay = data[a][1]
max_area = -1
while range_offs < range_to:
# Calculate triangle area over three buckets
area = math.fabs(
(point_ax - avg_x)
* (data[range_offs][1] - point_ay)
- (point_ax - data[range_offs][0])
* (avg_y - point_ay)
) * 0.5
if area > max_area:
max_area = area
max_area_point = data[range_offs]
next_a = range_offs # Next a is this b
range_offs += 1
sampled.append(max_area_point) # Pick this point from the bucket
a = next_a # This a is the next a (chosen b)
sampled.append(data[len(data) - 1]) # Always add last
return sampled

Some files were not shown because too many files have changed in this diff Show More