Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b60e68197 | |||
| 6efc5e1097 | |||
| a23394d3d1 | |||
| c96f76f438 | |||
| f0f1c9fc49 | |||
| 512c80af40 | |||
| 499964b287 | |||
| 16f12d40bf | |||
| 63c1ba8efb | |||
| 4a28348db4 | |||
| 64dfe87d05 | |||
| f7a5a867a4 | |||
| 1accaa9b9f | |||
| b810e35fb2 | |||
| 82c9d2b0e3 | |||
| e5a3f33b9f | |||
| 1dd4ae2ce4 | |||
| 515cc9f7a2 | |||
| 9b92249fdb | |||
| adcc13b4de | |||
| 0cd0fd7027 | |||
| 5228290eac | |||
| 034b7976cd | |||
| fa8c219f76 | |||
| f233c019fd | |||
| ced466f3aa | |||
| f36906c034 | |||
| 0b5c62e525 | |||
| abe86238ca | |||
| f9ab0bc59b | |||
| 5dbe231c73 | |||
| c721950bf7 | |||
| 70c0271771 |
@@ -1,5 +1,7 @@
|
||||
.DS_Store
|
||||
*.cpython-37.opt-2.pyc
|
||||
*.cpython-39-arm-linux-gnueabihf.so
|
||||
*.
|
||||
*.pyc
|
||||
*/__pycache__
|
||||
/.vscode
|
||||
|
||||
@@ -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 Buster Lite
|
||||
###### Download Raspbian Bullseye 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-raspbian-buster.img of=/dev/sdX conv=fsync status=progress
|
||||
sudo dd bs=4M if=yyyy-mm-dd-raspios-bullseye-armhf-lite.img of=/dev/sdX conv=fsync status=progress
|
||||
|
||||
# enable ssh on first boot
|
||||
add ssh.txt in boot folder
|
||||
@@ -77,18 +77,22 @@ 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
|
||||
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
|
||||
|
||||
### Setup project
|
||||
###### Clone project
|
||||
###### Clone data server project
|
||||
```
|
||||
cd ~
|
||||
git clone https://gitlab.com/bioproscientific/bioprorelife.git
|
||||
git clone https://gitlab.com/wisetop/controller/wisetopdataserver.git
|
||||
```
|
||||
|
||||
###### Install python dependencies
|
||||
```
|
||||
cd ~/bioprorelife
|
||||
cd ~/wisetopdataserver
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
##### Packages version
|
||||
@@ -112,14 +116,14 @@ psycopg2 2.8.5
|
||||
|
||||
###### Build C extension
|
||||
```
|
||||
cd ~/bioprorelife/python/biopro/ext/
|
||||
cd ~/wisetopdataserver/python/biopro/ext/
|
||||
rm -rf *.so build
|
||||
python3 setup.py build_ext --inplace
|
||||
```
|
||||
|
||||
###### Check setup-uart file
|
||||
```
|
||||
cd ~/bioprorelife
|
||||
cd ~/wisetopdataserver/
|
||||
sudo vim ./run-setup-uart.sh
|
||||
```
|
||||
##### Modify the code as following
|
||||
@@ -149,44 +153,72 @@ 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/bioprorelife/run-data-server.sh > /home/pi/.data_server.out &
|
||||
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 &
|
||||
```
|
||||
##### /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
|
||||
```
|
||||
###### 1.Install mosquitto
|
||||
$sudo apt-get install mosquitto mosquitto-clients
|
||||
|
||||
|
||||
###### 2.Enable Remote Access
|
||||
$sudo vim /etc/mosquitto/conf.d/default.conf
|
||||
|
||||
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
|
||||
```
|
||||
restart mqtt broker
|
||||
```
|
||||
sudo systemctl restart mosquitto
|
||||
```
|
||||
###### 3.restart mqtt broker
|
||||
$sudo systemctl restart mosquitto
|
||||
|
||||
3. Robust MQTT
|
||||
###### 4.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" ]
|
||||
@@ -200,25 +232,129 @@ fi
|
||||
echo "$SERVICE is currently running" >> /home/ubuntu/cron.log
|
||||
exit 0
|
||||
```
|
||||
add mosquitto_restart.sh to root routing
|
||||
```
|
||||
sudo chmod +x mosquitto_restart.sh
|
||||
sudo -i
|
||||
crontab -e
|
||||
```
|
||||
###### 5.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
|
||||
```
|
||||
3. Auto run MQTT broker when when pi-start
|
||||
```
|
||||
sudo vim /etc/rc.local
|
||||
```
|
||||
###### 6.Auto run MQTT broker when when pi-start
|
||||
$sudo vim /etc/rc.local
|
||||
|
||||
add the code following as:
|
||||
```
|
||||
sudo nohup systemctl restart mosquitto
|
||||
```
|
||||
|
||||
### Setup routed wireless access point
|
||||
### 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>
|
||||
```
|
||||
|
||||
##### raspberry website document:
|
||||
> https://www.raspberrypi.org/documentation/configuration/wireless/access-point-routed.md
|
||||
|
||||
@@ -1578,10 +1578,13 @@ class ControlAPI(metaclass=Router):
|
||||
raise NotImplementedError()
|
||||
|
||||
def run_project(self, project) -> bool:
|
||||
print('create_project', project)
|
||||
self._project_manager.create(project)
|
||||
return False
|
||||
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)):
|
||||
|
||||
@@ -253,6 +253,7 @@ 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.
|
||||
@@ -267,7 +268,6 @@ class DeviceManager(MasterDevice, Synchronized):
|
||||
options: DeviceManagerOptions,
|
||||
handler: Optional[DeviceInternalCommandHandler] = None):
|
||||
"""
|
||||
|
||||
:param options: manager options.
|
||||
:param handler: device internal command handler
|
||||
"""
|
||||
@@ -277,6 +277,7 @@ class DeviceManager(MasterDevice, Synchronized):
|
||||
if handler is None:
|
||||
handler = DeviceInternalCommandHandler()
|
||||
|
||||
# control server api
|
||||
self._handler: DeviceInternalCommandHandler = handler
|
||||
self._options: DeviceManagerOptions = options
|
||||
|
||||
@@ -286,7 +287,9 @@ 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
|
||||
@@ -336,6 +339,10 @@ 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"""
|
||||
@@ -562,7 +569,6 @@ class DeviceManager(MasterDevice, Synchronized):
|
||||
|
||||
@logging_verbose
|
||||
def get_device(self, device: Union[int, DeviceInfo]) -> Optional[CompletedDevice]:
|
||||
print('get_device', device)
|
||||
"""get connected device
|
||||
|
||||
:param device: device ID or response info
|
||||
@@ -1024,15 +1030,9 @@ 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)
|
||||
|
||||
|
||||
@@ -881,12 +881,17 @@ 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
|
||||
voltage = struct.unpack('<i', data[12:16])[0] # unit: uV
|
||||
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]
|
||||
@@ -945,18 +950,18 @@ 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)
|
||||
# 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(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))
|
||||
|
||||
|
||||
|
||||
@@ -1396,39 +1401,33 @@ class EISZeroOneDataDecoder(RecDataDecoder):
|
||||
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_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[2] 5k
|
||||
index = 100
|
||||
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[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_d.append(struct.unpack('>B', cali_coeff[index+13:index+14])[0])
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -1585,10 +1584,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]
|
||||
@@ -1597,10 +1596,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
|
||||
|
||||
@@ -653,15 +653,9 @@ 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"""
|
||||
|
||||
@@ -750,8 +744,6 @@ 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:
|
||||
|
||||
@@ -1268,14 +1268,15 @@ class InstructionTable(DictNode['Instruction']):
|
||||
instruction: str):
|
||||
"""call instruction. chain context with :class:`DeviceInternalInstructionScope`
|
||||
|
||||
:param master: master device
|
||||
:param device: device
|
||||
:param master: DeviceManager
|
||||
:param device: CompletedDevice
|
||||
:param context: scope
|
||||
:param instruction: instruction name
|
||||
:raises InstructionNotFound: If instruction not found.
|
||||
"""
|
||||
|
||||
try:
|
||||
# ex. start
|
||||
ins = self[instruction]
|
||||
|
||||
except KeyError as e:
|
||||
@@ -1284,6 +1285,7 @@ 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):
|
||||
|
||||
@@ -966,7 +966,7 @@ class CompletedDevice(Device):
|
||||
"""
|
||||
__slots__ = ('_master', '_device_id', '_device', '_library', '_context',
|
||||
'_parameter', '_configuration', '_lock', '_feature_mask',
|
||||
'_cache_battery', '_cache_battery_timestamp', '_coeff')
|
||||
'_cache_battery', '_cache_battery_timestamp', '_coeff', '_status')
|
||||
|
||||
def __init__(self, master: MasterDevice, library: DeviceLibrary, device_id: int, device: Device):
|
||||
"""
|
||||
@@ -976,11 +976,15 @@ 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)
|
||||
@@ -1008,6 +1012,10 @@ 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
|
||||
@@ -1020,6 +1028,10 @@ 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
|
||||
@@ -1056,11 +1068,18 @@ 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]:
|
||||
@@ -1115,7 +1134,8 @@ class CompletedDevice(Device):
|
||||
self._master.log_warn('on_change instruction not found : ' + on_change)
|
||||
|
||||
else:
|
||||
self.call_instruction(on_change)
|
||||
if self._status == 1:
|
||||
self.call_instruction(on_change)
|
||||
|
||||
# device instruction call
|
||||
|
||||
@@ -1258,14 +1278,20 @@ 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),
|
||||
|
||||
@@ -76,6 +76,8 @@ class MultiExtMemSpiInterface(LowLevelHardwareInterface):
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -118,13 +118,13 @@ class Selector:
|
||||
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)
|
||||
# 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)
|
||||
# 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)
|
||||
# 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)
|
||||
# print('mem_sel is not use gray code, value:', value, ', last value:', self._last_sel, datetime.now())
|
||||
|
||||
self._last_sel = value
|
||||
|
||||
|
||||
@@ -1,68 +1,26 @@
|
||||
|
||||
class Action():
|
||||
def __init__(self, task, action_id, action):
|
||||
self._task = task
|
||||
self._id:str = action_id
|
||||
self._type:str = action['type']
|
||||
self._target:str = action['target']
|
||||
self._condition_list:list[str] = action['condition']
|
||||
self._duration = action.get('duration', None)
|
||||
self._goto = action.get('goto', None)
|
||||
self._cycle = action.get('cycle', None)
|
||||
self._instruction = None
|
||||
|
||||
self.update_instruction()
|
||||
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
|
||||
|
||||
def update_instruction(self):
|
||||
instruction_type = self._type
|
||||
if instruction_type == 'stop':
|
||||
instruction_type = 'interrupt'
|
||||
|
||||
self._instruction = {
|
||||
"device_instruction": {
|
||||
"header": "call_instruction",
|
||||
"device": self._target,
|
||||
"arguments": {
|
||||
"instruction": instruction_type,
|
||||
}
|
||||
},
|
||||
"device_parameter": {
|
||||
"header": "set_multi_parameters",
|
||||
"device": self._target,
|
||||
"arguments": {
|
||||
"parameter": self._task.get_parameter_set_by_device(self._target),
|
||||
}
|
||||
},
|
||||
"device_recording_file_name": {
|
||||
"header": "update_recording_file_name_info",
|
||||
"device": self._target,
|
||||
"arguments": {
|
||||
"content": self._task.file_name,
|
||||
}
|
||||
},
|
||||
"device_parent": {
|
||||
"header": "update_parent_info",
|
||||
"device": self._target,
|
||||
"arguments": {
|
||||
"content": self._task.parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def get_condition_list(self):
|
||||
return self._condition_list
|
||||
|
||||
def get_instruction(self, value):
|
||||
return self._instruction[value]
|
||||
|
||||
def get_instruction_list(self):
|
||||
instruction_set = {
|
||||
'start': ['device_parent', 'device_recording_file_name', 'device_parameter', 'device_instruction'],
|
||||
'stop': ['device_instruction'],
|
||||
}
|
||||
|
||||
return map(self.get_instruction, instruction_set[self._type])
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
return self._target
|
||||
|
||||
@property
|
||||
def condition(self):
|
||||
return self._condition
|
||||
|
||||
@@ -7,6 +7,7 @@ class Condition():
|
||||
self._type = condition['type']
|
||||
self._comparsion = condition['comparsion']
|
||||
self._value = condition['value']
|
||||
self._active = False
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
@@ -24,16 +25,24 @@ class Condition():
|
||||
def value(self):
|
||||
return self._value
|
||||
|
||||
def set_comparsion(self, comparsion):
|
||||
self._comparsion = comparsion
|
||||
|
||||
def set_type(self, type):
|
||||
self._type = type
|
||||
@id.setter
|
||||
def id(self, new_id):
|
||||
self._id = new_id
|
||||
|
||||
def set_value(self, value):
|
||||
self._value = value
|
||||
@type.setter
|
||||
def type(self, new_type):
|
||||
self._type = new_type
|
||||
|
||||
def compareWith(self, operator:str, x, y) -> bool:
|
||||
@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,
|
||||
@@ -47,34 +56,38 @@ class Condition():
|
||||
return getattr(self, self.type)(**kwargs)
|
||||
|
||||
def absolute_time(self, **kwargs):
|
||||
now = time()
|
||||
time_condition = self.datetime_to_timestamp(self.str_to_datetime(self._value))
|
||||
now = int(time())
|
||||
time_condition = round(self.datetime_to_timestamp(self.str_to_datetime(self._value)))
|
||||
|
||||
return self.compareWith(self.comparsion, int(now), int(time_condition))
|
||||
return self.compareWith(self.comparsion, now, time_condition)
|
||||
|
||||
def relative_time(self, **kwargs):
|
||||
def after_project_run(self, **kwargs):
|
||||
project_start_time = kwargs['project_start_time']
|
||||
task_start_time = kwargs['task_start_time']
|
||||
delay_time = kwargs['delay_time']
|
||||
now = time()
|
||||
|
||||
time_diff = int(now - project_start_time - 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 task_status(self, **kwargs):
|
||||
print('task_status')
|
||||
|
||||
def method_mapping(self, method_name):
|
||||
methods = {
|
||||
"relative_time": self.relative_time,
|
||||
"absolute_time": self.absolute_time,
|
||||
"device": self.device,
|
||||
"task_status": self.task_status,
|
||||
}
|
||||
return methods[method_name]
|
||||
def 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')
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
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
|
||||
@@ -1,14 +1,22 @@
|
||||
from .task import Task
|
||||
from .task_manager import TaskManager
|
||||
import sys
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
|
||||
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 *
|
||||
from copy import copy
|
||||
|
||||
key_list = {
|
||||
'deviceList': 'device',
|
||||
}
|
||||
|
||||
class Project(threading.Thread):
|
||||
def __init__(self, project, device_manager: DeviceManager, mqttThread = None, name="project"):
|
||||
@@ -18,65 +26,199 @@ class Project(threading.Thread):
|
||||
self._mqtt_thread = mqttThread
|
||||
self._time_interval = 1
|
||||
self._start_time = None
|
||||
self._end_time = None
|
||||
|
||||
self._task_manager = TaskManager(self._project['taskList'])
|
||||
self._stop = False
|
||||
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._project['desc']
|
||||
return self._desc
|
||||
|
||||
@desc.setter
|
||||
def desc(self, new_desc):
|
||||
self._desc = new_desc
|
||||
|
||||
@property
|
||||
def status(self) -> str:
|
||||
return self._project['status']
|
||||
return self._status
|
||||
|
||||
@status.setter
|
||||
def status(self, new_status):
|
||||
self._status = new_status
|
||||
|
||||
@property
|
||||
def device(self) -> list:
|
||||
return self._project['deviceList']
|
||||
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 task(self) -> list:
|
||||
return self._task_manager.get()
|
||||
|
||||
@property
|
||||
def get_current_task(self) -> str:
|
||||
return self._task_manager.get_current_task()
|
||||
def mqtt_thread(self):
|
||||
return self._mqtt_thread
|
||||
|
||||
def run(self):
|
||||
self._start_time = time.time()
|
||||
|
||||
while not self._stop:
|
||||
# current task
|
||||
task_check_list = copy(self._task_manager.get_check_task_list())
|
||||
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
|
||||
for task in task_check_list:
|
||||
# [<match_condition>, ...]
|
||||
match_condition_list = task.check_condition(project_start_time = self._start_time, delay_time = delay_time)
|
||||
# [[<match_action>, ...], ...]
|
||||
match_action_list = task.get_match_action_list(match_condition_list)
|
||||
|
||||
for action_list in match_action_list:
|
||||
for action in action_list:
|
||||
if action.type == 'start':
|
||||
task.reset_start_time()
|
||||
self._task_manager.set_current_task(task)
|
||||
task.do_action(action)
|
||||
|
||||
now = time.time()
|
||||
while len(task.instruction_list) > 0:
|
||||
instruction = task.instruction_list.pop(0)
|
||||
if instruction.get('device', None) != None:
|
||||
device = self._device_manager.get_device(self.device[instruction['device']]['connectDevice']['device_address'])
|
||||
check_list = copy(self._task_manager.check_list)
|
||||
|
||||
if device is None:
|
||||
raise RuntimeError(DEVICE_NOT_FOUND, device)
|
||||
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)
|
||||
|
||||
getattr(device, instruction['header'])(*instruction['arguments'].values())
|
||||
delay_time += (time.time() - now)
|
||||
time.sleep(self._time_interval)
|
||||
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._stop = True
|
||||
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._stop = True
|
||||
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
|
||||
|
||||
@@ -18,16 +18,22 @@ class ProjectManager():
|
||||
new_project = Project(project, self._device_manager, self._mqtt_thread)
|
||||
self._project_list.append(new_project)
|
||||
return new_project
|
||||
|
||||
|
||||
def remove(self, index):
|
||||
self._project_list[index].stop()
|
||||
del self._project_list[index]
|
||||
|
||||
def get(self):
|
||||
return self._project_list
|
||||
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(self, project):
|
||||
def run_project(self, project):
|
||||
project.start()
|
||||
|
||||
def stop(self, project):
|
||||
project.stop()
|
||||
def stop_project(self, project):
|
||||
_project = self.get(project)
|
||||
_project.stop()
|
||||
|
||||
+200
-117
@@ -2,112 +2,186 @@ 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._task = task
|
||||
|
||||
self._condition_list:List[Condition] = []
|
||||
self._action_list:List[Action] = []
|
||||
|
||||
self._start_time = time()
|
||||
self._idle_time = []
|
||||
self._period = None
|
||||
self._next_time = None
|
||||
self._last_time = None
|
||||
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.setup()
|
||||
self._record_list = []
|
||||
|
||||
self._start_time = []
|
||||
self._idle_time = []
|
||||
self._end_time = []
|
||||
self._period = None
|
||||
|
||||
self.load_task(task)
|
||||
|
||||
def setup(self) -> None:
|
||||
# add condition
|
||||
for key, condition in self._task['condition'].items():
|
||||
new_condition = Condition(key, condition)
|
||||
self._condition_list.append(new_condition)
|
||||
def 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)
|
||||
|
||||
# add action
|
||||
for key, action in self._task['action'].items():
|
||||
new_action = Action(self, key, action)
|
||||
self._action_list.append(new_action)
|
||||
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._task['id']
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def file_name(self):
|
||||
return self._task['name']
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
return self._task.get('parent', {"folder": [2]})
|
||||
|
||||
@property
|
||||
def instruction_list(self):
|
||||
return self._instruction_list
|
||||
|
||||
@property
|
||||
def cycle(self) -> int:
|
||||
return self._task['cycle']
|
||||
|
||||
@property
|
||||
def order(self) -> int:
|
||||
return self._task['order']
|
||||
|
||||
@property
|
||||
def max_cycle(self) -> int:
|
||||
return self._task['max_cycle']
|
||||
|
||||
@property
|
||||
def devices(self) -> dict:
|
||||
return self._task['devices']
|
||||
|
||||
@property
|
||||
def devices_id(self) -> list:
|
||||
return [device_id for device_id in self.devices]
|
||||
|
||||
@property
|
||||
def events(self) -> list:
|
||||
return self._task['events']
|
||||
|
||||
@property
|
||||
def triggers(self) -> list:
|
||||
return self._task['triggers']
|
||||
|
||||
@property
|
||||
def parameter_set(self) -> list:
|
||||
return self._task['parameterSet']
|
||||
|
||||
@property
|
||||
def instructions(self) -> list:
|
||||
return self._task['instructions']
|
||||
|
||||
@property
|
||||
def conditions(self) -> list:
|
||||
return self._task['conditions']
|
||||
|
||||
@property
|
||||
def actions(self) -> list:
|
||||
return self._task['actions']
|
||||
|
||||
@property
|
||||
def condition_list(self) -> list:
|
||||
return self._condition_list
|
||||
|
||||
@property
|
||||
def status(self) -> str:
|
||||
return self._task['status']
|
||||
@id.setter
|
||||
def id(self, new_id):
|
||||
self._id = new_id
|
||||
|
||||
@property
|
||||
def uuid(self) -> str:
|
||||
return self._task['uuid']
|
||||
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._task['next']
|
||||
return self._next
|
||||
|
||||
def reset_start_time(self):
|
||||
self._start_time = time()
|
||||
@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']]
|
||||
@@ -124,46 +198,55 @@ class Task:
|
||||
return '|'.join(name_list) if name_or_value == 'name' else '|'.join(value_list)
|
||||
|
||||
def run(self):
|
||||
self._status = 'running'
|
||||
self._start_time = time()
|
||||
|
||||
def pause(self):
|
||||
self._status = 'pause'
|
||||
|
||||
def set_cycle(self, cycle):
|
||||
self._cycle = cycle
|
||||
|
||||
def set_status(self, status):
|
||||
self._status = status
|
||||
print(self._name, 'run at', datetime.now())
|
||||
self._status = 1
|
||||
self._start_time.append(time())
|
||||
|
||||
def start(self):
|
||||
self.set_status('running')
|
||||
self.status = 1
|
||||
|
||||
def stop(self):
|
||||
if self.status == 'running':
|
||||
self.do_action('stop')
|
||||
self.set_status('idle')
|
||||
|
||||
def done(self):
|
||||
self.set_status('done')
|
||||
|
||||
def end(self):
|
||||
self.set_status('end')
|
||||
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 status_mapping(self, status):
|
||||
statuses = {
|
||||
"start": self.task_start,
|
||||
"done": self.task_done,
|
||||
}
|
||||
return statuses[status]
|
||||
|
||||
def check_condition(self, **kwargs):
|
||||
kwargs['task_start_time'] = self._start_time
|
||||
return [condition for condition in self._condition_list if condition.match_or_not(**kwargs)]
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
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
|
||||
@@ -12,45 +13,76 @@ def json_stringify(o: Any) -> str:
|
||||
|
||||
class TaskManager():
|
||||
def __init__(self, task_list):
|
||||
self._current_task = None
|
||||
self._task_list = []
|
||||
self._need_to_check_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)
|
||||
|
||||
self.set_current_task(self._task_list[0])
|
||||
self._current_task.run()
|
||||
|
||||
def remove(self, index):
|
||||
pass
|
||||
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]
|
||||
|
||||
def get_check_task_list(self) -> List[Task]:
|
||||
return self._need_to_check_task_list
|
||||
|
||||
def get_current_task(self) -> Task:
|
||||
return self._current_task
|
||||
|
||||
def get_next_task(self):
|
||||
return self._current_task.next
|
||||
|
||||
def set_current_task(self, task):
|
||||
try:
|
||||
if self._current_task != None and self._current_task.id != task.id:
|
||||
self._current_task.stop()
|
||||
|
||||
self._current_task = task
|
||||
self._need_to_check_task_list = []
|
||||
# append current task
|
||||
self._need_to_check_task_list.append(self._current_task)
|
||||
|
||||
# append next task
|
||||
for task_uuid in self._current_task.next:
|
||||
task = next((task for task in self._task_list if task.uuid == task_uuid), None)
|
||||
self._need_to_check_task_list.append(task)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
@@ -9,23 +9,9 @@ _jwt = None
|
||||
|
||||
def controller_device_id() -> str:
|
||||
### device ID
|
||||
return 'b8:27:eb:52:ea:97'
|
||||
ret = None
|
||||
try:
|
||||
with open('/etc/BPS/device_id') as f:
|
||||
for line in f:
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
|
||||
ret = line.strip()
|
||||
break
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
if ret is None or len(ret) == 0:
|
||||
return str(uuid.uuid1())
|
||||
else:
|
||||
return ret
|
||||
f = open("/sys/class/net/eth0/address", "r")
|
||||
mac = f.read()[:-1]
|
||||
return mac
|
||||
|
||||
def get_api_jwt() -> str:
|
||||
global _jwt
|
||||
|
||||
@@ -104,18 +104,9 @@ class DataServer(SocketServer, DataAPI):
|
||||
self.log_verbose,
|
||||
'MQTT-data-server')
|
||||
# api socket
|
||||
self._queue_spi = queue.Queue()
|
||||
# self._queue_spi_dict = {}
|
||||
self._queue_ds_dict = {}
|
||||
self._queue_db = Queue()
|
||||
self._queue_db_error = Queue()
|
||||
# self._queue_ds = Queue()
|
||||
|
||||
# if len(self._queue_spi_dict) == 0:
|
||||
# self._queue_spi_dict[4] = Queue()
|
||||
# self._queue_spi_dict[5] = Queue()
|
||||
# self._queue_spi_dict[6] = Queue()
|
||||
# self._queue_spi_dict[7] = Queue()
|
||||
self._queue_msg = Queue()
|
||||
|
||||
if len(self._queue_ds_dict) == 0:
|
||||
self._queue_ds_dict[4] = Queue()
|
||||
@@ -123,9 +114,12 @@ class DataServer(SocketServer, DataAPI):
|
||||
self._queue_ds_dict[6] = Queue()
|
||||
self._queue_ds_dict[7] = Queue()
|
||||
|
||||
self.database_process = DataBaseProcess('data_server', self.log_verbose, self._queue_db, self._queue_ds_dict, self._queue_db_error)
|
||||
self.database_process = DataBaseProcess('data_server', self.log_verbose, self._queue_db, self._queue_ds_dict, self._queue_msg)
|
||||
self.database_process.start()
|
||||
|
||||
# self._no_data_total_duration = 0
|
||||
# self._recv_memory_cost_time = 0
|
||||
|
||||
@logging_info
|
||||
def setup(self):
|
||||
self._available_channel.clear()
|
||||
@@ -146,7 +140,6 @@ class DataServer(SocketServer, DataAPI):
|
||||
try:
|
||||
self._stop_runtime()
|
||||
self.stop_sync()
|
||||
self._queue_spi.queue.clear()
|
||||
finally:
|
||||
self._configurations.clear()
|
||||
|
||||
@@ -185,9 +178,6 @@ class DataServer(SocketServer, DataAPI):
|
||||
|
||||
self._configurations.clear()
|
||||
|
||||
with CacheClient() as client:
|
||||
client.clear()
|
||||
|
||||
# reopen spi, which has close at close()
|
||||
if spi_mode is None:
|
||||
spi_mode = self._spi_mode
|
||||
@@ -417,7 +407,7 @@ class DataServer(SocketServer, DataAPI):
|
||||
c.join()
|
||||
self._spi.set_wait_flag(c._device, True)
|
||||
data_runtime_process = RecordingProcess('data_server', self.log_verbose, device_id,
|
||||
Queue(), self._queue_ds_dict[int(device_id)], meta_file,
|
||||
Queue(), self._queue_ds_dict[int(device_id)], self._queue_msg, meta_file,
|
||||
json_stringify(_device), _parameter, _parent, _recording_file_name,
|
||||
data_format, self.socket_mqtt_ip, self.socket_mqtt_port,
|
||||
database = self.database_process )
|
||||
@@ -618,8 +608,18 @@ class DataServer(SocketServer, DataAPI):
|
||||
return
|
||||
|
||||
def open_cache_client(self):
|
||||
self._cache_client = None
|
||||
return
|
||||
self.log_verbose('open cache client')
|
||||
try:
|
||||
client = CacheClient()
|
||||
client.open_socket()
|
||||
|
||||
except RuntimeError as e:
|
||||
self.log_warn(e)
|
||||
|
||||
else:
|
||||
client.clear()
|
||||
|
||||
self._cache_client = client
|
||||
|
||||
def _ensure_cache_client(self) -> Optional[CacheClient]:
|
||||
if self._cache_client is None:
|
||||
@@ -669,20 +669,34 @@ class DataServer(SocketServer, DataAPI):
|
||||
print("q size= ", self._configurations[device]._queue_rec.qsize())
|
||||
|
||||
self._configurations[device].put_rec_queue(data)
|
||||
# self._no_data_total_duration = 0
|
||||
ret = True
|
||||
# else:
|
||||
# time_diff = time() - self._recv_memory_cost_time
|
||||
# self._no_data_total_duration += time_diff
|
||||
# if self._no_data_total_duration > 1:
|
||||
# print('data is None', device, datetime.now(), self._no_data_total_duration)
|
||||
# print("sync.get_pin_mem_req() != sync.get_pin_ram_sel()")
|
||||
# print("no~", sync.select, sync.get_pin_mem_req(), sync.get_pin_ram_sel(), datetime.now())
|
||||
# print()
|
||||
# self._recv_memory_cost_time = time()
|
||||
|
||||
return ret
|
||||
|
||||
def rec_update(self) -> bool:
|
||||
|
||||
device_id = self._queue_db_error.get()
|
||||
self.mqtt_thread.broadcast_command('runtime error: ' + str(device_id))
|
||||
content = {
|
||||
'header': 'device_instruction/0',
|
||||
'device': device_id,
|
||||
'instruction': 'interrupt'
|
||||
}
|
||||
self.mqtt_thread.publish('device_instruction', json_stringify(content), inter = True)
|
||||
ds_db_msg = self._queue_msg.get()
|
||||
with CacheClient() as client:
|
||||
if isinstance(ds_db_msg, List):
|
||||
if ds_db_msg[0] == 'ds':
|
||||
client.send_command('device_instruction', ds_db_msg[1], ds_db_msg[2])
|
||||
else:
|
||||
self.mqtt_thread.broadcast_command('runtime error: ' + str(ds_db_msg))
|
||||
content = {
|
||||
'header': 'device_instruction/0',
|
||||
'device': ds_db_msg,
|
||||
'instruction': 'interrupt'
|
||||
}
|
||||
self.mqtt_thread.publish('device_instruction', json_stringify(content), inter = True)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -69,7 +69,8 @@ class DataBaseProcess(Process):
|
||||
|
||||
self._data_raw_create_sql_str = None
|
||||
self._data_raw_update_sql_str = None
|
||||
self._data_raw_recording_sql_str = 'UPDATE "public"."%s_recording_data_raws" SET data = concat(data, %s), end_time=%s, size=%s where id = %s'
|
||||
self._data_raw_recording_sql_str = 'UPDATE "public"."%s_recording_data_raws" SET data = concat(data, %s) where id = %s'
|
||||
self._new_data_raw_recording_sql_str = 'UPDATE "public"."%s_recording_data_raws" SET data = concat(data, %s), end_time=%s, size=%s where id = %s'
|
||||
self._data_mini_recording_sql_str = 'UPDATE "public"."%s_recording_data_minis" SET data_mean = concat(data_mean, %s) where id = %s'
|
||||
|
||||
@property
|
||||
@@ -384,7 +385,7 @@ class DataBaseProcess(Process):
|
||||
para_list.append([_channel, _data_dict[_channel] + '"***"', _end_time_dict[_channel], _size_dict[_channel], _id_dict[_channel]])
|
||||
with self._psql_conn as conn:
|
||||
with conn.cursor() as sql_cursor:
|
||||
execute_batch(sql_cursor, self._data_raw_recording_sql_str, para_list)
|
||||
execute_batch(sql_cursor, self._new_data_raw_recording_sql_str, para_list)
|
||||
except psycopg2.Error as e:
|
||||
print('recording error', e)
|
||||
return None
|
||||
|
||||
+20
-533
@@ -43,7 +43,7 @@ def _fc(c):
|
||||
return c % len(FORE_COLOR) + FORE_COLOR[0]
|
||||
|
||||
|
||||
class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler):
|
||||
class ControlServer(SocketServer, ControlServerAPI):
|
||||
"""Data Server"""
|
||||
|
||||
def __init__(self, *options):
|
||||
@@ -60,7 +60,7 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
|
||||
:param options: server options
|
||||
"""
|
||||
super().__init__((ControlAPI, CacheAPI), 'Controller', self.ADDR,
|
||||
super().__init__((ControlAPI), 'Controller', self.ADDR,
|
||||
socket_listen=10,
|
||||
thread_pool_count=16)
|
||||
|
||||
@@ -87,21 +87,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
data_options.repository = device_option.repository
|
||||
self.data_server = DataServerProcess(data_options)
|
||||
|
||||
# experimental protocol
|
||||
# exp_options = ExpManagerOption.get_options(*options)
|
||||
# self.exp_manager = ExpManager(self, exp_options)
|
||||
|
||||
# cache
|
||||
self.cache_manager = CacheManager(self,
|
||||
c.cache_server_cache_size,
|
||||
# time unit from sec to ms
|
||||
c.cache_server_cache_time * 1000)
|
||||
|
||||
# websocket server ###
|
||||
### self.websocket_thread = WebsocketThread(self,
|
||||
### c.websocket_address,
|
||||
### c.websocket_port)
|
||||
|
||||
# remote server / MQTT
|
||||
self.mqtt_thread = None
|
||||
if not c.flag_disable_remote_server:
|
||||
@@ -112,17 +97,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
self.log_verbose,
|
||||
'MQTT-control-server')
|
||||
|
||||
# routine trash thread
|
||||
self._routine_trash_thread = None
|
||||
# if not c.flag_disable_routine_trash:
|
||||
# self._routine_trash_thread = RoutineTrashThread(self._file_manager,
|
||||
# c.trash_routine_time,
|
||||
# c.trash_expire_time,
|
||||
# c.trash_remove_expire_time)
|
||||
|
||||
# routine data routine
|
||||
self._data_stream_thread = None
|
||||
|
||||
# LED control
|
||||
self.led_thread = LEDControlThread(disable=c.flag_disable_led_control,
|
||||
led_count=c.led_count,
|
||||
@@ -131,6 +105,8 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
# device detect
|
||||
self._detect_device_thread = RoutineConnectDeviceThread(self._device_manager, self.mqtt_thread, self.led_thread, 3)
|
||||
|
||||
self._project_manager = ProjectManager(self.device_manager, self.mqtt_thread)
|
||||
|
||||
if not c.flag_disable_led_control:
|
||||
self.led_thread.set_state(LED.COLOR_RED)
|
||||
|
||||
@@ -144,8 +120,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
self._session = {}
|
||||
self._user_session_expire = c.user_session_expire_days * 24 * 60 * 60 # sec
|
||||
|
||||
# temp use variable
|
||||
|
||||
# indicate those device together
|
||||
self._device_instruction_group = None
|
||||
|
||||
@@ -155,9 +129,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
# _api request build
|
||||
self._api = ApiRequests()
|
||||
|
||||
self._project_manager = ProjectManager(self.device_manager, self.mqtt_thread)
|
||||
|
||||
|
||||
# init method
|
||||
|
||||
def _init_file_manager(self, option: ControlServerOptions) -> FileManager:
|
||||
@@ -249,32 +220,16 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
# device manager reset
|
||||
self.submit_thread(self._setup_device)
|
||||
|
||||
# self.submit_thread(self._setup_controller)
|
||||
self._setup_controller()
|
||||
|
||||
# self.run_thread(self._detect_device_thread)
|
||||
|
||||
# experimental protocol manager
|
||||
# if not self._controller_options.flag_disable_exp_manager:
|
||||
# self.exp_manager.setup()
|
||||
# self.exp_manager.reload_protocol()
|
||||
|
||||
# websocket server
|
||||
### self.log_verbose('setup websocket server', pc(self.websocket_thread.websocket_address, YELLOW))
|
||||
# websocket server thread run ###
|
||||
### self.run_thread(self.websocket_thread)
|
||||
|
||||
# broadcast greeting
|
||||
self.broadcast_command(DataMessage.greeting())
|
||||
|
||||
# routine trashing
|
||||
if self._routine_trash_thread is not None:
|
||||
self.run_thread(self._routine_trash_thread)
|
||||
|
||||
# remote server
|
||||
if self.mqtt_thread is not None:
|
||||
self.mqtt_thread.start()
|
||||
# self.run_thread(self.mqtt_thread)
|
||||
|
||||
def _setup_file_manager(self):
|
||||
self.log_verbose('setup file manager')
|
||||
@@ -449,90 +404,14 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
self.broadcast_command('shutdown')
|
||||
self.data_server.stop_process(external_interrupt)
|
||||
|
||||
self._stop_data_stream_thread()
|
||||
|
||||
self.device_manager.shutdown()
|
||||
|
||||
self.file_manager.shutdown(umount_external=True)
|
||||
|
||||
self.cache_manager.shutdown()
|
||||
|
||||
### self.websocket_thread.close()
|
||||
|
||||
self.led_thread.close()
|
||||
|
||||
if self.mqtt_thread is not None:
|
||||
self.mqtt_thread.shutdown()
|
||||
# self.mqtt_thread.close()
|
||||
|
||||
# user session
|
||||
|
||||
def new_user_session(self) -> str:
|
||||
session = UserSession(None)
|
||||
|
||||
self._session[session.user_session] = session
|
||||
|
||||
self.log_info('new user session', session.user_session)
|
||||
|
||||
return session.user_session
|
||||
|
||||
@logging_info
|
||||
def del_user_session(self, user_session: Optional[str] = None):
|
||||
if user_session is None:
|
||||
self._session.clear()
|
||||
|
||||
else:
|
||||
try:
|
||||
del self._session[user_session]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def get_user_session(self,
|
||||
user_session: Optional[str],
|
||||
new_session = False,
|
||||
allow_empty = False) -> Optional[UserSession]:
|
||||
"""
|
||||
|
||||
:param user_session: user session ID
|
||||
:param new_session: new a user session if *user_session* not found.
|
||||
:param allow_empty: consider empty *user_session* None
|
||||
:return: user session
|
||||
"""
|
||||
|
||||
if user_session is None:
|
||||
session = None
|
||||
|
||||
elif len(user_session) == 0:
|
||||
if allow_empty:
|
||||
session = None
|
||||
else:
|
||||
raise None
|
||||
|
||||
else:
|
||||
session = self._session.get(user_session, None)
|
||||
|
||||
if session is not None:
|
||||
session.time()
|
||||
|
||||
elif new_session:
|
||||
session = UserSession(user_session)
|
||||
|
||||
self._session[session.user_session] = session
|
||||
|
||||
self.log_info('new user session', session.user_session)
|
||||
|
||||
return session
|
||||
|
||||
def expire_user_session(self):
|
||||
current_time = time()
|
||||
|
||||
for user_session in list(self._session.keys()):
|
||||
session = self._session[user_session]
|
||||
|
||||
if session.time(update=False) - current_time > self._user_session_expire:
|
||||
del self._session[user_session]
|
||||
|
||||
# general post action
|
||||
|
||||
def _post_device_list(self) -> List[CompletedDevice]:
|
||||
"""general post action for getting list of connected devices.
|
||||
@@ -869,12 +748,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
def get_device_info(self, device: int) -> Optional[CompletedDevice]:
|
||||
return super().get_device_info(device)
|
||||
|
||||
# @logging_info
|
||||
# def device_call(self, device: int) -> bool:
|
||||
# self.device_manager.call_device(device)
|
||||
#
|
||||
# return True
|
||||
|
||||
@logging_info
|
||||
def device_instruction_all(self, device: int) -> List[str]:
|
||||
return self.device_manager.list_device_instruction(device)
|
||||
@@ -1048,8 +921,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
|
||||
@logging_info
|
||||
def device_parameter(self, device: int, parameter: str, content: Optional[str] = None) -> Any:
|
||||
# if content is not None:
|
||||
# self.broadcast_command('parameter:' + str(device) + '/' + parameter + '/' + content)
|
||||
return self.device_manager.handle_device_parameter_command(device, parameter, content)
|
||||
|
||||
@logging_info
|
||||
@@ -1141,255 +1012,22 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
def device_history_delete(self, user: str, device: Optional[int]) -> bool:
|
||||
raise NotImplementedError()
|
||||
|
||||
# server provide method implement : file manager functions
|
||||
|
||||
@logging_info
|
||||
def file_clean_storage(self) -> bool:
|
||||
return super().file_clean_storage()
|
||||
|
||||
@logging_info
|
||||
def file_index(self) -> RootInfo:
|
||||
return super().file_index()
|
||||
|
||||
@logging_info
|
||||
def file_list(self, path: str) -> DirectoryInfo:
|
||||
return super().file_list(path)
|
||||
|
||||
@logging_info
|
||||
def file_list_range(self, path: str, start: int, end: int) -> DirectoryInfo:
|
||||
return super().file_list_range(path, start, end)
|
||||
|
||||
@logging_info
|
||||
def file_rename(self, path: str, content: str) -> FileInfo:
|
||||
return super().file_rename(path, content)
|
||||
|
||||
@logging_info
|
||||
def file_mkdir(self, path: str) -> DirectoryInfo:
|
||||
return super().file_mkdir(path)
|
||||
|
||||
@logging_info
|
||||
def file_delete(self, path: str) -> bool:
|
||||
return super().file_delete(path)
|
||||
|
||||
@logging_info
|
||||
def file_info(self, path: str) -> FileInfo:
|
||||
return super().file_info(path)
|
||||
|
||||
@logging_info
|
||||
def file_meta_info(self, path: str) -> RecordingMetaFile:
|
||||
return super().file_meta_info(path)
|
||||
|
||||
@logging_info
|
||||
def file_export_info(self, path: str) -> Dict[str, Optional[Path]]:
|
||||
return super().file_export_info(path)
|
||||
|
||||
@logging_info
|
||||
def file_export_functions(self) -> Tuple[ExternalExportFunction, ...]:
|
||||
return super().file_export_functions()
|
||||
|
||||
@logging_info
|
||||
def file_export_parameters(self, ftype: str) -> Tuple[ExternalParameter, ...]:
|
||||
return super().file_export_parameters(ftype)
|
||||
|
||||
@logging_info
|
||||
def file_export(self, ftype: str, path: str, **options: str) -> str:
|
||||
return super().file_export(ftype, path, **options)
|
||||
|
||||
@logging_info
|
||||
def file_segment(self, method: str, path: str) -> Union[None, bool, FileSegment]:
|
||||
return super().file_segment(method, path)
|
||||
|
||||
@logging_info
|
||||
def file_segment_get(self, segment: int, path: str):
|
||||
return super().file_segment_get(segment, path)
|
||||
|
||||
@logging_info
|
||||
def file_handle_open(self, content: str) -> Optional[FileHandle]:
|
||||
info = self.file_manager.info(content)
|
||||
meta = info.meta_file
|
||||
|
||||
if meta is None:
|
||||
raise RuntimeError('not a meta file')
|
||||
|
||||
handle = self.cache_manager.file_open(meta)
|
||||
|
||||
return self.file_handle_info(handle)
|
||||
|
||||
@logging_info
|
||||
def file_handle_info(self, handle: int) -> Optional[FileHandle]:
|
||||
info = self.cache_manager.file_info(handle)
|
||||
|
||||
if info is None:
|
||||
return None
|
||||
|
||||
# modify file path
|
||||
info.shadow_path(self.file_manager.shadow_path(Path(info.filepath)))
|
||||
|
||||
return info
|
||||
|
||||
@logging_info
|
||||
def file_handle_close(self, handle: int):
|
||||
self.cache_manager.file_close(handle)
|
||||
|
||||
@logging_info
|
||||
def user_all(self) -> List[str]:
|
||||
raise super().user_all()
|
||||
|
||||
@logging_info
|
||||
def user_info(self, user: str, *, user_session: Optional[str] = None) -> Optional[AbstractUserSetting]:
|
||||
return super().user_info(user, user_session=user_session)
|
||||
|
||||
@logging_info
|
||||
def user_login(self, user: str, content: str, *,
|
||||
user_session = None) -> ComplexResponse[AbstractUserSetting]:
|
||||
raise super().user_login(user, content, user_session=user_session)
|
||||
|
||||
@logging_info
|
||||
def user_logout(self, user_session: Optional[str] = None) -> None:
|
||||
super().user_logout(user_session)
|
||||
|
||||
@logging_info
|
||||
def user_add(self, user: str, content: str, *,
|
||||
user_session = None) -> ComplexResponse[AbstractUserSetting]:
|
||||
return super().user_add(user, content, user_session=user_session)
|
||||
|
||||
@logging_info
|
||||
def user_del(self, user: str, content: str) -> bool:
|
||||
return super().user_del(user, content)
|
||||
|
||||
@logging_info
|
||||
def user_sync(self, user_session: Optional[str]) -> bool:
|
||||
return super().user_sync(user_session)
|
||||
|
||||
@logging_info
|
||||
def user_configuration_list(self, user_session: Optional[str], **options: str) -> List[str]:
|
||||
return super().user_configuration_list(user_session, **options)
|
||||
|
||||
@logging_info
|
||||
def user_configuration_get(self, name: str, user_session: Optional[str] = None) -> Optional[RecordingMetaFile]:
|
||||
return super().user_configuration_get(name, user_session)
|
||||
|
||||
@logging_info
|
||||
def user_configuration_add(self, name: str, content: str, user_session: Optional[str] = None) -> bool:
|
||||
return super().user_configuration_add(name, content, user_session)
|
||||
|
||||
@logging_info
|
||||
def user_configuration_del(self, name: str, user_session: Optional[str] = None) -> bool:
|
||||
return super().user_configuration_del(name, user_session)
|
||||
|
||||
@logging_info
|
||||
def user_configuration_clone(self, name: str, content: str, user_session: Optional[str] = None) -> bool:
|
||||
return super().user_configuration_clone(name, content, user_session)
|
||||
|
||||
@logging_info
|
||||
def user_setup_list(self, user_session: Optional[str] = None) -> List[str]:
|
||||
return super().user_setup_list(user_session)
|
||||
|
||||
@logging_info
|
||||
def user_setup_info(self, name: str, user_session: Optional[str] = None) -> Optional[UserSetup]:
|
||||
return super().user_setup_info(name, user_session)
|
||||
|
||||
@logging_info
|
||||
def user_setup_new(self, name: str, user_session: Optional[str] = None) -> Optional[UserSetup]:
|
||||
return super().user_setup_new(name, user_session)
|
||||
|
||||
@logging_info
|
||||
def user_setup_del(self, name: str, user_session: Optional[str] = None) -> bool:
|
||||
return super().user_setup_del(name, user_session)
|
||||
|
||||
@logging_info
|
||||
def user_alias_list(self, user_session: Optional[str] = None) -> List[UserDeviceAlias]:
|
||||
return super().user_alias_list(user_session)
|
||||
|
||||
@logging_info
|
||||
def user_alias_info(self, name: str, user_session: Optional[str] = None) -> Optional[UserDeviceAlias]:
|
||||
return super().user_alias_info(name, user_session)
|
||||
|
||||
@logging_info
|
||||
def user_alias_edit(self, name: str, content: str, user_session: Optional[str] = None) -> Optional[UserDeviceAlias]:
|
||||
return super().user_alias_edit(name, content, user_session)
|
||||
|
||||
# @logging_info
|
||||
# def exp_pro_list(self) -> List[ExpProtocolInfo]:
|
||||
# return self.exp_manager.list_available_protocol()
|
||||
|
||||
@logging_info
|
||||
def exp_pro_create(self, content: str, *,
|
||||
user_session = None) -> int:
|
||||
user_name = self._session_user_name(user_session)
|
||||
|
||||
if user_name is None:
|
||||
raise RuntimeError('require user login')
|
||||
|
||||
self.file_manager.user_setting.user_info(user_name)
|
||||
|
||||
# p = self.exp_manager.create_protocol(content, user_name)
|
||||
return
|
||||
|
||||
@logging_info
|
||||
def run_project(self, project) -> bool:
|
||||
project = self._project_manager.create(project)
|
||||
self._project_manager.run(project)
|
||||
return False
|
||||
if project is not None:
|
||||
project = self._project_manager.create(project)
|
||||
self._project_manager.run_project(project)
|
||||
return project.as_json()
|
||||
|
||||
@logging_info
|
||||
def stop_project(self, project) -> bool:
|
||||
if project is not None:
|
||||
self._project_manager.stop_project(project)
|
||||
return True
|
||||
|
||||
# @logging_info
|
||||
# def exp_pro_options(self, protocol: int) -> List[ExpOption]:
|
||||
# return self.exp_manager.protocol_options(protocol)
|
||||
|
||||
# @logging_info
|
||||
# def exp_pro_option(self, protocol: int, option: str, content: Optional[str] = None) -> bool:
|
||||
# if content == '':
|
||||
# # option reset
|
||||
# if option == '':
|
||||
# # reset all options
|
||||
# self.exp_manager.protocol_reset(protocol)
|
||||
# else:
|
||||
# self.exp_manager.protocol_reset(protocol, *option.split(','))
|
||||
# else:
|
||||
# # option get/set
|
||||
# self.exp_manager.protocol_option(protocol, option, content)
|
||||
|
||||
# return True
|
||||
|
||||
# @logging_info
|
||||
# def exp_pro_status_all(self) -> List[AbstractExpProtocolState]:
|
||||
# ret = []
|
||||
|
||||
# for p in self.exp_manager.list_protocol():
|
||||
# s = self.exp_manager.protocol_status(p)
|
||||
|
||||
# if s is not None:
|
||||
# ret.append(s)
|
||||
# else:
|
||||
# ret.append(AbstractExpProtocolState(p))
|
||||
|
||||
# return ret
|
||||
|
||||
# @logging_info
|
||||
# def exp_pro_status(self, protocol: int) -> Optional[ExpProtocolState]:
|
||||
# return self.exp_manager.protocol_status(protocol)
|
||||
|
||||
# @logging_info
|
||||
# def exp_pro_command(self, protocol: int, content: str) -> bool:
|
||||
# if content == 'ensure':
|
||||
# return self.exp_manager.ensure_protocol(protocol)
|
||||
|
||||
# elif content == 'start':
|
||||
# self.exp_manager.submit_protocol(protocol)
|
||||
|
||||
# elif content == 'interrupt':
|
||||
# self.exp_manager.interrupt_protocol(protocol)
|
||||
|
||||
# elif content == 'suspend':
|
||||
# self.exp_manager.suspend_protocol(protocol)
|
||||
|
||||
# elif content == 'resume':
|
||||
# self.exp_manager.resume_protocol(protocol)
|
||||
|
||||
# else:
|
||||
# raise RuntimeError('unknown protocol command : ' + content)
|
||||
|
||||
# return True
|
||||
@logging_info
|
||||
def get_running_project(self) -> bool:
|
||||
return self._project_manager.get()
|
||||
|
||||
@logging_info
|
||||
def device_internal_command(self, device: int, oper: str, value: Any) -> bool:
|
||||
@@ -1406,8 +1044,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
return True
|
||||
|
||||
elif oper == InternalInstruction.PREDEFINED_DISABLE_CACHE:
|
||||
if device is not None:
|
||||
self._device_set_disable_cache(device, value)
|
||||
return True
|
||||
|
||||
else:
|
||||
@@ -1459,10 +1095,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
|
||||
def _device_set_sync(self, device: CompletedDevice, sync: bool):
|
||||
if sync:
|
||||
self.log_verbose('start sync', device.device_id)
|
||||
|
||||
self.cache_manager.drop_data(device)
|
||||
|
||||
client = self.data_server.client()
|
||||
if client is not None:
|
||||
info = self.file_manager.use(device)
|
||||
@@ -1473,22 +1105,14 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
filename = format_filename(self.TEMP_FILENAME_EXPR, device)
|
||||
info = self.file_manager.save(device, filename)
|
||||
client.update_device_configuration(device, info.meta_file, RawDataDecoder())
|
||||
print('***456')
|
||||
|
||||
if self._device_instruction_group is None:
|
||||
client.start_sync(device)
|
||||
|
||||
if self._device_instruction_group is None:
|
||||
# broadcast
|
||||
self.broadcast_command(DataMessage.start(device.device_id))
|
||||
|
||||
|
||||
device.status = 1
|
||||
else:
|
||||
self.log_verbose('stop sync', device.device_id)
|
||||
|
||||
if self._device_instruction_group is None:
|
||||
|
||||
start_sync = False
|
||||
|
||||
client = self.data_server.client()
|
||||
if client is not None:
|
||||
with client:
|
||||
@@ -1501,12 +1125,10 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
if start_sync:
|
||||
# unset file info, but keep file path cache
|
||||
self.file_manager.unset(device.device_id)
|
||||
device.status = 0
|
||||
|
||||
# broadcast
|
||||
self.broadcast_command(DataMessage.stop(device.device_id))
|
||||
|
||||
# server provide method implement : websocket broadcast functions
|
||||
|
||||
@logging_info
|
||||
def broadcast_command(self, command: str, device: Optional[int] = None):
|
||||
self.log_verbose(pc('broadcast_command', GREEN),
|
||||
@@ -1516,96 +1138,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
if self.mqtt_thread is not None:
|
||||
self.mqtt_thread.broadcast_command(command, device=device)
|
||||
|
||||
# data stream thread
|
||||
|
||||
def data_request_stream(self, start: bool, handler: DataMessageHandler):
|
||||
if start:
|
||||
t = self._start_data_stream_thread()
|
||||
t.add_handle(handler)
|
||||
|
||||
else:
|
||||
t = self._data_stream_thread
|
||||
|
||||
if t is not None:
|
||||
t.del_handle(handler)
|
||||
|
||||
if not t.has_handle():
|
||||
self._stop_data_stream_thread()
|
||||
|
||||
def _start_data_stream_thread(self) -> DataStreamThread:
|
||||
t = self._data_stream_thread
|
||||
|
||||
if self._data_stream_thread is None:
|
||||
self._data_stream_thread = t = self.run_thread(DataStreamThread(self))
|
||||
|
||||
return t
|
||||
|
||||
def _stop_data_stream_thread(self):
|
||||
t = self._data_stream_thread
|
||||
self._data_stream_thread = None
|
||||
|
||||
if t is not None:
|
||||
t.close()
|
||||
|
||||
# noinspection PyShadowingNames
|
||||
@logging_info
|
||||
def websocket_command(self, command: str) -> Union[None, str, bytes, List[str]]:
|
||||
if command == DataMessage.GREETING:
|
||||
return DataMessage.greeting()
|
||||
|
||||
elif command == DataMessage.SYNC:
|
||||
return self._websocket_command_sync(map(lambda d: d.device_id, self._post_device_list()))
|
||||
|
||||
elif command.startswith(DataMessage.SYNC):
|
||||
_, device_list = part_suffix(command, ':')
|
||||
|
||||
if ',' in device_list:
|
||||
return self._websocket_command_sync(map(int, device_list.split(',')))
|
||||
|
||||
else:
|
||||
return self._websocket_command_sync([int(device_list)])
|
||||
|
||||
elif command.startswith(DataMessage.BROADCAST):
|
||||
_, message = part_suffix(command, ':')
|
||||
|
||||
if message is not None:
|
||||
self.broadcast_command(DataMessage.broadcast(message))
|
||||
|
||||
return None
|
||||
|
||||
# noinspection PyShadowingNames
|
||||
def _websocket_command_sync(self, device_list: Iterable[int]) -> Union[str, List[str]]:
|
||||
client = self.data_server.client()
|
||||
|
||||
if client is None:
|
||||
return DataMessage.sync(False)
|
||||
|
||||
else:
|
||||
result_start = []
|
||||
result_stop = []
|
||||
|
||||
with client:
|
||||
for device in device_list:
|
||||
if client.is_sync(device):
|
||||
result_start.append(device)
|
||||
else:
|
||||
result_stop.append(device)
|
||||
|
||||
return [DataMessage.start(result_start), DataMessage.stop(result_stop)]
|
||||
|
||||
# noinspection PyShadowingNames
|
||||
def websocket_data_request(self, handler: DataMessageHandler, command: Optional[str]):
|
||||
try:
|
||||
self._handle_command_response(handler, command)
|
||||
|
||||
except (ValueError, IndexError) as e:
|
||||
self.log_warn(e)
|
||||
handler.on_error(DataMessage.warn(str(e)))
|
||||
|
||||
except BaseException as e:
|
||||
self.log_warn(e)
|
||||
handler.on_error(DataMessage.error(str(e)))
|
||||
|
||||
# noinspection PyShadowingNames
|
||||
def _handle_command_response(self, handler: DataMessageHandler, command: Optional[str]):
|
||||
"""
|
||||
@@ -1627,29 +1159,7 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
else:
|
||||
self.cache_manager.get_data_message(handler, response)
|
||||
|
||||
# server provide method implement : cache API
|
||||
|
||||
@logging_verbose
|
||||
def clear(self):
|
||||
self.cache_manager.clear()
|
||||
|
||||
def push_data(self, data: Union[bytes, List[bytes]]):
|
||||
self.cache_manager.push_data(data)
|
||||
|
||||
@logging_verbose
|
||||
def drop_data(self, *device: int):
|
||||
self.cache_manager.drop_data(*device)
|
||||
|
||||
def stop_data(self, device: int):
|
||||
self.cache_manager.stop_data(device)
|
||||
|
||||
# noinspection PyShadowingNames
|
||||
@logging_verbose
|
||||
def notify_data(self, device: int, message: str):
|
||||
self.broadcast_command(message, device=device)
|
||||
|
||||
# led function
|
||||
|
||||
def get_led_state(self) -> str:
|
||||
return self.led_thread.state
|
||||
|
||||
@@ -1657,7 +1167,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
self.led_thread.set_state(state)
|
||||
|
||||
# hardware send test
|
||||
|
||||
if not _RUNTIME_COMPILE:
|
||||
|
||||
def _hardware_send_test(self, options: Dict[str, str]):
|
||||
@@ -1700,30 +1209,9 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
'export_root': str(self.file_manager.export_root)
|
||||
}
|
||||
|
||||
if section is None or 'cache' in section:
|
||||
ret['cache'] = {
|
||||
'cache_size': self.cache_manager.cache_size,
|
||||
'cache_time': self.cache_manager.cache_time,
|
||||
}
|
||||
|
||||
# if section is None or 'websocket' in section:
|
||||
# ret['websocket'] = {
|
||||
# 'address': self.websocket_thread.websocket_address,
|
||||
# 'port': self.websocket_thread.websocket_port,
|
||||
# }
|
||||
|
||||
if section is None or 'mqtt' in section:
|
||||
ret['mqtt'] = None if self.mqtt_thread is None else self.mqtt_thread.hardware_test()
|
||||
|
||||
if section is None or 'trash' in section:
|
||||
trash_manager = self._routine_trash_thread
|
||||
|
||||
ret['trash'] = None if trash_manager is None else {
|
||||
'routine_interval': trash_manager.routine_interval,
|
||||
'trash_expire': trash_manager.trash_expire,
|
||||
'remove_expire': trash_manager.remove_expire,
|
||||
}
|
||||
|
||||
if section is None or 'led' in section:
|
||||
ret['led'] = {
|
||||
'state_all': list(LED.COLOR.keys()),
|
||||
@@ -1852,7 +1340,6 @@ class ControlServer(SocketServer, ControlServerAPI, CacheAPI, CacheNotifyHandler
|
||||
# TODO write options files
|
||||
return False
|
||||
|
||||
|
||||
class _RandomCrashThread(ServerThread):
|
||||
def __init__(self):
|
||||
super().__init__('Crash')
|
||||
|
||||
@@ -41,7 +41,7 @@ class RecordingProcess(Process):
|
||||
connect to DB and send sql command
|
||||
"""
|
||||
|
||||
def __init__(self, client_id: str, log_verbose, device_id, queue_rec, queue_ds, meta_path,
|
||||
def __init__(self, client_id: str, log_verbose, device_id, queue_rec, queue_ds, queue_msg, meta_path,
|
||||
device_info, parameter, parent, recording_file_name, data_format,
|
||||
mqtt_ip, mqtt_port,
|
||||
name = 'Recording-Process', database = None):
|
||||
@@ -82,6 +82,7 @@ class RecordingProcess(Process):
|
||||
|
||||
self._queue_rec = queue_rec
|
||||
self._queue_ds = queue_ds
|
||||
self._queue_msg = queue_msg
|
||||
self.queue_flag = True
|
||||
|
||||
self._is_close = True
|
||||
@@ -104,6 +105,9 @@ class RecordingProcess(Process):
|
||||
self._mem_tailer_len = 6
|
||||
self._single_data_len = self._elite_data_len + self._mem_header_len + self._mem_tailer_len
|
||||
|
||||
self._decoder = self.data_format()
|
||||
self._start_time = time()
|
||||
|
||||
def ensure_data_format(self) -> DataDecodeFormat:
|
||||
if isinstance(self._data_format, (str, bytes)):
|
||||
self._data_format = DataDecodeFormat.parse(self._data_format)
|
||||
@@ -132,9 +136,9 @@ class RecordingProcess(Process):
|
||||
decoder._prev_data = self._prev_data
|
||||
decoder._prev_delta_time = self._prev_delta_time
|
||||
decoder._prev_time_stamp = self._prev_time_stamp
|
||||
# elif isinstance(decoder, I4V4Z4T4DataDecoder):
|
||||
elif isinstance(decoder, I4V4Z4T4DataDecoder):
|
||||
# get cycle_time from meta file
|
||||
# decoder._mode = self._meta_file.configuration.MODE
|
||||
decoder._mode = self._meta_file.configuration.get_parameter('MODE')
|
||||
# decoder._cycle_start_time = self._cycle_start_time
|
||||
elif isinstance(decoder, NeuliveThreeOneDataDecoder):
|
||||
# get amp_gain from meta file
|
||||
@@ -150,9 +154,8 @@ class RecordingProcess(Process):
|
||||
decoder._prev_time_stamp = self._prev_time_stamp
|
||||
elif isinstance(decoder, EISZeroOneDataDecoder):
|
||||
# get amp_gain from meta file
|
||||
|
||||
decoder._ac_amp = self._meta_file.configuration.get_parameter('AC_AMP')
|
||||
decoder._mode = self._meta_file.configuration.get_parameter('MODE')
|
||||
decoder._ac_amp = self._meta_file.configuration.get_parameter('AC_AMP')
|
||||
decoder._freq_start = self._meta_file.configuration.get_parameter('FREQ_START')
|
||||
decoder._freq_stop = self._meta_file.configuration.get_parameter('FREQ_STOP')
|
||||
|
||||
@@ -199,14 +202,13 @@ class RecordingProcess(Process):
|
||||
self._isTimeOut = True
|
||||
self._timer = current_time
|
||||
|
||||
decoder = self.data_format()
|
||||
# print('sync_data')
|
||||
# print('data',data)
|
||||
|
||||
# print('server/data', self._prev_delta_time,self._prev_time_stamp, self._prev_data)
|
||||
|
||||
if data is None or len(data) == 0:
|
||||
result = decoder.decode(b'')
|
||||
result = self._decoder.decode(b'')
|
||||
if result is not None:
|
||||
ret = result
|
||||
|
||||
@@ -220,7 +222,7 @@ class RecordingProcess(Process):
|
||||
for offset, section in self._foreach_data_section(data):
|
||||
# for section in self._neu_foreach_data_section(data):
|
||||
|
||||
result = decoder.decode(section)
|
||||
result = self._decoder.decode(section)
|
||||
# if self._isTimeOut:
|
||||
# try:
|
||||
# print('result: ', result.data_size)
|
||||
@@ -228,13 +230,20 @@ class RecordingProcess(Process):
|
||||
# pass
|
||||
|
||||
try:
|
||||
if isinstance(decoder, I4V4Z4T4DataDecoder) or isinstance(decoder, EISZeroOneDataDecoder):
|
||||
if decoder.isFinishMode is not None and decoder.isFinishMode() == 1:
|
||||
content = {}
|
||||
content['header'] = 'device_instruction/0'
|
||||
content['device'] = result.device
|
||||
content['instruction'] = 'interrupt'
|
||||
self._mqtt_thread.publish('device_instruction',json_stringify(content), True)
|
||||
time_duration = self._meta_file.configuration.get_parameter('TIME_DURATION')
|
||||
if time_duration and time_duration is not 0 and time() - self._start_time >= time_duration:
|
||||
self._queue_msg.put(['ds', self._device, 'interrupt'])
|
||||
return None
|
||||
|
||||
if isinstance(self._decoder, I4V4Z4T4DataDecoder) or isinstance(self._decoder, EISZeroOneDataDecoder):
|
||||
if self._decoder.isFinishMode is not None and self._decoder.isFinishMode() == 1:
|
||||
self._queue_msg.put(['ds', self._device, 'interrupt'])
|
||||
return None
|
||||
# content = {}
|
||||
# content['header'] = 'device_instruction/0'
|
||||
# content['device'] = result.device
|
||||
# content['instruction'] = 'interrupt'
|
||||
# self._mqtt_thread.publish('device_instruction',json_stringify(content), True)
|
||||
# self.server.stop_sync(self.device)
|
||||
except RuntimeError as e:
|
||||
print(e)
|
||||
@@ -295,8 +304,8 @@ class RecordingProcess(Process):
|
||||
self._last_cnt[7] = head_counter - 1
|
||||
last_data_cnt = self._last_cnt[7]
|
||||
|
||||
# dont save to section when head or id is wrong
|
||||
if (head != 255 or device_id != device):
|
||||
# dont save to section when head or id is wrong
|
||||
if (head != 255 or device_id != device or head_counter > 255 or head_counter < 0):
|
||||
save = False
|
||||
last_data_cnt = last_data_cnt + 1
|
||||
|
||||
@@ -347,13 +356,14 @@ class RecordingProcess(Process):
|
||||
|
||||
if colum_total == 1:
|
||||
print('this ram data < 3 records, colum_total = ', colum_total)
|
||||
print('raw_data[0]', raw_data[0])
|
||||
return save
|
||||
print('raw_data[0]', list(raw_data[0]))
|
||||
save = True
|
||||
# return save
|
||||
|
||||
elif colum_total == 2:
|
||||
print('this ram data < 3 records, colum_total = ', colum_total)
|
||||
print('raw_data[0]', raw_data[0])
|
||||
print('raw_data[1]', raw_data[1])
|
||||
print('raw_data[0]', list(raw_data[0]))
|
||||
print('raw_data[1]', list(raw_data[1]))
|
||||
if (raw_data[col + 1][1] - raw_data[col][1] == 1) or (raw_data[col][1] == 255 and raw_data[col + 1][1] == 0):
|
||||
save = True
|
||||
|
||||
|
||||
@@ -11,9 +11,21 @@
|
||||
"constant": {
|
||||
"FREQ_MAX": 4294967296,
|
||||
"VOLT_MAX": 65536,
|
||||
"BLE_WRITE_MAX": 255
|
||||
"BLE_WRITE_MAX": 255,
|
||||
"TIME_MAX": 100000
|
||||
},
|
||||
"parameters": {
|
||||
"TIME_DURATION": {
|
||||
"description": "Run duration",
|
||||
"record_meta": true,
|
||||
"initial": 0,
|
||||
"domain": [
|
||||
"TIME_MAX"
|
||||
],
|
||||
"value": {
|
||||
"expression": "VALUE"
|
||||
}
|
||||
},
|
||||
"CHANNEL": {
|
||||
"derzteription": "record channels",
|
||||
"record_meta": true,
|
||||
@@ -48,9 +60,9 @@
|
||||
"description": "working mode",
|
||||
"record_meta": true,
|
||||
"value": [
|
||||
"EIS CURVE",
|
||||
"Cyclic Voltammetry",
|
||||
"Dev Mode"
|
||||
"EIS CURVE",
|
||||
"Cyclic Voltammetry",
|
||||
"Dev Mode"
|
||||
]
|
||||
},
|
||||
"FREQ_START": {
|
||||
@@ -58,10 +70,10 @@
|
||||
"record_meta": true,
|
||||
"initial": 13422819,
|
||||
"domain": [
|
||||
"FREQ_MAX"
|
||||
"FREQ_MAX"
|
||||
],
|
||||
"value": {
|
||||
"expression": "VALUE"
|
||||
"expression": "VALUE"
|
||||
}
|
||||
},
|
||||
"FREQ_STOP": {
|
||||
@@ -69,10 +81,10 @@
|
||||
"record_meta": true,
|
||||
"initial": 7,
|
||||
"domain": [
|
||||
"FREQ_MAX"
|
||||
"FREQ_MAX"
|
||||
],
|
||||
"value": {
|
||||
"expression": "VALUE"
|
||||
"expression": "VALUE"
|
||||
}
|
||||
},
|
||||
"DELAY": {
|
||||
@@ -91,21 +103,21 @@
|
||||
"record_meta": true,
|
||||
"initial": 8,
|
||||
"value": [
|
||||
2,
|
||||
4,
|
||||
8,
|
||||
16
|
||||
2,
|
||||
4,
|
||||
8,
|
||||
16
|
||||
]
|
||||
},
|
||||
"DC_BIAS": {
|
||||
"description": "DC voltage bias in mV",
|
||||
"record_meta": true,
|
||||
"initial": 15000,
|
||||
"initial": 25000,
|
||||
"domain": [
|
||||
35001
|
||||
"VOLT_MAX"
|
||||
],
|
||||
"value": {
|
||||
"expression": "VALUE"
|
||||
"expression": "VALUE"
|
||||
}
|
||||
},
|
||||
"AC_AMP": {
|
||||
@@ -113,7 +125,7 @@
|
||||
"record_meta": true,
|
||||
"initial": 25,
|
||||
"domain": [
|
||||
2048
|
||||
2048
|
||||
]
|
||||
},
|
||||
"SCALE": {
|
||||
@@ -121,8 +133,8 @@
|
||||
"record_meta": true,
|
||||
"initial": 0,
|
||||
"value": [
|
||||
0,
|
||||
1
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
"PPD": {
|
||||
@@ -130,7 +142,7 @@
|
||||
"record_meta": true,
|
||||
"initial": 10,
|
||||
"domain": [
|
||||
11
|
||||
11
|
||||
]
|
||||
},
|
||||
"RTIA": {
|
||||
@@ -138,11 +150,11 @@
|
||||
"record_meta": true,
|
||||
"initial": 4,
|
||||
"value": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
},
|
||||
"ADC_VALUE_I": {
|
||||
@@ -154,10 +166,10 @@
|
||||
"record_meta": true,
|
||||
"initial": 10000,
|
||||
"domain": [
|
||||
100001
|
||||
100001
|
||||
],
|
||||
"value": {
|
||||
"expression": "VALUE"
|
||||
"expression": "VALUE"
|
||||
}
|
||||
},
|
||||
"CTRL_HIGH_Z_15": {
|
||||
@@ -242,9 +254,9 @@
|
||||
"BLE_WRITE": {
|
||||
"description": "send msg to elite",
|
||||
"domain": {
|
||||
"list": [
|
||||
"list": [
|
||||
"BLE_WRITE_MAX"
|
||||
]
|
||||
]
|
||||
},
|
||||
"initial": "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]",
|
||||
"value": "VALUE"
|
||||
@@ -254,12 +266,13 @@
|
||||
"domain": "int"
|
||||
}
|
||||
},
|
||||
|
||||
"instruction": {
|
||||
"start": [
|
||||
{
|
||||
"expression": "MODE",
|
||||
"when": {
|
||||
"*": "start_data"
|
||||
"*": "start_data"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -430,4 +443,4 @@
|
||||
"_cdr('20X>ADC_VALUE_I')"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
"minor_version_number": 6
|
||||
},
|
||||
"constant": {
|
||||
"TIME_MAX": 100000,
|
||||
"VOLT_MAX": 65536,
|
||||
"Const_Current_Range": 1500001,
|
||||
"BLE_WRITE_MAX": 255
|
||||
@@ -17,7 +18,7 @@
|
||||
"DPV_e_init": {
|
||||
"description": "DPV initial voltage ",
|
||||
"record_meta": true,
|
||||
"initial":20000,
|
||||
"initial": 20000,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -28,7 +29,7 @@
|
||||
"DPV_e_final": {
|
||||
"description": "DPV final voltage",
|
||||
"record_meta": true,
|
||||
"initial":30000,
|
||||
"initial": 30000,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -39,7 +40,7 @@
|
||||
"DPV_e_1": {
|
||||
"description": "DPV voltage return 1",
|
||||
"record_meta": true,
|
||||
"initial":35000,
|
||||
"initial": 35000,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -50,7 +51,7 @@
|
||||
"DPV_e_2": {
|
||||
"description": "DPV voltage return 2",
|
||||
"record_meta": true,
|
||||
"initial":15000,
|
||||
"initial": 15000,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -61,7 +62,7 @@
|
||||
"DPV_amp": {
|
||||
"description": "DPV pulse amplitude",
|
||||
"record_meta": true,
|
||||
"initial":25125,
|
||||
"initial": 25125,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -72,7 +73,7 @@
|
||||
"DPV_pul_width": {
|
||||
"description": "DPV pulse width",
|
||||
"record_meta": true,
|
||||
"initial":50,
|
||||
"initial": 50,
|
||||
"domain": [
|
||||
1000001
|
||||
],
|
||||
@@ -83,7 +84,7 @@
|
||||
"DPV_increment": {
|
||||
"description": "DPV increment",
|
||||
"record_meta": true,
|
||||
"initial":25025,
|
||||
"initial": 25025,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -94,7 +95,7 @@
|
||||
"DPV_step_time": {
|
||||
"description": "DPV step time = time of one period",
|
||||
"record_meta": true,
|
||||
"initial":500,
|
||||
"initial": 500,
|
||||
"domain": [
|
||||
1000001
|
||||
],
|
||||
@@ -105,7 +106,7 @@
|
||||
"DPV_notify_rate": {
|
||||
"description": "DPV sample rate",
|
||||
"record_meta": true,
|
||||
"initial":1000,
|
||||
"initial": 1000,
|
||||
"domain": [
|
||||
1001
|
||||
],
|
||||
@@ -116,7 +117,7 @@
|
||||
"DPV_curr_rec_min": {
|
||||
"description": "DPV current recording period start",
|
||||
"record_meta": true,
|
||||
"initial":25,
|
||||
"initial": 25,
|
||||
"domain": [
|
||||
101
|
||||
],
|
||||
@@ -127,7 +128,7 @@
|
||||
"DPV_curr_rec_max": {
|
||||
"description": "DPV current recording period end",
|
||||
"record_meta": true,
|
||||
"initial":100,
|
||||
"initial": 100,
|
||||
"domain": [
|
||||
101
|
||||
],
|
||||
@@ -166,7 +167,7 @@
|
||||
"V_initial_0": {
|
||||
"description": "Pulse Mode Segment Voltage 0",
|
||||
"record_meta": true,
|
||||
"initial":50000,
|
||||
"initial": 50000,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -177,7 +178,7 @@
|
||||
"V_initial_1": {
|
||||
"description": "Pulse Mode Segment Voltage 1",
|
||||
"record_meta": true,
|
||||
"initial":25000,
|
||||
"initial": 25000,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -188,7 +189,7 @@
|
||||
"V_initial_2": {
|
||||
"description": "Pulse Mode Segment Voltage 2",
|
||||
"record_meta": true,
|
||||
"initial":25000,
|
||||
"initial": 25000,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -199,7 +200,7 @@
|
||||
"V_initial_3": {
|
||||
"description": "Pulse Mode Segment Voltage 3",
|
||||
"record_meta": true,
|
||||
"initial":25000,
|
||||
"initial": 25000,
|
||||
"domain": [
|
||||
50001
|
||||
],
|
||||
@@ -210,7 +211,7 @@
|
||||
"t_pulse_0": {
|
||||
"description": "Pulse Mode Segment Duration 0",
|
||||
"record_meta": true,
|
||||
"initial":750,
|
||||
"initial": 750,
|
||||
"domain": [
|
||||
1000001
|
||||
],
|
||||
@@ -221,7 +222,7 @@
|
||||
"t_pulse_1": {
|
||||
"description": "Pulse Mode Segment Duration 1",
|
||||
"record_meta": true,
|
||||
"initial":9250,
|
||||
"initial": 9250,
|
||||
"domain": [
|
||||
1000001
|
||||
],
|
||||
@@ -232,7 +233,7 @@
|
||||
"t_pulse_2": {
|
||||
"description": "Pulse Mode Segment Duration 2",
|
||||
"record_meta": true,
|
||||
"initial":1000,
|
||||
"initial": 1000,
|
||||
"domain": [
|
||||
1000001
|
||||
],
|
||||
@@ -243,7 +244,7 @@
|
||||
"t_pulse_3": {
|
||||
"description": "Pulse Mode Segment Duration 3",
|
||||
"record_meta": true,
|
||||
"initial":1000,
|
||||
"initial": 1000,
|
||||
"domain": [
|
||||
1000001
|
||||
],
|
||||
@@ -254,7 +255,7 @@
|
||||
"CURR_REC_START_0": {
|
||||
"description": "Pulse Mode Current Recording Period Start",
|
||||
"record_meta": true,
|
||||
"initial":35,
|
||||
"initial": 35,
|
||||
"domain": [
|
||||
100
|
||||
],
|
||||
@@ -265,7 +266,7 @@
|
||||
"CURR_REC_START_1": {
|
||||
"description": "Pulse Mode Current Recording Period Start",
|
||||
"record_meta": true,
|
||||
"initial":35,
|
||||
"initial": 35,
|
||||
"domain": [
|
||||
100
|
||||
],
|
||||
@@ -276,7 +277,7 @@
|
||||
"CURR_REC_START_2": {
|
||||
"description": "Pulse Mode Current Recording Period Start",
|
||||
"record_meta": true,
|
||||
"initial":35,
|
||||
"initial": 35,
|
||||
"domain": [
|
||||
100
|
||||
],
|
||||
@@ -287,7 +288,7 @@
|
||||
"CURR_REC_START_3": {
|
||||
"description": "Pulse Mode Current Recording Period Start",
|
||||
"record_meta": true,
|
||||
"initial":35,
|
||||
"initial": 35,
|
||||
"domain": [
|
||||
100
|
||||
],
|
||||
@@ -298,7 +299,7 @@
|
||||
"CURR_REC_END_0": {
|
||||
"description": "Pulse Mode Current Recording Period End",
|
||||
"record_meta": true,
|
||||
"initial":90,
|
||||
"initial": 90,
|
||||
"domain": [
|
||||
100
|
||||
],
|
||||
@@ -309,7 +310,7 @@
|
||||
"CURR_REC_END_1": {
|
||||
"description": "Pulse Mode Current Recording Period End",
|
||||
"record_meta": true,
|
||||
"initial":99,
|
||||
"initial": 99,
|
||||
"domain": [
|
||||
100
|
||||
],
|
||||
@@ -320,7 +321,7 @@
|
||||
"CURR_REC_END_2": {
|
||||
"description": "Pulse Mode Current Recording Period End",
|
||||
"record_meta": true,
|
||||
"initial":95,
|
||||
"initial": 95,
|
||||
"domain": [
|
||||
100
|
||||
],
|
||||
@@ -331,7 +332,7 @@
|
||||
"CURR_REC_END_3": {
|
||||
"description": "Pulse Mode Current Recording Period End",
|
||||
"record_meta": true,
|
||||
"initial":95,
|
||||
"initial": 95,
|
||||
"domain": [
|
||||
100
|
||||
],
|
||||
@@ -478,13 +479,14 @@
|
||||
"Constant Current",
|
||||
"Cyclic Voltammetry",
|
||||
"Linear Sweep Voltammetry",
|
||||
"Chronoamperometric Graph",
|
||||
"Chronoamperometric",
|
||||
"Cali DAC - test",
|
||||
"Cali ADC - test",
|
||||
"Dev Mode",
|
||||
"Open Circuit Potential",
|
||||
"Pulse Sensing",
|
||||
"Differential Pulse Voltammetry (DPV)"
|
||||
"Differential Pulse Voltammetry (DPV)",
|
||||
"Chronopotentiometry"
|
||||
]
|
||||
},
|
||||
"VOLT_ORIGIN": {
|
||||
@@ -644,7 +646,7 @@
|
||||
},
|
||||
"CURRNET_LIMIT_VALUE": {
|
||||
"description": "Current value Setting",
|
||||
"initial":1500000,
|
||||
"initial": 1500000,
|
||||
"domain": [
|
||||
"Const_Current_Range"
|
||||
],
|
||||
@@ -685,6 +687,15 @@
|
||||
"expression": "VALUE"
|
||||
}
|
||||
},
|
||||
"CC_CP_SPEED": {
|
||||
"description": "charge speed for CC and CP mode",
|
||||
"initial": 1,
|
||||
"value": [
|
||||
"low",
|
||||
"normal",
|
||||
"high"
|
||||
]
|
||||
},
|
||||
"ADC_LEVEL_I_15": {
|
||||
"description": "ADC level",
|
||||
"record_meta": true,
|
||||
@@ -762,6 +773,17 @@
|
||||
"s",
|
||||
"ms"
|
||||
]
|
||||
},
|
||||
"TIME_DURATION": {
|
||||
"description": "Run duration",
|
||||
"record_meta": true,
|
||||
"initial": 0,
|
||||
"domain": [
|
||||
"TIME_MAX"
|
||||
],
|
||||
"value": {
|
||||
"expression": "VALUE"
|
||||
}
|
||||
}
|
||||
},
|
||||
"instruction": {
|
||||
@@ -812,7 +834,8 @@
|
||||
"7": "set_adc_gain_I",
|
||||
"8": "set_adc_gain_I",
|
||||
"9": "set_adc_gain_I",
|
||||
"13": "set_adc_gain_I"
|
||||
"13": "set_adc_gain_I",
|
||||
"16": "set_adc_gain_I"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -827,7 +850,8 @@
|
||||
"7": "set_adc_gain_Vin",
|
||||
"8": "set_adc_gain_Vin",
|
||||
"9": "set_adc_gain_Vin",
|
||||
"13": "set_adc_gain_Vin"
|
||||
"13": "set_adc_gain_Vin",
|
||||
"16": "set_adc_gain_Vin"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -845,7 +869,8 @@
|
||||
"9": "curve_const_vscan",
|
||||
"13": "curve_ocp",
|
||||
"14": "curve_pulse_sensing",
|
||||
"15": "curve_dpv"
|
||||
"15": "curve_dpv",
|
||||
"16": "curve_cp"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1043,6 +1068,7 @@
|
||||
"vb": "Const_Current_value",
|
||||
"vc": "VOLTSTOP_MAX",
|
||||
"vd": "VOLTSTOP_MIN",
|
||||
"ve": "CC_CP_SPEED",
|
||||
"pa": "ADC_LEVEL_I_15",
|
||||
"pb": "ADC_LEVEL_V_IN_15",
|
||||
"pc": "DAC_LEVEL_V_OUT_15",
|
||||
@@ -1054,7 +1080,31 @@
|
||||
"B>va;4B>vb;2B>vc;2B>vd;",
|
||||
"4b>pa;4b>pb;",
|
||||
"4b>pc;4b>pd;",
|
||||
"2B>pe"
|
||||
"2B>pe;",
|
||||
"4b>0b0001;4b>ve"
|
||||
]
|
||||
},
|
||||
"curve_cp": {
|
||||
"type": "RIS",
|
||||
"parameter": {
|
||||
"va": "Charge",
|
||||
"vb": "Const_Current_value",
|
||||
"vc": "VOLTSTOP_MAX",
|
||||
"vd": "VOLTSTOP_MIN",
|
||||
"ve": "CC_CP_SPEED",
|
||||
"pa": "ADC_LEVEL_I_15",
|
||||
"pb": "ADC_LEVEL_V_IN_15",
|
||||
"pc": "DAC_LEVEL_V_OUT_15",
|
||||
"pd": "CTRL_HIGH_Z_15",
|
||||
"pe": "SAMPLE_RATE"
|
||||
},
|
||||
"data": [
|
||||
"X07;",
|
||||
"B>va;4B>vb;2B>vc;2B>vd;",
|
||||
"4b>pa;4b>pb;",
|
||||
"4b>pc;4b>pd;",
|
||||
"2B>pe;",
|
||||
"4b>0b0000;4b>ve"
|
||||
]
|
||||
},
|
||||
"curve_cv3": {
|
||||
@@ -1219,9 +1269,18 @@
|
||||
],
|
||||
"pul_sen_start": {
|
||||
"type": "RIS",
|
||||
"parameter": {
|
||||
"pa": "ADC_LEVEL_I_15",
|
||||
"pb": "ADC_LEVEL_V_IN_15",
|
||||
"pc": "DAC_LEVEL_V_OUT_15",
|
||||
"pd": "CTRL_HIGH_Z_15",
|
||||
"pe": "SAMPLE_RATE"
|
||||
},
|
||||
"data": [
|
||||
"1X0D;",
|
||||
"1XFF"
|
||||
"1XFF",
|
||||
"4b>pa;4b>pb;",
|
||||
"4b>pc;4b>pd"
|
||||
]
|
||||
},
|
||||
"pul_sen_segment_0": {
|
||||
@@ -1304,9 +1363,7 @@
|
||||
"2B>va;4B>vb;1B>vc;1B>vd;1B>ve"
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
"curve_dpv":[
|
||||
"curve_dpv": [
|
||||
{
|
||||
"expression": "DPV_mode",
|
||||
"when": {
|
||||
@@ -1365,14 +1422,20 @@
|
||||
},
|
||||
"dpv_auto_mode_ins_final": {
|
||||
"type": "RIS",
|
||||
"parameter": {
|
||||
"pa": "ADC_LEVEL_I_15",
|
||||
"pb": "ADC_LEVEL_V_IN_15",
|
||||
"pc": "DAC_LEVEL_V_OUT_15",
|
||||
"pd": "CTRL_HIGH_Z_15",
|
||||
"pe": "SAMPLE_RATE"
|
||||
},
|
||||
"data": [
|
||||
"1X0E;",
|
||||
"1XFF"
|
||||
"1XFF;",
|
||||
"4b>pa;4b>pb;",
|
||||
"4b>pc;4b>pd"
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
|
||||
"dpv_advanced_mode": [
|
||||
"dpv_advanced_mode_ins_1",
|
||||
"dpv_advanced_mode_ins_2",
|
||||
@@ -1437,9 +1500,18 @@
|
||||
},
|
||||
"dpv_advanced_mode_ins_final": {
|
||||
"type": "RIS",
|
||||
"parameter": {
|
||||
"pa": "ADC_LEVEL_I_15",
|
||||
"pb": "ADC_LEVEL_V_IN_15",
|
||||
"pc": "DAC_LEVEL_V_OUT_15",
|
||||
"pd": "CTRL_HIGH_Z_15",
|
||||
"pe": "SAMPLE_RATE"
|
||||
},
|
||||
"data": [
|
||||
"1X10;",
|
||||
"1XFF"
|
||||
"1XFF;",
|
||||
"4b>pa;4b>pb;",
|
||||
"4b>pc;4b>pd"
|
||||
]
|
||||
},
|
||||
"dpv_engineering_mode": [
|
||||
@@ -1457,8 +1529,6 @@
|
||||
"dpv_engineering_mode_advanced": [
|
||||
"dpv_advanced_mode"
|
||||
],
|
||||
|
||||
|
||||
"VIS_CC_ZERO": [
|
||||
"_data_format('I4V4Z4T4')",
|
||||
"_disable_cache(False)",
|
||||
@@ -1499,6 +1569,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
find /home/pi -maxdepth 1 -type f -iname '*.out' -mtime +30 -delete
|
||||
Reference in New Issue
Block a user